Python, thread and gobject - python

I am writing a program by a framework using pygtk. The main program doing the following things:
Create a watchdog thread to monitor some resource
Create a client to receive data from socket
call gobject.Mainloop()
but it seems after my program enter the Mainloop, the watchdog thread also won't run.
My workaround is to use gobject.timeout_add to run the monitor thing.
But why does creating another thread not work?
Here is my code:
import gobject
import time
from threading import Thread
class MonitorThread(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
print "Watchdog running..."
time.sleep(10)
def main():
mainloop = gobject.MainLoop(is_running=True)
def quit():
mainloop.quit()
def sigterm_cb():
gobject.idle_add(quit)
t = MonitorThread()
t.start()
print "Enter mainloop..."
while mainloop.is_running():
try:
mainloop.run()
except KeyboardInterrupt:
quit()
if __name__ == '__main__':
main()
The program output only "Watchdog running...Enter mainloop..", then nothing.
Seems thread never run after entering mainloop.

Can you post some code? It could be that you have problems with the Global Interpreter Lock.
Your problem solved by someone else :). I could copy-paste the article here, but in short gtk's c-threads clash with Python threads. You need to disable c-threads by calling gobject.threads_init() and all should be fine.

You have failed to initialise the threading-based code-paths in gtk.
You must remember two things when
using threads with PyGTK:
GTK Threads must be initialised with gtk.gdk.threads_init:
From http://unpythonic.blogspot.com/2007/08/using-threads-in-pygtk.html, copyright entirely retained by author. This copyright notice must not be removed.
You can think glib/gobject instead of pygtk, it's the same thing.

Related

How to make a PySide.QtCore.QTimer.singleShot call its timeout method

I am working on a complex and poorly commented Qt-based Python application. It employs a PySide.QtCore.QTimer.singleShot(int,slot) timer to delay the execution of a slot within a thread, and I am confused about how this timer works.
Here is a MWE. This example uses the approach of subclassing QThread and reimplementing run(). I put the following in a file called timertester.py:
import PySide
import time
class SubClassThread(PySide.QtCore.QThread):
def run(self):
print('called SubClassThread.run()')
self.delayed_print()
def delayed_print(self):
print('called SubClassThread.delayed_print()')
PySide.QtCore.QTimer.singleShot(1000, self.print_things)
time.sleep(2)
print('end of delayed_print()')
def print_things(self):
print('called print_things')
The code I am using to test this (call it test.py):
import sys
import time
import PySide
from timertester import SubClassThread
print('Test: QThread subclassing')
app = PySide.QtCore.QCoreApplication([])
sct = SubClassThread()
sct.finished.connect(app.exit)
sct.start()
sys.exit(app.exec_())
The output of python test.py:
Test: QThread subclassing
called SubClassThread.run()
called SubClassThread.delayed_print()
end of delayed_print()
The curious part is that the callable passed to QTimer.singleShot never seems to get called (there is no called print_things() in the output!) I would greatly appreciate any clarity that you can shed on this. I feel that I am missing some simple ingredient of the Qt framework. Please bear with me- I did spend some hours searching for answers to this.
The default implementaion of QThread.run() calls QThread.exec(), which starts the thread's own event-loop. A QTimer requires a running event-loop, and its timeout() signal will be emitted in the thread it is started in. Your implementation of run() does not start an event-loop, so the timer will do nothing.
As #ekhumoro pointed out, my reimplementation of run() failed to call exec_(). This was the main problem. It took me a minute to find out where to put the exec_() call and how to properly quit() the thread when its work was finished. Here is the code that works.
Class definition, timertester.py:
import PySide
class SubClassThread(PySide.QtCore.QThread):
def __init__(self,parent=None):
print('called SubClassThread.__init__()')
super(SubClassThread,self).__init__(parent)
def run(self):
print('called SubClassThread.run()')
self.delayed_print()
self.exec_()
def delayed_print(self):
print('called SubClassThread.delayed_print()')
PySide.QtCore.QTimer.singleShot(1000, self.print_things)
print('end of delayed_print()')
def print_things(self):
print('called print_things')
self.quit()
Application, test.py:
import sys
import PySide
from timertester import SubClassThread
print('Test: QThread subclassing')
# instantiate a QApplication
app = PySide.QtCore.QCoreApplication([])
# instantiate a thread
sct = SubClassThread()
# when the thread finishes, make sure the app quits too
sct.finished.connect(app.quit)
# start the thread
# it will do nothing until the app's event loop starts
sct.start()
# app.exec_() starts the app's event loop.
# app will then wait for signals from QObjects.
status=app.exec_()
# print status code and exit gracefully
print('app finished with exit code {}'.format(status))
sys.exit(status)
And, finally, the output of python test.py:
Test: QThread subclassing
called SubClassThread.__init__()
called SubClassThread.run()
called SubClassThread.delayed_print()
end of delayed_print()
called print_things
app finished with exit code 0
What I learned about QTimers: the call to delayed_print() finishes while the timer is still running, and by putting quit() in the method called by the timer, the app does not exit until after that method has been called.
If there are any further comments on this code, or on the aspects of Qt relating to this simple app, please do post! I have had some trouble finding accessible PyQt/PySide information for beginners.

python threads, multithreading with threading.thread

I have to launch a thread in the background but the output seems to be following the thread rather than sticking within main. I have something like this:
import threading
def work()
while True:
do stuff
def recieve()
while True:
await instruction
#main
recieve()
if instruction == "do work"
threading.Thread(target=work()).start()
I have many other instructions that get recieved and dealt with sequentially but as work() takes a long time to complete I need the thread, now, I would expect a thread to be launched that does work in the background whilst we continue to await instruction except this doesn't happen. What happens is focus is kept on the newly created thread so further instructions can't be received.
Why is this? What is wrong?
Many thanks
receive() never end because of endless loop; thread does not start.
Start thread first.
if instruction == "do work":
threading.Thread(target=work).start()
recieve()
and drop () from threading.Thread(target=work()).start(). work() make work function call run in main thread.

interactive scripts with threads

I'm trying to wrap the blocking calls in pyaudio with a thread to give me non-blocking access through queues. However, the problem I have is not with pyaudio, or queues, but with the issue of trying to test a thread. In keeping with "strip the example down to the minimum possible", all the pyaudio stuff has vanished, to leave only the thread class, and its instantiation in a main.
What I was hoping for was an object that I could create, and leave to get on with its stuff in the background, while I do control things with the console or tk. I figure the following max-stripped down example should have the thread doing stuff, while main runs and asks me if it is working. The raw_input prompt never appears. I would not be surprised at this if I was running it from IDLE, which is not thread safe, but I get the same behaviour if I run the script directly from the OS. I was prepared to see the raw input prompt disappear up the screen pushed by 'running' prints, but not even that happens. The prompt never appears. What's going on? It does respond to ctrl-C and to closing the window, but I'd still like to be able to see main running.
import threading
import time
class TestThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.running=True
self.run()
def run(self):
while self.running:
time.sleep(0.5)
print 'running'
def stop(self):
self.running=False
if __name__=='__main__':
tt=TestThread()
a=raw_input('simple stuff working ? -- ')
tt.stop()
You should start the thread with self.start() instead of self.run(). In this case you are just running the thread function like any other normal function.
Normally you do not inherit from Thread. Instead, you use Thread(target=func2run).start()

Threading not working with pygtk and always hangs

Here is a small implementation of my problem. when i click "Press" button it hangs and some time it is not. How can overcome this hang ?
here is my code.
To install Sane "sudo apt-get install python-imaging-sane"
import gtk
import gtk.glade
import threading
import os
import sane
gtk.gdk.threads_init()
class process(threading.Thread):
def __init__(self,main_object):
self.main_object = main_object
threading.Thread.__init__(self)
def run(self):
sane_version = sane.init()
sane_devices = sane.get_devices()
gtk.threads_enter()
self.main_object.pb.set_fraction(self.main_object.value)
gtk.threads_leave()
self.main_object.value = self.main_object.value + .1
class main():
def __init__(self):
#GUI
window = gtk.Window()
window.set_default_size(200,200)
vbox = gtk.VBox(False,0)
self.pb = gtk.ProgressBar()
button = gtk.Button("Press me ")
button.connect("clicked",self.fun_to_call)
vbox.pack_end(self.pb)
vbox.pack_end(button)
button.show()
self.pb.show()
vbox.show()
window.add(vbox)
self.value = .1
window.show()
def fun_to_call(self,data=None):
pro = process(self)
pro.start()
main()
gtk.main()
Looking at the PyGTK FAQ on the subject, it would seem that you're missing a step; (the whole FAQ entry is an interesting read though)
...you have to wrap your main loop with gtk.threads_enter()/gtk.threads_leave(), like this:
gtk.threads_enter()
gtk.main()
gtk.threads_leave()
They also have some advice how to use finally to assure that gtk.threads_leave() is always run even if the code throws an exception.
Note though that using this kind of GUI threading isn't necessarily portable, some operating systems may have problems with a non main thread running GUI operations even with correct locking.
The internals of the standard python implementation ("cpython") are not thread-safe.
So a lock (the Global Interpreter Lock) is used to assure that only one python thread is running at a time. This lock is released e.g. when a running thread does I/O. But if the lock isn't released, the other threads won't get a chance to run.
I suspect that this is the source of the problem. It depends on the implementation of the sane bindings.
You could try using the multiprocessing module instead of threading. Multiprocessing starts a completely different process for the long-running task, so it is not bound by the GIL. But you'd have to use the facilities provided by multiprocessing like Queue and Pipe to exchange information between the processes.

Why is the work being done by my QThread starving the main thread?

Here's my lovely thread I've written based on QThread. You'll notice it has an event queue. After 4 seconds an event fires and does some work in doWork. doWork should sleep in between all its printing and give other threads a chance to run. Suffice it to say with all the printing and sleeping doWork runs long enough that another thread really should get some time to execute.
from PySide.QtCore import *
from PySide.QtGui import *
class DoStuffPeriodically(QThread):
def __init__(self):
super(DoStuffPeriodically, self).__init__()
def doWork(self):
#... do work, post signal to consumer
print "Start work"
for i in range(0,100):
print "work %i" % i
QThread.msleep(10)
print "Done work"
return
def run(self):
""" Setup "pullFiles" to be called once a second"""
self.timer= QTimer()
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.doWork)
self.timer.start(4000)
self.exec_()
Here's the top-level QT widget I'm using to control my thread. Its basically just a push button that starts/stops the thread.
class Widg(QWidget):
def __init__(self):
super(Widg, self).__init__()
self.thread = DoStuffPeriodically()
self.startStopButton = QPushButton()
hBoxLayout = QHBoxLayout()
hBoxLayout.addWidget(self.startStopButton)
self.startStopButton.pressed.connect(self.startStopThread)
self.setLayout(hBoxLayout)
self.threadRunning = False
def startStopThread(self):
if self.threadRunning:
print "Stopping..."
self.thread.exit(0)
self.threadRunning = False
print "Stopped"
else:
print "Starting..."
self.thread.start()
self.threadRunning = True
print "Started"
if __name__ == "__main__":
from sys import argv
qApp = QApplication(argv)
widg = Widg()
widg.show()
qApp.exec_()
If I click the startStopButton, I expect to see the thread begin printing
Starting...
Started...
Start Work
work 0
work 1
...
work 99
Done Work
But what I want to do is to be able to stop the thread while its doing work. I expect something along the lines of
Starting...
Started...
Start Work
work 0
work 1
...
work N
Stopping...
work 99
Done Work
Stopped...
Instead, the worker thread appears to be preventing the main thread from executing? And I have to wait for the work to be done before I can click the startStopButton, giving me
Starting...
Started...
Start Work
work 0
work 1
...
work 99
Done Work
Stopping...
Stopped...
It doesn't matter how long doWork runs. I`ve upped it to loop 10000 times. It doesn't appear to ever give time back to the main thread and the widget is unresponsive. Am I doing something thats preventing real threading from actually working?
(I'm using python 2.7 and pyside 1.10.)
Update
If I modify run to do the work directly, not based on the QTimer the threading appears to work correctly. Ie change run to:
def run(self):
self.doWork()
return
This doesn't solve my problem, because I want to run using the event queue. I suspect therefore, that this is some kind of signals/slots problem where the QTimer signal is associated with the wrong thread.
Note I'm not exeriencing that exit or quit blocks until the work is done. I'm simply experiencing the threading not work at all. Namely the main window is blocked and I can't even click the button to even initiating quiting the thread
The problem is that the QThread method is doing the work. The thread affinity of QThread is always the thread that created the QThread. Therefore, the signal tells QThread's owning thread to execute doWork--in this case the main thread. So even though doWork is defined in this QThread, the work is done by the main thread. I know kind of mind twisting. To explain, let me begin by quoting the docs
QThread object is living in another thread, namely, the one in which it was created.
so when this signal/slot connection is setup
self.timer.timeout.connect(self.doWork)
it, by default is an AutoConnection:
(default) If the signal is emitted from a different thread than the receiving object, the signal is queued, behaving as Qt::QueuedConnection. Otherwise, the slot is invoked directly, behaving as Qt::DirectConnection. The type of connection is determined when the signal is emitted.
The source of the signal is my QThread, because QTimer was created in the run method, but the destination is the main thread. Its being queued in the main thread's event queue! The solution is to create a second worker QObject which will have the affinity of the current thread:
class Worker(QObject):
def __init__(self, parent):
super(Worker, self).__init__(parent=parent)
def doWork(self):
#... do work, post signal to consumer
print "Start work"
for i in range(0,1000):
print "work %i" % i
QThread.msleep(100)
print "Done work"
return
Then run becomes:
def run(self):
""" Setup "pullFiles" to be called once a second"""
print "Running..."
self.worker = Worker(parent=None) #affinity = this thread
self.timer= QTimer() #affinity = this thread
print self.timer.thread()
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.worker.doWork)
self.timer.start(4000)
self.exec_()
print "Exec_ done"
And this works. The source and destination of the signal is all in one thread and doesn't traverse back to the main thread. Voila!
From QThread::exit() documentation:
Tells the thread's event loop to exit with a return code.
Your doWork is a single event in the event loop. The event loop called your event, therefore it waits for it to finish. exit is yet another event, enqueued in the event loop and waiting for doWork to finish. The msleep helps the responsiveness of your GUI (gives it time to repaint and execute the button handler), but it really does not enable the exit event to sneak in somehow.
If you want your doWork to be interruptable at any time, you must change your logic. Make the timer fire more often and increment only by one. exit then can act anytime in between.

Categories