I have built a desktop kivy application with some methods that perform changes in my app as well as other calculations that take some time (around 30 seconds). When I call these methods, the app freezes during their execution, which is not desirable. Is there a way to prevent the app from freezing and instead display some kind of popup with an animated gif, so that the user knows the app is processing something and did not crash/stop responding?
I have tried to use multiprocessing to run these methods in the background while an informative Popup appears, but these processes do not share memory and data structures with the main app process. So, even though the method run successfully and the popup was shown, there were no changes in the app...
Anyway, this is the multiprocessing code:
def run_in_background(self, func, *args):
def run_func(m, a):
# Execute the function with args
m(*a)
def check_process(proc, dt):
if not proc.is_alive():
Clock.unschedule(check_func)
self.dismiss_popup()
# Create background process and start it
p = multiprocessing.Process(target=run_func, args=(func, args))
p.start()
# Create waiting dialog
content = CrunchData()
# Show waiting popup
self.show_popup(title="Some title", content=content, size=(400, 300))
# Create schedulled check for process state
check_func = partial(check_process, p)
Clock.schedule_interval(check_func, .1)
Any help would be much appreciated.
You've got a good start to your problem, but polling is_alive is not what you want to do. Since you are only doing one background task and showing a dialog, you could use either threading OR multiprocessing for this. Their API is very similar.
To avoid polling, you can use a processing pool (easier to deal with) and pass a callback to pool.apply_async.
This callback will not be run on the main thread, though, so if you need to run code on the main thread you will need to schedule it in your callback.
An alternative is just to use a separate thread instead of process, and use Kivy's clock to schedule a function call at the end of the thread function. See kivy threading docs.
This SO answer is also an option for transferring data from a thread to the main thread: https://stackoverflow.com/a/22033066. It's from a Kivy dev.
Related
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)
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.
I'm new for python but willing to learn. I have a set of hardware to receive touch coordinators and draw line accordingly to coordinators.
My problem is that the wxpython won't draw line if coordinator changes.
Here is my code : https://github.com/eleghostliu/homework/blob/master/DrawXY_byWxPython/PythonApplication1/PythonApplication1.py
can someone give advise, thanks.
You registered for EVT_PAINT, yet you are not triggering the event as the data changes. The frame has no idea whether data changed or not, unless you specifically inform it.
You can trigger the event simply by calling
frame.Refresh()
You can hook it in several ways. For instance, you could pass frame.Refresh bound method as a parameter to MainProcess so that it can make the function call to refresh the frame. Something like the following:
WARNING: Erroneous code piece
# Start a socket server
def MainProcess(refresh_callback):
while True:
refresh_callback()
***********************************************
frame = DrawPanel()
frame.Show()
start_new_thread(MainProcess, (frame.Refresh,))
Edit:
The above code piece calling UI methods directly is wrong!
Worker thread should not directly manipulate GUI, instead it should inform the GUI thread about a change, and the GUI thread which is the main thread will handle it in its context. There are again several approaches here, the quickest to implement is through wx.CallAfter.
Which you can incorporate as below, instead of directly calling the function:
wx.CallAfter(refresh_callback)
Another way to handle the communication between worker thread and GUI thread is via wx.PostEvent.
class DrawPanel(wx.Frame):
"""Draw a line to a panel."""
def notify(self):
wx.PostEvent(self, wx.PaintEvent())
Then from the secondary thread, you can safely call frame.notify() whenever new data arrives.
For a more elegant solution involving wx.CallAfter, refer to https://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/ where pubsub is used.
I am building a Qt-based GUI using Pyside. Inside a specific class who has access to a QMainWindow (_theMainWindow) class which in turn has access to 2 other Qt Widgets (theScan & theScanProgress) I am trying to show() the last one by executing
def initiateScan(self):
self._theMainWindow.theScan.theScanProgress.show()
This works just fine, the theScanProgress widget appears.
However, when I add the line that makes the application sleep (and a print statement), as below
def initiateScan(self):
self._theMainWindow.theScan.theScanProgress.show()
print("test")
time.sleep(3)
the program seems to go to sleep BEFORE the widget appears, i.e. as if the time.sleep(3) gets executed before self._theMainWindow.theScan.theScanProgress.show()
Any ideas why this happens?
This is because of the main loop which processes gui events. If you are not using threads you can only execute one function at a time. I strongly suspect that show emits a signal which goes into the event queue, which is in turn blocked until the current function returns.
Put another way, Qt is event driven, and it can only do one event at a time. What ever you did call initiateScan added an event to the stack that was to execute the function (something like you pushed a button, which emitted a signal, which then triggered the function), and that function can do some computation, alter internal state of your objects, and add events to the stack. What show does underneath is emit a signal to all of it's children for them to show them selves. For that code to run, it has to wait for the current event (the function with your sleep) to return. During the sleep the entire gui will be unresponsive for the exact same reason.
[I probably butchered some of the terms of art]
show only schedules the appearance of the progress widget. But since you are blocking the main thread with your sleep, the main thread cannot perform the scheduled action until you release it.
You have to use threads or find another way to wait 3 seconds.
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.