Python : Timer without blocking the window in Tkinter - python

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

Related

appJar status bar doesn't update in real time

I've set up a simple appJar UI, which has an "execute" button that calls a function containing code that takes a minute to run. I have injected my gui() variable, app, into this function.
There are 4 major steps, after each of which I would like a Statusbar to update to reflect that a step has been completed. However, what tends to happen is that as the function code runs, the GUI becomes unresponsive and it isn't until the code completes execution that ALL of the changes to the status bar are displayed at once.
My question is how should I be handling the UI such that the Statusbar is updated in real time?
appJar is just a wrapper around python tkinter module from standard library.
While your code is running, the ui is not running, thus it becomes unresponsive. If you want the ui to remain responsible, you have to return control from your code to the ui library from time to time.
That can be done by calling gui.topLevel.update() in your code, or by using asynchronous programming and having the main async loop call it, or by using threads.
Which one of those is the best, depends on what your program is doing.
appJar also has built in support for threads: http://appjar.info/pythonThreads/
You can call the function that takes a long time in a thread: app.thread(myFunction, param1, param2)
And, if you want to get the thread to update the GUI, you'll need to use the update queue: app.queueFunction(app.setMeter, "myMeter", 50)

Python, Threading with Tkinter

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.

Time.sleep in Kivy

I am looking for some thing like time.sleep in Kivy, because kivy does not support time.sleep and it hangs when i run a program using time.sleep.
I searched and found a function called:
Clock.schedule_interval(self.callback, interval)
but its not probably like time.sleep. Clock.schedule calls a function every x seconds, but I want to make a delay.
kivy.clock
kivy does not support time.sleep
Kivy supports time.sleep just fine, it just doesn't do what you want it to do - as per the function name, it sleeps, which means kivy 'freezes' as it isn't taking input, updating the graphics etc.
Anyway, it sounds like you want Clock.schedule_once(function, time). That will call the function (with a default argument dt) in time seconds.

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.

Threaded Tkinter script crashes when creating the second Toplevel widget

I have a Python script which uses Tkinter for the GUI. My little script should create a Toplevel widget every X seconds. When I run my code, the first Toplevel widget is created successfully, but when it tries to create a second one the program crashes.
What I am doing is using the after method to call the function startCounting every 5 seconds alongside root's mainloop. Every time this function is called, I append a Toplevel widget object into a list and start a new thread which hopefully will be running the new mainloop.
I would be very grateful if someone could figure this problem out. By the way, this is just a little script that I am currently using to solve my problem, which is preventing me from going on with my real school project.
The code:
import threading,thread
from Tkinter import *
def startCounting():
global root
global topLevelList
global classInstance
topLevelList.append (Toplevel())
topLevelList[len(topLevelList)-1].title("Child")
classInstance.append(mainLoopThread(topLevelList[len(topLevelList)-1]))
root.after(5000,startCounting)
class mainLoopThread(threading.Thread):
def __init__(self,toplevelW):
self.toplevelW = toplevelW
threading.Thread.__init__(self)
self.start()
def run(self):
self.toplevelW.mainloop()
global classInstance
classInstance = []
global topLevelList
topLevelList = []
global root
root = Tk()
root.title("Main")
startCounting()
root.mainloop()
Tkinter is designed to run from the main thread, only. See the docs:
Just run all UI code in the main
thread, and let the writers write to a
Queue object; e.g.
...and a substantial example follows, showing secondary threads writing requests to a queue, and the main loop being exclusively responsible for all direct interactions with Tk.
Many objects and subsystems don't like receiving requests from multiple various threads, and in the case of GUI toolkit it's not rare to need specfically to use the main thread only.
The right Python architecture for this issue is always to devote a thread (the main one, if one must) to serving the finicky object or subsystem; every other thread requiring interaction with said subsystem or object must them obtain it by queueing requests to the dedicated thread (and possibly waiting on a "return queue" for results, if results are required as a consequence of some request). This is also a very sound Python architecture for general-purpose threading (and I expound on it at length in "Python in a Nutshell", but that's another subject;-).
Tkinter has issues dealing with input from multiple threads, I use mtTkinter instead, you won't need to change any code and everything will work fine. Just import mtTkinter instead of Tkinter.
You can get it here:
http://tkinter.unpythonic.net/wiki/mtTkinter
Is there a reason you want (or think you need) one event loop per toplevel window? A single event loop is able to handle dozens (if not hundreds or thousands) of toplevel windows. And, as has been pointed out in another answer, you can't run this event loop in a separate thread.
So, to fix your code you need to only use a single event loop, and have that run in the main thread.

Categories