Real-Time-Plotting using pyqtgraph and threading - python

this is a bit longer, the first part is just a description of the problem, the second one the question if my "fix" is correct.
I started with python programming. I created a program that communicates with an Arduino that reads the temperature of a furnace of our melting lab. The temperature is then used in a PID algorithm and an output is set to the Arduino. The communication is done via pyserial. So far, everthing works, including live plotting of the temperature signals, PID-variables and so on. The script includes a the main loop and 3 threads (serial communication, a datashifter that reads from serialport, the set temperature from the QWidget and the output of the PID algorithm. This values are used to create an array for displaying within pyqtgraph. Finally, the third thread shifts the data from the datashifter to the QWidget.
When using my Linux-Notebook, everything works fine, and the GUI never stops updating. In contrast, when using any Windows-Host, i have the problem that some pyqtgraphs stop to refresh. The behavior is strange, because i set all data at more or less the same time, with the same numpy array (just different columns) - some plots refresh longer (hours), some stop earlier (minutes). After searching more or less the hole internet ;-) I think that I found the problem: Its the passing of data from from a thread to the GUI. Some dummy code to explain what's going on:
DataUpdaterToGUI(QThread):
#sets the QWidget from main loop
def setGUI(self, gui):
self.gui = gui
def run()
while True:
with lock(): # RLock() Instance
copyArray = self.dataArray[:] # copy the array from the shifter
self.gui.plot1.copyArray(dataArray[:, 0], copyArray[:, 1])
self.gui.plot2.copyArray(dataArray[:, 0], copyArray[:, 2])
# self.gui.update()
# QApplication.instance().processEvents()
Neither calling self.gui.update() nor processEvents() has any influence on the outcome: The plots stop redrawing after a while (on windows).
Now i have a very simple example, and just want to make sure if I'm using the threading-stuff correctly. It works fine, but I have some questions:
Does the signal-slot approach copy the passed data?
Why is it not necessary to call the update() method of the QWidget?
Do I have to use any kind of locks when using signals?
class Main(QWidget):
def __init__(self):
super().__init__()
self.layout = QGridLayout(self)
self.graph = pg.PlotWidget()
self.graph.setYRange(0,1000)
self.plot = self.graph.plot()
self.layout.addWidget(self.graph,0,0)
self.show()
def make_connection(self, data_object):
data_object.signal.connect(self.grab_data)
#pyqtSlot(object)
def grab_data(self, data):
print(data)
self.plot.setData(data)
class Worker(QThread):
signal = pyqtSignal(object)
def __init__(self):
super().__init__()
def run(self):
self.data = [0, 1]
i = 2
while True:
self.data[1] = i
self.signal.emit(self.data)
time.sleep(0.01)
i += 1
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = Main()
worker = Worker()
widget.make_connection(worker)
worker.start()
sys.exit(app.exec_())

Does the signal-slot approach copy the passed data? The signals are thread-safe and when transferring data they make a copy so the thread that precedes the data and the thread that consumes it (GUI Thread) will not have conflicts
Why is it not necessary to call the update() method of the QWidget? Actually pyqtgraph calls the update method, plot is a PlotDataItem, so if we check the source code of setData() method, it calls the updateItems() method, in that method the setData() method of the curve or scatter attribute is called (according to the type of graphics), in the case of curve its setData() method calls updateData(), and the updateData() method calls update, and in the case of the scatter its setData() method calls addpoint(), and addPoints() calls invalidate(), and this invalidate() method calls update().
Do I have to use any kind of locks when using signals? No, as the signals are thread-safe so Qt already has the guards set to avoid the collision.

Related

Is there in PyQt a signal that warns me on the presence of data in the input serial buffer?

I'm trying to create a GUI application with PyQt for a board that send me data packets with fixed lenght. The application reads these packets and shows some values contained in them. Leaving out the structure of the packets, my first attempt was to connect a timeout signal with my Update() function that update the values in the GUI:
timer = pg.QtCore.QTimer()
timer.timeout.connect(main.Update)
timer.start(10)
This solution emits a signal every 10 ms that activates the Update() routine.
My question now is simple.
Since I don't like that, every some time, the application calls the Update() function, can I create a signal that warns on the presence of data in the input buffer? In this case the Update() function would be called only if necessary.
Is it possible or the only solution is the polling?
I would just create a worker QObject in a separate thread that constantly checks the input buffer and emits a signal to the main thread when data is available
class Worker(QObject):
data_ready = pyqtSignal(object)
#pyqtSlot()
def forever_read(self):
while True:
# Check input buffer
data = something.read()
if data:
self.data_ready.emit(data)
time.sleep(0.1)
In the main thread
class WindowOrObject(...)
def __init__(self):
...
self.worker = Worker(self)
self.thread = QThread(self)
self.worker.data_ready.connect(self.handle_data)
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.forever_read)
self.thread.start()
#pyqtSlot(object)
def handle_data(self, data):
# do something with data
...

Display busy progressBar for long process, no thread

I'm writing a PyQt GUI application, and I would like to display a busy progress bar for one of my function, which takes time to finish. Here is the code:
self.progress = QtGui.QProgressDialog("Canceling...", None, 0, 0, self)
self.progress.setWindowTitle("Canceling refresh")
self.progress.show()
timer = QtCore.QTimer()
timer.singleShot(0, self.loadNotifications)
while timer.isActive():
app.processEvents()
For now, I tried something with a QTimer, but it doesn't work. The CPU-bound function is loadNotifications.
I would like to start it in a sort of a thread, and while it's running, update the QProgressBar.
I could of course create a QThread class around the function loadNotifications, but it's a bit overkill for what I want to do: simply display a smooth progressBar while the function is running.
Do you have any idea ?
I've no experience with PyQT, but in tkinter, I usually accomplish this by making my slow function periodically call some update_progbar function passed to it. This update_progbar function then updates the progress bar in the same thread as the slow function (which is therefore temporarily on hold).
Rudimentary:
def update_progbar(prog):
print(prog)
def slow_function(prog_func):
for i in range(100000):
if i%1000 == 0:
prog_func(i / 100000.0)
do_something()

Signals and Slots PyQt clarification

I have noticed that there are a lot of users, myself included, who don't quite grasp the concept of signals and slots in Qt. I was hoping to get some clarification on the following:
#I have a function that runs as soon as the GUI is built, this takes the information from
#a list and puts it into a string which is then uploaded to a texbox. At the bottom of this
#loop, I want it to call a function in the parent thread via signals and slots, as
#recommended by other users.
class MainWindow(QtGui.QMainWindow):
#all the code needed to build the GUI
thread_mythread = threading.Thread(target = self.updateText, args = ())
thread_mythread.start()
def clearText(self):
self.TextEdit.clear()
def updateText(self):
self.trigger.connect(self.clearText)
while True:
self.trigger.emit()
NewString = list.pop(0)
#I think I may have to use append, as setText() is not safe outside of the parent thread
self.TextEdit.append(NewString)
Although probably terribly incorrect, I attempt to use signals. Is this the proper way to do it? I also get an error that says that the Main Window object has no attribute "trigger",why is this?
thank you.
The reason you get that error is exactly the reason described by the error message - the signal trigger has not been defined anywhere in your class. You need to define it before you can emit it.
Signals and slots are used to communicate between different objects. In your example you are trying to do everything from within your MainWindow class and there is no interaction with other objects. You also only need to make the call to connect() once. You would typically call this either in the class constructor or from your main function after instantiating the objects you want to connect together.
Take a look at http://pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html for some examples of how to use signals and slots properly in PyQt.
For threading, use QThread rather than threading.Thread as it is better integrated with the Qt framework. This post shows some simple examples of how to use QThread in PyQt. The second method (using moveToThread()) is considered to be the most correct way to create new threads.
The basic idea for your kind of problem is:
handle GUI operations from the main thread
handle blocking operations (in your case the while loop) in a separate thread
emit signals from the worker thread to call functions (slots) in the main thread and vice versa
Also note that:
You cannot call any methods of QWidget its descendents from a secondary thread
Signals can also send data if you need to pass it between threads
To add to #user3419537 good answer. A very quick threading example:
from PyQt4.QtCore import QObject, pyqtSlot, pyqtSignal, QThread, \
Q_ARG, Qt, QMetaObject
class MyWorker(QObject):
# define signal
clear = pyqtSignal()
update_text_signal = pyqtSignal(str) # passes a string back
finished = pyqtSignal()
def __init__(self, parent=None):
super(MyWorker, self).__init__(parent)
# Add functions etc.
#pyqtSlot(list)
def update_text(self, string_list):
#Intensive operation
self.clear.emit() # moved outside of while
while(True):
#This is infinite loop so thread runs forever
new_string = self.string_list.pop(0)
self.update_text_signal.emit(new_string) # Fixed this line
#Finished
self.finished.emit()
Then in your MainWindow class
self.my_thread = QThread()
self.handler = MyWorker()
self.handler.moveToThread(self.my_thread)
self.handler.clear.connect(self.clearText)
self.handler.update_text_signal.connect(self.update_line_edit)
self.handler.finished.connect(self.my_thread.quit)
# Start Thread
self.my_thread.start()
#pyqtSlot(str)
def update_line_edit(self, text):
self.TextEdit.append(text)
QMetaObject.invokeMethod(self.handler, 'update_text',
Qt.QueuedConnection,
Q_ARG(list, string_list))
You will need to call self.my_thread.quit() before your application closes to stop the thread and avoid the error: QThread: Destroyed while thread is still running
Please read docs for QMetaObject.invokeMethod.

Wrap a Python class in an asynchronous PyQt class

I am developing my pet project. It is a small app which downloads my inbox folder from Facebook. I want it to be available in both CLI and GUI (in PyQt) mode. My idea was that I write a class for the communication, and then the front-ends. I know that the downloading process is blocking which is not problem in CLI mode but it is in GUI.
I know that there is QNetworkAccessManager but I would have to re-implement the class that I have already written and then maintain two classes the same time.
I was searching for a while and I came up with one solutions where I subclass QObject and my FB class, implement the signals, than create a QThread object and use moveToThread() method which would be OK, but I must take care of creating and stopping the tread.
Is it possible to wrap my Python class to something to make it behave like QNetworkAccessManager? So the methods would return immediately and the object will emit signals when the data is ready.
Update
Thank you the comments. I have 2 main classes. The first is called SimpleGraph which just hides urllib2.urlopen(). It prepares the query and returns the decoded json got from Facebook. The actual work is happening in FBMDown class:
class FBMDown(object):
def __init__(self, token):
self.graph = SimpleGraph(token)
self.last_msg_count = 0
def _message_count(self, thread_id):
#TODO: Sanitize thread_id
p = {'q': 'SELECT message_count FROM thread WHERE thread_id = {0} LIMIT 1'.format(thread_id)}
self.last_msg_count = int(self.graph.call(params=p, path='fql')['data'][0]['message_count'])
return self.last_msg_count
So here when _message_count is called it returns the number of messages of the given thread id. This method works great in CLI mode, blocking is not a problem there.
I want to wrap this class into a class (if it is possible) which works asynchronous like QNetworkAccessManager, so it would not block the GUI but it would emit a signal when the data is ready.
The only technique I know now is to subclass QObject. It looks like this:
class QFBMDown(QtCore.QObject):
msg_count_signal = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
QtCore.QObject.__init__(self, parent)
def get_msg_count(self):
#Here happens the IO
time.sleep(2)
self.msg_count_signal.emit(1)
And this is my Windows class:
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.centralwidget = QtGui.QWidget(self)
self.button_getmsgcount = QtGui.QPushButton(self.centralwidget)
self.setCentralWidget(self.centralwidget)
self.i = 0
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.block_indicator)
self.timer.start(20)
#self.worker = QtCore.QThread()
self.fbobj = QFBMDown()
#self.fbobj.moveToThread(self.worker)
#self.worker.start()
self.button_getmsgcount.clicked.connect(self.fbobj.get_msg_count)
self.fbobj.msg_count_signal.connect(self.show_msg_count)
def block_indicator(self):
self.button_getmsgcount.setText(str(self.i))
self.i += 1
def show_msg_count(self, data):
print 'Got {0} msgs in the thread'.format(data)
Now when I press the button the GUI blocks. If I de-comment line 13, 15, 16 (where I create a thread, move fbobj into this thread then start it) it does not block, it works nearly as I want but I have to do everything by hand every time and I have to take care of shutting down the thread (now I implement QMainWindow's closeEvent method where I shut down the thread before quit but I'm sure that this is not the right way to do).
Is it even possible what I want to do? I would not want to implement a new class and then maintain two classes which are doing the same thing.
The reason I am so insisted to do it like this is to make my app work without Qt but give a nice gui for those who don't want to type command line arguments.

wxPython OnExit() not stoping thread?

I'm trying to create a thread, and end it when the frame of a wxPython app is closed. Here's my code:
#! /usr/bin/env python
import time, wx
from threading import Thread
class UpdateThread(Thread):
def __init__(self):
self.stopped = False
Thread.__init__(self)
def run(self):
while not self.stopped:
self.updateExchange()
time.sleep(1)
def updateExchange(self):
print("Updated...")
class tradeWindow(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, "Exchange", size = (500, 190))
panel = wx.Panel(self)
def OnExit(self):
tickThread.stopped # I've also tried: tickThread.stopped = True
tickThread = UpdateThread()
tickThread.start()
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = tradeWindow(parent = None, id = -1)
frame.Show()
app.MainLoop()
But when I close the frame, it keeps printing.
There is no magic Frame.OnExit method. You're mixing up frames and apps. Frames, like other windows, close. Apps exit.
So, you could put your code in your app class's OnExit method. But that's not what you want here.
Look at this simple tutorial for the OnExit method. Again, it's not what you want here, but you should know how it works (and which object it gets called on).
You can always bind EVT_CLOSE to call anything you want in your window. But you have to do this explicitly.
Normally, you'd call the method OnClose or OnCloseWindow. Calling it OnExit is just going to lead to major confusion (as it has).
The event handler method that you bind to has to actually be an event handler, meaning it takes an event parameter (as well as the self).
Next, if you add an EVT_CLOSE handler, you're overriding the default one, meaning that Destroy never gets called unless you do it yourself.
Here's a tutorial on binding EVT_CLOSE, which shows all of the above steps.
Finally, as DavidRobinson explained, just doing tickThread.stopped won't do anything; you have to set it to True.
Putting it all together:
class tradeWindow(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, "Exchange", size = (500, 190))
panel = wx.Panel(self)
self.Bind(wx.EVT_CLOSE, self.OnClose)
def OnClose(self, event):
tickThread.stopped = True
self.Destroy()
One more note:
In any serious threaded program, if you want to share a value between threads, you generally need to synchronize it with some kind of sync object. If you're waiting for a signal from another thread, the typical way to handle that is with a Condition. If, on the other hand, you just want to share the value, you can use a Lock.
If you really know what you're doing, you can often get away with letting the Global Interpreter Lock handle synchronization for you. But in general, this is a bad idea. For example, your main thread could be running on core 0, and your background thread on core 1, and there is nothing in the Python language definition that guarantees that the computer will ever copy the updated value from core 0's cache to core 1's. So, your background thread could be spinning forever, watching the old value and never getting the new value to look at. As it turns out, with CPython 2.0-3.3 on an x86, this can't possibly happen with your code—but on less you can prove that (or at least identify the cases that are safe), don't count on it.
Finally, you asked whether a daemon thread is an appropriate solution. From the docs:
The significance of this flag is that the entire Python program exits when only daemon threads are left.
In other words, your program can exit without having to stop your daemon threads. But…
Note Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.) may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable signalling mechanism such as an Event.
Try setting tickThread.setDaemon(True) before tickThread.start() - Daemon threads should exit with their parent threads.

Categories

Resources