Python, Threading with Tkinter - python

So I'm pretty deep into a program I am doing and I realized that with my program, I think I need to implement threading in some manner in order to stop it from locking up.
My program uses Tkinter for it's GUI, and when the program starts, I have a process that is running every second uses Tkinter's after() function. The method in question reads data and appends it to a fixed length deque(). In other parts of my program I have methods which read the last appended value, and process it.
My issue however is a simple loop that goes like this:
value = valDeque.getLastAppendedValue()
while value != "this specific value":
value = valDeque.getLastAppendedValue()
When the inital variable call before the while loop sets value as something other than the specific value I am looking for the program goes into an endless loop and it seems everything else stops functioning.
I am assuming this is because the while loop keeps executing while my after() function that would append the value I am looking for is sitting in limbo waiting for it's turn to execute. This is why I believe I need to use threading, since I can set up my update/append function to run separate of the rest of the processes so I don't have this error.
With that said, I am not super experienced with Python, and have no idea how to integrate threading with the Tkinter after() function since I know Tkinter's mainloop() doesn't interact with with something like time.sleep().
I've tried looking online for some examples to get some headway, but I can't really make heads or tails or what I am finding.

Related

python tkinter window going unresponsive and hanging

MyButton1 =Button(master, text='Quit',bg="grey",width=20,
command=master.quit)
MyButton1.place(x=200, y=100)
MyButton2 =Button(master, text='Propagate', bg="grey",width=20,
command=mainmethod)
MyButton2.place(x=1000, y=100)
master.geometry("1500x1500")
master.mainloop( )
In the above code after pressing propagate button mainmethod is invoking..
I wrote my logic in main method where this method alone taking 2minutes to execute in the mean time GUI going unresponsive state for few min and later displaying all my required output on text box i inserted
whether any away to avoid the unresponsive issue apart from using multi threading
and i am looking such that after pressing propagate button button should disabled and window should not go unresponsive and display text.insert statements continuously which i added in main method ?????
To prevent hanging, you need to separate the calculations in the mainmethod from Tkinter's main loop by executing them in different threads. However, threading system in Python is not that well-developed as in other languages (AFAIK) because of GIL (Global Interpreter Lock), but there is an alternative - using processes just like threads. This is possible with multiprocessing library.
In order to just prevent hanging, you could create another function
from multiprocessing import Process
def mainmethodLaunch():
global mainmethodProcess
mainmethodProcess = Process(target=mainmethod)
mainmethodProcess.start()
And bind this function to MyButton2 instead of mainmethod itself.
Docs: https://docs.python.org/2/library/multiprocessing.html#the-process-class
You can see p.join in the example. join method will cause your main process to wait for the other one to complete, which you don't want.
So when you press the button, mainmethodLaunch function will be invoked, and it will create another process executing mainmethod. mainmethodLaunch function's own run duration should be insignificant. Due to usage of another process, Tkinter window will not hang. However, if you do just this, you will not be able to interact with mainmethod process in any kind while it will be working.
In order to let these processes communicate with each other, you could use pipes (https://docs.python.org/2/library/multiprocessing.html#exchanging-objects-between-processes)
I guess the example is quite clear.
In order to receive some data from the mainmethod process over time, you will have to poll the parent_conn once a little time, let's say, second. This can be achieved with Tkinter's after method
(tkinter: how to use after method)
IMPORTANT NOTE: when using multiprocessing, you MUST initialize the program in if __name__ == '__main__': block. I mean, there should be no doing-something code outside functions and this block, no doing-something code with zero indent.
This is because multiprocessing is going to fork the same Python executable file, and it will have to distinguish the main process from the forked one, and not do initializing stuff in the forked one.
Check twice if you have done that because if you make such a mistake, it can cost you hanging of not just Tkinter window, but the whole system :)
Because the process will be going to fork itself endlessly, consuming all RAM you have, regardless of how much you have.

Python's Tkinter after() function jumps CPU

I'm building a Python GUI app using tkinter.
Basically I'm starting and integrating with a different thread, while communication goes using input and output queues.
In the GUI side (the main thread where tkinter's mainloop() goes) I want to add a function which will be called on every iteration of the mainloop (I'm processing and displaying information on real-time).
So my function does something like that:
def loop(self):
try:
output_type, data = wlbt.output_q.get_nowait()
pass # if got something out of the queue, display it!
except Queue.Empty:
pass
self.loop_id = self.after(1, self.loop)
While when starting the program I just call self.loop_id = self.after(1, self.loop).
So two things that bother me:
The loop function raise the CPU usage by 30%-50%. If I disable it then it's good.
I want to be able to use after_idle() to maximize the refresh-rate, but I wasn't able to just replace it - got and error.
I'm sensing there's something I don't fully understand. What can be done to address these issues?
When you call self.after(1, self.loop) you are asking for a function to be run roughly once per millisecond. It's not at all surprising that the CPU usage goes up since you are making 1000 function calls per second.
Given that humans cannot perceive that many changes, if all you're doing is updating the display then there's no reason to do that more than 20-30 times per second.

PyGObject (Glade) Window Never Showing (Multithreaded)

I've been fighting for three hours now to get this process multithreaded, so that I can display a progress box. I finally got it working, insomuch as the process completes as expected, and all the functions call, including the ones to update the progress indicator on the window.
However, the window never actually displays. This is a PyGObject interface designed in Glade. I am not having fun.
def runCompile(obj):
compileWindow = builder.get_object("compilingWindow")
compileWindow.show_all()
pool = ThreadPool(processes=1)
async_result = pool.apply_async(compileStrings, ())
output = async_result.get()
#output = compileStrings() #THIS IS OLD
compileWindow.hide()
return output
As I mentioned, everything works well, except for the fact that the window doesn't appear. Even if I eliminate the compileWindow.hide() command, the window never shows until the process is done. In fact, the whole stupid program freezes until the process is done.
I'm at the end of my rope. Help?
(By the way, the "recommended" processes of using generators doesn't work, as I HAVE to have a return from the "long process".)
I'm not a pyGobject expert and i don't really understand your code. I think that you should post more. Why are you calling the builder in a function? you can call it at the init of the GUI?
Anyways.. It seems that you are having the common multithread problems..
are you using at the startup GObject.threads_init() and Gdk.threads_init() ?
Then, if you want to show a window from a thread you need to use Gdk.threads_enter() and Gdk.threads_leave().
here is an useful doc
I changed the overall flow of my project, so that may affect it. However, it is imperative that Gtk be given a chance to go through its own main loop, by way of...
if Gtk.events_pending():
Gtk.main_iteration()
In this instance, I only want to call it once, to ensure the program doesn't hang.
(The entire program source code can be found on SourceForge. The function in question is on line 372 as of this posting, in function compileModel().

Python : Timer without blocking the window in Tkinter

For a project I need to do, I code a Bomberman.
I'm working with the software Pyzo, in Python language, and with Tkinter.
The problem I have is that I need to a timer for the bomb, for example, I put a bomb and it exploded 3 seconds after.
But I have tested with many different things like .after; time module (time.sleep), a loop.
The consequence is always the same, the windows freezes and I can't move anymore, but when the loop is finished, the screen is refreshed and players are at new positions.
How can I do a proper timer to permit my bombs to explode after 3 seconds ?
Thank you!
You can use
widget.after(milliseconds, function, *arguments)
to let the function(*arguments) be called after milliseconds. If the function takes no arguments use widget.after(milliseconds, function). One argument widget.after(milliseconds, function, arg1), ....
widget can be Tk(), Canvas(), Frame(), Label(), ... object.
If you are interested in loops: tkinter loop and serial write

Tkinter I/O Events

I'm new to Python as well as event-driven/GUI programming in general. As far as I can tell, all the event choices are things like mouse clicks and key presses.
I've written a set of functions in a separate library that read from an I2C device (on Raspberry Pi). The functions return -1 if nothing is read. So basically, I want to loop, calling the read function each time, until something besides -1 is returned.
My first instinct was to write something like:
readResult = -1
while (readResult == -1):
readResult = IO.read()
changeGUI()
This doesn't seem to work though in the tkinter structure. I get how to make a function get called on a button press, but I don't know how to do a custom event.
There are a few ways to go with this -- you could give up using Tkinter's mainloop(), and build your own event loop that polled for both types of events. Or, you could spawn a separate thread to monitor IO. Or, you could use the after() method from Tkinter.
For the first two cases, if IO.read() returns immediately, whether or not there's a result, then you probably want to throw a time.sleep() call in the loop, to avoid hogging the CPU.
If your call to IO.read() doesn't block, and doesn't take very long, it's very easy to set up a loop to poll the device every few milliseconds. All you need to do is something like this:
def read_one_result():
readResult = IO.read()
if readResult != -1:
changeGUI()
root.after(100, read_one_result)
This will read one result, update the GUI if anything was read, and the schedule itself to run again in 100ms.

Categories