Replies: 11 comments 12 replies
-
Dear @PySimpleGUI, is there, or could we have, a way of retrieving an initial |
Beta Was this translation helpful? Give feedback.
-
To get the values dictionary, you'll need to have the window created. I suggest you set Then to get the initial values, call |
Beta Was this translation helpful? Give feedback.
-
I see, but this doesn't work for us, because calling
windows.read(timeout=0) outside the thread crashes. I'll try to add the
error message here. Does it make sense to you why I'd like to have a
pre-initialized values dict before the first frame of the main drawing
loop, right?
…On Sat, 12 Nov 2022, 08:57 PySimpleGUI, ***@***.***> wrote:
To get the values dictionary, you'll need to have the window created. I
suggest you set finalize=True when making the window, just in case... you
may not need it.
Then to get the initial values, call window.read(timeout=0). This will
get you the value dictionary without any delay.
—
Reply to this email directly, view it on GitHub
<#187 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA4GADBSLYS3U3OUMTMHX6DWH6AZFANCNFSM6AAAAAAR52INCY>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
I dunno if this is of help, but here's a demo showing getting data through a queue. No, it does not make sense to me. import PySimpleGUI as sg
import random
import time
import queue
"""
Demo - Multi-threaded "Data Pump"
A thread gets data from a queue object and passes it over to the main event loop
Copyright 2022 PySimpleGUI
"""
THREAD_KEY = '-THREAD-'
THREAD_INCOMING_DATA = '-INCOMING DATA-'
THREAD_EXITNG = '-THREAD EXITING-'
THREAD_EXTERNAL_EXITNG = '-EXTERNAL THREAD EXITING-'
thread_queue = queue.Queue()
def external_thread(thread_queue:queue.Queue):
"""
Represents some external source of data
:param thread_queue:
:return:
"""
i = 0
while True:
wait_time = random.randint(0,10)
time.sleep(wait_time)
thread_queue.put(f'Incoming data #{i} wait was {wait_time} seconds')
i += 1
def the_thread(window:sg.Window, thread_queue:queue.Queue):
"""
The thread that communicates with the application through the window's events.
Waits for data from a queue and sends that data on to the event loop
:param window:
:param thread_queue:
:return:
"""
while True:
data = thread_queue.get()
window.write_event_value((THREAD_KEY, THREAD_INCOMING_DATA), data) # Data sent is a tuple of thread name and counter
def main():
layout = [ [sg.Text('My Simulated Data Pump')],
[sg.Multiline(size=(80,20), k='-MLINE-')],
[sg.Button('Go'), sg.Button('Exit')] ]
window = sg.Window('Simulated Data Pump', layout, finalize=True, relative_location=(0, -300))
downloading, max_value = False, 0
while True: # Event Loop
event, values = window.read()
# print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Go' and not downloading:
window.start_thread(lambda: external_thread(thread_queue), (THREAD_KEY, THREAD_EXTERNAL_EXITNG))
window.start_thread(lambda: the_thread(window, thread_queue), (THREAD_KEY, THREAD_EXITNG))
# Events coming from the Thread
elif event[0] == THREAD_KEY:
if event[1] == THREAD_INCOMING_DATA:
data = values[event]
downloading = True
window['-MLINE-'].print('Incoming data:', c='white on red', end='')
window['-MLINE-'].print(data)
elif event[1] == THREAD_EXITNG:
window['-MLINE-'].print('Thread has exited')
elif event[1] == THREAD_EXTERNAL_EXITNG:
window['-MLINE-'].print('Data Pump thread has exited')
window.close()
if __name__ == '__main__':
main() |
Beta Was this translation helpful? Give feedback.
-
Do you have a simple architecture diagram of your entire application that you have in mind. That would be likely the best way for me to get up to speed. And I would also stop guessing at what you might need. I only knew data was incoming from elsewhere and sent to the event loop. Not sure what happens in your event loop. I usually set uninitialized variables to |
Beta Was this translation helpful? Give feedback.
-
If your other event loop is controlling CPU usage by pending, then you can run the event loop in PySimpleGUI with a |
Beta Was this translation helpful? Give feedback.
-
import PySimpleGUI as sg
import random
import time
import queue
"""
Demo - Multi-threaded "Data Pump"
A thread gets data from a queue object and passes it over to the main event loop
Copyright 2022 PySimpleGUI
"""
THREAD_KEY = '-THREAD-'
THREAD_INCOMING_DATA = '-INCOMING DATA-'
THREAD_EXITNG = '-THREAD EXITING-'
THREAD_EXTERNAL_EXITNG = '-EXTERNAL THREAD EXITING-'
thread_queue = queue.Queue()
gsize = (400,400)
def external_thread(thread_queue:queue.Queue):
"""
Represents some external source of data
:param thread_queue:
:return:
"""
i = 0
while True:
time.sleep(.01)
point = (random.randint(0,gsize[0]), random.randint(0,gsize[1]))
radius = random.randint(10, 40)
thread_queue.put((point, radius))
i += 1
def the_thread(window:sg.Window, thread_queue:queue.Queue):
"""
The thread that communicates with the application through the window's events.
Waits for data from a queue and sends that data on to the event loop
:param window:
:param thread_queue:
:return:
"""
while True:
data = thread_queue.get()
window.write_event_value((THREAD_KEY, THREAD_INCOMING_DATA), data) # Data sent is a tuple of thread name and counter
def main():
layout = [ [sg.Text('My Simulated Data Pump')],
[sg.Multiline(size=(80,20), k='-MLINE-')],
[sg.Graph(gsize, (0, 0), gsize, expand_x=True, expand_y=True, k='-G-', background_color='gray')],
[sg.Button('Go'), sg.Button('Exit')] ]
window = sg.Window('Simulated Data Pump', layout, finalize=True, relative_location=(0, -300))
graph = window['-G-'] # type: sg.Graph
while True: # Event Loop
event, values = window.read()
# print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Go':
window.start_thread(lambda: external_thread(thread_queue), (THREAD_KEY, THREAD_EXTERNAL_EXITNG))
window.start_thread(lambda: the_thread(window, thread_queue), (THREAD_KEY, THREAD_EXITNG))
# Events coming from the Thread
elif event[0] == THREAD_KEY:
if event[1] == THREAD_INCOMING_DATA:
point, radius = values[event]
graph.draw_circle(point, radius=radius, fill_color='green')
window['-MLINE-'].print(f'Drawing at {point} radius {radius}', c='white on red')
elif event[1] == THREAD_EXITNG:
window['-MLINE-'].print('Thread has exited')
elif event[1] == THREAD_EXTERNAL_EXITNG:
window['-MLINE-'].print('Data Pump thread has exited')
window.close()
if __name__ == '__main__':
main() Maybe this is closer? (sorry my screen capture is slow) How do you want to communicate GUI events back to py5? |
Beta Was this translation helpful? Give feedback.
-
The error message is that you're trying to call PySimpleGUI from a thread... no can do. |
Beta Was this translation helpful? Give feedback.
-
I would write the data routing code for them and hide it. Give them a clean interface to work with so they don't see any ugly code. |
Beta Was this translation helpful? Give feedback.
-
I don't know the py5 package, clearly. I saw in the docs that both PySimpleGUI and py5 are trying to manage some of the same devices, such as the mouse. If using the Graph element to draw, then the mouse handling is already built-into the Graph element itself. I'm wondering if there are going to be collisions between tkinter and py5 such that events get lost. |
Beta Was this translation helpful? Give feedback.
-
Thank you, @PySimpleGUI , for your assistance and contributions to this discussion.
Words to live by.
There's a lot I need to explain about py5, threading, and py5's event loop, both here and in the py5 documentation. First, you should know that py5 is a Python library that is backed by the Processing Java core libraries. It uses jpype to make calls from Python to Java && to make calls from Java back to Python. The later is actually what makes py5 special (and possible). Here's some pseudocode for how py5's def run_sketch():
call_user_setup()
while True:
call_user_draw() If this were the case, the thread that makes the call to Here's some pseudocode for how py5's def run_sketch():
launch_java_thread_for_running_sketch() The Here's some pseudocode for what is happening on the Java side of py5: void runAnimation():
jniCallToUserSetup()
while (true):
jniCallToUserDraw() py5's Java code is making JNI calls back to Python to execute the user's Try running this code: import threading
import py5
def setup():
print('setup', threading.current_thread().getName())
def draw():
if py5.frame_count < 10:
print('draw', threading.current_thread().getName())
def mouse_entered():
print('mouse_entered', threading.current_thread().getName())
print(threading.current_thread().getName())
py5.run_sketch() When run from the IPython REPL, I get: MainThread
setup Dummy-1
draw Dummy-1
draw Dummy-1
draw Dummy-1
draw Dummy-1
draw Dummy-1
draw Dummy-1
draw Dummy-1
draw Dummy-1
draw Dummy-1
mouse_entered Dummy-1
mouse_entered Dummy-1
mouse_entered Dummy-1 As you can see, all of the user's methods are executed on the same thread that is different from the one I started with. However, you should know that everything is more complicated on OSX. That operating system requires GUIs to run on the main thread. py5 has to do some extra stuff to run on that platform. Tragically, when I tried running @villares 's first example code (from the first post) on my Mac, I got a horrific Python seg-fault. The PySimpleGUI documentation says:
Can we get around the problems discussed in this thread by changing the GUI backend? And do any of them work better on OSX? @villares , I assume you want what you are doing to be cross-platform? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
The simplest, no thread, approach: https://github.com/villares/sketch-a-day/blob/main/2022/sketch_2022_11_10/sketch_2022_11_10.py
(it slows down the animation a bit, Mike warns it can hang too much the CPU)
A first repeating thread approach:
Summary: We can't guarantee that
controls()
, called from the repeating thread, will run beforedraw()
. Callingcontrols()
insetup()
doesn't work. So I created a dummy values dict. But I wish people didn't have to do it, to manually initialize values before the first frame-run ofdraw()
, because it can get tedious and error prone in larger layouts."module mode" version of the code below
Beta Was this translation helpful? Give feedback.
All reactions