Threading in GTK3 python - python

I'm building a GTK GUI in Python and I need to get some data from the database which takes quite long, so the GUI freezes.
So I'm now using Threads to run the refreshing "in the background":
Thread(target=self.updateOrderList).start()
I have GUI class with alle relevant methods to manipulate the GUI. My solution work 80% of the time, but when it doesn't GTK crashed and it outputs this:
[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python3.6: ../../src/xcb_io.c:165: dequeue_pending_request:
The other times it works good, the data get loaded and its refreshing the gui.
edit: Sometimes I get this error:
Gdk-Message: 11:13:42.848: main.py: Fatal IO error 11 (Die Ressource ist zur Zeit nicht verfügbar) on X server :0
Sometimes I click the refresh button several times and it works, but then it doesn't at some point.
My main.py looks like this:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
import gui
GObject.threads_init()
# start gui
gui.Gui()
Gtk.main()
Any ideas whats happening here?
Markus

Okay, GTK3 is not threadsafe. So I changed the program logic - doing requests in a new thread, and handling the GUI manipulation in the GUI thread ONLY. So that means I have to emit a "requests done" signal to the event loop:
Creating a new signal and registering it:
GObject.signal_new("my-custom-signal", self.window, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT,
(GObject.TYPE_PYOBJECT,))
self.window.connect("my-custom-signal", self.updateOrderListCallback)
So when I click a button, start a thread:
Thread(target=self.updateOrderListThread).start()
In that thread, do the calculations, and then emit the signal:
self.window.emit("my-custom-signal", None)
so than the callback will be called after the calculations/requests/whatever are done and it works!

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).

Multithreaded Gstreamer w/ PyGTK crashing (xcb_xlib_threads_sequence_lost)

I understand that one is not supposed to update the UI from other threads in gtk, or face consequences, but I am not sure how I can avoid that while using gstreamer.
My application crashes from time to time during video stream initialization with a following complaint:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python: ../../src/xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
In my code I have added gtk.thread_init() call right in the beginning of the GUI class:
import pygtk, gtk, gobject
gtk.gdk.threads_init()
(I have also tried gobject.threads_init(), but this doesnt seem any different). In a separate class, which is run in a separate thread, I start a gstreamer stream to a tcpserversink (this gstreamer thread is already a third thread, if someone is keeping count). And then another thread receives this data before pushing the data to xvimagesink in the end.
The xvimagesink needs a viewport, and I believe this gstreamer callback function is where sometimes gtk goes crazy, when I assign it:
def on_sync_message(self, bus, message):
...
if message_name == "prepare-xwindow-id":
# Assign the viewport
imagesink = message.src
imagesink.set_property("force-aspect-ratio", True)
imagesink.set_xwindow_id(self.window_handle.window.xid)
The self.window_handle is a pointer to self.movie_window = gtk.DrawingArea(), assigned during GUI initialization.
TL;DR Is there a safe way of using gtk with gstreamer, since I cannot avoid threading when calling gst.Pipeline("name").set_state(gst.STATE_PLAYING) and view will be a GTK drawing area ?
I think your issue is the fact that you are accessing unprotected Xlib/xcb from 2 threads - once impicitly from your Gtk+ UI and once in the thread where your gstreamer backend callback is executed - which by default is the mainloop you told gstreamer to use (or the thread default mainloop).
gtk.gdk.threads_init() is already called once the type system is initialized (if I recall correctly, correct me if I am wrong).
Use g_idle_add (or use GSource with a higher priority) (which are threadsafe) with a callback which schedules UI changes in the Gtk mainloop (run by gtk_main()).

Tkinter doesn't play nicely with multiprocessing

The following code hangs without doing anything in Python 3.2.2 in Linux:
import tkinter
from multiprocessing import Process
def f():
root = tkinter.Tk()
label = tkinter.Label(root)
label.pack()
root.mainloop()
p = Process(target=f)
p.start()
The only information I have found about this problem is issue 5527, in which it is noted that the problem is with tkinter being imported before the process is forked, and that it can be fixed by importing tkinter inside the function f, and that the problem occurs in Linux but not Solaris.
What exactly is causing this problem, and is it a bug? Is there any workaround other than to import tkinter locally everywhere I need it (which isn't very Pythonic)? Do any other modules have similar issues with multiprocessing?
My suspicion is that the problem has to do with the connection to the X server (usually a socket). If this is created before the process is fork()-ed, the child process inherits this connection. But if it tries to use it, the X server gets confused.
After a cursory look at at Tkinter.py, it looks like maybe calling the NoDefaultRoot function before starting the process might be useful. It all depends on when the connection to the X server is made.
Otherwise importing Tkinter after the fork seems the way to go.
As of September 2013, there are some additional comments on the bug report that give more insight into what the actual problem is.
http://bugs.python.org/issue5527#msg194848
http://bugs.python.org/issue5527#msg195480
Based on the above, I'm guessing something like the following is happening: Tkinter is not thread safe, so (for whatever reason) Tkinter wants to know which thread is the main thread. Tkinter assumes that the main thread when the Tkinter module is loaded will also be the main thread for program execution. When you fork or multiprocess after loading Tkinter, this assumption is broken. (For example, after a fork, the remembered main thread is in the parent, not the child.)
If you can get a thread started, just do root.mainloop() after it and it should work fine.

TKinter windows do not appear when using multiprocessing on Linux

I want to spawn another process to display an error message asynchronously while the rest of the application continues.
I'm using the multiprocessing module in Python 2.6 to create the process and I'm trying to display the window with TKinter.
This code worked okay on Windows, but running it on Linux the TKinter window does not appear if I call 'showerror("MyApp Error", "Something bad happened.")'. It does appear if I run it in the same process by calling showerrorprocess directly. Given this, it seems TKinter is working properly. I can print to the console and do other things from processes spawned by multiprocessing, so it seems to be working too.
They just don't seem to work together. Do I need to do something special to allow spawned subprocesses to create windows?
from multiprocessing import Process
from Tkinter import Tk, Text, END, BOTH, DISABLED
import sys
import traceback
def showerrorprocess(title,text):
"""Pop up a window with the given title and text. The
text will be selectable (so you can copy it to the
clipboard) but not editable. Returns when the
window is closed."""
root = Tk()
root.title(title)
text_box = Text(root,width=80,height=15)
text_box.pack(fill=BOTH)
text_box.insert(END,text)
text_box.config(state=DISABLED)
def quit():
root.destroy()
root.quit()
root.protocol("WM_DELETE_WINDOW", quit)
root.mainloop()
def showerror(title,text):
"""Pop up a window with the given title and text. The
text will be selectable (so you can copy it to the
clipboard) but not editable. Runs asynchronously in
a new child process."""
process = Process(target=showerrorprocess,args=(title,text))
process.start()
Edit
The issue seems to be that TKinter was imported by the parent process, and "inherited" into the child process, but somehow its state is inextricably linked to the parent process and it cannot work in the child. So long as you make sure not to import TKinter before you spawn the child process, it will work because then it is the child process that is importing it for the first time.
This discussion could be helpful.
Here's some sample problems I found:
While the multiprocessing module follows threading closely, it's definitely not an exact match. One example: since parameters to a
process must be pickleable, I had to go through a lot of code
changes to avoid passing Tkinter objects since these aren't
pickleable. This doesn't occur with the threading module.
process.terminate() doesn't really work after the first attempt. The second or third attempt simply hangs the interpreter, probably
because data structures are corrupted (mentioned in the API, but this
is little consolation).
Maybe calling the shell command xhost + before calling your program from that same shell will work?
I am guessing your problem lies with the X-server.

Categories