How to use right_click_menu with multiple InputText elements? - python

How do you distinguish between multiple elements on the same window when using right_click_menu in PySimpleGUI? For example, with the code below, how do I tell which one of the two InputText elements I am trying to use the right_click_menu with? If I copy something to the clipboard and then right-click 'Paste' on one of the Input fields, the same data will appear in both fields. When I right-click on one of the InputText fields, how can I write the code to identify which one I'm on?:
import PySimpleGUI as sg
INPUT1 = 'INPUT1'
INPUT2 = 'INPUT2'
right_click_menu = ['',['Paste']]
layout = [
[sg.Text('Input1'), sg.InputText('', key='INPUT1', right_click_menu = right_click_menu)],
[sg.Text('Input2'), sg.InputText('', key='INPUT2', right_click_menu = right_click_menu)],
[sg.Button(' OK '), sg.Button(' Exit ')]
]
window = sg.Window('Multiple Elements', layout)
input1:sg.InputText = window[INPUT1]
input2:sg.InputText = window[INPUT2]
while True:
event, values = window.read()
if event in (' Exit ', None):
break
if event == 'Paste':
# How to tell whether I am right-clicking on INPUT1 or INPUT2?
# With just one Input element, I could just do this:
input1.Widget.insert(sg.tk.INSERT, window.TKroot.clipboard_get())
# What do I do when there is a second InputText field?
# Below won't work because I'll get the same text pasted into both fields.
input2.Widget.insert(sg.tk.INSERT, window.TKroot.clipboard_get())
if event == ' OK ':
pass
#Do blah
window.close()

Refer https://pysimplegui.readthedocs.io/en/latest/#keys-for-menus
A key is indicated by adding :: after a menu entry, followed by the key.
import PySimpleGUI as sg
INPUT1 = 'INPUT1'
INPUT2 = 'INPUT2'
right_click_menu = [['',[f'Paste::Paste {i}']] for i in range(2)]
layout = [
[sg.Text('Input1'), sg.InputText('', key='INPUT1', right_click_menu = right_click_menu[0])],
[sg.Text('Input2'), sg.InputText('', key='INPUT2', right_click_menu = right_click_menu[1])],
[sg.Button(' OK '), sg.Button(' Exit ')]
]
window = sg.Window('Multiple Elements', layout)
input1:sg.InputText = window[INPUT1]
input2:sg.InputText = window[INPUT2]
while True:
event, values = window.read()
if event in (' Exit ', None):
break
if event.startswith('Paste'):
element = input1 if event.split()[1] == '0' else input2
element.Widget.insert(sg.tk.INSERT, window.TKroot.clipboard_get())
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.

Is there a way to make the output text copyable (Python PySimpleGUI)

import random
import PySimpleGUI as sg
def password_generator(pw_len):
password = "".join(random.sample(chars, pw_len))
return password
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789|!\"£$%&/()=?^é*ç°§;:_è+òàù,.-[]##{}"
sg.theme('DarkPurple3')
layout = [[sg.Text('Password Generator', font=('Roboto', 20), justification='center')],
[sg.Text('Set password length: ', size=(15, 1)), sg.InputText(size=(15, 1), key='length')],
[sg.Text(size=(40,1), key='-OUTPUT-'), ],
[sg.Button('Generate', size=(15, 1)), sg.Button('Exit', size=(15, 1))]]
window = sg.Window('Password Generator', layout)
while(True):
event, values = window.read()
if event == 'Exit':
break
elif event == 'Generate' or sg.WINDOW_CLOSED:
pw_len = int(values['length'])
window['-OUTPUT-'].update('Your password: ' + password_generator(pw_len))
window.close()
Code Output: password generator
Im trying to make a password generator that generate a passoword from the length chosen by the user. Everything works but i cant make the output text copyable, can someone explain me how to do it? Thank you in advance
The content of Text element cannot be selected and not copyable.
Using Input or Multiline element for it and set disabled=True.
For example
sg.Input(size=(10,1), disabled=True, text_color=sg.theme_text_color(), disabled_readonly_background_color=sg.theme_text_element_background_color(), key='-OUTPUT-')

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

Categories