How to keep PyQt5 UI responsive while waiting for serial feedback? - python

I'm developing a simple software where serial would be feeding back information and update on UI.
This software will be running in Raspbian Jessie, Python 3.4, PyQt5 and Pyserial.
This UI is drawn via Qt Designer and converted via pyuic.
The purpose of this software is to monitor 4 step motors that attached to my Pi, via MCU chip, and the serial information is from this MCU. I am not using Pi's GPIO as they has their other usage.
I wanted to get the text displayed on screen once the serial buffer is filled with information, for now.
I had tried the following code, but it's not good enough as I have to depend on timer to check on serial read. Is there another more efficient way to get the texts on screen once the serial port had received information?
import sys, datetime
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QWidget
#import UI drawn from QtDesigner
from QFrame.Main_btn6 import Ui_MainWindow
import serial
def setupserial(object):
com = serial.Serial(port = "/dev/ttyUSB0",
baudrate = 115200,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
bytesize = serial.EIGHTBITS,
timeout = 3) #ms
return (com)
class Form_Operation(QtWidgets.QMainWindow):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.timer = QtCore.QTimer(self)
self.ui = Ui_MainWindow()
self.serialcom = setupserial()
self.timer.setInterval(10)
self.timer.timeout.connect(self.hidden)
self.ui.setupUi(self)
self.initUI()
def initUI(self):
self.ui.MainDisplay.setText("Output from Serial comn.")
Exit = self.ui.Btn_Exit
Exit.clicked.connect(self.Exit_buttonClicked)
Clear = self.ui.Btn_Clear
Clear.clicked.connect(self.Clear_buttonClicked)
def hidden(self):
"""
Get texts from serial
"""
self.comread = self.com.readline().decode()
self.ui.MainDisplay.setText(self.comread)
def Exit_buttonClicked(self):
sys.exit()
def Clear_buttonClicked(self):
"""
Click to clear
"""
self.ui.MainDisplay.setText("")
def close(self):
self.timer.stop()
self.destroy()
def show(self):
self.timer.start()
self.showFullScreen()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
start = Form_Operation()
start.show()
sys.exit(app.exec_())

Related

create multiple thread for read and write with PyQt5

I write a code with PyQt5 for send and receive data via serial port. when a button pressed, data sends without any problem. in receive, data packet length is different in every time and time between any send is random. I create a thread for read serial port buffer in while loop and if data was received, then put that in a global queue. in other thread , queue will be checked and if it is not empty, some process will be done on data.
but when I test program and send data to application , the bellow error display and application crash.
the error:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTextDocument(0x1d1166f7620), parent's thread is QThread(0x1d116712ff0), current thread is
QThread(0x1d1166f7560)
here is the main part of code:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
import errordialog as D1
import serial
import serial.tools.list_ports
import threading
import queue
class Ui_Form(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def setupUi(self, Form):
self.q = queue.Queue()
.
.
.
def open_port(self):
if self.comboBoxPORT.count()==0:
dialog.exec_()
else:
self.ser.port = self.comboBoxPORT.currentText()
b=int(self.lineEditBaudrate.text())
if b==0:
self.ser.baudrate = 9600
else:
self.ser.baudrate = int(self.lineEditBaudrate.text())
try:
self.ser.open()
self.pushButtonClose.setEnabled(True)
self.pushButtonOpen.setEnabled(False)
self.pushButtonOpen.setStyleSheet("background-color: lime")
self.pushButtonSend.setEnabled(True)
self.comboBoxPORT.setEnabled(False)
self.lineEditBaudrate.setEnabled(False)
self.textEditReceived.setReadOnly(False)
g=threading.Thread(target=ui.read_data)
e=threading.Thread(target=ui.printData)
g.start()
e.start()
except:
dialog.exec_()
def read_data(self):
while True:
t = self.ser.read().decode()
if t:
self.q.put(t)
def printData(self):
while True:
while not self.q.empty():
print(self.q.get())
self.textEditReceived.setText(self.q.get())
def close_port(self):
try:
self.ser.close()
self.pushButtonClose.setEnabled(False)
self.pushButtonOpen.setEnabled(True)
self.pushButtonOpen.setStyleSheet("background-color: azure")
self.pushButtonSend.setEnabled(False)
self.comboBoxPORT.setEnabled(True)
self.lineEditBaudrate.setEnabled(True)
except:
dialog.exec_()
def send(self):
self.ser.write(str.encode(self.textEditSent.toPlainText()))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
##============= add error dialog
d1 = D1.Ui_DialogError()
dialog = QtWidgets.QDialog()
d1.setupUi(dialog)
##---======================----------
ui.pushButtonOpen.clicked.connect(lambda : ui.open_port())
d1.pushButtonCanceError.clicked.connect(lambda: dialog.close())
ui.pushButtonExit.clicked.connect(lambda: Form.close())
ui.pushButtonClose.clicked.connect(lambda: ui.close_port())
ui.pushButtonSend.clicked.connect(lambda: ui.send())
##=============-----------------
##----------====================
sys.exit(app.exec_())

PyQt5 Signals and Threading

I watched a short tutorial on PyQt4 signals on youtube and am having trouble getting a small sample program running. How do I connect my signal being emitted from a thread to the main window?
import cpuUsageGui
import sys
import sysInfo
from PyQt5 import QtCore
"""Main window setup"""
app = cpuUsageGui.QtWidgets.QApplication(sys.argv)
Form = cpuUsageGui.QtWidgets.QWidget()
ui = cpuUsageGui.Ui_Form()
ui.setupUi(Form)
def updateProgBar(val):
ui.progressBar.setValue(val)
class ThreadClass(QtCore.QThread):
def run(self):
while True:
val = sysInfo.getCpu()
self.emit(QtCore.pyqtSignal('CPUVALUE'), val)
threadclass = ThreadClass()
# This section does not work
connect(threadclass, QtCore.pyqtSignal('CPUVALUE'), updateProgBar)
# This section does not work
if __name__ == "__main__":
threadclass.start()
Form.show()
sys.exit(app.exec_())
The signal must be created, inside your ThreadClass, or before but as you emit the signal inside the ThreadClass, it is better to create it inside your class.
After creation, you need to connect it to the progress bar function. Here is an example of the signal created and connected inside your class.
class ThreadClass(QtCore.QThread):
# Create the signal
sig = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(ThreadClass, self).__init__(parent)
# Connect signal to the desired function
self.sig.connect(updateProgBar)
def run(self):
while True:
val = sysInfo.getCpu()
# Emit the signal
self.sig.emit(val)
Keep in mind that signals have changed style since PyQt5 : Description
if you watched a tutorial for PyQt4, it is not be the same.

Interrupting an IPython kernel embedded in a QT4 widget

I want to embed an Ipython terminal in a QT widget so I can access objects in my QT application. I understand that this is possible with examples such as this: https://github.com/gpoulin/python-test/blob/master/embedded_qtconsole.py
however, if the kernel is blocking (in an infinite while loop for example) the whole application is unresponsive and can only be killed with a keyboard interrupt from the terminal where the qt application was run. If I use the QtKernelManager instead of QtInProcessKernelManager I can successfully interrupt the kernel from the qt application, however, I cannot access objects within it as the kernel is embedded in a different process.
Is there a way to catch the keyboard interrupt when embedding the IPython terminal in the same process ? or should I use a different implementation of embedding the kernel ?
my adapted code is below
import os
os.environ['QT_API'] = 'pyqt'
import sip
sip.setapi("QString", 2)
sip.setapi("QVariant", 2)
import sys
from PyQt4 import QtGui, QtCore
from pkg_resources import require
require('ipython')
from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager
class IPythonWidget(QtGui.QWidget):
def __init__(self, **kwarg):
super(IPythonWidget, self).__init__()
self.initUI()
def startIpKernel(self):
self.kernel_manager = QtInProcessKernelManager()
self.kernel_manager.start_kernel()
self.kernel_manager.kernel.gui = 'qt4'
self.kernel_client = kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels()
def initUI(self):
self.startIpKernel()
self.addWidgets()
def addWidgets(self):
self.button = QtGui.QPushButton()
self.button.setText("test button")
self.console = RichIPythonWidget(self)
self.console.kernel_manager =self.kernel_manager
self.console.kernel_client = self.kernel_client
self.console.kernel_manager.kernel.shell.push({"button": self.button})#this can be wrapped into another method
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.console)
vbox.addWidget(self.button)
self.setLayout(vbox)
def mousePressEvent(self, event):
self.mousepos = event.pos()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = IPythonWidget()
ex.show()
sys.exit(app.exec_())

PyQt GUI control access from another thread

i'm trying to create an client server application in python. When the server closes i wish the gui of the client wich is on a separate thread to close, but the application crushes with Xlib error: bad implementation... I've searched and seems to be from accessing GUI interface from other thread. What should I do?python gui access from other thread
this might help you..
from PyQt4 import QtGui as gui
from PyQt4 import QtCore as core
import sys
import time
class ServerThread(core.QThread):
def __init__(self, parent=None):
core.QThread.__init__(self)
def start_server(self):
for i in range(1,6):
time.sleep(1)
self.emit(core.SIGNAL("dosomething(QString)"), str(i))
def run(self):
self.start_server()
class MainApp(gui.QWidget):
def __init__(self, parent=None):
super(MainApp,self).__init__(parent)
self.label = gui.QLabel("hello world!!")
layout = gui.QHBoxLayout(self)
layout.addWidget(self.label)
self.thread = ServerThread()
self.thread.start()
self.connect(self.thread, core.SIGNAL("dosomething(QString)"), self.doing)
def doing(self, i):
self.label.setText(i)
if i == "5":
self.destroy(self, destroyWindow =True, destroySubWindows = True)
sys.exit()
app = gui.QApplication(sys.argv)
form = MainApp()
form.show()
app.exec_()

How to access GUI elements from another thread in PyQt

I'm trying to create a client-server application, and when the server closes I wish that client GUI to close, which is running on another thread. I wish to access the GUI and close but I get X error:
Bad implementation(...).
How can I resolve this problem?
what you can do is emit a custom signal when the first thread goes down..
from PyQt4 import QtGui as gui
from PyQt4 import QtCore as core
import sys
import time
class ServerThread(core.QThread):
def __init__(self, parent=None):
core.QThread.__init__(self)
def start_server(self):
for i in range(1,6):
time.sleep(1)
self.emit(core.SIGNAL("dosomething(QString)"), str(i))
def run(self):
self.start_server()
class MainApp(gui.QWidget):
def __init__(self, parent=None):
super(MainApp,self).__init__(parent)
self.label = gui.QLabel("hello world!!")
layout = gui.QHBoxLayout(self)
layout.addWidget(self.label)
self.thread = ServerThread()
self.thread.start()
self.connect(self.thread, core.SIGNAL("dosomething(QString)"), self.doing)
def doing(self, i):
self.label.setText(i)
if i == "5":
self.destroy(self, destroyWindow =True, destroySubWindows = True)
sys.exit()
app = gui.QApplication(sys.argv)
form = MainApp()
form.show()
app.exec_()

Categories