How to make progress bar work using PySimpleGUI in Python? - python

I am using PySimpleGui's one-line progress meter to show the files processed while my main function is running. It is working for the most part, however when I change the parameter from value of 1000 to my variable total_files it just flickers and never stays on screen. It's only when I keep the parameter to a value of 1000 does the screen stay put...but I want to just show the progress of the true amount of files processed.
For example, I have 8 files I am processing as a test. I want the progress bar to stay on the screen and just show those 8 files processed. Does anyone know what I'm doing wrong?
Documentation on progress meter:
https://www.pysimplegui.org/en/latest/#progress-meters
for i in range(1,total_files):
sg.one_line_progress_meter('Loading...', i+1, 1000, 'key','Files Processed', orientation="horizontal")
Below is my code:
import PySimpleGUI as sg, pandas as pd, os, time
from pathlib import (
Path,
)
def convert_to_csv(input_folder, output_folder, dataframe2, dataframe3):
start = time.time()
# Parentsupplierbrand mapping file. Change the filepath to where the file is stored on your computer.
df2 = pd.read_csv(rf"{dataframe2}", sep="|")
# KCS Section Codes mapping file. Change the filepath to where the file is stored on your computer.
df3 = pd.read_excel(rf"{dataframe3}")
# loop through only text files in the input folder and convert data
for files in os.listdir(input_folder):
if files.endswith(".txt"):
df1 = pd.read_csv(rf"{input_folder}/{files}", sep="\t", encoding="latin-1")
df1["BrandName"] = df1["Comp_Brand"].apply(
xlookup, args=(df2["BrandID"], df2["BrandName"])
)
# map columns to the desired headers
df1.rename(
columns={
"BrandName": "Competitor_Name",
"Comp_PN": "Competitor_PartNumber",
"CQ_Brand": "AAIA_Code",
"CQ_PN": "PartNumber",
},
inplace=True,
)
remove_columns = df1.loc[
:, ~df1.columns.isin(["Comp_Brand", "CQ_Desc", "PartID"])
]
new_columns = [
"Competitor_Name",
"Competitor_PartNumber",
"AAIA_Code",
"PartNumber",
]
table_output = remove_columns[new_columns]
# below code for constructing file name convention
kcs_section_codes = df3.values.tolist()
stringtwo = table_output.loc[0][2] # stringtwo is the AAIA_Code
for code in kcs_section_codes:
if code[0] == stringtwo:
# stringone is the matching value under BrandName in the KCS_Section_Codes file
# by looking up AAIA_Code from the initial file in the BrandCode column from the KCS_Section_Codes file
stringone = "".join(
e for e in code[1] if e.isalnum()
) # replace any character that is not a letter or number with an empty character.
filename = rf"{stringone}-{stringtwo}_Interchanges.txt"
outputfile = rf"{output_folder}/{filename}"
table_output.to_csv(
outputfile,
sep="\t",
index=False,
columns=[
"Competitor_Name",
"Competitor_PartNumber",
"AAIA_Code",
"PartNumber",
],
)
# progress bar
total_files = sum([len(files) for r, d, files in os.walk(output_folder)])
for i in range(1, total_files):
sg.one_line_progress_meter(
"Loading...",
i + 1,
1000,
"key",
"Files Processed",
orientation="horizontal",
)
# code below for returning stats of main function in a popup window
total_files = sum([len(files) for r, d, files in os.walk(output_folder)])
end = time.time()
result_time = end - start
time_formatted = time.strftime("%H:%M:%S", time.gmtime(result_time))
sg.popup_no_titlebar(
f"Done! :)\nTotal Files Processed: {total_files}\nExecution Time (hh:mm:ss): {time_formatted}"
)
def xlookup(lookup_value, lookup_array, return_array, if_not_found: str = ""):
match_value = return_array.loc[lookup_array == lookup_value]
if match_value.empty:
return f'"{lookup_value}" not found!' if if_not_found == "" else if_not_found
else:
return match_value.tolist()[0]
def is_valid_path(filepath):
if filepath and Path(filepath).exists():
return True
sg.popup_error("Filepath not correct")
return False
def gui():
sg.theme("DarkBlue3") # Add a touch of color
# All the stuff inside your window.
layout = [
[
sg.Text("Input Folder: "),
sg.Input(key="-IN-"),
sg.FolderBrowse(),
],
[sg.Text("Output Folder: "), sg.Input(key="-OUT-"), sg.FolderBrowse()],
[
sg.Text("Data Mapping File: Parent Supplier Brand"),
sg.Input(key="-DF2-"),
sg.FileBrowse(),
],
[
sg.Text("Data Mapping File: KCS Section Codes"),
sg.Input(key="-DF3-"),
sg.FileBrowse(),
],
[sg.Exit(), sg.Button("Convert to CSV")],
]
# Create the Window
window = sg.Window("Data Conversion", layout)
# Event Loop to process "events" and get the "values" of the inputs
while True:
event, values = window.read()
if (
event == sg.WIN_CLOSED or event == "Cancel"
): # if user closes window or clicks cancel
break
if event in (sg.WIN_CLOSED, "Exit"):
break
if (
event == "Convert to CSV"
): # if user clicks covert to csv button then call convert_to_csv function using inputs
if (is_valid_path(values["-IN-"])) and (is_valid_path(values["-OUT-"])):
convert_to_csv(
input_folder=values["-IN-"],
output_folder=values["-OUT-"],
dataframe2=values["-DF2-"],
dataframe3=values["-DF3-"],
)
window.close()
if __name__ == "__main__":
gui()

Following code update the progressbar without any time delay, so it will show in short time and reach the maximum value immediately, then the window for progressbar closed.
for i in range(1, total_files):
sg.one_line_progress_meter(
"Loading...",
i + 1,
1000,
"key",
"Files Processed",
orientation="horizontal",
)
Reduce your code to ask question, it will help people to help.
Demo code:
from time import sleep
import PySimpleGUI as sg
from threading import Thread
def job(window):
window.write_event_value("Update", 0)
for i in range(1, 11):
sleep(0.2) # Simulate each step in the job
window.write_event_value("Update", i) # Cannot update GUI not in the main thread
window.write_event_value("Done", None)
sg.theme("DarkBlue3")
layout = [[sg.Button("Submit"), sg.Button("Exit")],]
window = sg.Window("Title", layout)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, "Exit"):
break
elif event == "Submit":
thread = Thread(target=job, args=(window, ), daemon=True)
thread.start()
elif event == "Update":
i = values[event]
sg.one_line_progress_meter("Loading...", i, 10, "key", "Files Processed", orientation="horizontal")
elif event == "Done":
print('Job done !')
window.close()

Related

How can I pass multiple user input values from PySimpleGUI to a function, then output result to PySimpleGUI?

GitHub for this project
I am building a financial trading profit/loss calculator. I have built the script to function in the command line interface (main_cli.py).
Now, I am trying to convert to a desktop GUI via PySimpleGUI, but am having difficulty with passing multiple user input values from PySimpleGUI (main_gui.py) to the calculator function (functions.py), to then output the result in the same PySimpleGUI window.
When I run the main_gui.py script, it still asks for user_input in the CLI.
Just pass values to your function, then convert all fields into your data for the function to calculate the result, and return it back to event loop to update the GUI.
Note: No any entry validation in the code.
import PySimpleGUI as sg
def calculate(values):
ticker = ticker_list[[values[("Ticker", i)] for i in range(len(tickers))].index(1)]
tick_size, contract_amount = tickers[ticker]
position = "Long" if values[("Position", 0)] else "Short"
purchase_price = float(values['Entry'])
selling_price = float(values['Exit'])
contract_size = float(values['Contract'])
tick_value = (((purchase_price - selling_price)*contract_size)/tick_size)
dollar_value = (tick_value*contract_amount)
if position == "Long":
tick_value = abs(tick_value)
dollar_value = abs(dollar_value)
return tick_value, dollar_value
tickers = {
'ES' : (0.25, 12.50),
'NQ' : (0.25, 5.00),
'RTY': (0.10, 5.00),
'YM' : (1.00, 5.00),
'GC' : (0.10, 10.00),
'CL' : (0.01, 10.00),
}
ticker_list = list(tickers.keys())
positions = ['Long', 'Short']
keys = {
'Entry':'Entry price',
'Exit':'Exit price',
'Contract':'Contract size',
}
sg.theme('Dark')
sg.set_options(font=('Helvetica', 10))
layout = [
[sg.Text("Ticker:", size=10)] + [sg.Radio(tick, "Radio 1", key=("Ticker", i)) for i, tick in enumerate(tickers)],
[sg.Text("Position:", size=10)] + [sg.Radio(pos, "Radio 2", key=("Position", i)) for i, pos in enumerate(positions)]] + [
[sg.Text(text, size=10), sg.InputText(size=10, expand_x=True, key=key)] for key, text in keys.items()] + [
[sg.Text("Result"), sg.Text(text_color="white", key="Output")],
[sg.Push(), sg.Button("Reset"), sg.Button("Calculate", button_color=('white', '#007339'))],
]
window = sg.Window('Futures Profit/Loss Calculator', layout=layout)
while True:
event, values = window.read()
print(event, values)
if event == sg.WIN_CLOSED:
break
elif event == "Calculate":
tick_value, dollar_value = calculate(values)
result = f"ticks: {tick_value} | profit/loss: ${dollar_value}"
window['Output'].update(result)
elif event == "Reset":
for key in keys:
window[key].update('')
window.close()
If this function take long time to finish the caluclation, you can call method window.perform_long_operation to run your function, Lambda expression can help to pass the arguments here, then update your GUI when the event '-FUNCTION COMPLETED-' generated, like
elif event == "Calculate":
window.perform_long_operation(lambda x=values:calculate(x), '-FUNCTION COMPLETED-')
elif event == '-FUNCTION COMPLETED-':
tick_value, dollar_value = values[event]
result = f"ticks: {tick_value} | profit/loss: ${dollar_value}"
window['Output'].update(result)

Is there a way to clear a tree element in PySimpleGUI before adding new data?

I have a window with a tree element that shows files and folders when I enter a path. I need this tree to clear if I enter a new path. At the moment the tree appends the files/folders from each path I enter. Is there a way to refresh the tree element before entering new information into it?
My code is similar to this:
import PySimpleGUI as sg
import os
starting_path = os.getcwd()
treedata = sg.TreeData()
folder_icon = b'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAABnUlEQVQ4y8WSv2rUQRSFv7vZgJFFsQg2EkWb4AvEJ8hqKVilSmFn3iNvIAp21oIW9haihBRKiqwElMVsIJjNrprsOr/5dyzml3UhEQIWHhjmcpn7zblw4B9lJ8Xag9mlmQb3AJzX3tOX8Tngzg349q7t5xcfzpKGhOFHnjx+9qLTzW8wsmFTL2Gzk7Y2O/k9kCbtwUZbV+Zvo8Md3PALrjoiqsKSR9ljpAJpwOsNtlfXfRvoNU8Arr/NsVo0ry5z4dZN5hoGqEzYDChBOoKwS/vSq0XW3y5NAI/uN1cvLqzQur4MCpBGEEd1PQDfQ74HYR+LfeQOAOYAmgAmbly+dgfid5CHPIKqC74L8RDyGPIYy7+QQjFWa7ICsQ8SpB/IfcJSDVMAJUwJkYDMNOEPIBxA/gnuMyYPijXAI3lMse7FGnIKsIuqrxgRSeXOoYZUCI8pIKW/OHA7kD2YYcpAKgM5ABXk4qSsdJaDOMCsgTIYAlL5TQFTyUIZDmev0N/bnwqnylEBQS45UKnHx/lUlFvA3fo+jwR8ALb47/oNma38cuqiJ9AAAAAASUVORK5CYII='
file_icon = b'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAABU0lEQVQ4y52TzStEURiHn/ecc6XG54JSdlMkNhYWsiILS0lsJaUsLW2Mv8CfIDtr2VtbY4GUEvmIZnKbZsY977Uwt2HcyW1+dTZvt6fn9557BGB+aaNQKBR2ifkbgWR+cX13ubO1svz++niVTA1ArDHDg91UahHFsMxbKWycYsjze4muTsP64vT43v7hSf/A0FgdjQPQWAmco68nB+T+SFSqNUQgcIbN1bn8Z3RwvL22MAvcu8TACFgrpMVZ4aUYcn77BMDkxGgemAGOHIBXxRjBWZMKoCPA2h6qEUSRR2MF6GxUUMUaIUgBCNTnAcm3H2G5YQfgvccYIXAtDH7FoKq/AaqKlbrBj2trFVXfBPAea4SOIIsBeN9kkCwxsNkAqRWy7+B7Z00G3xVc2wZeMSI4S7sVYkSk5Z/4PyBWROqvox3A28PN2cjUwinQC9QyckKALxj4kv2auK0xAAAAAElFTkSuQmCC'
def add_files_in_folder(parent, dirname):
files = os.listdir(dirname)
for f in files:
fullname = os.path.join(dirname, f)
if os.path.isdir(fullname): # if it's a folder, add folder and recurse
treedata.Insert(parent, fullname, f, values=[], icon=folder_icon)
add_files_in_folder(fullname, fullname)
else:
treedata.Insert(parent, fullname, f, values=[os.stat(fullname).st_size], icon=file_icon)
def main_window():
column_one = sg.Column([
[sg.Tree(data=treedata,
headings=['Size', ],
auto_size_columns=True,
select_mode=sg.TABLE_SELECT_MODE_EXTENDED,
num_rows=20,
col0_width=40,
key='-TREE-',
show_expanded=False,
enable_events=True,
expand_x=True,
expand_y=True,
), ],
[sg.B('Open', k='-OPEN-'), sg.B('Add', k='-ADD-')],
])
layout = [
[sg.T('Some text')],
[sg.HorizontalSeparator()],
[sg.T('Select the files / folders')],
[column_one],
[sg.B('Cancel', k='-EXIT-')]
]
return sg.Window('some window', layout, resizable=True, finalize=True)
window = main_window()
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, '-EXIT-'):
break
print(event, values)
if event == '-OPEN-':
starting_path = sg.popup_get_folder('Folder to display')
add_files_in_folder('', starting_path)
window['-TREE-'].update(values=treedata)
window.close()
Any help is appreciated!
Following code demo how to create/clear/append data to treedata for Table element. Here all item added as children of root ''.
import PySimpleGUI as sg
def add_data(treedata, offset):
default = [[str((i+1)*(j+offset+1)) for i in range(9)] for j in range(9)]
for i, value in enumerate(default):
treedata.insert('', i+offset+1, i+offset+1, value)
return treedata
treedata = sg.TreeData()
treedata = add_data(treedata, 0)
index = 10
layout = [
[sg.Tree(
data=treedata,
headings=[str(i+1) for i in range(9)],
col0_heading='9x9',
col0_width=5,
num_rows=20,
key='-TREE-',
)],
[sg.Button('Clear'), sg.Button('Reset'), sg.Button('Append')],
]
window = sg.Window('Title', layout, finalize=True)
tree = window['-TREE-']
tree.Widget.column('#0', anchor='e')
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == 'Clear':
treedata = sg.TreeData()
tree.update(values=treedata)
index = 0
elif event == 'Reset':
treedata = add_data(sg.TreeData(), 0)
tree.update(values=treedata)
index = 10
elif event == 'Append':
treedata = add_data(treedata, index)
tree.update(values=treedata)
index += 10
window.close()

How to keep PySimpleGUI from seeing "SaveAs" button from being treated like a "key="

I made a little barcode making GUI project for fun and when the "clear" function takes place it deletes the text in the "SaveAs" button. All other buttons have no issues with this and I was trying to avoid putting a line of code for each key to clear. I made some minor changes to the code before pasting it here so I know that the if len(values['Area']) > 0: window.perform_long_operation(lambda: get_barcode(),'Saved Pop') needs to be updated as of now this would always be true. I forgot when I added the combo box and set a default value you this would be true. Also if anyone could tell me why I have to have "lambda" above although I'm not passing a parameter?
Update 10:45 CT - I compiled the program and and the program is not saving correctly/not at all. If I put in a name like "test1" it should be saved as test1.pdf at the location I specified. When I go to the location the file does not exist but if I search the C drive it shows up but it cannot be located almost as if it is deleted the moment it is created. This only happens when I compile the program.
import PySimpleGUI as sg
import string
pixel_size = [64,128,192,256]
tab1_layout = [
[sg.Text('Area Letter:', size=(28,1)), sg.Combo(list(string.ascii_uppercase[:]), default_value= 'A',key='Area')],
[sg.Text('Start Number Range:', size=(28,1)), sg.InputText(key='start_number')],
[sg.Text('End Number Range:', size=(28,1)), sg.InputText(key='end_number')],
]
tab2_layout = [
[sg.Text('Single Barcode ABC123:', size=(28,1)), sg.InputText(key='single_bc')],
[sg.Text('Number of Single Barcode Replicas:', size=(28,1)), sg.InputText(default_text= 1,key='number_of_barcodes')],
]
layout = [
[sg.Text('Barcode Font Size in Pixels'), sg.Combo(pixel_size, default_value=64, s=(15,22), enable_events=True, readonly=True, key='PIXEL')],
[sg.TabGroup([[sg.Tab('Range of Barcodes', tab1_layout), sg.Tab('Single Barcodes', tab2_layout)]])],
[[sg.Input(key = 'input_loc'), sg.SaveAs(target= 'input_loc' , default_extension= '.pdf')]],
[sg.Submit(),sg.Button('Clear'), sg.Exit(),]
]
def get_barcode():
print('get barcode')
window = sg.Window('Barcode Maker', layout, font=("Helvetica", 12))
def clear_input():
for key in values:
window[key]('')
window['Area']('A')
window['PIXEL'](64)
window['number_of_barcodes'](1)
return None
while True:
event, values = window.read(timeout = 10)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Clear':
clear_input()
if event == 'Submit':
save_location = values['input_loc']
if len(values['Area']) > 0:
window.perform_long_operation(lambda: get_barcode(),'Saved Pop')
elif len(values['single_bc']) >=1:
bc_size = values['PIXEL']
single_code = values['single_bc'].upper()
replicas = values['number_of_barcodes']
replicas = int(replicas)
start_rep = 0
while start_rep < replicas:
layout_borb1.add(Barcode(data=single_code, type=BarcodeType.CODE_128, width=Decimal(bc_size), height=Decimal(bc_size),))
with open(save_location, 'wb') as pdf_file_handle:
PDF.dumps(pdf_file_handle, doucment)
start_rep +=1
clear_input()```
Following code in your function clear_input clear the value of all the elements which with the key in values.
for key in values:
window[key]('')
The key of element in values not only Input, Combo elements, also Tab, TabGroup and Button elements.
>>> values
{'PIXEL': 64, 'Area': 'A', 'start_number': '', 'end_number': '', 'single_bc': '', 'number_of_barcodes': '1', 0: 'Range of Barcodes', 'input_loc': '', 'Save As...': ''}
The key in values include the key of button Save As..., that's why the text of this button also cleared.
There should be a rule to specify which elements to be cleared, like
for key, element in window.key_dict.items():
if isinstance(element, (sg.Input, sg.Combo)):
element.update(value='')
Using lambda to define a function without arguments is almost the same as the function name.
window.perform_long_operation(get_barcode,'Saved Pop')
If possible, reduce your code to only with related issues and an executable code, like
import PySimpleGUI as sg
import string
pixel_size = [64,128,192,256]
tab1_layout = [
[sg.Text('Area Letter:', size=(28,1)), sg.Combo(list(string.ascii_uppercase[:]), default_value= 'A',key='Area')],
[sg.Text('Start Number Range:', size=(28,1)), sg.InputText(key='start_number')],
[sg.Text('End Number Range:', size=(28,1)), sg.InputText(key='end_number')],
]
tab2_layout = [
[sg.Text('Single Barcode ABC123:', size=(28,1)), sg.InputText(key='single_bc')],
[sg.Text('Number of Single Barcode Replicas:', size=(28,1)), sg.InputText(default_text= 1,key='number_of_barcodes')],
]
layout = [
[sg.Text('Barcode Font Size in Pixels'), sg.Combo(pixel_size, default_value=64, s=(15,22), enable_events=True, readonly=True, key='PIXEL')],
[sg.TabGroup([[sg.Tab('Range of Barcodes', tab1_layout), sg.Tab('Single Barcodes', tab2_layout)]])],
[[sg.Input(key = 'input_loc'), sg.SaveAs(target= 'input_loc' , default_extension= '.pdf')]],
[sg.Submit(),sg.Button('Clear'), sg.Exit(),]
]
def get_barcode():
print("get bar_code function called")
window = sg.Window('Barcode Maker', layout, font=("Helvetica", 12))
def clear_input():
for key, element in window.key_dict.items():
if isinstance(element, (sg.Input, sg.Combo)):
element.update(value='')
window['Area']('A')
window['PIXEL'](64)
window['number_of_barcodes'](1)
return
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Clear':
clear_input()
elif event == 'Submit':
save_location = values['input_loc']
if len(values['Area']) > 0:
window.perform_long_operation(get_barcode,'Saved Pop')
clear_input()
elif event == 'Saved Pop':
print("get_barcode complete")
window.close()
For some conditions, like element.taget==(None, None) or element.Key is not None, "chooser" Buttons will hold the information of selection in the dictionary values which returned from window.read().
"chooser" Buttons with any one of following button_type:
BUTTON_TYPE_COLOR_CHOOSER
BUTTON_TYPE_SAVEAS_FILE
BUTTON_TYPE_BROWSE_FILE
BUTTON_TYPE_BROWSE_FILES
BUTTON_TYPE_BROWSE_FOLDER
BUTTON_TYPE_CALENDAR_CHOOSER
Function SaveAs defined as a Button element with file_types=FILE_TYPES_ALL_FILES.

Popups in PySimpleGUI for displaying a list of objects and then place selected object in an inputbox

I am trying to create a popup that is triggered when the SELECTION button is pressed. It should show a column from a particular excel sheet, users can select entries and this will populate the STRATEGY input box.
I have this code:
import PySimpleGUI as sg
import pandas as pd
columns = ["STRATEGY", "FOR_SHOW"]
param = (20,3) # size of the main window
df_strategies = pd.read_excel(r'path/Strategies.xlsx', 'Strategies')
def GUI(df_strategies):
sg.theme('Dark Brown 1')
listing = [sg.Text(u, size = param) for u in columns]
core = [
sg.Input(size = param, key='STRATEGY'),
sg.Listbox(list(df_strategies['STRATEGIES ']), size=(20,2), enable_events=False)
]
mesh = [[x,y] for (x,y) in list(zip(listing, core))]
layout =[[sg.Button("SEND")]]+ mesh
mesh[0].append(sg.Button('SELECTION'))
window = sg.Window('GUI', layout, font='Courier 12').Finalize()
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == "SEND":
break
elif event == 'SELECTION':
sg.popup_scrolled(list(df_strategies['STRATEGIES ']))
else:
print("OVER")
window.close()
GUI(df_strategies)
Unfortunately this sg.popup_scrolled only shows the text itself with no option to actually select.
Essentially, i am trying to create something that is very similar to the FOR_SHOW listbox apart from it is shown in a Popup and the selected entry populates the STRATEGY input box.
You will have to create own popup from scratch - instead of using popup_scrolled - and then you can use Listbox and/or other widget(s).
import PySimpleGUI as sg
import pandas as pd
# --- functions ---
def GUI_POPUP(text, data):
layout = [
[sg.Text(text)],
[sg.Listbox(data, size=(20,5), key='SELECTED')],
[sg.Button('OK')],
]
window = sg.Window('POPUP', layout).Finalize()
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == 'OK':
break
else:
print('OVER')
window.close()
print('[GUI_POPUP] event:', event)
print('[GUI_POPUP] values:', values)
if values and values['SELECTED']:
return values['SELECTED']
def GUI_MAIN(data):
strategies = data['STRATEGIES'].tolist()
for_show = data['FOR_SHOW'].tolist()
layout = [
[sg.Button('SEND')],
[sg.Text('STRATEGY', size=(20,1)), sg.Input(size=(20,1), key='STRATEGY'), sg.Button('SELECTION')],
[sg.Text('FOR_SHOW', size=(20,1)), sg.Listbox(for_show, size=(20,5), key='FOR_SHOW', enable_events=False)],
]
window = sg.Window('GUI', layout).Finalize()
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == 'SEND':
break
elif event == 'SELECTION':
selected = GUI_POPUP('STRATEGY', strategies)
print('selected:', selected)
if selected:
window['STRATEGY'].update(selected[0])
mask = (data['STRATEGIES'] == selected[0])
for_show = data['FOR_SHOW'][ mask ].tolist()
window['FOR_SHOW'].update(for_show)
else:
print('OVER')
window.close()
print('[GUI_MAIN] event:', event)
print('[GUI_MAIN] values:', values)
# --- main ---
if __name__ == '__main__':
#df_strategies = pd.read_excel(r'path/Strategies.xlsx', 'Strategies')
df_strategies = pd.DataFrame({
'STRATEGIES': ['A', 'B', 'C'],
'FOR_SHOW': [1, 2, 3]
})
GUI_MAIN(df_strategies)
BTW:
Using print( sg.__file__ ) you can get path to source code
and you can check how popup_scrolled was created and use it to create own popups.
On my Linux Mint I have source code in
/usr/local/lib/python3.8/dist-packages/PySimpleGUI

Python Script/Programm to Search & Display an Image from Local Folder

I'm an absolute beginner in python and trying to build a script/programm that searches images from a folder by their name (meaning that it would need a search bar) and displays these image in the same window.
Think of it as sort of a phonebook where I have stored 100 names and pictures and I want to type in "Henry" to see Henry's picture.
On my search for a software that achieves this, I've come accross something similar:
# img_viewer.py
import PySimpleGUI as sg
import os.path
# First the window layout in 2 columns
file_list_column = [
[
sg.Text("Image Folder"),
sg.In(size=(25, 1), enable_events=True, key="-FOLDER-"),
sg.FolderBrowse(),
],
[
sg.Listbox(
values=[], enable_events=True, size=(40, 20), key="-FILE LIST-"
)
],
]
# For now will only show the name of the file that was chosen
image_viewer_column = [
[sg.Text("Choose an image from list on left:")],
[sg.Text(size=(40, 1), key="-TOUT-")],
[sg.Image(key="-IMAGE-")],
]
# ----- Full layout -----
layout = [
[
sg.Column(file_list_column),
sg.VSeperator(),
sg.Column(image_viewer_column),
]
]
window = sg.Window("Image Viewer", layout)
# Run the Event Loop
while True:
event, values = window.read()
if event == "Exit" or event == sg.WIN_CLOSED:
break
# Folder name was filled in, make a list of files in the folder
if event == "-FOLDER-":
folder = values["-FOLDER-"]
try:
# Get list of files in folder
file_list = os.listdir(folder)
except:
file_list = []
fnames = [
f
for f in file_list
if os.path.isfile(os.path.join(folder, f))
and f.lower().endswith((".png", ".gif"))
]
window["-FILE LIST-"].update(fnames)
elif event == "-FILE LIST-": # A file was chosen from the listbox
try:
filename = os.path.join(
values["-FOLDER-"], values["-FILE LIST-"][0]
)
window["-TOUT-"].update(filename)
window["-IMAGE-"].update(filename=filename)
except:
pass
window.close()
This is almost perfect, but I would need to implement a search bar instead of clicking through the names as I will have ~20.000 entries and clicking through them is just not viable.
Any idea how I would go about achieving this?
Just one file after filtered not so good, My suggestion
Add one more sg.InputText() as filter for filename.ext
Event loop
Event for New holder or new filter
Get list of filenames of all files in folder, maybe also filtered by PNG, GIF.
Build new list to update 'sg.Listbox` if filter_string.lower() in filename_lower()
Event for listbox
Update sg.Image by clicked filename in listbox.
Update - Working sample code
from pathlib import Path
from io import BytesIO
from PIL import Image
import PySimpleGUI as sg
def update_listbox(listbox_element, folder, extension, substring):
path = Path(folder)
filter_ = substring.lower()
lst = []
if folder != '' and path.is_dir():
files = list(path.glob("*.*"))
lst = [file for file in files if file.suffix.lower() in extension
and filter_ in str(file).lower() and file.is_file()]
listbox_element.update(lst)
def update_image(image_element, filename):
im = Image.open(filename)
w, h = size_of_image
scale = max(im.width/w, im.height/h)
if scale <= 1:
image_element.update(filename=filename)
else:
im = im.resize((int(im.width/scale), int(im.height/scale)),
resample=Image.CUBIC)
with BytesIO() as output:
im.save(output, format="PNG")
data = output.getvalue()
image_element.update(data=data)
sg.theme('Dark')
sg.set_options(font=('Courier New', 11))
w, h = size_of_image = (700, 600)
layout_top = [
[sg.InputText(enable_events=True, key='-FOLDER-'),
sg.FolderBrowse('Browse', size=(7, 1), enable_events=True)],
[sg.InputText(enable_events=True, key='-FILTER-'),
sg.Button('Search', size=(7, 1))],
]
layout_bottom = [
[sg.Listbox([], size=(52, 30), enable_events=True,
select_mode=sg.LISTBOX_SELECT_MODE_SINGLE, key='-LISTBOX-')],
]
layout_left = [
[sg.Column(layout_top, pad=(0, 0))],
[sg.Column(layout_bottom, pad=(0, 0))],
]
layout_right = [[sg.Image(background_color='green', key='-IMAGE-')]]
layout = [
[sg.Column(layout_left), sg.Column(layout_right, pad=(0, 0), size=(w+15, h+15), background_color='blue', key='-COLUMN-')],
]
window = sg.Window("PNG/GIF Viewer", layout, finalize=True)
window['-IMAGE-'].Widget.pack(fill='both', expand=True)
window['-IMAGE-'].Widget.master.pack(fill='both', expand=True)
window['-IMAGE-'].Widget.master.master.pack(fill='both', expand=True)
window['-COLUMN-'].Widget.pack_propagate(0)
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
# print(event, values)
if event in ('-FOLDER-', '-FILTER-', 'Search'):
update_listbox(window['-LISTBOX-'], values['-FOLDER-'],
('.png', '.gif'), values['-FILTER-'])
elif event == '-LISTBOX-':
lst = values['-LISTBOX-']
if lst != []:
update_image(window['-IMAGE-'], values['-LISTBOX-'][0])
window.close()

Categories