PySimpleGUI/DemoPrograms/Demo_Table_CSV_Display.pyw

146 lines
6.4 KiB
Python

#!/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,
key='-TABLE-', selected_row_colors='red on yellow', enable_events=True,
expand_x=True, expand_y=True,
enable_click_events=True)],
[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()