How to implement QLCDNumbers with PyQt? - python

By designing a GUI for my python script which works with PySerial for implementing some serial interface functions I want to have shown up some parameters reading out a source.
So this GUI for example has to show the actual voltage which is represented by the parameter volt_act.
I thought that I can connect the QLCDNumber lcdVoltage with the following code:
self.connect(self, QtCore.SIGNAL("selfVoltage"), self.lcdVoltage, QtCore.SLOT("display(int)"))
And at the point I want to read the voltage I emit the parameter:
self.emit(QtCore.SIGNAL("selfVoltage"), volt_act)
But that doesn't work. How can I correctly implement a QLCDNumber where the parameter is updated in real-time when I emit it?

From docs:
Short-circuit signals do not have a list of arguments or the
surrounding parentheses.
Short-circuit signals may only be connected to slots that have been
implemented in Python. They cannot be connected to Qt slots or the
Python callables that wrap Qt slots.
You need to declare the variable type explicitly, if you intend to use Qt slots:
self.connect(self, QtCore.SIGNAL("selfVoltage(int)"), self.lcdVoltage, QtCore.SLOT("display(int)"))
and
self.emit(QtCore.SIGNAL("selfVoltage(int)"), volt_act)
But, I'd really suggest you to use the new style signals.
First, you'd define a signal as class variable:
voltage = QtCore.pyqtSignal(int)
then connect it:
self.voltage.connect(self.lcdVoltage.display)
and finally, you'd emit:
self.voltage.emit(volt_act)

Related

Is it possible to use PyQt5 QtSerialPort without GUI's or classes?

The point would be to have the functionality of reading only when there is something to be read, instead of using pyserial which doesn't have a special method for that. I guess this may go into a bigger question of whether signals and slots could be used without a GUI classes (that inherit from other objects). I could get the serial port to write, but not read, with
from PyQt5 import QtCore, QtSerialPort
serial_port = QtSerialPort.QSerialPort('COM3')
serial_port.open(QtCore.QIODevice.ReadWrite)
serial_port.write(bytes([255]))
def handle_ready_read():
while serial_port.canReadLine():
print(serial_port.readAll())
print('here')
serial_port.close()
serial_port.readyRead.connect(handle_ready_read)
Nothing prints out, even though something is read when using pyserial.
You do not need GUI to use Qt. There is a dedicated GUI module in Qt and what does not depend on it, doesn't need a GUI.
However, to use slots and signals you need to run Qt's event loop. The regular way would be to use a QCoreApplication.
app = QCoreApplication([])
# setup your serial port here
sys.exit(app.exec_())

Using setattr between QThreads in PyQt5

I have a program that contains two main classes:
A GUI class that inherits from QWidget, and has several user input and output fields (graphs, spin boxes etc.)
A Serial Monitor class that inherits from QObject and contains various loops for continuously polling serial attached devices in accordance with a set of parameters (sample period, polling commands, etc.)
An instance of the Serial Monitor class is created from within the GUI class and moved to a secondary thread like so:
# Create Serial monitor instance in this thread
self.serial_monitor = Serial_Monitor(
formatter=self.poll,
prompt="2 poll\n")
# Create a secondary QThread to run the serial monitor
self.serial_thread = QThread()
# Move the serial monitor to the secondary thread and start
self.serial_monitor.moveToThread(self.serial_thread)
self.serial_thread.start()
My question arises when trying to directly pass the output of user interface items (spin boxes, buttons etc.) to variables in the Serial Monitor. My main aims are:
I want to avoid creating several pyqtSlot functions in the Serial Monitor just to set each variable, as it makes adding further variables a faff and bloats the code.
I would also like to keep all setting of Serial Monitor variables in the correct thread, rather than setting them in the GUI command thread.
My initial solution to directly setting variables from the GUI was this:
self.set_box_period.valueChanged.connect(
lambda val: setattr(self.serial_monitor, "sample_period", val))
It avoids creating any new methods in the Serial Monitor, and appears to set the variables just fine, however I am not sure if it is "thread safe", and further more am not sure how to find out.
After some digging I found out that you can re-implement the __setattr__ method within a class like so:
#pyqtSlot()
def __setattr__(self, name, value):
"""
------------------------------------------------------------------------
Re-implementation of "getattr" method to correctly wire up to slots
------------------------------------------------------------------------
"""
self.__dict__[name] = value
self.logger.info(F"Set {name} to {value}")
The logging output indicates that every Serial Monitor variable is set within the Serial Monitor thread, so this solution does work, however I am not sure if it is necessary.
To sum up, do I need to worry about re-implementing __setattr__ in my Serial Monitor class, or is my initial lambda implementation perfectly safe to use?
Thanks in advance!

QThread use in Python

I am fairly new in writing bigger programs in Python (I was only writing short scripts before). The program I'm about to write recives data from an external device, saves it to database and displays it in GUI (continuously). Since handling the data is time consuming I want to use threads (PySide QtCore QThreads to be specific). The best way to do this that I can think of is to set up two threads, one for the database processes and one for the handling of serial port, with GUI running in the MainThread. Now I've read a whole bunch of stuff about proper/improper QThreading, starting with Maya's post and digging deeper, up to this post where I found that:
When to subclass and when not to?
If you do not really need an event loop in the thread, you should subclass.
If you need an event loop and handle signals and slots within the thread, you may not need to subclass.
Question 1: Now the first thing I don't know is which way to go (subclassing or not). My guess is subclassing, since (Question2), but I'm not entirely sure and for now I'm sticking to moveToThread().
Question 2: The other thing I have no clue about is how to make the actions in my threads event-driven (the SerialThread receives the data from the device, sends it via signal to the DatabaseThread, the DatabaseThread collects the data and puts it in the database).
Question 3: When using moveToThread() I get AttributeError: 'PySide.QtCore.QThread' object has no attribute 'my_awesome_method' for every method I write for my class. It's clear to me that I don't understand the principle of operation here. How do I implement my methods that I want my_class to have while using my_class.moveToThread(my_thread)?
I've found all tutorials possible on QThreads and C++, so this discussion on QThreads in Python is interesting, but does not explain everything I want to know. I need a simple example with signals, slots and an exact explanation on how to use run(). Do I decorate it? Why? How? How does it work?
The short version of my code looks like this:
from PySide import QtCore
from app.thrads.gui.gui import Gui #my own class
from app.threads.db.db import Database #my own class
from app.threads.serial.serial import Serial #my own class
class AppManager():
def __init__(self):
self.gui = Gui()
self.db = Database()
self.db_thread = QtCore.QThread()
self.db.moveToThread(self.db_thread)
self.db_thread.start()
self.serial = Serial()
self.serial_thread = QtCore.QThread()
self.serial.moveToThread(self.serial_thread)
self.serial_thread.start()
and my Database and Serial class look somewhat like this:
from PySide import QtCore
from .db_signal import DbSignal
class Database(QtCore.QObject):
def __init__(self):
super(Database, self).__init__()
self.signal = DbSignal()
def my_awesome_method(self):
''' does something awesome '''
pass
Question 1
I think the best way is the easiest: don't subclass, just create two different threads. In the first, move the Database object, in the second the Serial one. As it, you won't make mistakes in the implementation of your sub-classed threads,and bug fixing will be quicker.
Question 2
I don't know the architecture of your application, but you could do as follows, after creating the threads and moving the worker objects in them:
self.serial_thread.started.connect(self.serial.start)
self.serial_thread.finished.connect(self.serial.stop)
self.db_thread.started.connect(self.db.start)
self.serial_thread.finished.connect(self.db.stop)
self.serial.data_ready.connect(self.db.handle_new_data)
# Starting the database first, as it we won't lose a single packet of data
self.db_thread.start()
self.serial_thread.start()
In fact, the advantage of QThreads is that they doesn't really modify the code.
Question 3
I think the problem is you're trying to call my_awesome_method with the QThread where your data base or your serial listener has been moved to. On the contrary, you should call the method with the object itself, as if it wasn't in a different thread !
# Bad!
obj.moveToThread(thread)
thread.method_of_obj(param)
# Good!
obj.moveToThread(thread)
obj.method_of_obj(param)

Using external GUI libraries to make user interfaces in Autodesk Maya

I develop tools in Autodesk Maya. Many of the tools I build have simple windowed GUIs for the animators and modellers to use. These GUIs often contain what you'd normally expect to see in any basic window; labels, lists, menus, buttons, textfields, etc. However, there are limitations to the complexity of the UIs you can build with the available tools, specifically in the types of available widgets.
I'm interested in using some of the more advanced wxPython widgets such as the ListView (grid), Tree, etc. This would involve using a complete wxFrame (window) to display the whole UI, which would essentially mean that window would no longer be tied to Maya. Not a deal breaker, but it means when Maya is minimized, the window won't follow suit.
I've tried something like this before with tkinter as a test, but found that it needed a MainLoop to run in its own thread. This is logical, but in my case, it conflicts with Maya's own thread, essentially making Maya hang until the window is closed. This is due to the fact that Maya runs all scripts, be they MEL or Python, in a single thread that the main Maya GUI shares. This is to prevent one script from, say, deleting an object while another script is trying to do work on the same object.
wxPython has this same "mainloop" methodolgy. I'm wondering if there's any way around it so that it can work within Maya?
I'm not sure if this is germane, but some googling turns up that PyQt is pretty popular inside of Maya. You could try the technique here or here (explained here with source code) of creating a new threadloop via Maya and executing inside of that. It seems Maya has a module included that sets up a new thread object, with a QApplication inside it:
def initializePumpThread():
global pumpedThread
global app
if pumpedThread == None:
app = QtGui.QApplication(sys.argv)
pumpedThread = threading.Thread(target = pumpQt, args = ())
pumpedThread.start()
and then sets up a function to process the Qt events:
def pumpQt():
global app
def processor():
app.processEvents()
while 1:
time.sleep(0.01)
utils.executeDeferred( processor )
You can probably do something similar with wxPython as well. (utils.executeDeferred is a Maya function.) Be sure to check out how to create a non-blocking GUI on the wxPython wiki. Instead of processEvents(), you'll want to set up an event loop and check for "Pending" events inside the (hopefully renamed?) pumpQt function above. (The wxPython source has a Python implementation of MainLoop.) Likely this should be done through the app.Yield() function, but I'm not sure.
def pumpWx():
global app
def processor():
app.Yield(True)
while 1:
time.sleep(0.01)
utils.executeDeferred( processor )
def initializePumpThread():
global pumpedThread
global app
if pumpedThread == None:
app = wx.App(False)
pumpedThread = threading.Thread(target = pumpWx, args = ())
pumpedThread.start()
The wxPython docs indicate SafeYield() is preferred. Again, this seems like it could be a first step, but I'm not sure it will work and not just crash horribly. (There's some discussion about what you want to do on the wxPython mailing list but it's from a few minor versions of wx ago.) There is also some indication in various forums that this technique causes problems with keyboard input. You might also try doing:
def processor():
while app.Pending(): app.Dispatch()
to deal with the current list of events.
Good luck!
I don't know if there is a way around a mainloop for the gui, since it is needed to handle all event chains and redraw queues.
But there are several means of inter-process communication, like pipes or semaphores. Maybe it is an option to split your Maya extension into the actual plugin, being tight into maya, and a separate application for the gui. These two could use such means to communicate and exchange model information between plugin and gui.
I'm not sure, however, if I can really recommend this approach because it very much complicates the application.
You could have a look at IPython, an interactive Python shell, whose dev team has put some effort into integrating it with wxPython. They have some way of interrupting the event loop and hooking into it to do their own stuff.
The best way to go is creating a QWidget with what you need, and using it from within a MPxCommand thru the C++ API. That way you also have the chance to inject complete custom editors into Maya via scriptedPanels.
But if you're bound to Python, pyQt is the way to go.

wx's idle and UI update events in PyQt

wx (and wxPython) has two events I miss in PyQt:
EVT_IDLE that's being sent to a frame. It can be used to update the various widgets according to the application's state
EVT_UPDATE_UI that's being sent to a widget when it has to be repainted and updated, so I can compute its state in the handler
Now, PyQt doesn't seem to have these, and the PyQt book suggests writing an updateUi method and calling it manually. I even ended up calling it from a timer once per 0.1 seconds, in order to avoid many manual calls from methods that may update the GUI. Am I missing something? Is there a better way to achieve this?
An example: I have a simple app with a Start button that initiates some processing. The start button should be enabled only when a file has been opened using the menu. In addition, there's a permanent widget on the status bar that displays information.
My application has states:
Before the file is opened (in this state the status bar show something special and the start button is disabled)
File was opened and processing wasn't started: the start button is enabled, status bar shows something else
The processing is running: the start button now says "Stop", and the status bar reports progress
In Wx, I'd have the update UI event of the button handle its state: the text on it, and whether it's enabled, depending on the application state. The same for the status bar (or I'd use EVT_IDLE for that).
In Qt, I have to update the button in several methods that may affect the state, or just create a update_ui method and call it periodically in a timer. What is the more "QT"-ish way?
The use of EVT_UPDATE_UI in wxWidgets seems to highlight one of the fundamental differences in the way wxWidgets and Qt expect developers to handle events in their code.
With Qt, you connect signals and slots between widgets in the user interface, either handling "business logic" in each slot or delegating it to a dedicated method. You typically don't worry about making separate changes to each widget in your GUI because any repaint requests will be placed in the event queue and delivered when control returns to the event loop. Some paint events may even be merged together for the sake of efficiency.
So, in a normal Qt application where signals and slots are used to handle state changes, there's basically no need to have an idle mechanism that monitors the state of the application and update widgets because those updates should occur automatically.
You would have to say a bit more about what you are doing to explain why you need an equivalent to this event in Qt.
I would send Qt signals to indicate state changes (e.g. fileOpened, processingStarted, processingDone). Slots in objects managing the start button and status bar widget (or subclasses) can be connected to those signals, rather than "polling" for current state in an idle event.
If you want the signal to be deferred later on in the event loop rather than immediately (e.g. because it's going to take a bit of time to do something), you can use a "queued" signal-slot connection rather than the normal kind.
http://doc.trolltech.com/4.5/signalsandslots.html#signals
The connection type is an optional parameter to the connect() function:
http://doc.trolltech.com/4.5/qobject.html#connect , http://doc.trolltech.com/4.5/qt.html#ConnectionType-enum
As far as I understand EVT_IDLE is sent when application message queue is empty. There is no such event in Qt, but if you need to execute something in Qt when there are no pending events, you should use QTimer with 0 timeout.
In general, the more Qt-ish way is to update the button/toolbar as necessary in whatever functions require the update, or to consolidate some of the functionality and directly call that function when the program needs it (such as an updateUi function).
You should be aware that in Qt, changing an attribute of a Ui element doesn't cause an immediate redraw, but queues a redraw in the event system, and multiple redraw calls are compressed into one where possible.
As for the multiple changes relating to state, have a look at this blog post about a hopefully-upcoming addition to Qt to more easily handle states. It looks like this would take care of a lot of your complaints, because in your multiple functions, you could just transition the state variable, and the other parts of the UI should update to match. It's not positive this will make it into the next Qt release (although I would bet on it, or something similar), and I have no idea how closely PyQt tracks the Qt releases. Or alternately, you could use the concept and create your own class to track the state as needed.

Categories