Run pyQT GUI main app in seperate Thread - python

I am trying to add a PyQt GUI console in my already established application. But the PyQt GUI blocks the whole application making it unable to do rest of the work. I tried using QThread, but that is called from the mainWindow class. What I want is to run the MainWindow app in separate thread.
def main()
app = QtGui.QApplication(sys.argv)
ex = Start_GUI()
app.exec_() #<---------- code blocks over here !
#After running the GUI, continue the rest of the application task
doThis = do_Thread("doThis")
doThis.start()
doThat = do_Thread("doThat")
doThat.start()
My application already uses Python Threads, So my question is, what is the best approach to achieve this process in a threaded form.

One way of doing this is
import threading
def main()
app = QtGui.QApplication(sys.argv)
ex = Start_GUI()
app.exec_() #<---------- code blocks over here !
#After running the GUI, continue the rest of the application task
t = threading.Thread(target=main)
t.daemon = True
t.start()
doThis = do_Thread("doThis")
doThis.start()
doThat = do_Thread("doThat")
doThat.start()
this will thread your main application to begin with, and let you carry on with all the other stuff you want to do after in the code below.

Related

PyQt app.exec() seems to be non-blocking

I have a quite complicated PyQt app (Qt5, running in Spyder), where at the end I do
def main():
from PyQt5 import QtWidgets
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
app = QtWidgets.QApplication.instance()
main_window = MainWindow()
main_window.show()
status = app.exec_()
print status
sys.exit(0)
if __name__ == "__main__":
main()
(The if-else check is needed because of this(second answer).) When I run this code, my app shows, and the status code -1 is printed at the same time (due to a raised error in spyder/utils/ipython/start_kernel.py). My question is, why this error is printed at all, because I thougt that app.exec_() is a blocking call and the status is not returned until the app is exited somehow. Is this due to Spyder running its own QApplication?
It is not possible to execute the application event-loop more than once. This is easy enough to test with a simple script:
import sys
from PyQt5 import QtCore, QtWidgets
app = QtWidgets.QApplication(sys.argv)
btn = QtWidgets.QPushButton('Test')
btn.clicked.connect(lambda: print(QtWidgets.QApplication.exec_()))
btn.show()
sys.exit(app.exec_())
Output:
QCoreApplication::exec: The event loop is already running
-1
So, if the event-loop is already running, exec just returns immediately without blocking.
(NB: obviously you will need to run the above script in a normal console to test it properly).

Why am I unable to run a PySide GUI in a spawned child process?

I'm attempting to run a PySide GUI in a spawned child process, but it keeps exiting with exitcode 11 (segfault). This simplified test code works fine if run_gui is called directly, but fails if it's run inside a child process:
# std lib imports
import time
import sys
from multiprocessing import Process
# third party imports
from PySide.QtGui import QApplication, QMainWindow
def run_gui():
app = QApplication([])
w = QMainWindow()
w.show()
w.raise_()
sys.exit(app.exec_())
def main():
gui_proc = Process(target=run_gui)
#gui_proc.daemon = True # this doesn't seem to matter
gui_proc.start()
while True:
if not gui_proc.is_alive():
if gui_proc.exitcode != 0:
print 'GUI exited with code {}'.format(gui_proc.exitcode)
else:
print 'GUI exited cleanly.'
break
time.sleep(1)
if __name__ == "__main__":
## GUI in main process:
#run_gui()
## GUI in child process:
main()
Is there a way around this? I'm not sure how to debug what's causing the segfault.
The reason I'm interested in doing this is so that I can restart the GUI if it crashes unexpectedly, all the while keeping a couple daemon processes running (one for disk access and another for interacting with instruments).
Edit: I just tried another test script (below) using QCoreApplication and no GUI, and I see the same problem. Apparently PySide just won't run in a child process.
# std lib imports
import time
import sys
from multiprocessing import Process
# third party imports
from PySide.QtCore import QCoreApplication, QTimer
def quit():
print "Quiting..."
sys.exit()
def run():
app = QCoreApplication([])
timer = QTimer()
timer.timeout.connect(quit)
timer.start(1000)
print "Starting..."
sys.exit(app.exec_())
def main():
proc = Process(target=run)
#proc.daemon = True # this doesn't seem to matter
proc.start()
while True:
if not proc.is_alive():
print 'Process exited with code {}'.format(proc.exitcode)
break
time.sleep(1)
if __name__ == "__main__":
## GUI in main process:
#run()
## GUI in child process:
main()
I have had similar issues,
I chose to rearrange my code, so that data collection and analysis is running in process it self.
This was, I got a very simple Gui thread and plotting thread. If the gui for some reason stalled, I would still be able to sample my data "in time"
I don't like the Phrase
so that I can restart the GUI if it crashes unexpectedly
Basically if your gui crashes, you should fix the problem and not do a workaround.

pyside second qt gui doesn't show up

I have a Qapplication in my python script that gives a logingui to my chat server.
When the login is complete I want to call upon my chat gui. To achieve this I've used the following
code:
app = QApplication(sys.argv)
form = LoginWindow()
form.show()
app.exec_()
#login done
form = ChatWindow()
form.show()
app.exec_()
This worked when I fired it up with an "empty" gui of the chat. So only the necessary things in it for it to boot up. However when I start connecting signals and stuff the second window just doesn't show up anymore. The console prints a statement from the beginning of the init but after that it falls silent and no gui is present.
Does anyone know how I can fix this weird problem? How is switching a form supposed to be done?
The login window should be a subclass of QDialog, so that it can be run separately from the main application. A QDialog has its own event loop, and provides a return code that can be used to check which action was taken by the user.
So, given this, your code would become:
app = QApplication(sys.argv)
dialog = LoginWindow()
if dialog.exec_() == QDialog.Accepted:
window = ChatWindow()
window.show()
app.exec_()
else:
print('Login cancelled')

Stopping a WxPython app's main loop from a thread requires window focus

I have a command line utility that starts and stops a WxPython app instance. I'm starting the app in a thread so that I can continue to run commands as well as use the gui app.
My problem is that when I stop the app the console hangs until I focus the app window, then it closes. I'm not sure why the app requires focus to exit.
The app thread looks like this:
class RunAppThread(threading.Thread):
def run(self):
self.app = wx.App(redirect=False)
self.frame = AppWindow(None)
self.app.MainLoop()
def stop(self):
self.app.ExitMainLoop()
The relevant CLI actions are:
def startapp(self):
self.app = gui.RunAppThread()
self.app.start()
def stopapp(self):
self.app.stop()
self.app.join()
Is there a better/correct way to exit the main loop rather than calling wx.App.ExitMainLoop()?
After calling wx.App.ExitMainLoop, use wx.WakeUpMainThread to get the main thread to process events.

Updating a wxPython progress bar after calling app.MainLoop()

I have a python script that performs a calculation, and I have created a class for a pop-up wxPython progress bar. Currently I have:
app=wx.App()
progress = ProgressBar()
app.MainLoop()
for i in xrange(len(toBeAnalysed)):
analyse(toBeAnalysed[i])
progress.update(i/len(toBeAnalysed)*100)
Now, this example doesn't work for obvious reasons. Is there any way I can run the app.MainLoop() in a different thread but still communicate the progress (and .update() it) as the calculations are completed?
Thanks for the help.
You should run your logic in a background thread and use wx.CallAfter to periodically update the GUI. CallAfter will invoke the provided function on the GUI thread, so it is safe to make GUI calls.
import wx
import threading
import time
def do_stuff(dialog): # put your logic here
for i in range(101):
wx.CallAfter(dialog.Update, i)
time.sleep(0.1)
wx.CallAfter(dialog.Destroy)
def start(func, *args): # helper method to run a function in another thread
thread = threading.Thread(target=func, args=args)
thread.setDaemon(True)
thread.start()
def main():
app = wx.PySimpleApp()
dialog = wx.ProgressDialog('Doing Stuff', 'Please wait...')
start(do_stuff, dialog)
dialog.ShowModal()
app.MainLoop()
if __name__ == '__main__':
main()

Categories