PyQT5 problem with reading from port with QtSerialPort - pyqt5

I am currently trying to communicate with a device which sends a data stream by COM. I am having a weird problem with QtSerialPort which I currently don't know how to solve. The description of the problem is: When I restart the device and restart my application, in the application I can open the corresponding port without any error. However, I do not receive any data from the device. I am trying to read data from the COM-Port with the signal readyRead. Now, when I open the port with another program B which uses python's serial, I can successfully read from the device after a restart. Now, after reading successfully from the device with program B, I can also successfully read from the device using the QT-Program A, but only if I do not restart the device. The following code contains the isolated port with QtSerial which reproduces the above mentioned problem.
import sys
from PyQt5 import QtSerialPort
from PyQt5.QtCore import *
from PyQt5.QtSerialPort import *
from PyQt5.QtWidgets import QApplication, QMainWindow
class QtSerialTest(QMainWindow):
def __init__(self, parent=None) -> None:
super().__init__()
self.port_com_port = QtSerialPort.QSerialPort()
self.port_com_port.setPortName("COM4")
self.port_com_port.setBaudRate(QSerialPort.BaudRate.Baud115200)
self.port_com_port.setParity(QSerialPort.Parity.NoParity)
self.port_com_port.setDataBits(QSerialPort.DataBits.Data8)
self.port_com_port.setStopBits(QSerialPort.StopBits.OneStop)
self.port_com_port.open(QIODevice.ReadWrite)
self.port_com_port.readyRead.connect(self.readFromSerial)
def readFromSerial(self):
print(self.port_com_port.readAll())
if __name__ == '__main__':
app = QApplication(sys.argv)
test = QtSerialTest()
app.exec()
I can confirm that after a restart, the function "readFromSerial" is never called, although the device sends.
EDIT: I forgot to mention: I compared the port-settings from program A and B, they are equal

After some search, I could remove the workaround and it works now. I missed an important configuration-parameter. After opening the QtSerialPort, self.port_com_port.setDataTerminalReady(True) needs to be called.

Related

Updating widget from background thread in Colab

In my Colab script, I would like to regularly update an IPython widget through a background task (ie. without blocking the main thread and prevent interaction with the notebook).
I found this section in the Jupyter Widgets documentation: https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Asynchronous.html#Updating-a-widget-in-the-background
import threading
from IPython.display import display
import ipywidgets as widgets
import time
progress = widgets.FloatProgress(value=0.0, min=0.0, max=1.0)
def work(progress):
total = 100
for i in range(total):
time.sleep(0.2)
progress.value = float(i+1)/total
thread = threading.Thread(target=work, args=(progress,))
display(progress)
thread.start()
This does not work in Google Colab, unless I follow this with a thread.join() - which obviously defeats the point...
Does anyone have a solution?

Using matplotlib with Qt5 backend with an existing QApplication running, in the same process

We have a Qt5 application that uses PySide2. Recently, we wanted to show plots using matplotlib while our PySide2 application is running in the same process (in a different thread) but then matplotlib crashes (when using PySide2) or freezes before drawing (when using PyQt5).
Here is a minimal sample, uncomment the 23rd line to make matplotlib crash or freeze:
from threading import Thread
from PySide2.QtWidgets import QApplication, QLabel
import matplotlib
matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt
def start_qt_app():
t = Thread(target=qt_app_thread_func)
t.start()
def qt_app_thread_func():
app = QApplication()
label = QLabel("Hello World")
label.show()
app.exec_()
# Uncomment the line below to make matplotlib crash.
#start_qt_app()
plt.plot([1, 2, 3, 4])
plt.show()
input("Press enter to quit.")
print("Finished.")
My guess is that this is related to the limitation that we can only have 1 QApplication running in a process. So this causes something to go wrong in matplotlib.
How can I solve this problem? One solution that comes to my mind is to create a proxy object for matplotlib that runs matplotlib in another process but I am not sure if this would be the least labor intensive solution. Maybe I can somehow make matplotlib use the existing QApplication? I cannot run our PySide2 app in another process since it uses dynamically created numpy arrays to be passed from main thread to the GUI, and it would cost performance to start it in another process.
As #ImportanceOfBeingErnest points out, matplotlib can live with Qt as the official example shows: Embedding in Qt.
Example:
from PySide2.QtWidgets import QApplication, QLabel
import matplotlib
matplotlib.use('QT5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
if __name__ == '__main__':
app = QApplication()
label = QLabel("Hello World")
label.show()
canvas = FigureCanvas(Figure(figsize=(5, 3)))
ax = canvas.figure.subplots()
ax.plot([1, 2, 3, 4])
canvas.show()
app.exec_()
you should use asyncqt to manage ui and application threads.from asyncqt QEventLoop is a way you can accomplish with
await loop.run_in_executor(exec, method_name)
asyncqt is a spin off library from quamash which is written for PyQt5. example codes are same. So upon your preference, you may use asyncqt with PySide2 or quamash with PyQt5 to make your app responsive when background tasks are running.
asyncqt examples
asyncqt 0.7.0 pypi
Quamash 0.6.1

PyQt5 Main window doesn't render on Raspberry 3B+ (Raspian Stretch, Python 3.5)

This question applies to Raspberry Pi 3B+ running Stretch. Python 3.5. The user interface for a Qt5 application is created. If there is some time-consuming setup before app.exec_(), only an empty window frame is visible, instead of the fully rendered window.
This behavior is different from what's seen on MacOs, for example, where the window is, indeed, rendered.
import time
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
# the main GUI window
class App(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Test')
# create and run the interactive application
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWindow = App() # create the application window
mainWindow.show() # this should show the window
app.processEvents() # everything should be updated
# delay. only an empty window frame is shown.
print ('Delay 10s...')
time.sleep(10)
# enter the GUI loop. now the window is rendered.
sys.exit(app.exec_())
Only an empty window frame is shown. What's expected is a fully rendered window (in this case, it should be just a white empty window but if there are other GUI elements, they should show as well.

PyQt5 Display Issue

I've asked pretty much the same question here before (I've since deleted it after getting no solutions), but one peculiarity has arose since then.
The following program:
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
app = QtWidgets.QApplication([])
w = QtWidgets.QWidget()
w.setWindowTitle('Test')
w.show()
sys.exit(app.exec_())
does not produce a window, generates no errors, and continues to run until forcefully terminated. However, the same code does produce a window in the interactive interpreter.
I have repaired python; uninstalled, reinstalled, upgraded, and downgraded both python and PyQt including PyQt's other versions, as well as the dependencies any PyQt version requires. I am at a total loss as to what/where the problem is, especially since PyQt5 had worked properly before on the same machine.
Any help in fixing this would be immensely appreciated.

pygtk statusBar will not update from signal handler

So I have been pulling my hair out all day at this, and I am out of patience.
Basically I have a pygtk program that is built from glade. At the bottom of the main window, there is a status bar that I use to display usage errors, and status messages about the connected hardware and they use the functions below. So all of the calls to displayError come from within my object based on user interaction, and they all seem to work. In testing I tried making calls to displayCurrent and it works as well. The problem comes when the hardware process tries to use displayCurrent. The way the system works is that one of the members of my main window class is an interface object to the hardware. This has a separate process, using multiprocessing.Process, which sends a signal every time it gets data with the data being the message to output. Does anyone have any ideas? I'll be happy to explain anything in more depth if needed, it's just a LOT of code to post to get all the details.
def displayCurrent(self, message):
print message
if self.lastMess:
self.statusBar.remove(self.normalID, self.lastMess)
self.lastMess = self.statusBar.push(self.normalID, message)
def displayError(self, message, timeout = 5):
"""
Function that takes an error message and raises it to the user via the statusbar
timeout seconds later.
"""
print message
mess = self.statusBar.push(self.urgentID, message)
# clear statusbar
gobject.timeout_add_seconds(timeout, self.clearStatus, self.urgentID, mess)
def clearStatus(self, cID, mID):
#time.sleep(timeout)
self.statusBar.remove(cID, mID)
#print self.statusBar.remove_all(self.urgentID)
print 'popped'
return False
Post the whole code.
In general, this won't work if you are in a separate process. GTK (and everything else) has been forked. You need to use some form of interprocess communication. Multiprocessing module handles it for you. There are also hacks you can do in GTK such as using a window title (like Vim) or perhaps a plug and socket to communicate; don't be tempted by these.