PySimpleGUI breaks pynput. How do I combine them? - python

As soon as I've pressed the alt+ctrl+t shortcut once and closed the pop-up window, pynput will react to any double click of the shortcut keys. For example, if I press "t" twice, it will open the window again (even though alt+ctrl are not pressed).
Is there a way to combine pynput's global hotkeys with PySimpleGUI?
import PySimpleGUI as sg
from pynput import keyboard
def f_test():
layout = [[sg.Text("Hello from PySimpleGUI")], [sg.Button("OK")]]
window = sg.Window("Demo", layout)
while True:
event, values = window.read()
if event == "OK" or event == sg.WIN_CLOSED:
break
window.close()
with keyboard.GlobalHotKeys({
'<alt>+<ctrl>+t': f_test}) as h:
h.join()

I tried it with different threads for PySimpleGUI and pynput.
import threading
import PySimpleGUI as sg
from pynput import keyboard
def f(key):
window.write_event_value(key, None)
def func():
with keyboard.GlobalHotKeys({
'<alt>+<ctrl>+t': lambda key='Hotkey1':f(key),
'<alt>+<ctrl>+q': lambda key='Hotkey2':f(key)}) as h:
h.join()
def f_test():
layout = [[sg.Text("Hello from PySimpleGUI")], [sg.Button("OK")]]
window = sg.Window("Demo", layout)
while True:
event, values = window.read()
if event == "OK" or event == sg.WIN_CLOSED:
break
window.close()
layout = [[sg.Text("")]]
window = sg.Window("Main Script", layout, finalize=True)
window.hide()
threading.Thread(target=func, daemon=True).start()
while True:
event, values = window.read()
print(event)
if event in (sg.WINDOW_CLOSED, 'Hotkey2'):
break
elif event == 'Hotkey1':
f_test()
window.close()

Related

Prevent PySimpleGUI InputText event from executing while element is disabled when using FocusIn bind?

My GUI has an InputText box with default text that is to be cleared upon focus; however, the InputText is disabled until a ListBox selection is made. Even if a ListBox selection has not been made and the InputText box is disabled, the default text will still be cleared if the InputText box is clicked. Is there a way to prevent this? Or to check if the InputText is disabled when clicked?
Sample Code:
import PySimpleGUI as sg
def main():
layout = [[sg.Input("Search", s=(30,1), disabled=True, k="-SEARCH-")],
[sg.Listbox(values=["One", "Two", "Three"], s=(30,5), k='-LIST-', enable_events=True)]]
window = sg.Window("Test", layout, use_default_focus=False, finalize=True)
window['-SEARCH-'].bind('<FocusIn>', '_Focus')
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Cancel':
break
if event == '-LIST-':
window['-SEARCH-'].Update(disabled=False)
if event == '-SEARCH-' + "_Focus":
window['-SEARCH-'].Update("")
window.refresh()
window.close()
if __name__ == "__main__":
main()
Per Jason Yang's comment, the solution was to simply add the following to my Input declaration:
use_readonly_for_disable=False
Such that:
layout = [[sg.Input("Search", s=(30,1), disabled=True, use_readonly_for_disable=False, k="-SEARCH-")],
[sg.Listbox(values=["One", "Two", "Three"], s=(30,5), k='-LIST-', enable_events=True)]]

Dynamically Updating an Element in a Multiple Window Setup

I have a quick question about updating fields dynamically in a multiple window setup such as the example above. I want it so that when the user selects autofill, values[SAVEINVENTORYPATH] is printed to the 'testinputtext' field in the settings window however when I use window.update, it searches for the keys in the main window rather than the settings window. How can I direct PySimpleGUI towards the settings window?
EDIT: Thank you for your help, I have edited the code and the autofill function works however currently, Autofill appears to freeze the window but I want people to be able to select Autofill and then hit Save and then the Settings window refreshes however I'm not sure where to currently place the while True loop. Thank you again for your assistance with this.
def create_settings_window(settings):
sg.theme('LightGrey6')
settings = load_settings(SETTINGS_FILE, DEFAULT_SETTINGS )
def TextLabel(text): return sg.Text(text+':', justification='r', size=(20,1))
layout = [ [sg.Text('Set Up Connection', font='Any 15')],
[TextLabel('Inventory List'), sg.Input(key='SAVEINVENTORYPATH'), sg.FileBrowse(key='INVENTORYLIST')],
[sg.Text(text='', key = 'testinputtext')],
[sg.Button('Save'), sg.Button('Autofill'), sg.Button('Exit')] ]
window = sg.Window('Settings', layout, keep_on_top=True, finalize=True,element_padding = (3,3.5))
test = window['testinputtext']
event, values = window.read()
if event == 'Autofill':
test.update(value = 'hi')
if event == 'Save':
save_settings(SETTINGS_FILE, settings, values)
sg.popup('Settings saved')
else:
print(event, values)
for key in SETTINGS_KEYS_TO_ELEMENT_KEYS:
try:
window[SETTINGS_KEYS_TO_ELEMENT_KEYS[key]].update(value=settings[key])
except Exception as e:
print(f'Problem updating PySimpleGUI window from settings. Key = {key}')
return window
def main():
window, settings = None, load_settings(SETTINGS_FILE, DEFAULT_SETTINGS )
while True: # Event Loop
#LAYOUT
if window is None:
settings = load_settings(SETTINGS_FILE, DEFAULT_SETTINGS )
sg.theme('LightGrey6')
layout = [[sg.Multiline(size=(180,10),key='Output',pad=(0,20))],
[sg.Button(button_text = 'Settings',key='Settings'),sg.Button(button_text = 'Load Output',key='LOAD_OUTPUT'),sg.Exit()]]
window = sg.Window('Settings Dynamic Update Test', layout, element_justification='center', size= (600,300))
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Exit':
break
window.close()
if event == 'Settings':
create_settings_window(settings)
else:
print(event, values)
window.close()
main()
It' better to handle each window in it's own event loop, mix windows together may get things mush difficult or complex.
Following example show the way to go.
import PySimpleGUI as sg
def popup():
sg.theme('DarkBlue4')
layout = [
[sg.Text("You name"), sg.Input("", key='Name')],
[sg.Text("", size=0, key='Test')],
[sg.Button('Save'), sg.Button('Auto Fill'), sg.Button('Cancel')],
]
window = sg.Window('Settings', layout, modal=True)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Cancel'):
flag = False
break
elif event == 'Save':
settings['Name'] = values['Name']
flag = True
break
elif event == 'Auto Fill':
window['Test'].update(values['Name'])
window.close()
return flag
def main_window():
sg.theme('DarkBlue3')
name = settings['Name']
value = name if name else 'None'
layout = [
[sg.Text("User Name:"), sg.Text(value, key='Name')],
[sg.Button('Settings'), sg.Button('Exit')],
]
return sg.Window('Main', layout)
settings = {'Name':None}
window = main_window()
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif event == 'Settings':
flag = popup()
if flag:
window.close()
window = main_window()
""" or just update element
name = settings['Name']
value = name if name else 'None'
window['Name'].update(value)
"""
window.close()

PySimpleGUIQt - pressing key or button as action

import PySimpleGUIQt as sg
layout = [
[sg.Button('Button1')],
[sg.Button('Exit')],
]
window = sg.Window('Mechanical Turk tool', self._layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Exit':
break
elif event == 'Button1': # how to make it possible to also press "a" on your keyboard to run that event?
print("You pressed button 1")
How do I modify above code, so I can press "button1" on GUI but also "a" on keyboard to start specified event?
Set option return_keyboard_events=True in sg.Window, then simulate a click on button1 in your event loop.
import PySimpleGUIQt as sg
layout = [
[sg.Button('Button1')],
[sg.Button('Exit')],
]
window = sg.Window('Mechanical Turk tool', layout, finalize=True, return_keyboard_events=True)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif event == 'Button1':
print("You pressed button 1")
elif event == 'a':
window['Button1'].click()
window.close()

Value view/modification window

I'm working with plots and I want to call a PySimpleGUI window on a press of a TK button which will show my plot tuples and allow modifying them to later re-draw the plot. So far I took this cookbook example and added a save button:
layout += [[sg.Button('Save')]]
This is how I call it:
def readWindow(event):
values = window.read()
print(values)
editButton.on_clicked(readWindow)
and it successfully passes values when I click "save". But if I close the window and try to open it again, I get (None, None) in the console.
You don't need tkinter Button.
import PySimpleGUI as sg
# layout = ...
layout += [[sg.Button("Save", ...]]
window = sg.Window("Title", layout)
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == "Save":
print(event, values)
window.close()

PySimpleGUI call a function when pressing button

import PySimpleGUI as sg
import os
layout = [[sg.Text('Velg mappe som skal tas backup av og hvor du vil plassere backupen')],
[sg.Text('Source folder', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
[sg.Text('Backup destination ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
[sg.Text('Made by Henrik og Thomas™')],
[sg.Submit(), sg.Cancel()]]
window = sg.Window('Backup Runner v2.1')
event, values = window.Layout(layout).Read()
How can I call a function when I press the submit button? or any other button?
The PySimpleGUI documentation discusses how to do this in the section on events / callbacks
https://pysimplegui.readthedocs.io/#the-event-loop-callback-functions
It is not lot the other Python GUI frameworks that use callbacks to signal button presses. Instead all button presses are returned as "events" coming back from a Read call.
To achieve a similar result, you check the event and make the function call yourself.
import PySimpleGUI as sg
def func(message):
print(message)
layout = [[sg.Button('1'), sg.Button('2'), sg.Exit()] ]
window = sg.Window('ORIGINAL').Layout(layout)
while True: # Event Loop
event, values = window.Read()
if event in (None, 'Exit'):
break
if event == '1':
func('Pressed button 1')
elif event == '2':
func('Pressed button 2')
window.Close()
To see this code run online, you can run it here using the web version:
https://repl.it/#PySimpleGUI/Call-Func-When-Button-Pressed
Added 4/5/2019
I should have also stated in my answer that you could add the event checks to right after your call to Read. You don't have to use an Event Loop as I showed. It could look like this:
event, values = window.Layout(layout).Read() # from OP's existing code
if event == '1':
func('Pressed button 1')
elif event == '2':
func('Pressed button 2')
[ Edit Nov 2020 ] - Callable keys
This isn't a new capability, just didn't mention it in the answer previously.
You can set keys to be functions and then call them when the event is generated. Here is an example that uses a few ways of doing this.
import PySimpleGUI as sg
def func(message='Default message'):
print(message)
layout = [[sg.Button('1', key=lambda: func('Button 1 pressed')),
sg.Button('2', key=func),
sg.Button('3'),
sg.Exit()]]
window = sg.Window('Window Title', layout)
while True: # Event Loop
event, values = window.read()
if event in (None, 'Exit'):
break
if callable(event):
event()
elif event == '3':
func('Button 3 pressed')
window.close()

Categories

Resources