From 5be4ac34a904d04223612cf2a0dde2cb787ccd04 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Thu, 8 Sep 2022 16:40:39 -0400 Subject: [PATCH] New Demo Program - CSV Table Display --- DemoPrograms/Demo_Table_CSV_Display.pyw | 153 ++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 DemoPrograms/Demo_Table_CSV_Display.pyw diff --git a/DemoPrograms/Demo_Table_CSV_Display.pyw b/DemoPrograms/Demo_Table_CSV_Display.pyw new file mode 100644 index 00000000..df4d9442 --- /dev/null +++ b/DemoPrograms/Demo_Table_CSV_Display.pyw @@ -0,0 +1,153 @@ +#!/usr/bin/env python +import PySimpleGUI as sg +import csv +import os +import operator + +""" + Demo - Simple CSV Table Display + + Enables you to easily filter and sort tables that are in a CSV file format + + Choose your CSV file and then a table will be displayed. + Clicking on a heading will sort on that column if no value is entered for the filter. + If a filter value is entered and then a heading is clicked, then only rows matchines the filter in that column as are displayed + The filtering is not case sensative so no need to worry about exact matches + Use the checkbox to specify ascending or descending sorting + + The first row in your table needs to be the Column Names + + Copyright 2022 PySimpleGUI +""" + +sg.theme('Dark gray 13') + +CSV_FILE = sg.popup_get_file('CSV File to Display', file_types=(("CSV Files", "*.csv"),), initial_folder=os.path.dirname(__file__), history=True) +if CSV_FILE is None: + sg.popup_error('Canceling') + exit() + +csv.field_size_limit(2147483647) # enables huge tables + +def sort_table(table, cols, descending=False): + """ sort a table by multiple columns + table: a list of lists (or tuple of tuples) where each inner list + represents a row + cols: a list (or tuple) specifying the column numbers to sort by + e.g. (1,0) would sort by column 1, then by column 0 + """ + + for col in reversed(cols): + try: + table = sorted(table, key=operator.itemgetter(col), reverse=descending) + except Exception as e: + sg.popup_error('Error in sort_table', 'Exception in sort_table', e) + return table + + +def read_csv_file(filename): + data = [] + header_list = [] + if filename is not None: + try: + with open(filename, encoding='UTF-16') as infile: + reader = csv.reader(infile,delimiter='\t') + # reader = fix_nulls(filename) + header_list = next(reader) + try: + data = list(reader) # read everything else into a list of rows + except Exception as e: + print(e) + sg.popup_error('Error reading file', e) + return None, None + except: + with open(filename, encoding='utf-8') as infile: + reader = csv.reader(infile, delimiter=',') + # reader = fix_nulls(filename) + header_list = next(reader) + try: + data = list(reader) # read everything else into a list of rows + except Exception as e: + with open(filename) as infile: + reader = csv.reader(infile, delimiter=',') + # reader = fix_nulls(filename) + header_list = next(reader) + try: + data = list(reader) # read everything else into a list of rows + except Exception as e: + print(e) + sg.popup_error('Error reading file', e) + return None, None + return data, header_list + + +def main(): + data, header_list = read_csv_file(CSV_FILE) + + sg.popup_quick_message('Building your main window.... one moment....', background_color='#1c1e23', text_color='white', keep_on_top=True, font='_ 30') + + # ------ Window Layout ------ + layout = [ [sg.Text('Click a heading to sort on that column or enter a filter and click a heading to search for matches in that column')], + [sg.Text(f'{len(data)} Records in table', font='_ 18')], + [sg.Text(k='-RECORDS SHOWN-', font='_ 18')], + [sg.T('Filter:'), sg.Input(k='-FILTER-', focus=True, tooltip='Not case sensative\nEnter value and click on a col header'), + sg.B('Reset Table', tooltip='Resets entire table to your original data'), + sg.Checkbox('Sort Descending', k='-DESCENDING-'), sg.Push()], + [sg.Table(values=data, headings=header_list, max_col_width=25, + auto_size_columns=True, + display_row_numbers=True, + vertical_scroll_only=True, + justification='right', + num_rows=50, + # alternating_row_color='lightyellow', + key='-TABLE-', + selected_row_colors='red on yellow', + enable_events=True, + expand_x=True, + expand_y=True, + enable_click_events=True, # Comment out to not enable header and other clicks + ),], + [sg.Sizegrip()]] + + # ------ Create Window ------ + window = sg.Window('CSV Table Display', layout, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, resizable=True) + + original_data = data # save a copy of the data + # ------ Event Loop ------ + while True: + event, values = window.read() + # print(event, values) + if event in (sg.WIN_CLOSED, 'Exit'): + break + if event[0] == '-TABLE-': + # if isinstance(event, tuple): + filter_value = values['-FILTER-'] + # TABLE CLICKED Event has value in format ('-TABLE=', '+CLICKED+', (row,col)) + if event[0] == '-TABLE-': + if event[2][0] == -1 and event[2][1] != -1: # Header was clicked and wasn't the "row" column + col_num_clicked = event[2][1] + # if there's a filter, first filter based on the column clicked + if filter_value not in (None, ''): + new_data = [] + for line in data: + if filter_value.lower() in line[col_num_clicked].lower(): + new_data.append(line) + data = new_data + new_table = sort_table(data, (col_num_clicked, 0), values['-DESCENDING-']) + window['-TABLE-'].update(new_table) + data = new_table + window['-RECORDS SHOWN-'].update(f'{len(new_table)} Records shown') + window['-FILTER-'].update('') # once used, clear the filter + elif event == 'Reset Table': + data = original_data + window['-TABLE-'].update(data) + window['-RECORDS SHOWN-'].update(f'{len(data)} Records shown') + elif event == 'Edit Me': + sg.execute_editor(__file__) + elif event == 'Version': + sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True) + window.close() + + +if __name__ == '__main__': + main()