I am creating an oven monitoring program, that reaches out to some PID controllers over Modbus TCP. I am trying to implement an email alerting part that will monitor the temperature and if it's within tolerance. If it goes out of tolerance it is to send an email and then send another every 10 mins after that it stays out of tolerance. I am sure that I have something screwed up in my scope, but for the life of me I cannot figure out what. When the oven goes out of tolerance my 'inTolerance' function goes to work. It sends 1 email and should start the timer, but my 'is_alive' call does not return true. So when 'inTolerance' calls again it send another email and then bombs out as I believe it attempts to start another 't' timer.
Any help and a sanity check would be extremely helpful and appreciated.
from PyQt5 import QtCore, QtGui, QtWidgets
from modbusTest import Oven
from emailModule import emailer
import time
from threading import Timer
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(170, 260)
self.spinBox = QtWidgets.QSpinBox(Form)
self.spinBox.setGeometry(QtCore.QRect(10, 90, 61, 20))
self.spinBox.setObjectName("setpoint")
self.lcdNumber = QtWidgets.QLCDNumber(Form)
self.lcdNumber.setGeometry(QtCore.QRect(10, 10, 150, 60))
self.lcdNumber.setObjectName("lcdNumber")
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(120, 89, 41, 22))
self.pushButton.setObjectName("pushButton")
self.lineEdit = QtWidgets.QLineEdit(Form)
self.lineEdit.setGeometry(QtCore.QRect(10, 130, 105, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.lineEdit.setFont(font)
self.lineEdit.setObjectName("lineEdit")
self.spinBox_2 = QtWidgets.QSpinBox(Form)
self.spinBox_2.setGeometry(QtCore.QRect(77, 90, 35, 20))
self.spinBox_2.setObjectName("tolerance")
self.spinBox_2.setValue(5)
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(20, 70, 41, 15))
self.label.setObjectName("label")
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setGeometry(QtCore.QRect(10, 112, 71, 16))
self.label_2.setObjectName("label_2")
self.listWidget = QtWidgets.QListWidget(Form)
self.listWidget.setGeometry(QtCore.QRect(10, 160, 150, 91))
self.listWidget.setObjectName("listWidget")
self.label_3 = QtWidgets.QLabel(Form)
self.label_3.setGeometry(QtCore.QRect(70, 70, 51, 16))
self.label_3.setObjectName("label_3")
self.pushButton_2 = QtWidgets.QPushButton(Form)
self.pushButton_2.setGeometry(QtCore.QRect(120, 129, 40, 22))
self.pushButton_2.setObjectName("pushButton_2")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
self.p1 = Oven('IP')
self.p1.connect()
self.lcdNumber.display(self.p1.readTemp())
#print(self.t.is_alive())
#self.t.start()
#print(self.t.is_alive())
#TODO set spinbox values to database table values
########################################################################################################
self.tolerance = float(self.spinBox_2.value())
self.spinBox.setValue(self.p1.readSP())
self.setPoint = float(self.spinBox.value())
########################################################################################################
self.pushButton.clicked.connect(self.setter)
QtCore.QTimer.singleShot(1000, self.displayer)
self.pushButton_2.clicked.connect(self.emailerList)
self.emailList = []
self.t = Timer(10.0, None)
def emailerList(self):
if len(self.lineEdit.text()) == 0:
None
else:
self.emailList.append(self.lineEdit.text())
self.listWidget.addItem(self.lineEdit.text())
self.lineEdit.clear()
def displayer(self):
temp = self.p1.readTemp()
self.lcdNumber.display(temp)
self.inTolerance(self.tolerance, temp, self.setPoint)
QtCore.QTimer.singleShot(1000, self.displayer)
def setter(self):
self.setPoint = float(self.spinBox.value())
self.p1.writeSP(self.setPoint)
self.tolerance = float(self.spinBox_2.value())
def inTolerance(self, tolerance, temp, setPoint):
if temp > (setPoint + tolerance) or temp < (setPoint - tolerance):
self.lcdNumber.setStyleSheet("background-color: rgb(255, 0, 0);")
print(self.t.is_alive())
if not self.t.is_alive():
emailer(self.emailList, 'Test Oven', temp, setPoint)
print('Email Sent')
self.t.start()
time.sleep(1)
print(self.t.is_alive())
else:
self.t.cancel()
self.lcdNumber.setStyleSheet("background-color: rgb(255, 255, 255);")
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.pushButton.setText(_translate("Form", "Set"))
self.label.setText(_translate("Form", "Setpoint"))
self.label_2.setText(_translate("Form", "Alarm Emails:"))
self.label_3.setText(_translate("Form", "Tolerance"))
self.pushButton_2.setText(_translate("Form", "Add"))
if __name__ == "__main__":
import sys
sys_argv = sys.argv
sys_argv += ['--style', 'Fusion']
app = QtWidgets.QApplication(sys_argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
The main reason of your issue is that you're calling cancel(), which causes the timer to invalidate itself even if it has not been started yet, and the result is that after this the timer will be never actually started.
After that there is another problem: Thread objects can only be started once, and you would need to create a new Timer object whenever you want to start it again.
That said, you should not mix timer objects, and when dealing with threads it's usually better to use what Qt provides.
In your case, the solution is to use a QElapsedTimer, which is an object that can return the elapsed time since it was (re)started.
Note that I'm using a QWidget class (and simplified the ui just for this example), more about that after the code.
class Window(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.resize(170, 260)
layout = QtWidgets.QGridLayout(self)
self.lcdNumber = QtWidgets.QLCDNumber(self)
layout.addWidget(self.lcdNumber, 0, 0, 1, 2)
self.spinBox = QtWidgets.QSpinBox(self)
layout.addWidget(self.spinBox, 1, 0)
self.spinBox_2 = QtWidgets.QSpinBox(self)
layout.addWidget(self.spinBox_2, 1, 1)
self.spinBox_2.setValue(5)
self.p1 = Oven('P1')
self.p1.connect()
self.lcdNumber.display(self.p1.readTemp())
self.tolerance = float(self.spinBox_2.value())
self.setPoint = float(self.spinBox.value())
self.emailList = []
# create a timer that will call displayer each second; having a reference
# to the timer allows to stop it if required
self.displayerTimer = QtCore.QTimer(interval=1000, timeout=self.displayer)
self.displayerTimer.start()
self.elapsedTimer = QtCore.QElapsedTimer()
def displayer(self):
temp = self.p1.readTemp()
self.lcdNumber.display(temp)
self.inTolerance(self.tolerance, temp, self.setPoint)
def inTolerance(self, tolerance, temp, setPoint):
if temp > (setPoint + tolerance) or temp < (setPoint - tolerance):
self.lcdNumber.setStyleSheet("background-color: rgb(255, 0, 0);")
if not self.elapsedTimer.isValid():
# the timer has not been started or has been invalidated
self.elapsedTimer.start()
elif self.elapsedTimer.elapsed() > 10000:
# ten seconds have passed, send the email
emailer(self.emailList, 'Test Oven', temp, setPoint)
# restart the timer, otherwise another mail could possibly be
# sent again the next cycle
self.elapsedTimer.start()
# in any other case, the elapsed time is less than the limit, just
# go on
else:
# temperature is within tolerance, invalidate the timer so that it can
# be restarted when necessary
self.elapsedTimer.invalidate()
self.lcdNumber.setStyleSheet("background-color: rgb(255, 255, 255);")
if __name__ == "__main__":
import sys
sys_argv = sys.argv
sys_argv += ['--style', 'Fusion']
app = QtWidgets.QApplication(sys_argv)
window = Window()
window.show()
sys.exit(app.exec_())
As said, I'm using a QWidget class.
It seems like you're using the output of the pyuic utility to create your program, which is something you should never do. You should create your program in a separate script, and use that generated file as an imported module. The py file created from pyuic should NEVER be edited.
In this case I created the interface completely from the code, but you can still recreate your ui in designer and use the generated py file as explained in the documentation (the third method, multiple inheritance approach, is usually the best).
Related
Hi guys I want the user to choose the motor speed before going into step. So, in the first class, I made the first window which asked for the motor speed and another one is the recording and predicting process. In the recording process, I want to collect the WAV file as future data and use the motor speed as a file name. But I cannot pass the self.RPM value from one class to another. This is some part of the code.
class Ui_Form(object):
def setupUi(self, Form):
#The rest of the code
def retranslateUi(self, Form):
#The rest of the code
def start_click(self):
print('Start button click')
_translate = QtCore.QCoreApplication.translate
self.label.setText(_translate("Form", "<html><head/><body><p align=\"center\"><span style=\" color:#000000;\">Recording</span></p></body></html>"))
app.processEvents()
time.sleep(1)
count = 0
round = 0
for i in range(3):
round = i + 1
text = "Round " + str(round) + "/3"
self.label.setText(text)
app.processEvents()
print(text)
#Recording
now = datetime.now()
day = now.strftime("%d")
month = now.strftime("%m")
year = now.strftime("%y")
hour = now.strftime("%H")
minute = now.strftime("%M")
second = now.strftime("%S")
print(day,"/",month,"/",year,hour,":",minute,":",second)
CHUNK = 1024 #The number of frames in the buffer
FORMAT = pyaudio.paInt16
CHANNELS = 1 #Each frame will have 2 samples
RATE = 44100 #The number of samples collected per second or we can called sample rate
RECORD_SECONDS = 2 #Duration of recording
WAVE_OUTPUT_FILENAME = f"Data2/Test/{day}{month}{year}_{hour}{minute}{second}.wav" <--- I want to add RPM value here
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT, channels = CHANNELS, rate = RATE, input = True, frames_per_buffer = CHUNK)
print("* recording")
frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK) #read audio data from the stream
frames.append(data)
print("* done recording")
self.label.setText(_translate("Form", "<html><head/><body><p align=\"center\"><span style=\" color:#000000;\">Done Recording</span></p></body></html>"))
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') # 'rb' read only, 'wb' write only
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames)) #
wf.close()
#The rest of the code
class Ui_Form2(object):
def openWindow(self):
self.window = QtWidgets.QWidget()
self.ui = Ui_Form()
self.ui.setupUi(self.window)
self.window.show()
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(422, 202)
Form.setFixedSize(422, 202)
self.pushButton = QtWidgets.QPushButton(Form, clicked = lambda: self.openWindow())
self.pushButton.setGeometry(QtCore.QRect(10, 120, 121, 71))
font = QtGui.QFont()
font.setPointSize(15)
self.pushButton.setFont(font)
self.pushButton.setObjectName("pushButton")
self.pushButton_2 = QtWidgets.QPushButton(Form, clicked = lambda: self.openWindow())
self.pushButton_2.setGeometry(QtCore.QRect(150, 120, 121, 71))
font = QtGui.QFont()
font.setPointSize(15)
self.pushButton_2.setFont(font)
self.pushButton_2.setObjectName("pushButton_2")
self.pushButton_3 = QtWidgets.QPushButton(Form, clicked = lambda: self.openWindow())
self.pushButton_3.setGeometry(QtCore.QRect(290, 120, 121, 71))
font = QtGui.QFont()
font.setPointSize(15)
self.pushButton_3.setFont(font)
self.pushButton_3.setObjectName("pushButton_3")
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(70, 20, 281, 81))
self.label.setFrameShape(QtWidgets.QFrame.WinPanel)
self.label.setFrameShadow(QtWidgets.QFrame.Sunken)
self.label.setObjectName("label")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.pushButton.setText(_translate("Form", "1300 RPM"))
self.pushButton.clicked.connect(lambda: self.rpm_button_clicked(self.pushButton.text()))
self.pushButton_2.setText(_translate("Form", "1500 RPM"))
self.pushButton_2.clicked.connect(lambda: self.rpm_button_clicked(self.pushButton_2.text()))
self.pushButton_3.setText(_translate("Form", "1800 RPM"))
self.pushButton_3.clicked.connect(lambda: self.rpm_button_clicked(self.pushButton_3.text()))
self.label.setText(_translate("Form", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">RPM Selection</span></p></body></html>"))
def rpm_button_clicked(self, button_text):
rpm_values = {'1300 RPM': 1300, '1500 RPM': 1500, '1800 RPM': 1800}
self.RPM = rpm_values[button_text]
print(self.RPM)
if __name__ == "__main__":
Form = QtWidgets.QWidget()
ui = Ui_Form2()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
I want to use the value from one class on another or anyone got a better idea than this can suggest
Either you use signal/slots (a Qt mechanism for observer/observable), or do it the Python object way.
Signal and slot
In the first case, you need to declare a signal in the class (A) where you select the motor speed, then in your other class (B) you connect the signal for your instance of A to a slot in your B instance.
When doing Qt graphical applications, I prefer to use toy examples to better understand how it works.
Here is what I mean :
import sys
from typing import Optional
from PyQt5 import QtWidgets, QtCore
class WindowA(QtWidgets.QMainWindow):
def __init__(self):
super().__init__(parent=None)
self.setWindowTitle("A")
central_widget = QtWidgets.QPushButton("Choose")
central_widget.clicked.connect(self.on_button_Choose_pushed)
self.setCentralWidget(central_widget)
def on_button_Choose_pushed(self, checked: bool) -> None:
print("'choose' button clicked")
file_dialog = QtWidgets.QFileDialog(parent=self)
if file_dialog.exec():
print("a file was selected")
filenames = file_dialog.selectedFiles()
print(filenames)
assert len(filenames) == 1
print("emitting the signal")
self.file_selected.emit(filenames[0])
else:
print("no file selected")
file_selected = QtCore.pyqtSignal(str)
class WindowB(QtWidgets.QMainWindow):
def __init__(self, window_a: WindowA):
super().__init__(parent=None)
self.setWindowTitle("B")
self.filename: Optional[str] = None
central_widget = QtWidgets.QPushButton("Do something")
central_widget.clicked.connect(self.on_button_Do_Something_pushed)
self.setCentralWidget(central_widget)
window_a.file_selected.connect(self.on_file_selected)
def on_button_Do_Something_pushed(self, checked: bool) -> None:
print("'do something' button clicked")
print("filename is :", repr(self.filename))
# do something
#QtCore.pyqtSlot(str)
def on_file_selected(self, filename: str) -> None:
print("'file selected' slot received a signal, filename =", repr(filename))
self.filename = filename
def main() -> int:
app = QtWidgets.QApplication([])
window_a = WindowA()
window_a.setVisible(True)
window_b = WindowB(window_a) # passing A to B
window_b.setVisible(True)
return app.exec()
if __name__ == "__main__":
sys.exit(main())
(the 2 windows may appear one on top of the other)
Click the button B "do something" button, then the A button "choose" and select a file, then click B again. You will get :
'do something' button clicked
filename is : None
'choose' button clicked
a file was selected
['/home/stack_overflow/log.log']
emitting the signal
'file selected' slot received a signal, filename = '/home/stack_overflow/log.log'
'do something' button clicked
filename is : '/home/stack_overflow/log.log'
Just objects
This is the over way around. You need your A to know of B, so that when a file is selected from A, it sets the variable for B.
import sys
from typing import Optional
from PyQt5 import QtWidgets, QtCore
class WindowA(QtWidgets.QMainWindow):
def __init__(self, window_b: "WindowB"):
super().__init__(parent=None)
self.setWindowTitle("A")
self._window_b = window_b # save it for later
central_widget = QtWidgets.QPushButton("Choose")
central_widget.clicked.connect(self.on_button_Choose_pushed)
self.setCentralWidget(central_widget)
def on_button_Choose_pushed(self, checked: bool) -> None:
print("'choose' button clicked")
file_dialog = QtWidgets.QFileDialog(parent=self)
if file_dialog.exec():
print("a file was selected")
filenames = file_dialog.selectedFiles()
print(filenames)
assert len(filenames) == 1
print("setting the variable of B")
self._window_b.filename = filenames[0]
else:
print("no file selected")
class WindowB(QtWidgets.QMainWindow):
def __init__(self):
super().__init__(parent=None)
self.setWindowTitle("B")
self.filename: Optional[str] = None
central_widget = QtWidgets.QPushButton("Do something")
central_widget.clicked.connect(self.on_button_Do_Something_pushed)
self.setCentralWidget(central_widget)
def on_button_Do_Something_pushed(self, checked: bool) -> None:
print("'do something' button clicked")
print("filename is :", self.filename)
# do something
def main() -> int:
app = QtWidgets.QApplication([])
window_b = WindowB()
window_b.setVisible(True)
window_a = WindowA(window_b) # passing B to A
window_a.setVisible(True)
return app.exec()
if __name__ == "__main__":
sys.exit(main())
This way, A sets the variable of B.
Conclusion
If you understand these 2 techniques, you can change the structure. You can have a class containing the data, and the other being given a reference to the first class.
Or you could make the code that instantiate both of them (here is is the main() function) connect the signal from the first to the slot of the second.
This question already has answers here:
time.sleep() and BackGround Windows PyQt5
(1 answer)
Equivalent to time.sleep for a PyQt application
(5 answers)
Closed 2 years ago.
I have created a big project involving communication with a controller via serial port.
My project is divided to 3 files:
File A - a pyqt5 designer file - including all my buttons, line edits, etc.
File B - a serial communication file - basically ask questions and receive answers from the controller.
File C - the main file; in this file I am importing file A and file B and using them.
In file C, I have created a worker, so all the communications with the controller are being made by this thread. (using the class and functions of file B..)
Now, I am trying to make a create a new function within the worker. The functions is using time.sleep every x seconds for y times. From what I understand, because I am using a thread, the GUI should not get stuck, but for some reason, it does.
Since the code is very long, I have only attaching what I think is relevant to the question.
I tried to copy as little of the code, yet still keep it understandable, I hope it's ok.
File A - pyqt5 designer code:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
################creating general veriables#####################
MainWindow.setObjectName("MainWindow")
MainWindow.setFixedSize(780, 585)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
MainWindow.setFont(font)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.frame_Holder = QtWidgets.QFrame(self.centralwidget)
self.frame_Holder.setGeometry(QtCore.QRect(0, 85, 780, 500))
self.frame_Holder.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_Holder.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_Holder.setObjectName("frame_Holder")
#########################################################################
################creating veriables for the Top Frame#####################
self.frame_Top = QtWidgets.QFrame(self.centralwidget)
self.frame_Top.setEnabled(True)
self.frame_Top.setGeometry(QtCore.QRect(10, 5, 760, 80))
font = QtGui.QFont()
font.setStrikeOut(False)
self.frame_Top.setFont(font)
self.frame_Top.setFrameShape(QtWidgets.QFrame.WinPanel)
self.frame_Top.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_Top.setLineWidth(1)
self.frame_Top.setObjectName("frame_Top")
################creating Buttons#####################
self.btn_Connect = QtWidgets.QPushButton(self.frame_Top)
self.btn_Connect.setGeometry(QtCore.QRect(85, 50, 75, 23))
self.btn_Connect.setObjectName("btn_Connect")
self.btn_Connect.setEnabled(False)
self.btn_PortList = QtWidgets.QPushButton(self.frame_Top)
self.btn_PortList.setGeometry(QtCore.QRect(10, 50, 65, 23))
self.btn_PortList.setObjectName("btn_PortList")
self.productMenu=QtWidgets.QMenu(self.frame_Top)
self.btn_PortList.setMenu(self.productMenu)
self.btn_Init = QtWidgets.QPushButton(self.frame_Top)
self.btn_Init.setEnabled(False)
self.btn_Init.setGeometry(QtCore.QRect(300, 50, 75, 23))
self.btn_Init.setObjectName("btn_Init")
################creating text edits#####################
self.edit_version = QtWidgets.QLineEdit(self.frame_Top)
self.edit_version.setGeometry(QtCore.QRect(170, 50, 120, 23))
self.edit_version.setObjectName("edit_version")
self.edit_version.setEnabled(False)
################creating labels#####################
self.lbl_Version = QtWidgets.QLabel(self.frame_Top)
self.lbl_Version.setGeometry(QtCore.QRect(170, 30, 71, 21))
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.lbl_Version.setFont(font)
self.lbl_Version.setObjectName("lbl_Version")
self.lbl_FrameTop = QtWidgets.QLabel(self.frame_Top)
self.lbl_FrameTop.setEnabled(True)
self.lbl_FrameTop.setGeometry(QtCore.QRect(5, 5, 90, 25))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
font.setStrikeOut(False)
font.setKerning(True)
self.lbl_FrameTop.setFont(font)
self.lbl_FrameTop.setObjectName("lbl_FrameTop")
self.lbl_On = QtWidgets.QLabel(self.frame_Top)
self.lbl_On.setGeometry(QtCore.QRect(528, 30, 41, 16))
self.lbl_On.setObjectName("lbl_On")
self.lbl_Enable = QtWidgets.QLabel(self.frame_Top)
self.lbl_Enable.setGeometry(QtCore.QRect(560, 30, 41, 16))
self.lbl_Enable.setObjectName("lbl_Enable")
self.lbl_Rls = QtWidgets.QLabel(self.frame_Top)
self.lbl_Rls.setGeometry(QtCore.QRect(608, 30, 41, 16))
self.lbl_Rls.setObjectName("lbl_Rls")
self.lbl_Chk = QtWidgets.QLabel(self.frame_Top)
self.lbl_Chk.setGeometry(QtCore.QRect(645, 30, 41, 16))
self.lbl_Chk.setObjectName("lbl_Chk")
self.lbl_Wafer = QtWidgets.QLabel(self.frame_Top)
self.lbl_Wafer.setGeometry(QtCore.QRect(683, 30, 41, 16))
self.lbl_Wafer.setObjectName("lbl_Wafer")
self.lbl_Err = QtWidgets.QLabel(self.frame_Top)
self.lbl_Err.setGeometry(QtCore.QRect(725, 30, 20, 16))
self.lbl_Err.setObjectName("lbl_Err")
self.lbl_Port = QtWidgets.QLabel(self.frame_Top)
self.lbl_Port.setGeometry(QtCore.QRect(10, 30, 51, 21))
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.lbl_Port.setFont(font)
self.lbl_Port.setObjectName("lbl_Port")
#########################################################################
self.frame_AutoRun = QtWidgets.QFrame(self.frame_Holder)
self.frame_AutoRun.setEnabled(False)
self.frame_AutoRun.setGeometry(QtCore.QRect(10, 90, 760, 320))
font = QtGui.QFont()
self.frame_AutoRun.setFont(font)
self.frame_AutoRun.setFrameShape(QtWidgets.QFrame.WinPanel)
self.frame_AutoRun.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_AutoRun.setLineWidth(1)
self.frame_AutoRun.setObjectName("frame_AutoRun")
self.lbl_AutoRun = QtWidgets.QLabel(self.frame_AutoRun)
self.lbl_AutoRun.setGeometry(QtCore.QRect(5, 5, 110, 25))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
font.setStrikeOut(False)
font.setKerning(True)
self.lbl_AutoRun.setFont(font)
self.lbl_AutoRun.setObjectName("lbl_AutoRun")
self.btn_RunStop = QtWidgets.QPushButton(self.frame_AutoRun)
self.btn_RunStop.setGeometry(QtCore.QRect(500, 40, 60, 23))
font = QtGui.QFont()
font.setPointSize(8)
font.setBold(False)
font.setWeight(50)
font.setKerning(True)
self.btn_RunStop.setFont(font)
self.btn_RunStop.setObjectName("btn_RunStop")
########################4##########################
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "ECC GUI"))
self.btn_Connect.setText(_translate("MainWindow", "Connect"))
self.btn_PortList.setText(_translate("MainWindow", "Ports"))
self.lbl_Version.setText(_translate("MainWindow", "Version"))
self.btn_Init.setText(_translate("MainWindow", "Initiate"))
self.lbl_FrameTop.setText(_translate("MainWindow", "Connect"))##########
self.lbl_Port.setText(_translate("MainWindow", "Ports"))
self.lbl_On.setText(_translate("MainWindow", "ON"))
self.lbl_Enable.setText(_translate("MainWindow", "Enable"))
self.lbl_Rls.setText(_translate("MainWindow", "RLS"))
self.lbl_Chk.setText(_translate("MainWindow", "CHK"))
self.lbl_Wafer.setText(_translate("MainWindow", "Wafer"))
self.lbl_Err.setText(_translate("MainWindow", "ERR"))
self.lbl_AutoRun.setText(_translate("MainWindow", "Auto Run"))
self.btn_RunStop.setText(_translate("MainWindow", "Run"))
File B - the serial communication file
import serial
from string import hexdigits
from re import search
class ECC():
def __init__(self,Port ):
self.ser = serial.Serial(port ='COM'+str(Port), baudrate= 38400,timeout=1)
def cmd(self,command):
return ('00'+command+chr(13)).encode('utf-8')
def disconnect(self):
self.ser.close()
def init(self):
self.ser.write(self.cmd('INI'))
return(self.ser.readline().decode('utf-8')[:3])
def spr(self,address,value):
self.ser.write(self.cmd('SPR'+str(address)+str(value)))
return (self.ser.readline().decode('utf-8')[:3])
def rpr(self,address):
self.ser.write(self.cmd('RPR'+str(address)))
return(self.ser.readline().decode('utf-8')[3:7])
def chuck(self):
self.ser.write(self.cmd('CHK'))
return(self.ser.readline().decode('utf-8')[:3])
def rls(self):
self.ser.write(self.cmd('RLS'))
return(self.ser.readline().decode('utf-8')[:3])
def cap(self):
self.ser.write(self.cmd('cap'))
cap=self.ser.readline()[3:].decode('utf-8')
cap=cap.rstrip()
if (all(c in hexdigits for c in cap) and cap!=""):
return int(cap,16)
else:
return("Busy...")
def version(self):
self.ser.write(self.cmd('VER'))
return(self.ser.readline().decode('utf-8')[:9])
def gst(self):
self.ser.flushInput()
self.ser.flushOutput()
self.ser.write(self.cmd('GST'))
gst=self.ser.readline()
gst=gst.decode('utf-8')
is_STV=search('^STV',str(gst))
if (is_STV):
stat_hex=gst[3:]
stat_bin=(bin(int(stat_hex,16))[2:].zfill(16))
stat_bool=[bool(int(bit)) for bit in stat_bin]
else:
stat_hex="ERR"
stat_bool="ERR"
return stat_hex, stat_bool
def statLights(self):
[stat_hex, stat_bool]=self.gst()
if(not stat_hex=="ERR"):
lights=[not(stat_bool[3]),not(stat_bool[5]),not(stat_bool[10]),stat_bool[10],stat_bool[11],stat_bool[0]]
else:
lights="ERR"
return lights
def msrphys(self):
self.ser.flushInput()
self.ser.flushOutput()
self.ser.write(self.cmd('msr'))
A=list()
A.append(self.ser.readline())
A[0]=A[0][3:7]
exists=search('[0-9A-Fa-f]{4}',str(A[0]))
if(exists):
A[0]=int(A[0],16)*-0.08398628+2500
for ind in range(1,32):
A.append(self.ser.readline())
exists=search('[0-9A-Fa-f]{4}',str(A[ind]))
if (exists):
A[ind]=int(A[ind].decode('utf-8'),16)*-0.08398628+2500
else:
A[0]="ERR"
break
self.ser.readline()
A[12]=A[12]*5.75
A[13]=A[13]*5.75
A[14]=A[14]*0.1
else:
A[0]="ERR"
return A
File C - the operative file
from test_des import Ui_MainWindow
from PyQt5 import QtWidgets, QtCore, QtGui
from test_serialCom import ECC
import serial.tools.list_ports
import sys
from re import findall
import time
from functools import partial
try:
import queue
except ImportError:
import Queue as queue
class eccWorker(QtCore.QRunnable):
def __init__(self,port,q):
super(eccWorker, self).__init__()
self.finished = QtCore.pyqtSignal()
self.port=port
self.Q=q
self.threadactive=True
def run(self):
if(self.threadactive==True):
try:
self.ecc=ECC(self.port)
except:
self.threadactive=False
self.Q.put("ERROR-Not connected")
self.Q.put("ERROR-Not connected")
else:
self.Q.put(self.ecc.version())
self.Q.put(self.ecc.rpr('04'))
def init(self):
if(self.threadactive):
self.ecc.init()
def disconnect(self):
self.ecc.disconnect()
self.threadactive=False
def readCap(self):
if(self.threadactive==True):
self.Q.put(self.ecc.cap())
def chk(self):
if(self.threadactive==True):
self.ecc.chuck()
def rls(self):
if(self.threadactive==True):
self.ecc.rls()
def rprWorker(self,par):
if(self.threadactive==True):
self.Q.put(self.ecc.rpr(par))
def sprWorker(self,par,val):
if(self.threadactive==True):
# par=par.zfill(2)
isOk=self.ecc.spr(par,val)
if (isOk!="NAK"):
self.Q.put(self.ecc.rpr(par))
def getStat(self):
if(self.threadactive==True):
statLight=self.ecc.statLights()
if(statLight=="ERR"):
self.Q.put("ERR")
else:
for i in statLight:
self.Q.put(i)
def sprWorkerNoRpr(self,par,val):
if(self.threadactive==True):
isOk=self.ecc.spr(par,val)
if (isOk!="NAK"):
return True
def test1(self,Vchk,N,dt,Dt,threshold):
Vchk=str(Vchk).zfill(4)
if(self.sprWorkerNoRpr('04',Vchk)):
cap1=list()
cap2=list()
for i in range(N):
self.rls()
time.sleep(dt)
self.readCap()
res=self.Q.get()
if (res!="Busy..."):
cap1.append(int(str(res)))
time.sleep(Dt)
self.chk()
time.sleep(dt)
self.readCap()
res2=self.Q.get()
if (res2!="Busy..."):
cap2.append(int(str(res2)))
time.sleep(Dt)
self.rls()
cap_Rls=sum(cap1)/len(cap1)
cap_Chk=sum(cap2)/len(cap2)
if((max(cap2)-min(cap1))>threshold):
isPass=True
else:
isPass=False
self.Q.put(cap_Rls)
self.Q.put(cap_Chk)
self.Q.put(isPass)
print("Done!")
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.threadpool=QtCore.QThreadPool()
self.Q=queue.Queue()
self.ui.productMenu.aboutToShow.connect(self.findPort)
self.ui.btn_Connect.clicked.connect(self.connectClicked)
self.ui.btn_RunStop.clicked.connect(self.run)
self.ui.btn_Init.clicked.connect(self.initClicked)
self.redOff=QtGui.QColor(120,0,0)
self.redOn=QtGui.QColor(255,0,0)
self.grnOff=QtGui.QColor(7,100,0)
self.grnOn=QtGui.QColor(16,243,0)
self.yelOff=QtGui.QColor(155,80,0)
self.yelOn=QtGui.QColor(255,127,0)
self.onPen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
self.enablePen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
self.rlsPen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
self.chkPen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
self.waferPen=QtGui.QPen(self.yelOff, 8, QtCore.Qt.SolidLine)
self.errPen=QtGui.QPen(self.redOff, 8, QtCore.Qt.SolidLine)
def paintEvent(self, e):
onLedPainter = QtGui.QPainter(self)
onLedPainter.setPen(self.onPen)
onLedPainter.setBrush(self.grnOff)
onLedPainter.drawEllipse(540,55,10,10)
enabledLedPainter = QtGui.QPainter(self)
enabledLedPainter.setPen(self.enablePen)
enabledLedPainter.setBrush(self.grnOff)
enabledLedPainter.drawEllipse(580,55,10,10)
rlsLedPainter = QtGui.QPainter(self)
rlsLedPainter.setPen(self.rlsPen)
rlsLedPainter.setBrush(self.grnOff)
rlsLedPainter.drawEllipse(620,55,10,10)
chkLedPainter = QtGui.QPainter(self)
chkLedPainter.setPen(self.chkPen)
chkLedPainter.setBrush(self.grnOff)
chkLedPainter.drawEllipse(660,55,10,10)
waferLedPainter = QtGui.QPainter(self)
waferLedPainter.setPen(self.waferPen)
waferLedPainter.setBrush(self.yelOff)
waferLedPainter.drawEllipse(700,55,10,10)
errLedPainter = QtGui.QPainter(self)
errLedPainter.setPen(self.errPen)
errLedPainter.setBrush(self.redOff)
errLedPainter.drawEllipse(740,55,10,10)
def updateStat(self):
#####updating LEDs####
self.worker.getStat()
self.ledStat=list()
self.ledStat.append(self.worker.Q.get())
if(not self.ledStat[0]=="ERR"):
for i in range(5):
self.ledStat.append(self.worker.Q.get())
if(self.ledStat[0]):
self.onPen=QtGui.QPen(self.grnOn, 8, QtCore.Qt.SolidLine)
else:
self.onPen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
if(self.ledStat[1]):
self.enablePen=QtGui.QPen(self.grnOn, 8, QtCore.Qt.SolidLine)
else:
self.enablePen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
if(self.ledStat[2]):
self.rlsPen=QtGui.QPen(self.grnOn, 8, QtCore.Qt.SolidLine)
self.chkPen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
else:
self.chkPen=QtGui.QPen(self.grnOn, 8, QtCore.Qt.SolidLine)
self.rlsPen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
if(self.ledStat[4]):
self.waferPen=QtGui.QPen(self.yelOn, 8, QtCore.Qt.SolidLine)
else:
self.waferPen=QtGui.QPen(self.yelOff, 8, QtCore.Qt.SolidLine)
if(self.ledStat[5]):
self.errPen=QtGui.QPen(self.redOn, 8, QtCore.Qt.SolidLine)
else:
self.errPen=QtGui.QPen(self.redOff, 8, QtCore.Qt.SolidLine)
self.update()
else:
self.connectClicked()
self.ui.edit_version.setText("GST Disconnected!")
def findPort(self):
self.ui.productMenu.clear()
comPorts = list(serial.tools.list_ports.comports()) # get a list of all devices connected through serial port
comPorts=sorted(comPorts)
for port in comPorts:
self.ui.productMenu.addAction(str(port),self.selectedPort)
def selectedPort(self):
self.portNum=self.sender()
self.portNum=self.portNum.text()
self.portNum=findall("COM\d+",self.portNum)
self.ui.btn_PortList.setText(str(self.portNum[0]))
self.portNum=self.portNum[0][3:]
self.ui.btn_Connect.setEnabled(True)
def connectClicked(self):
if (self.ui.btn_Connect.text()=="Connect"):
self.worker=eccWorker(self.portNum,self.Q)
self.threadpool.start(self.worker)
ver=self.worker.Q.get()
volHex=self.worker.Q.get()
if(ver[:3]=="VER"):
self.ui.btn_Connect.setText("Disconnect")
self.ui.btn_PortList.setEnabled(False)
self.ui.edit_version.setText(ver)
self.ui.btn_Init.setEnabled(True)
self.ui.frame_AutoRun.setEnabled(True)
self.timer_updateStat = QtCore.QTimer()
self.timer_updateStat.setInterval(500)
self.timer_updateStat.timeout.connect(self.updateStat)
self.timer_updateStat.start()
else:
self.ui.edit_version.setText("Error!")
else:
self.timer_updateStat.stop()
self.worker.disconnect()
self.ui.btn_Connect.setText("Connect")
self.ui.btn_Connect.setEnabled(False)
self.ui.btn_Init.setEnabled(False)
self.ui.frame_AutoRun.setEnabled(False)
self.ui.btn_PortList.setEnabled(True)
self.ui.edit_version.clear()
self.setFixedSize(780,585)
self.ui.btn_PortList.setText("Ports")
self.onPen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
self.enablePen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
self.chkPen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
self.rlsPen=QtGui.QPen(self.grnOff, 8, QtCore.Qt.SolidLine)
self.waferPen=QtGui.QPen(self.yelOff, 8, QtCore.Qt.SolidLine)
self.errPen=QtGui.QPen(self.redOff, 8, QtCore.Qt.SolidLine)
self.update()
def initClicked(self):
self.worker.init()
def run(self):
self.worker.test1(200,5,0.2,0.3,100)
cap_Rls=self.Q.get()
cap_Chk=self.Q.get()
ispass=self.Q.get()
print("cap_Rls:\n")
print(cap_Rls)
print("cap_Chk:\n")
print(cap_Chk)
print(ispass)
def closeEvent(self,event):
try:
self.worker.disconnect()
finally:
sys.exit()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
application = ApplicationWindow()
application.show()
sys.exit(app.exec_())
So what does the GUI gets stuck for the time of the function even though I am using a thread? What can I do to fix this?
Also, ideally, I would want to have another file, file D, that contains a main class called TESTS,and it has like 5 functions within it, each one represents a test.
The problem I faced doing the above, is failing to calling/getting information from the thread active in file C, so if you have an idea of how to make this one as well, it would be much appreciated. (if not creating another file, so store all the tests in a different class. Here, as well, I faced some issues and could not make it work).
After many many hours of searching online, I found that using QtTest.QTest.qWait(msecs)
instead of sleep is giving me the wanted result - it holds the commands but does not make the GUI freeze.
In order to use it I had to use from PyQt5 import QtTest.
If anyone has yet a better suggestions (to any of the questions above) I would love to see them.
I have a simple Python application using PyQt5 that shall perform an update.
Currently I'm stuck directly within the __init__, when I add two pixmaps and a lineEdit whose text that I want to update during a calculation.
Whenever I use the main_window.change() the GUI is not shown until the change() is finished.
The pictures are not shown.
Without the change method it's showing the pics
Without change method GUI is shown correctly
If I add a QMessageBox into the for loop, the message is shown of course, but also the updated GUI becomes visible.
The message box updates the GUI
Adding a self.update() did not help.
class AcselStarter(QtWidgets.QMainWindow, Ui_ACSEL_Starter):
def __init__(self, parent=None):
super(AcselStarter, self).__init__(parent)
Ui_ACSEL_Starter.__init__(self)
self.setupUi(self)
pixmapAcsel = QPixmap('../fig/ACSEL.png')
self.labelAcsel.setPixmap(pixmapAcsel)
pixmapMubea = QPixmap('../fig/CAELogo_height60.png')
self.labelMubea.setPixmap(pixmapMubea)
self.lineEditProgress.setText(str(0.001))
def change(self):
for i in range(0, 100, 10):
self.lineEditProgress.setText(str(i))
# QtWidgets.QMessageBox.information(self, PROGRAMM_NAME, 'Replot', QtWidgets.QMessageBox.Ok)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main_window = AcselStarter()
main_window.show()
time.sleep(5)
main_window.change()
sys.exit(app.exec_())
For completeness, here's my UI file from Qt Designer:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'UI_acsel_starter.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ACSEL_Starter(object):
def setupUi(self, ACSEL_Starter):
ACSEL_Starter.setObjectName("ACSEL_Starter")
ACSEL_Starter.resize(320, 180)
self.centralwidget = QtWidgets.QWidget(ACSEL_Starter)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.widgetPics = QtWidgets.QWidget(self.centralwidget)
self.widgetPics.setObjectName("widgetPics")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widgetPics)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.labelAcsel = QtWidgets.QLabel(self.widgetPics)
self.labelAcsel.setText("")
self.labelAcsel.setObjectName("labelAcsel")
self.horizontalLayout_2.addWidget(self.labelAcsel)
self.labelMubea = QtWidgets.QLabel(self.widgetPics)
self.labelMubea.setText("")
self.labelMubea.setObjectName("labelMubea")
self.horizontalLayout_2.addWidget(self.labelMubea)
self.gridLayout.addWidget(self.widgetPics, 0, 0, 1, 1)
self.widgetProgress = QtWidgets.QWidget(self.centralwidget)
self.widgetProgress.setMaximumSize(QtCore.QSize(16777215, 30))
self.widgetProgress.setObjectName("widgetProgress")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widgetProgress)
self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
self.horizontalLayout.setContentsMargins(0, 9, 0, 0)
self.horizontalLayout.setSpacing(6)
self.horizontalLayout.setObjectName("horizontalLayout")
self.labelProgress = QtWidgets.QLabel(self.widgetProgress)
self.labelProgress.setMaximumSize(QtCore.QSize(48, 16777215))
self.labelProgress.setObjectName("labelProgress")
self.horizontalLayout.addWidget(self.labelProgress)
self.lineEditProgress = QtWidgets.QLineEdit(self.widgetProgress)
self.lineEditProgress.setMaximumSize(QtCore.QSize(50, 16777215))
self.lineEditProgress.setObjectName("lineEditProgress")
self.horizontalLayout.addWidget(self.lineEditProgress)
self.labelPercent = QtWidgets.QLabel(self.widgetProgress)
self.labelPercent.setObjectName("labelPercent")
self.horizontalLayout.addWidget(self.labelPercent)
self.gridLayout.addWidget(self.widgetProgress, 1, 0, 1, 1)
ACSEL_Starter.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(ACSEL_Starter)
self.menubar.setGeometry(QtCore.QRect(0, 0, 320, 21))
self.menubar.setObjectName("menubar")
ACSEL_Starter.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(ACSEL_Starter)
self.statusbar.setObjectName("statusbar")
ACSEL_Starter.setStatusBar(self.statusbar)
self.retranslateUi(ACSEL_Starter)
QtCore.QMetaObject.connectSlotsByName(ACSEL_Starter)
def retranslateUi(self, ACSEL_Starter):
_translate = QtCore.QCoreApplication.translate
ACSEL_Starter.setWindowTitle(_translate("ACSEL_Starter", "ACSEL_Starter"))
self.labelProgress.setText(_translate("ACSEL_Starter", "Progress"))
self.labelPercent.setText(_translate("ACSEL_Starter", "%"))
I expect the GUI to be updated whenever I set new text to the lineEdit.
Thank you for your help in advance!
I just found out a solution by my own and used the nicer progress bar instead of the lineEdit.
Here the solution when the progress bar is activated via code and not buttonClick. Because then a new thread has to be performed manually. The buttonClick does it automatically internally.
class ThreadClass(QThread):
valChanged = pyqtSignal(int)
def run(self):
print('run thread') # e.g. file download
count = 0
while count < 100:
count += 0.0005
self.valChanged.emit(count)
print('finished thread')
main_window.my_property = True
self.quit()
class AcselStarter_Thread(QtWidgets.QMainWindow, Ui_ACSEL_Starter):
"""
Activating the progress bar within the code without ButtonClick is not working.
"""
def __init__(self, parent=None):
super(AcselStarter_Thread, self).__init__(parent)
Ui_ACSEL_Starter.__init__(self)
self.setupUi(self)
pixmapAcsel = QPixmap('../fig/ACSEL.png')
self.labelAcsel.setPixmap(pixmapAcsel)
pixmapMubea = QPixmap('../fig/CAELogo_height60.png')
self.labelMubea.setPixmap(pixmapMubea)
self.progressBar.setMaximum(100)
self.progressBar.setValue(0)
self.__my_property = False
#property
def my_property(self):
return self.__my_property
#my_property.setter
def my_property(self, val):
self.__my_property = val
print('new val')
if self.__my_property:
self.do_something_after_progressbar()
def update_progressbar(self, val):
self.progressBar.setValue(val)
def do_thread(self):
# e.g. file update/download
print('do_thread')
self.threadClass = ThreadClass()
self.threadClass.valChanged.connect(self.update_progressbar)
self.threadClass.start()
def do_something_after_progressbar(self):
# e.g. moving the downloaded files and starting the updated app
print('do_something_after_progressbar')
if __name__ == '__main__':
"""
Main method. Starts the application
__name__ == '__main__' is true, if this file is run directly and not imported.
"""
app = QtWidgets.QApplication(sys.argv)
main_window = AcselStarter_Thread()
main_window.show()
main_window.do_thread()
sys.exit(app.exec_())
The output is the following, as expected/wanted:
do_thread
run thread
finished thread
new val
do_something_after_progressbar
I'm making a GUI with pyqt5 and have qlcdnumber to display a variable which I retrieve from an Android app through a socket server.
The problem is I can't find any method to display the variable that I have in the qlcdnumber.
Here is the UI code, I tried to use signal but didn't work.
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
while True:
global angle, strength ,speed, a
if(not enabled):
# self.rfile is a file-like object created by the handler;
# we can now use e.g. readline() instead of raw recv() calls
self.data = self.rfile.readline().strip()
if(a==0):
print("{} wrote:".format(self.client_address[0]))
a+=1
data = self.data.decode("utf-8").rstrip().strip("\n")
if(data!= ""):
if(data == "disconnect"):
print("restarting")
self.close()
sleep(1)
mainServer()
elif(data == "shutdown"):
p = subprocess.Popen("sudo shutdown -h now", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print(p)
else:
temp = data.split(":")
angle = int(temp[0])
strength = int(temp[1])
speed = float(temp[2])
class Ui_AppWindow(QtWidgets.QWidget):
def __init__(self):
super(Ui_AppWindow, self).__init__()
self.setStyleSheet('background-color:#efefef; color:white;')
global add, angle, speed
self.layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("\t\t"+add)
self.label.setStyleSheet("color: #005C99;")
self.layout.addWidget(self.label)
palette = QtGui.QPalette()
palette.setColor(palette.WindowText, QtGui.QColor(85, 85, 255))
palette.setColor(palette.Background, QtGui.QColor(0, 170, 255))
palette.setColor(palette.Light, QtGui.QColor(0, 92, 153))
palette.setColor(palette.Dark, QtGui.QColor(0, 92, 137))
self.speed = QtWidgets.QLCDNumber(self)
self.speed.setGeometry(QtCore.QRect(100, 100, 64, 23))
self.speed.setObjectName("speed")
self.speed.display(speed)
self.speed.setPalette(palette)
self.angle = QtWidgets.QLCDNumber(self)
self.angle.setGeometry(QtCore.QRect(200, 100, 64, 23))
self.angle.setObjectName("angle")
self.angle.display(angle)
self.angle.setPalette(palette)
self.speed_lable = QtWidgets.QLabel("Speed")
self.speed_lable.setGeometry(QtCore.QRect(100, 70, 47, 25))
self.speed_lable.setObjectName("speed_lable")
self.speed_lable.setStyleSheet('color:#00497a;')
self.angle_lable = QtWidgets.QLabel("Angle")
self.angle_lable.setGeometry(QtCore.QRect(200, 70, 47, 25))
self.angle_lable.setObjectName("angle_lable")
self.angle_lable.setStyleSheet('color:#00497a;')
self.hbl = QtWidgets.QHBoxLayout()
self.hbl.addWidget(self.speed_lable)
self.hbl.addWidget(self.angle_lable)
self.layout.addLayout(self.hbl)
self.hb = QtWidgets.QHBoxLayout()
self.hb.addWidget(self.speed)
self.hb.addWidget(self.angle)
self.layout.addLayout(self.hb)
self.setWindowTitle("RasControl GUI: Android Control")
self.setLayout(self.layout)
self.setGeometry(100, 100, 400, 200)
def setAngle(self):
global angle
self.angle.display(angle)
def setSpeed(self):
global speed
self.speed.display(speed)
and I have variables that I want to set to be displayed in the qlcdnumber;
global speed, angle
server = socketserver.TCPServer((ip, port), MyTCPHandler)
server.serve_forever()
and inside the main GUI im calling the Ui_AppWindow gui, but i need the qlcdnumber to be updated with the value of the global variable speed and angle each time they are changed.
The easiest way to do this would just to make functions that are always used to set the values of the global variables in your program, like so:
def set_speed(val):
global speed
speed = val
# ... whatever other logic you'd like to perform
def set_angle(val):
# ...
Right now through reading and asking questions I have gotten to a point where I can display the numerical values on LCDs, but now would like to make a graph.
In the Picture below there is a picture that I am going to have as a backdrop for the graph. For a Dyno the important information that is gathered is Torque and HorsePower. These I have being calculated with my Python 3.5 Code and the data is being gathered by using an arduino.
For my graph I actually want to plot two lines at the same time as the data comes in. I would like to plot both the Torque and the HorsePower at the same time. Both of those vs the time that the Dyno is being used. This however might be hard since they need to be plotted with different y-axis. From what I have been reading using pyqtGraph is the best option for the job but due to my in experience with this kind of work I really don't know how to do it.
Below is my code that I have tried to run based on some of the things I have found. Running it does not error out my code, however It also does not interact with the graph area. I tried to get it to work in a similar fashion that the LCDs work in, but even still I don't have anything working.
"""
SCSU DYNO GUI PROGRAM
created 10/20/2017
"""
import sys
import time
import csv
import numpy as np
import warnings
import serial
import serial.tools.list_ports
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread,QTimer, pyqtSignal
from PyQt5.QtWidgets import QMessageBox,QWidget, QApplication
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import random
from DynoTest1 import Ui_DynoTest1
__author__ = 'Matt Munn'
class GetData(QThread):
dataChanged = pyqtSignal(float, float, float, float, float, float, float, float)
#Distance = 0.5 #This is dependent on the lever arm.
def __init__(self, parent=None):
QThread.__init__(self, parent)
arduino_ports = [ # automatically searches for an Arduino and selects the port it's on
p.device
for p in serial.tools.list_ports.comports()
if 'Arduino' in p.description
]
if not arduino_ports:
raise IOError("No Arduino found - is it plugged in? If so, restart computer.")
if len(arduino_ports) > 1:
warnings.warn('Multiple Arduinos found - using the first')
self.Arduino = serial.Serial(arduino_ports[0], 9600, timeout=1)
def __del__(self): # part of the standard format of a QThread
self.wait()
def run(self): # also a required QThread function, the working part
self.Arduino.close()
self.Arduino.open()
self.Arduino.flush()
self.Arduino.reset_input_buffer()
start_time = time.time()
Distance = 0.5 #This is dependent on the lever arm.
Max_RPM = 0
Max_HorsePower = 0
Max_Torque = 0
while True:
while self.Arduino.inWaiting() == 0:
pass
try:
data = self.Arduino.readline()
dataarray = data.decode().rstrip().split(',')
self.Arduino.reset_input_buffer()
Force = round(float(dataarray[0]), 3)
RPM = round(float(dataarray[1]), 3)
if Max_RPM < RPM:
Max_RPM = RPM
Torque = round(Force * Distance, 3)
if Max_Torque < Torque:
Max_Torque = Torque
HorsePower = round(Torque * RPM / 5252, 3)
if Max_HorsePower < HorsePower:
Max_HorsePower = HorsePower
Run_Time = round(time.time() - start_time, 3)
print(Force, 'Grams', ",", RPM, 'RPMs', ",", Torque, "ft-lbs", ",", HorsePower, "hp", Run_Time,
"Time Elasped")
self.dataChanged.emit(Force, RPM, Max_RPM, Torque, Max_Torque, HorsePower, Max_HorsePower, Run_Time)
except (KeyboardInterrupt, SystemExit, IndexError, ValueError):
pass
class GUI(QWidget, Ui_DynoTest1):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setupUi(self)
self.thread = GetData(self)
self.thread.dataChanged.connect(self.onDataChanged)
self.thread.start()
"""
layout = QtGui.QHBoxLayout()
self.plot = pg.PlotWidget()
layout.addWidget(self.plot)
self.setLayout(layout)
def plotter(self):
self.data = [0]
self.curve = self.plot.getPlotItem().plot()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updater)
self.timer.start(0)
def updater(self):
self.data.append(self.data[-1]+0.2*(0.5-random.random()))
self.curve.setData(self.data)
"""
def onDataChanged(self, Force, RPM, Max_RPM, Torque,Max_Torque, HorsePower, Max_HorsePower, Run_Time):
self.lcdNumber.display(Max_RPM)
self.lcdNumber_2.display(Max_Torque)
self.lcdNumber_3.display(Max_HorsePower)
self.lcdNumber_4.display(RPM)
self.lcdNumber_5.display(Torque)
self.lcdNumber_6.display(HorsePower)
self.lcdNumber_7.display(Run_Time)
#self.graphicsView.display(Tourque,Run_Time)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
Dyno = GUI()
Dyno.show()
sys.exit(app.exec_())
This is the code generated by the QTDesigner.
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'dynotest1.ui'
#
# Created by: PyQt5 UI code generator 5.9
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_DynoTest1(object):
def setupUi(self, DynoTest1):
DynoTest1.setObjectName("DynoTest1")
DynoTest1.resize(1001, 695)
self.verticalLayout_4 = QtWidgets.QVBoxLayout(DynoTest1)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.pushButton_2 = QtWidgets.QPushButton(DynoTest1)
self.pushButton_2.setObjectName("pushButton_2")
self.gridLayout.addWidget(self.pushButton_2, 1, 0, 1, 1)
self.pushButton_4 = QtWidgets.QPushButton(DynoTest1)
self.pushButton_4.setObjectName("pushButton_4")
self.gridLayout.addWidget(self.pushButton_4, 1, 1, 1, 1)
self.pushButton_3 = QtWidgets.QPushButton(DynoTest1)
self.pushButton_3.setObjectName("pushButton_3")
self.gridLayout.addWidget(self.pushButton_3, 0, 1, 1, 1)
self.pushButton = QtWidgets.QPushButton(DynoTest1)
self.pushButton.setObjectName("pushButton")
self.gridLayout.addWidget(self.pushButton, 0, 0, 1, 1)
self.verticalLayout_3.addLayout(self.gridLayout)
self.label_3 = QtWidgets.QLabel(DynoTest1)
self.label_3.setObjectName("label_3")
self.verticalLayout_3.addWidget(self.label_3)
self.label_2 = QtWidgets.QLabel(DynoTest1)
self.label_2.setObjectName("label_2")
self.verticalLayout_3.addWidget(self.label_2)
self.label = QtWidgets.QLabel(DynoTest1)
self.label.setObjectName("label")
self.verticalLayout_3.addWidget(self.label)
self.horizontalLayout.addLayout(self.verticalLayout_3)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.label_5 = QtWidgets.QLabel(DynoTest1)
self.label_5.setObjectName("label_5")
self.verticalLayout.addWidget(self.label_5)
self.lcdNumber_4 = QtWidgets.QLCDNumber(DynoTest1)
self.lcdNumber_4.setFrameShape(QtWidgets.QFrame.Box)
self.lcdNumber_4.setFrameShadow(QtWidgets.QFrame.Raised)
self.lcdNumber_4.setLineWidth(1)
self.lcdNumber_4.setSmallDecimalPoint(True)
self.lcdNumber_4.setDigitCount(5)
self.lcdNumber_4.setMode(QtWidgets.QLCDNumber.Dec)
self.lcdNumber_4.setSegmentStyle(QtWidgets.QLCDNumber.Filled)
self.lcdNumber_4.setProperty("value", 0.0)
self.lcdNumber_4.setObjectName("lcdNumber_4")
self.verticalLayout.addWidget(self.lcdNumber_4)
self.lcdNumber_5 = QtWidgets.QLCDNumber(DynoTest1)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.lcdNumber_5.setFont(font)
self.lcdNumber_5.setSmallDecimalPoint(True)
self.lcdNumber_5.setObjectName("lcdNumber_5")
self.verticalLayout.addWidget(self.lcdNumber_5)
self.lcdNumber_6 = QtWidgets.QLCDNumber(DynoTest1)
self.lcdNumber_6.setSmallDecimalPoint(True)
self.lcdNumber_6.setObjectName("lcdNumber_6")
self.verticalLayout.addWidget(self.lcdNumber_6)
self.horizontalLayout.addLayout(self.verticalLayout)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.label_6 = QtWidgets.QLabel(DynoTest1)
self.label_6.setObjectName("label_6")
self.verticalLayout_2.addWidget(self.label_6)
self.lcdNumber = QtWidgets.QLCDNumber(DynoTest1)
self.lcdNumber.setSmallDecimalPoint(True)
self.lcdNumber.setObjectName("lcdNumber")
self.verticalLayout_2.addWidget(self.lcdNumber)
self.lcdNumber_2 = QtWidgets.QLCDNumber(DynoTest1)
self.lcdNumber_2.setSmallDecimalPoint(True)
self.lcdNumber_2.setObjectName("lcdNumber_2")
self.verticalLayout_2.addWidget(self.lcdNumber_2)
self.lcdNumber_3 = QtWidgets.QLCDNumber(DynoTest1)
self.lcdNumber_3.setSmallDecimalPoint(True)
self.lcdNumber_3.setObjectName("lcdNumber_3")
self.verticalLayout_2.addWidget(self.lcdNumber_3)
self.horizontalLayout.addLayout(self.verticalLayout_2)
self.horizontalLayout_2.addLayout(self.horizontalLayout)
self.verticalLayout_5 = QtWidgets.QVBoxLayout()
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.label_7 = QtWidgets.QLabel(DynoTest1)
self.label_7.setObjectName("label_7")
self.verticalLayout_5.addWidget(self.label_7)
self.lcdNumber_7 = QtWidgets.QLCDNumber(DynoTest1)
self.lcdNumber_7.setObjectName("lcdNumber_7")
self.verticalLayout_5.addWidget(self.lcdNumber_7)
self.horizontalLayout_2.addLayout(self.verticalLayout_5)
self.verticalLayout_4.addLayout(self.horizontalLayout_2)
self.graphicsView = QtWidgets.QGraphicsView(DynoTest1)
self.graphicsView.setStyleSheet("border-image: url(:/newPrefix/husky_head5.png);")
self.graphicsView.setLineWidth(1)
self.graphicsView.setObjectName("graphicsView")
self.verticalLayout_4.addWidget(self.graphicsView)
self.retranslateUi(DynoTest1)
QtCore.QMetaObject.connectSlotsByName(DynoTest1)
def retranslateUi(self, DynoTest1):
_translate = QtCore.QCoreApplication.translate
DynoTest1.setWindowTitle(_translate("DynoTest1", "DynoTest1"))
self.pushButton_2.setText(_translate("DynoTest1", "Pause"))
self.pushButton_4.setText(_translate("DynoTest1", "Print"))
self.pushButton_3.setText(_translate("DynoTest1", "Stop"))
self.pushButton.setText(_translate("DynoTest1", "Start"))
self.label_3.setText(_translate("DynoTest1", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">RPMs</span></p></body></html>"))
self.label_2.setText(_translate("DynoTest1", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">Torque (ft-lbs)</span></p></body></html>"))
self.label.setText(_translate("DynoTest1", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">Horse Power</span></p></body></html>"))
self.label_5.setText(_translate("DynoTest1", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">Now</span></p></body></html>"))
self.label_6.setText(_translate("DynoTest1", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">Max</span></p></body></html>"))
self.label_7.setText(_translate("DynoTest1", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">Run Time</span></p><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">(Seconds)</span></p></body></html>"))
import Resource_rc
Picture of the GUI
The first thing is to add the PlotWidget to the GUI. For this we use QGraphicsView (in fact any widget can be used), in the QGraphicsView we add a layout, and in this layout we add the PlotWidget.
layout = QHBoxLayout()
self.plot = pg.PlotWidget()
layout.addWidget(self.plot)
self.graphicsView.setLayout(layout)
Then the items are created, and as you wish to have 2 axes and in the same plot we take as an example the code found in the following link.
In the example you wanted to use, use a timer to update the data, but in this case that clock must be reading data for it every time the onDataChanged method is called. To obtain a view, a procedure has been created to only store and display the last 50 elements.
Code:
class GUI(QWidget, Ui_DynoTest1):
def __init__(self, parent=None):
[...]
self.thread.start()
self.torque = []
self.horse_power = []
layout = QHBoxLayout()
self.plot = pg.PlotWidget()
layout.addWidget(self.plot)
self.graphicsView.setLayout(layout)
self.p1 = self.plot.plotItem
self.p1.setLabels(left='Torque')
self.TorqueCurve = self.p1.plot()
self.TorqueCurve.setPen(pg.mkPen(color="#ff0000", width=2))
self.p2 = pg.ViewBox()
self.HorsePowerCurve = pg.PlotCurveItem()
self.HorsePowerCurve.setPen(pg.mkPen(QColor(0, 255, 0)), width=2)
self.p2.addItem(self.HorsePowerCurve)
self.p1.scene().addItem(self.p2)
self.p1.showAxis('right')
self.p1.getAxis('right').setLabel('HorsePower', color='#0000ff')
self.p1.getAxis('right').linkToView(self.p2)
self.p1.vb.sigResized.connect(self.updateViews)
def updateViews(self):
self.p2.setGeometry(self.p1.vb.sceneBoundingRect())
self.p2.linkedViewChanged(self.p1.vb, self.p2.XAxis)
def onDataChanged(self, Force, RPM, Max_RPM, Torque, Max_Torque, HorsePower, Max_HorsePower, Run_Time):
[...]
if len(self.torque) < 50:
self.torque.append(Torque)
else:
self.torque = self.torque[1:] + [Torque]
if len(self.horse_power) < 50:
self.horse_power.append(HorsePower)
else:
self.horse_power = self.horse_power[1:] + [HorsePower]
self.TorqueCurve.setData(self.torque)
self.HorsePowerCurve.setData(self.horse_power)
self.updateViews()