Merge pull request #6036 from PySimpleGUI/Dev-latest
New Demo Program - Make windows and PySimpleGUI calls from a thread.
This commit is contained in:
commit
33edf9ad64
|
@ -0,0 +1,145 @@
|
|||
import PySimpleGUI as sg
|
||||
import time
|
||||
import threading
|
||||
|
||||
"""
|
||||
Demo - Multi-threaded - Show Windows and perform other PySimpleGUI calls in what appread to be from a thread
|
||||
|
||||
Just so that it's clear, you CANNOT make PySimpleGUI calls directly from a thread. There is ONE exception to this
|
||||
rule. A thread may call window.write_event_values which enables it to communicate to a window through the window.read calls.
|
||||
|
||||
The main GUI will not be visible on your screen nor on your taskbar despite running in the background. The calls you
|
||||
make, such as popup, or even Window.read will create windows that your user will see.
|
||||
|
||||
The basic function that you'll use in your thread has this format:
|
||||
make_delegate_call(lambda: sg.popup('This is a popup', i, auto_close=True, auto_close_duration=2, keep_on_top=True, non_blocking=True))
|
||||
|
||||
Everything after the "lambda" looks exactly like a PySimpleGUI call.
|
||||
If you want to display an entire window, then the suggestion is to put it into a function and pass the function to make_delegate_call
|
||||
|
||||
Note - the behavior of variables may be a bit of a surprise as they are not evaluated until the mainthread processes the event. This means
|
||||
in the example below that the counter variable being passed to the popup will not appear to be counting correctly. This is because the
|
||||
value shown will be the value at the time the popup is DISPLAYED, not the value when the make_delegate_call was made.
|
||||
|
||||
Copyright 2022 PySimpleGUI
|
||||
"""
|
||||
|
||||
|
||||
# Design decision was to make the window a global. You can just as easily pass it to your function after initizing your window
|
||||
# But there becomes a problem then of wheere do you place the thread startup code. Using this global decouples them so that
|
||||
# the thread is not started in the function that makes and executes the GUI
|
||||
|
||||
window:sg.Window = None
|
||||
|
||||
# M""MMMM""M
|
||||
# M. `MM' .M
|
||||
# MM. .MM .d8888b. dP dP 88d888b.
|
||||
# MMMb dMMM 88' `88 88 88 88' `88
|
||||
# MMMM MMMM 88. .88 88. .88 88
|
||||
# MMMM MMMM `88888P' `88888P' dP
|
||||
# MMMMMMMMMM
|
||||
#
|
||||
# M""""""""M dP dP
|
||||
# Mmmm mmmM 88 88
|
||||
# MMMM MMMM 88d888b. 88d888b. .d8888b. .d8888b. .d888b88
|
||||
# MMMM MMMM 88' `88 88' `88 88ooood8 88' `88 88' `88
|
||||
# MMMM MMMM 88 88 88 88. ... 88. .88 88. .88
|
||||
# MMMM MMMM dP dP dP `88888P' `88888P8 `88888P8
|
||||
# MMMMMMMMMM
|
||||
|
||||
def the_thread():
|
||||
"""
|
||||
This is code that is unique to your application. It wants to "make calls to PySimpleGUI", but it cannot directly do so.
|
||||
Instead it will send the request to make the call to the mainthread that is running the GUI.
|
||||
|
||||
:return:
|
||||
"""
|
||||
|
||||
# loop 10 times, each time making 2 different popup calls that indicate they should autoclose and not block the main GUI
|
||||
for i in range(10):
|
||||
time.sleep(.2)
|
||||
make_delegate_call(lambda: sg.popup('This is a popup', i, auto_close=True, auto_close_duration=2, keep_on_top=True, non_blocking=True))
|
||||
make_delegate_call(lambda: sg.popup_scrolled(__file__, sg.get_versions(), auto_close=True, auto_close_duration=1.5, non_blocking=True))
|
||||
|
||||
# when finished and ready to stop, tell the main GUI to exit
|
||||
window.write_event_value('-THREAD EXIT-', None)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------------------------- #
|
||||
|
||||
# The remainder of the code is part of the overall design pattern. You should copy this code
|
||||
# and use it as the basis for creating this time of delegated PySimpleGUI calls
|
||||
|
||||
|
||||
# M""""""'YMM oo
|
||||
# M mmmm. `M
|
||||
# M MMMMM M .d8888b. .d8888b. dP .d8888b. 88d888b.
|
||||
# M MMMMM M 88ooood8 Y8ooooo. 88 88' `88 88' `88
|
||||
# M MMMM' .M 88. ... 88 88 88. .88 88 88
|
||||
# M .MM `88888P' `88888P' dP `8888P88 dP dP
|
||||
# MMMMMMMMMMM .88
|
||||
# d8888P
|
||||
# MM"""""""`YM dP dP
|
||||
# MM mmmmm M 88 88
|
||||
# M' .M .d8888b. d8888P d8888P .d8888b. 88d888b. 88d888b.
|
||||
# MM MMMMMMMM 88' `88 88 88 88ooood8 88' `88 88' `88
|
||||
# MM MMMMMMMM 88. .88 88 88 88. ... 88 88 88
|
||||
# MM MMMMMMMM `88888P8 dP dP `88888P' dP dP dP
|
||||
# MMMMMMMMMMMM
|
||||
|
||||
def make_delegate_call(func):
|
||||
"""
|
||||
Make a deletegate call to PySimpleGUI.
|
||||
|
||||
:param func: A lambda expression most likely. It's a function that will be called by the mainthread that's executing the GUI
|
||||
:return:
|
||||
"""
|
||||
if window is not None:
|
||||
window.write_event_value('-THREAD DELEGATE-', func)
|
||||
|
||||
|
||||
# oo
|
||||
#
|
||||
# 88d8b.d8b. .d8888b. dP 88d888b.
|
||||
# 88'`88'`88 88' `88 88 88' `88
|
||||
# 88 88 88 88. .88 88 88 88
|
||||
# dP dP dP `88888P8 dP dP dP
|
||||
|
||||
def main():
|
||||
global window
|
||||
|
||||
# create a window. A key is needed so that the values dictionary will return the thread's value as a key
|
||||
layout = [[sg.Text('Window will not be visible', k='-T-')]]
|
||||
|
||||
# set the window to be both invisible and have no taskbar icon
|
||||
window = sg.Window('Invisible window', layout, no_titlebar=True, alpha_channel=0)
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
if event in ('Exit', sg.WIN_CLOSED):
|
||||
break
|
||||
# if the event is from the thread, then the value is the function that should be called
|
||||
if event == '-THREAD DELEGATE-':
|
||||
try:
|
||||
values[event]()
|
||||
except Exception as e:
|
||||
sg.popup_error_with_traceback('Error calling your function passed to GUI', event, values, e)
|
||||
elif event == '-THREAD EXIT-':
|
||||
break
|
||||
window.close()
|
||||
|
||||
|
||||
# MP""""""`MM dP dP
|
||||
# M mmmmm..M 88 88
|
||||
# M. `YM d8888P .d8888b. 88d888b. d8888P dP dP 88d888b.
|
||||
# MMMMMMM. M 88 88' `88 88' `88 88 88 88 88' `88
|
||||
# M. .MMM' M 88 88. .88 88 88 88. .88 88. .88
|
||||
# Mb. .dM dP `88888P8 dP dP `88888P' 88Y888P'
|
||||
# MMMMMMMMMMM 88
|
||||
# dP
|
||||
|
||||
if __name__ == '__main__':
|
||||
# first your thread will be started
|
||||
threading.Thread(target=the_thread, daemon=True).start()
|
||||
# then startup the main GUI
|
||||
main()
|
Loading…
Reference in New Issue