CPython extension using omp freezes Qt UI - python

I am working on a scientific algorithm (image processing), which is written in C++, and uses lots of parallelization, handled by OpenMP. I need it to be callable from Python, so I created a CPython package, which handles the wrapping of the algorithm.
Now I need some UI, as user interaction is essential for initializing some stuff. My problem is that the UI freezes when I run the algorithm. I start the algorithm in a separate thread, so this shouldn't be a problem (I even proved it by replacing the function call with time.sleep, and it works fine, not causing any freeze). For testing I reduced the UI to two buttons: one for starting the algorithm, and another just to print some random string to console (to check UI interactions).
I also experienced something really weird. If I started moving the mouse, then pressed the button to start the computation, and after that kept moving the mouse continuously, the UI did not freeze, so hovering over the buttons gave them the usual blueish Windows-style tint. But if I stopped moving my mouse for a several seconds over the application window, clicked a button, or swapped to another window, the UI froze again. It's even more strange that the UI stayed active if I rested my mouse outside of the application window.Here's my code (unfortunately I cannot share the algorithm for several reasons, but I hope I manage to get some help even like this):
if __name__ == "__main__":
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
from PyQt5.QtCore import QThread, QObject, pyqtSignal
import time
from CustomAlgorithm import Estimator # my custom Python package implemented in C++
class Worker(QObject):
finished = pyqtSignal()
def run(self):
estimator = Estimator()
estimator.calculate()
# if the above two lines are commented, and the next line is uncommented,
# everything's fine
# time.sleep(5)
print("done")
app = QApplication([])
thread = QThread()
window = QWidget()
layout = QVBoxLayout()
# button to start the calculation
btn = QPushButton("start")
layout.addWidget(btn)
btn.clicked.connect(thread.start)
# button to print some text to console
btn2 = QPushButton("other button")
layout.addWidget(btn2)
btn2.clicked.connect(lambda: print("other button clicked"))
window.setLayout(layout)
# handling events
worker = Worker(app)
worker.moveToThread(thread)
thread.started.connect(worker.run)
worker.finished.connect(thread.quit)
worker.finished.connect(worker.deleteLater)
thread.finished.connect(thread.deleteLater)
window.show()
app.exec_()
I tried multiple variants of using threads, like threading.Thread, multiprocessing.Process, PyQt5.QtCore.QThread (as seen above), even napari's worker implementation, but the result was the same. I even tried removing omp from the code, just in case it interferes somehow with python threads, but it didn't help.
As for the reason I use python, is that the final goal is to make my implementation available in napari.
Any help would be highly appreciated!

Because of Python's "Global Interpreter Lock", only one thread can run Python code at a time. However, other threads can do I/O at the same time.
If you want to allow other threads to run (just like I/O does) you can surround your code with these macros:
Py_BEGIN_ALLOW_THREADS
// computation goes here
Py_END_ALLOW_THREADS
Other Python threads will be allowed to run while the computation is happening. You can't access anything from Python between these two lines - so get that data in order before Py_BEGIN_ALLOW_THREADS.
Reference

Related

How to manage “exec_()” within PyQt5 [duplicate]

I have a code which looks like:
app = QApplication(sys.argv)
self.interface = Interface()
# The figure
self.fig = self.interface.fig
self.ax = self.fig.add_subplot(111)
self.interface.show()
app.exec_()
print 'this is not printed'
The problem is that once app.exec_() is executed, nothing is until I close the window that pops up.
How can I continue running the code?
That is intended. What you have to do is use signals/slots, code inside your Qt classes, or spawn off threads before you call app.exec().
Signals and slots are your defacto way of interacting with Qt. Basically a signal is any "event" or custom "event" and slots can be thought of as "event handlers". For instance when someone hits a button on a GUI it creates a signal that seeks out any handler that is connected to it. You can connect none, one, or many slots to each signal (you can even connect the same one multiple times)! Here is a good reference for this in python.
Coding inside your Qt classes usually means creating slots that do useful work for you. Remember you don't want to hold up the event loop too long so spawn a new thread if you do this.
The third option available to you is to spin off other threads. Be careful interacting with Qt from threads, if you do you MUST us signals and slots. Implement threading as suggested in this SO.
app.exec_() does not lock anything, it runs a GUI event loop that waits for user actions (events) and dispatches them to the right widget for handling. It does this until there are no top level windows left open; if you leave at least one top level window of your app open, then exec() never returns, it can't (your app will be terminated during system shutdown). When no more top level windows the app cleans up and returns from exec(). At that point the GUI is no longer in the event loop.
Whatever it is you want to do after exec() it is likely you would either put it in a QThread or in a signal handler (which you would connect, for example, to a "Go!" button; you would connect a "Cancel" button to a handler that closes the app window).
You can put code after exec() but it would be rather unconventional: if anything goes wrong it is unlikely that user can see the problem since the GUI is no longer visible, a GUI app doesn't usually have a console terminal open where the error might be seen, there will not typically be a console for a GUI app (ie you will run the app via pythonw.exe instead of python.exe), or you have to open a new window and exec() again in order to show an error message and wait till user clicks ok, destroy message window in ok handler so app.exec() once again returns.
In addition to the previous answer, not every time when all windows are closed does the GUI event loop, run by app.exec_(), stop. If you want to terminate it manually you can use app.quit() inside any of the event handlers. It stops GUI event loop and launches your code after app.exec_().
First answer is a lot of words about nothing.
A dirty trick to remove blocking would be to abuse mathplot lib:
import matplotlib
import matplotlib as mpl
import matplotlib.cbook as cbook
import matplotlib.collections as mcol
import matplotlib.patches as mpatches
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import matplotlib.cm as cm # colors
from mpl_toolkits.mplot3d import Axes3D # imports options for 3-D plotting
from matplotlib.backends.backend_pdf import PdfPages
from PyQt5 import QtWidgets, uic
# ==============================================================
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
app = QtWidgets.QApplication.instance()
#endelse
MainWindow = QtWidgets.QMainWindow()
MainWindow=uic.loadUi('E:\Python\Python_lib\MyGui.ui',MainWindow)
MainWindow.show()
# There are two ways to start the gui, and i.e. it looping
# The correct way if to use app.exec_(), which
# blocks the command line :
#d=app.exec_()
# However, this will block the command line making
# We can misuse mathplot lib creating a Figure instead
# which seems to start the Gui loop without blocking the command line:
ImageFig = Figure((3,4))
ImagePanel = FigureCanvas(ImageFig)
ImagePanel.draw()

PyQt4, prevent app.exec_() from hanging the main thread

I have some simple code that loads google.com with the PyQt4 library.
This is the code:
import sys
from PyQt4.QtGui import QApplication
from PyQt4.QtCore import QUrl
from PyQt4.QtWebKit import QWebView
class Browser(QWebView):
def __init__(self):
QWebView.__init__(self)
self.loadFinished.connect(self._result_available)
def _result_available(self, ok):
frame = self.page().mainFrame()
#print(frame.toHtml())
app.exit()
if __name__ == '__main__':
app = QApplication(sys.argv)
view = Browser()
view.load(QUrl('http://www.google.com'))
print('start')
app.exec_()#hangs the main thread
print('end')
My problem with this code is that app.exec_() hangs the main thread for a while, between the print start and print end.
Is there a way to scrape a website in PyQt4 without making the main thread hang for a while.
I would like to resume the main thread's normal execution of code after app.exec_().
It's probably just taking time to download the content from the web. app.exec_() is what runs your application basically. So if anything is indeed hanging it's actually some other factor that influences the proper execution of your application. Any statements that are after the app.exec_() will be executed once you close the application. Usually app.exec_() is called at the end of the main (in C++ you do return app.exec_() which you can also do in PyQt of course).
When working with content that requires time to be downloaded and displayed in the UI, you have to add multithreading in order to allow the main thread to continue work properly (thus not creating the so called UI freeze) but at the same time do some work in the background. The sole purpose of the main thread is to keep track of the UI and run it. Anything else that you add to the main thread will prevent the UI from working in a fluent manner. You have several options here - inheriting QThread (I wouldn't suggest doing that for this scenario in particular), using QThread + QObject in order to incorporate slots and signal in your application (for example: page downloaded? -> if yes, signal the UI to display the content), QRunnable etc.
So my advice is to download the content in a separate thread and once it's done you can add it to your UI as well as provide some other form of visual feedback (this also includes print statements :P).

Can QtGui.QFileDialog.getOpenFileName() be used outside a GUI?

I am experiencing a weird behavior from this simple line of code, which I wanted to use outside a pyqt GUI class
from PyQt4 import QtGui
FilePath=QtGui.QFileDialog.getOpenFileName(None,'choose the file')
When I first implemented it, it worked (probably because I run some other code before that I cannot trace back).
Then after restarting python, it stopped working and it crashes saying:
QWidget: Must construct a QApplication before a QPaintDevice
The same exact code works fine when implemented in a GUI, where the first argument is of course self.
But I would like to use the same filedialog in an external function I use for a different purpose, outside a GUI context.
Is it possible and what could be a way/workaround to implement that ? I would like to avoid using wx, easygui or tk
In order to use the Qt UI elements (such as QFileDialog), you generally have to have a QApplication running. It seems a little unorthodox, but you can get your snippet to run just by instantiating a QApplication prior to your QFileDialog, like so
from PyQt4 import QtGui
app = QtGui.QApplication([])
FilePath=QtGui.QFileDialog.getOpenFileName(None,'choose the file')
Technically, this will work, though I'm not quite sure I can endorse doing this.
Qt applications are built around an event loop and require QApplication instance. Dialogs have their own event loop, so if all you want is a wizard (a series of dialogs that open/close in sequence), then in principle all you need is the QApplication instance. The following works in PyQt 5:
from PyQt5 import Qt
app = Qt.QApplication([])
FilePath=Qt.QFileDialog.getOpenFileName(None,'choose the file')
print(FilePath)
However, if you have other code that assumes an application event loop, all bets are off. In that case, you could do the following:
from PyQt4 import Qt
app = Qt.QApplication([])
FilePath=Qt.QFileDialog.getOpenFileName(None,'choose the file')
print(FilePath)
...create widgets....
QTimer.singleShot(someWidgetMethod)
app.exec_()
The single-shot timer will fire only during the app.exec_(), thus calling some of your code as part of the event loop.
All that being said,it is much better to create a main window and connect slots to signals and call app.exec_(). There are many examples included with PyQt, check those.

PyQt QWidget window closes immediately upon showing?

I realize that this question has been asking several times before, though none of them seem to apply to my situation. I have installed PyQt, and am simply trying to open up a window as such:
import sys
from PyQt4 import QtGui as qt
segmentation = qt.QApplication(sys.argv)
main = qt.QWidget()
main.show()
All the other questions I have looked at on here usually were caused by an error with the window going out of scope because of the window's show method being called from within a function, or something similar.
My code uses no functions at all so this cannot be the issue. This should work as it is, no? I am following this tutorial:
https://www.youtube.com/watch?v=JBME1ZyHiP8
and at time 8:58, the instructor has pretty much exactly what I have written, and their window shows up and stays around just fine. Mine displays for a fraction of a second and then closes.
Screen shot of the code block from the video to compare to the code block provided here:
Without seeing all of your code, I'm assuming that you're missing the sys.exit() bit.
For your specific code sys.exit(segmentation.exec_()) would be what you needed.
segmentation = qt.QApplication(sys.argv)
main = qt.QWidget()
main.show()
sys.exit(segmentation.exec_())
A little bit of detail of what's going on here.
segmentation = qt.QApplication(sys.argv) creates the actual application.
main = qt.QWidget() and main.show() creates the Widget and then displays.
When executing the python script, this does exactly what you tell it to:
Create an application
Create a widget
Show the widget
Finished. End of the script.
What the sys.exit() does is cleanly closes the python script. segmentation.exec_() starts the event driven background processing of QT. Once the segementation.exec_() is finished (user closes the application, your software closes the application, or a bug is encountered) it'll return a value which is then passed into the sys.exit() function which in turn terminates the python process.

How can I print to QTextEdit to mimic printing to console? (Python 3.3)

I have a piece of code that displays a Gui which has a QTextEdit Field. I would like to print to this field in real time similar to how the print function outputs to the console.
I have tried using multiple instances of the append function. Ex:
self.textEdit.append(_translate("MainWindow", ">>> Text", None))
The problem is that no matter where they are in the code, they seem to only show after the program is executed. My goal is to have them show in line like the print function does on the console.
I feel like this is an easy answer, but I have had no luck searching.. I am fairly new to Python and any help or guidance will be appreciated.
Thanks in advance!
Indeed, as mata mentioned the freezing comes from doing all your work in the same (main) thread, which also handles UI updates. One way to solve your responsiveness issue is indeed to frequently use QApplication.processEvents() in your blocking code. That will give the impression of a responsive GUI to the user if frequent enough.
However, using threads in Python (whether native or QThread) will not always work. That is because of the existence of the Global Interpreter Lock (GIL, the wiki has a good short intro). In short, Python does not allow more than one thread to execute code at the same time.
If your background task is light, or is based on IO, you can get around this as most IO-heavy modules for Python release the GIL while doing their job. However, if you are performing heavy computations in Python, the GIL will be locked by your processes, and as such your UI will still be unresponsive.
Consider the following example, built using PySide:
import sys, random
from threading import Thread
from time import sleep
from urllib import urlopen
from PySide import QtCore, QtGui
class Window(QtGui.QMainWindow):
update_signal = QtCore.Signal(int)
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.progress_bar = QtGui.QProgressBar(self)
self.progress_bar.setRange(0, 10)
self.setCentralWidget(self.progress_bar)
self.update_signal[int].connect(self.progress_bar.setValue)
self.show()
self.t = Thread(target=self.worker)
self.t.start()
def worker(self):
while self.progress_bar.value() < 10:
self.update_signal.emit(self.progress_bar.value()+1)
print "Starting Sleep"
sleep(5)
print "End of Sleep"
if __name__ == '__main__':
qapp = QtGui.QApplication(sys.argv)
win = Window()
sys.exit(qapp.exec_())
Then, try replacing the worker function to:
def worker(self):
while self.progress_bar.value() < 10:
self.update_signal.emit(self.progress_bar.value()+1)
v = 0
print "Starting Add"
for i in xrange(5000000):
v = v+random.uniform(0, 100)
print "End of Add"
The first case maintains a responsive UI, as the call to sleep() releases the GIL. But the second example does not, as the computationally-intense algorithm keeps the lock.
One solution could be using the multiprocessing package.
From the docs:
multiprocessing is a package that supports spawning processes using an
API similar to the threading module. The multiprocessing package
offers both local and remote concurrency, effectively side-stepping
the Global Interpreter Lock by using subprocesses instead of threads.
Due to this, the multiprocessing module allows the programmer to fully
leverage multiple processors on a given machine.
A simple, illustrative example of using python multipocessing.
Also, if you have further interest, this blog post about multi-processing techniques might be of interest.
This means that you're probably doing all your work in the GUI thread. This is a common mistake, and it means that the GUI will freeze and not respond while there is something else going on.
You can add a call to QApplication.processEvents() to allow for the GUI to update after you change the text in your QTextEdit, but that will only partially solve the problem, the GUI will nevertheless freeze up between those calls.
The solution is simple: do the work in a separate thread. You should read Threading Basics from the Qt documentation, that should get you started.

Categories