how to insert data from one function into multiple widgets in PyQt5 - python

I have a device connected with my interface and want to insert the data into QlineEdit widgets
This function def getdevice_data(self): receaves the data from the device and returns it as string
with self.get_output__button.clicked.connect(self.getdevice_data) I "start" the function
and with self.custom_attribute.connect(self.device_input1.setText) I send the output to the QLineEdit widget
How can I keep the function running and insert the new data from the function into empty line edit widgets, without adding multiple buttons to start the function again and again ?
full code
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
import serial
import time
class CustmClass(qtw.QWidget):
'''
description einfügen
'''
# Attribut Signal
custom_attribute = qtc.pyqtSignal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# your code will go here
# Interface
self.resize(300, 210)
# button
self.get_output__button = qtw.QPushButton("start function ?")
# lineEdit
self.device_input1 = qtw.QLineEdit()
self.device_input2 = qtw.QLineEdit()
# Layout
vboxlaout = qtw.QVBoxLayout()
vboxlaout.addWidget(self.get_output__button)
vboxlaout.addWidget(self.device_input1)
vboxlaout.addWidget(self.device_input2)
self.setLayout(vboxlaout)
self.show()
# Funktionalität
self.get_output__button.clicked.connect(self.getdevice_data)
self.custom_attribute.connect(self.device_input1.setText)
# self.custom_attribute.connect(self.device_input2.setText)
def getdevice_data(self):
try:
# Serial() opens a serial port
my_serial = serial.Serial(port='COM6', baudrate=2400, bytesize=7,
parity=serial.PARITY_NONE, timeout=None, stopbits=1)
if my_serial.is_open:
print("port open")
# log einfügen
while my_serial.is_open:
data = my_serial.read() # wait forever till data arives
time.sleep(1) # delay
data_left = my_serial.inWaiting()
data += my_serial.read(data_left)
data = data.decode("utf-8", "strict")
if type(data) == str:
print(data)
return self.custom_attribute.emit(data)
else:
print("zu")
except serial.serialutil.SerialException:
print("not open")
# logger hinzufügen
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = CustmClass()
sys.exit(app.exec_())

You should not execute time-consuming or time-consuming loops in the main thread since they block the event loop. What you must do is execute it on a secondary thread and send the information through signals. To obtain the data sequentially you can create an iterator and access each element through the next() function
import sys
import threading
import time
import serial
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
class SerialWorker(qtw.QObject):
dataChanged = qtw.pyqtSignal(str)
def start(self):
threading.Thread(target=self._execute, daemon=True).start()
def _execute(self):
try:
my_serial = serial.Serial(
port="COM6",
baudrate=2400,
bytesize=7,
parity=serial.PARITY_NONE,
timeout=None,
stopbits=1,
)
while my_serial.is_open:
data = my_serial.read() # wait forever till data arives
time.sleep(1) # delay
data_left = my_serial.inWaiting()
data += my_serial.read(data_left)
data = data.decode("utf-8", "strict")
print(data)
self.dataChanged.emit(data)
except serial.serialutil.SerialException:
print("not open")
class Widget(qtw.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(300, 210)
self.get_output__button = qtw.QPushButton("start function ?")
self.device_input1 = qtw.QLineEdit()
self.device_input2 = qtw.QLineEdit()
# Layout
vboxlaout = qtw.QVBoxLayout(self)
vboxlaout.addWidget(self.get_output__button)
vboxlaout.addWidget(self.device_input1)
vboxlaout.addWidget(self.device_input2)
self.serial_worker = SerialWorker()
self.device_iterator = iter([self.device_input1, self.device_input2])
self.get_output__button.clicked.connect(self.serial_worker.start)
self.serial_worker.dataChanged.connect(self.on_data_changed)
#qtw.pyqtSlot(str)
def on_data_changed(self, data):
try:
device = next(self.device_iterator)
device.setText(data)
except StopIteration:
pass
if __name__ == "__main__":
app = qtw.QApplication(sys.argv)
w = CustmClass()
w.show()
sys.exit(app.exec_())

Related

PyQt Messagebox is crashing

I'm working on a file transfer application including its server. When I try to send a file between two clients, I want to ensure that the receiving client will get a message box like "x user wants to send a file. Do you accept?". I accomplished this so far but when I clicked the "Yes" button for testing purposes, the Yes button disappear and the receiving client collapses. When I tried to view the error on console, I saw that it's "QObject::setParent: Cannot set parent, new parent is in a different thread" on the receiving client. I searched the error on the site but couldn't make sense out of the solutions. Could you explain to me how I can solve this?
Code:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
import socket
import json
import base64
from threading import Thread
from time import sleep
username = "admin"
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("144.122.86.204",5000)) #Fill here later
s.send(username.encode("utf-8"))
app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle("File Sharing")
window.setGeometry(0,0,500,350)
contactlist= QListWidget(window)
contactlist.setGeometry(5,5,100,250)
def add():
user, ok = QInputDialog.getText(window, "Add a contact","Enter a
username:")
if ok:
contactlist.addItem(str(user))
def send():
target_user=contactlist.currentItem().text()
name = QFileDialog.getOpenFileName(window, 'Open File')
file = open(name[0], 'rb')
base64_bytes = base64.b64encode(file.read())
base64_string = base64_bytes.decode('utf-8')
data= {"FRM": username, "TO": target_user, "DATA": base64_string}
encoded_data = json.dumps(data).encode()
s.sendall(encoded_data)
def receive():
while True:
data = s.recv(1024)
if data:
if 'wants to send you a file. Do you accept that?' in
data.decode('utf-8'):
choice = QMessageBox.question(window, 'FileTransfer',
data.decode('utf-8'),QMessageBox. Yes|QMessageBox. No)
if choice == QMessageBox.Yes:
s.send("CONFIRMED".encode('utf-8'))
t = Thread(target=receive)
t.start()
add_contact = QPushButton("Add a Contact", window)
add_contact.setGeometry(5,270,100,50)
add_contact.clicked.connect(add)
send_file = QPushButton("Send a File",window)
send_file.setGeometry(110,270,200,50)
send_file.clicked.connect(send)
window.show()
sys.exit(app.exec())
The problem in your case is that QMessageBox is being created in another thread which Qt prohibits, and its parent window lives in the main thread what Qt also forbids. The general approach in this case is to send the information of the secondary thread to the main thread through signals, events, metaobjects, etc. that are thread-safe and in the main thread is to create the GUI (in your case QMessageBox).
In this case it can be complicated so instead of using the above it is best to use the QtNetwork module of Qt that will send you the information received through signals making the while loop unnecessary and therefore the use of threads.
import json
import base64
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.initUi()
self.m_username = "admin"
self.m_socket = QtNetwork.QTcpSocket(self)
self.m_socket.connected.connect(self.onConnected)
self.m_socket.readyRead.connect(self.onReadyRead)
self.m_socket.connectToHost("144.122.86.204", 5000)
#QtCore.pyqtSlot()
def onConnected(self):
username = "admin"
self.m_socket.write(self.m_username.encode("utf-8"))
self.m_socket.flush()
#QtCore.pyqtSlot()
def onReadyRead(self):
data = self.m_socket.readAll().data()
text = data.decode("utf-8")
if "wants to send you a file. Do you accept that?" in text:
choice = QtWidgets.QMessageBox.question(
self,
"FileTransfer",
text,
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
)
if choice == QtWidgets.QMessageBox.Yes:
self.m_socket.write("CONFIRMED".encode("utf-8"))
#QtCore.pyqtSlot()
def add_contant(self):
user, ok = QtWidgets.QInputDialog.getText(
self, "Add a contact", "Enter a username:"
)
if ok:
self.m_listwidget.addItem(user)
#QtCore.pyqtSlot()
def send_file(self):
if self.m_socket.state() != QtNetwork.QAbstractSocket.ConnectedState:
print("Socket not connected")
return
item = self.m_listwidget.currentItem()
if item is None:
print("Not current item")
return
target_user = item.text()
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open File")
with open(filename, "rb") as file:
base64_bytes = base64.b64encode(file.read())
base64_string = base64_bytes.decode("utf-8")
data = {"FRM": self.m_username, "TO": target_user, "DATA": base64_string}
encoded_data = json.dumps(data).encode()
self.m_socket.write(encoded_data)
self.m_socket.flush()
def initUi(self):
self.m_listwidget = QtWidgets.QListWidget()
self.m_listwidget.setFixedWidth(100)
self.m_add_button = QtWidgets.QPushButton("Add a Contact")
self.m_add_button.clicked.connect(self.add_contant)
self.m_add_button.setFixedSize(100, 50)
self.m_send_file = QtWidgets.QPushButton("Send a File")
self.m_send_file.clicked.connect(self.send_file)
self.m_send_file.setFixedSize(200, 50)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(self.m_listwidget)
hlay.addStretch()
hlay2 = QtWidgets.QHBoxLayout()
hlay2.addWidget(self.m_add_button)
hlay2.addWidget(self.m_send_file)
hlay2.addStretch()
vlay = QtWidgets.QVBoxLayout(self)
vlay.addLayout(hlay)
vlay.addLayout(hlay2)
self.resize(500, 350)
def closeEvent(self, event):
if self.m_socket.state() == QtNetwork.QAbstractSocket.ConnectedState:
self.m_socket.disconnectFromHost()
super(Widget, self).closeEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec())

PyQT GUI Freeze with Infinite Loop in QThread

I'm new to PyQT and QThread. My PyQT program has a button which trigger a continuous writing/reading of the serial port every second.
Problem: When the button is clicked and the looping starts, the GUI freezes up. Am I using QThread wrongly?
import sys
from PyQt4.QtGui import *
from PyQt4 import QtCore
import serial
import time
from TemperatureReader import TemperatureReader # my module to talk to serial hardware
class Screen(QMainWindow):
def __init__(self):
super(Screen, self).__init__()
self.initTemperatureReader()
self.initUI()
def initTemperatureReader(self):
ser = serial.Serial(port='COM9', baudrate=115200, timeout=5)
self.temperatureReader = TemperatureReader(ser)
def initUI(self):
startReadingTCsBtn = QPushButton('Start Reading')
startReadingTCsBtn.clicked.connect(self.startReadingTCsThread)
startReadingTCsBtn.show()
directControlBoxLayout = QVBoxLayout()
directControlBoxLayout.addWidget(startReadingTCsBtn)
self.mainFrame = QWidget()
mainLayout = QVBoxLayout()
mainLayout.addWidget(directControlGroupBox)
self.mainFrame.setLayout(mainLayout)
self.setCentralWidget(self.mainFrame)
self.setGeometry(300,300,400,150)
self.show()
def startReadingTCsThread(self):
self.tcReaderThread = TCReaderThread(self.temperatureReader)
self.tcReaderThread.temperatures.connect(self.onTemperatureDataReady)
self.tcReaderThread.start()
def onTemperatureDataReady(self, data):
print data
class TCReaderThread(QtCore.QThread):
temperatures = QtCore.pyqtSignal(object)
def __init__(self, temperatureReader):
QtCore.QThread.__init__(self)
self.temperatureReader = temperatureReader
def run(self):
while True:
resultString = self.temperatureReader.getTemperature() # returns a strng
self.temperatures.emit(resultString)
time.sleep(1)
def main():
app = QApplication(sys.argv)
screen = Screen()
app.exec_()
if __name__ == '__main__':
main()

Pyqt5 GUI Still Hangs When Using Thread

I'm new to python and pyqt.
I'm learning how to use threading with GUI.
I followed this tutorial
http://www.xyzlang.com/python/PyQT5/pyqt_multithreading.html
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import threading
from _ast import While
class Communicate(QObject):
signal = pyqtSignal(int, str)
class My_Gui(QWidget):
def __init__(self):
super().__init__()
self.comm = Communicate()
self.comm.signal.connect(self.append_data)
self.initUI()
def initUI(self):
btn_count = QPushButton('Count')
btn_count.clicked.connect(self.start_counting)
self.te = QTextEdit()
vbox = QVBoxLayout()
vbox.addWidget(btn_count)
vbox.addWidget(self.te)
self.setLayout(vbox)
self.setWindowTitle('MultiThreading in PyQT5')
self.setGeometry(400, 400, 400, 400)
self.show()
def count(self, comm):
'''
for i in range(10):
data = "Data "+str(i)
comm.signal.emit(i, data)
'''
i = 0
while True:
data = "Data "+str(i)
comm.signal.emit(i, data)
i+=1
def start_counting(self):
my_Thread = threading.Thread(target=self.count, args=(self.comm,))
my_Thread.start()
def append_data(self, num, data):
self.te.append(str(num) + " " + data)
if __name__ == '__main__':
app = QApplication(sys.argv)
my_gui = My_Gui()
sys.exit(app.exec_())
I changed the for loop to infinite while loop(incrementing the 'i').
If I execute the program, the GUI still hangs but if I remove the emit signal inside the loop, it no longer hangs.
Are there some tricks to make it not hangs?
while True makes an endless loop in the background
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import threading
from _ast import While
class Communicate(QObject):
signal = pyqtSignal(int, str)
class My_Gui(QWidget):
def __init__(self):
super().__init__()
self.comm = Communicate()
self.comm.signal.connect(self.append_data)
self.initUI()
def initUI(self):
btn_count = QPushButton('Count')
btn_count.clicked.connect(self.start_counting)
self.te = QTextEdit()
vbox = QVBoxLayout()
vbox.addWidget(btn_count)
vbox.addWidget(self.te)
self.setLayout(vbox)
self.setWindowTitle('MultiThreading in PyQT5')
self.setGeometry(400, 400, 400, 400)
self.show()
def count(self, comm):
for i in range(10):
data = "Data "+str(i)
comm.signal.emit(i, data)
# While True below will never stop and cause your program to stuck
'''
i = 0
while True:
data = "Data "+str(i)
comm.signal.emit(i, data)
i+=1
'''
def start_counting(self):
my_Thread = threading.Thread(target=self.count, args=(self.comm,))
my_Thread.start()
def append_data(self, num, data):
self.te.append(str(num) + " " + data)
if __name__ == '__main__':
app = QApplication(sys.argv)
my_gui = My_Gui()
sys.exit(app.exec_())
I think you are getting downvoted for two things:
you did only copy and paste the code from the tutorial
you didn't read the tutorial
In the tutorial, the author states:
In the above example, we have created a QPushbutton and QTextEdit. When the button is clicked it creates a new Thread that counts from 0 to 9 and emits the signal in order to append the number and data in the QTextEdit. In class Communicate signal is initialized as pyqtSignal(int, str). This int and str means when a signal will be emitted it will also pass two arguments the first one will be of Integer type and second one will be of String type.
By changing the loop to while true you continuosly emit signals and append the text in the QTextEdit. Probably not what you want.
Also commenting the emit statement internally continues to run the while loop.

threading for tray icon application

I want to have a tray icon to inform me whether or not my COM port is plugged-in. It should change every 5 seconds according to the state of the COM port. I also want the ability to kill the program using the contextual menu of the tray icon. I figured out how to have the refreshing or the menu, but I don't know how to have both.
import sys
import glob
import serial
import time
from PyQt4 import QtGui, QtCore
import sys
import threading
from multiprocessing import Process, Queue
#script needing python 3.4 , pyserial (via pip) and pyqt4 (via .exe available online)
def serial_ports():
if sys.platform.startswith('win'):
ports = ['COM' + str(i + 1) for i in range(256)]
result = []
for port in ports:
try:
s = serial.Serial(port)
s.close()
result.append(port)
except (OSError, serial.SerialException):
pass
return result
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtGui.QMenu(parent)
changeicon = menu.addAction("Update")
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
exitAction.triggered.connect(QtGui.qApp.quit)
changeicon.triggered.connect(self.updateIcon)
def updateIcon(self):
resultats = serial_ports()
icone = "red.ico"
for resultat in resultats:
if "COM3" in resultat:
icone = "green.ico"
break
self.setIcon(QtGui.QIcon(icone))
#update the icon (its color) according to the content of "resultat"
#missing code; purpose : wait 5 seconds while having the contextual menu of the tray icon still available
def main():
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
trayIcon = SystemTrayIcon(QtGui.QIcon("red.ico"), w)
#always starts with red icon
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Found out using QTimer thanks to figs, seems repetitive and didn't understand everything but it works :
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtGui.QMenu(parent)
changeicon = menu.addAction("Update")
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
exitAction.triggered.connect(QtGui.qApp.quit)
changeicon.triggered.connect(self.updateIcon)
self.updateIcon()
def updateIcon(self):
try:
timer = QtCore.QTimer()
timer.timeout.connect(self.updateIcon)
timer.start(5000)
resultats = serial_ports()
icone = "red.ico"
for resultat in resultats:
if "COM3" in resultat:
icone = "green.ico"
break
self.setIcon(QtGui.QIcon(icone))
finally:
QtCore.QTimer.singleShot(5000,self.updateIcon)

Grabbing Device Connect Exception in Class and passing it to pyQT main window

I have a class that access an external LabJack device. The Labjack device is connected via USB and its main function is to turn something on or off via a python command. I'd like to pass an exception to my pyqt main window application just in case the labjack is not connected. I'm not entirely sure how here is my LabJack class:
import u3
class LabJack:
def __init__(self):
try:
self.Switch = u3.U3()
except:
print "Labjack Error"
#Define State Registers for RB12 Relay Card
self.Chan0 = 6008
Chan1 = 6009
Chan2 = 6010
Chan3 = 6011
Chan4 = 6012
Chan5 = 6013
#Turn the channel on
def IO_On(self,Channel):
self.Switch.writeRegister(Channel,0)
#Turn the channel off
def IO_Off(self,Channel):
self.Switch.writeRegister(Channel,1)
#The State of the Channel
def StateSetting(self,Channel):
self.Switch.readRegister(Channel)
if Switch.readRegister(Channel) == 0:
print ('Channel is On')
else:
print('Channel is Off')
#Direction of Current Flow
def CurrentDirection(self,Channel):
self.Switch.readRegister(6108)
print self.Switch.readRegister(6108)
Here is my pyqt code:
import re
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
from LabJackIO import *
from Piezo902 import *
import functools
import ui_aldmainwindow
class ALDMainWindow(QMainWindow,ui_aldmainwindow.Ui_ALDMainWindow):
def __init__(self, parent=None):
super(ALDMainWindow,self).__init__(parent)
self.setupUi(self)
self.ValveControl = LabJack()
self.Valve_ON.clicked.connect(functools.partial(self.ValveControl.IO_On,self.ValveControl.Chan0))
self.Valve_OFF.clicked.connect(functools.partial(self.ValveControl.IO_Off,self.ValveControl.Chan0))
self.statusBar().showMessage('Valve Off')
app = QApplication(sys.argv)
app.setStyle('motif')
form = ALDMainWindow()
form.show()
app.exec_()
Any suggestions?
Thanks.
If your LabJack could inherit from QObject you could emit a signal when the exception is caught:
class LabJack(QtCore.QObject):
switchFailed = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(LabJack, self).__init__(parent)
try:
self.Switch = u3.U3()
except:
self.switchFailed.emit()
Then set up a slot within your ALDMainWindow to handle the exception:
self.ValveControl = LabJack()
self.ValveControl.switchFailed.connect(self.on_ValveControl_switchFailed)
#QtCore.pyqtSlot()
def on_ValveControl_switchFailed(self):
print "Handle exception here."

Categories

Resources