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()
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:
PyQt - how to detect and close UI if it's already running?
(3 answers)
Closed 6 months ago.
Is there anyway I can run this program like this.
At the start of the program GUI shows and background process starts running. And the the GUI can be closed and opened anytime the user wants. But the background process keeps running uninterrupted.
Here is my current code. 4 classes.
FileHandler
Scanner <- Inherits from QThread
UserInterface <- Takes QMainWindow as an argument
Main <- Inherits from UserInterface
import math
import sys
import time
import psutil
import win32gui
import win32process
import threading
import atexit
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtChart import QChart, QChartView, QPieSeries
from PyQt5.QtCore import QThread, pyqtSignal
RECORDED_PROGRAMS = {}
class FileHandler():
def __init__ (self):
print("[+] Driver File Initiated...")
self.logFile = open("log.txt", "a+")
self.dataFile = open("data.txt", "a+")
def readFile(self):
pass
def writeFile(self):
print("[+] {} : Writing Data to the File...".format(time.strftime("%H:%M:%S".format(time.localtime()))))
self.dataFile.write("\nOn Write : {} : ".format(time.strftime("%H:%M:%S".format(time.localtime()))) + str(RECORDED_PROGRAMS))
def closeFile(self):
print("[+] File Handler Exiting...")
print("[+] Closing File...")
self.dataFile.write("\nOn Exit : {} : ".format(time.strftime("%H:%M:%S".format(time.localtime()))) + str(RECORDED_PROGRAMS))
self.dataFile.write("\n=================================================================================")
self.dataFile.write("\n[!] Program Exited. TimeStamp: {}".format(time.strftime("%H:%M:%S", time.localtime())))
self.dataFile.write("\n=================================================================================")
self.dataFile.close()
self.logFile.close()
#==============================================================================================================
class Scanner(QThread):
signal = pyqtSignal(bool)
def run(self):
print("[+] Scanner Initialized...")
count = 0
while True:
count += 1
try:
activeWindowId = win32gui.GetForegroundWindow()
threadList = win32process.GetWindowThreadProcessId(activeWindowId)
mainThreadName = psutil.Process(threadList[-1]).name()
if(mainThreadName in RECORDED_PROGRAMS.keys()):
RECORDED_PROGRAMS[mainThreadName] += 1
else:
RECORDED_PROGRAMS[mainThreadName] = 1
except Exception as E:
print("[-] Error in Scanner...")
print("======================================================")
print(E)
print("======================================================")
if count == 60:
self.signal.emit(True)
count = 0
time.sleep(1)
#==============================================================================================================
class UserInterface():
def __init__(self, MainWindow):
self.setupUi(MainWindow)
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
MainWindow.setSizePolicy(sizePolicy)
MainWindow.setMinimumSize(QtCore.QSize(640, 480))
MainWindow.setMaximumSize(QtCore.QSize(640, 480))
font = QtGui.QFont()
font.setFamily("Bahnschrift")
font.setPointSize(14)
MainWindow.setFont(font)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.centralWidgetHLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.centralWidgetHLayout.setObjectName("centralWidgetHLayout")
self.leftGroupBox = QtWidgets.QGroupBox(self.centralwidget)
self.leftGroupBox.setMinimumSize(QtCore.QSize(300, 462))
self.leftGroupBox.setMaximumSize(QtCore.QSize(300, 462))
self.leftGroupBox.setObjectName("leftGroupBox")
self.leftGroupVLayout = QtWidgets.QVBoxLayout(self.leftGroupBox)
self.leftGroupVLayout.setContentsMargins(5, 5, 5, 5)
self.leftGroupVLayout.setSpacing(5)
self.leftGroupVLayout.setObjectName("leftGroupVLayout")
self.widget = QtWidgets.QWidget(self.leftGroupBox)
self.widget.setMinimumSize(QtCore.QSize(288, 427))
self.widget.setMaximumSize(QtCore.QSize(288, 427))
self.widget.setObjectName("widget")
self.widgetLayout = QtWidgets.QVBoxLayout(self.widget)
self.widgetLayout.setContentsMargins(0, 0, 0, 0)
self.widgetLayout.setSpacing(0)
self.widgetLayout.setObjectName("widgetLayout")
self.series = QPieSeries()
self.chart = QChart()
self.chart.addSeries(self.series)
self.chartView = QChartView(self.chart)
self.chart.setAnimationOptions(QChart.SeriesAnimations)
self.chart.legend().hide()
self.widgetLayout.addWidget(self.chartView)
self.leftGroupVLayout.addWidget(self.widget)
self.centralWidgetHLayout.addWidget(self.leftGroupBox)
self.rightGroupBox = QtWidgets.QGroupBox(self.centralwidget)
self.rightGroupBox.setMinimumSize(QtCore.QSize(316, 462))
self.rightGroupBox.setMaximumSize(QtCore.QSize(316, 462))
self.rightGroupBox.setObjectName("rightGroupBox")
self.rightGroupVLayout = QtWidgets.QVBoxLayout(self.rightGroupBox)
self.rightGroupVLayout.setContentsMargins(5, 5, 5, 5)
self.rightGroupVLayout.setSpacing(5)
self.rightGroupVLayout.setObjectName("rightGroupVLayout")
self.tableView = QtWidgets.QTableWidget(self.rightGroupBox)
self.tableView.setMinimumSize(QtCore.QSize(304, 427))
self.tableView.setMaximumSize(QtCore.QSize(304, 427))
self.tableView.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.tableView.setFrameShadow(QtWidgets.QFrame.Plain)
self.tableView.setLineWidth(1)
self.tableView.setMidLineWidth(0)
self.tableView.setObjectName("tableView")
self.tableView.setColumnCount(2)
self.tableView.setRowCount(0)
item = QtWidgets.QTableWidgetItem()
self.tableView.setHorizontalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
self.tableView.setHorizontalHeaderItem(1, item)
self.tableView.horizontalHeader().setVisible(True)
self.tableView.horizontalHeader().setDefaultSectionSize(150)
self.tableView.horizontalHeader().setHighlightSections(False)
self.tableView.horizontalHeader().setMinimumSectionSize(150)
self.tableView.verticalHeader().setVisible(True)
self.tableView.verticalHeader().setDefaultSectionSize(31)
self.rightGroupVLayout.addWidget(self.tableView)
self.centralWidgetHLayout.addWidget(self.rightGroupBox)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.leftGroupBox.setTitle(_translate("MainWindow", "Top 10 Overview"))
self.rightGroupBox.setTitle(_translate("MainWindow", "Recorded Programs"))
self.tableView.setSortingEnabled(True)
item = self.tableView.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "Program Name"))
item = self.tableView.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "Time (mins)"))
MainWindow.show()
#===========================================================================================================================
class Main(UserInterface):
def __init__(self, MainWindow):
self.scanner = Scanner()
self.fileHandler = FileHandler()
self.runScanner()
super().__init__(MainWindow)
def runScanner(self):
self.scanner.signal.connect(self.update)
self.scanner.start()
def update(self):
self.updateChart()
self.updateTable()
self.updateLog()
def updateChart(self):
print("[+] Updating Chart...")
self.chart.removeSeries(self.series)
self.series.clear()
for key, val in RECORDED_PROGRAMS.items():
self.series.append(key, math.ceil(val/60))
self.chart.addSeries(self.series)
def updateTable(self):
print("[+] Updating Table...")
rowCount = len(RECORDED_PROGRAMS)
self.tableView.setRowCount(rowCount)
programNames = list(RECORDED_PROGRAMS.keys())
timeRec = list(RECORDED_PROGRAMS.values())
for row in range(rowCount):
for column in range(2):
if column == 0:
self.tableView.setItem(row, column, QtWidgets.QTableWidgetItem(str(programNames[row])))
if column == 1:
self.tableView.setItem(row, column, QtWidgets.QTableWidgetItem(str(math.ceil(timeRec[row]/60))))
def updateLog(self):
print("[+] Updating Log...")
self.fileHandler.writeFile()
def quit(self):
print("[+] Quitting Program...")
self.scanner.terminate()
self.fileHandler.closeFile()
#===========================================================================================================================
def _exit():
print("[+] At Exit Func Triggered...")
main.quit()
if __name__ == "__main__":
guiApplication = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()
main = Main(window)
atexit.register(_exit)
stop = False
while not stop:
answer = input("Stop [Y/N]? ")
if answer == "Y":
stop = True
time.sleep(0.5)
print("[+] Main Loop Exit hit...")
There are a couple ways you can do this. You can set it to hide windows with self.hide() and self.show() to show them again, or you can use the self.setVisible(False) on each window you want to hide. You can loop through them to hide all of them. You could either leave one window open with a push button to call the functions to show and hide them, or you can use another library like pynput to set keybinds to show or hide the window. Of course you should make a seperate function for this and call it by the push button or keybind, and you can choose what to do in the function by checking if self.isVisible(). The name doesn't have to be self, it can be each one you want to hide and you can loop through them and use a comprehension or lambda function.
I need to make a new window for video output. It should have some buttons (which I'll add later), and a video output. I have the main window with audio playback capability, and I can make a video output on the main window, not the second one.
The video check using magic.python executes videopop, which opens a new window (VideoWindow).
Code:
file: main.py
import sys
import magic
# bude obsahovat menu (File, Edit, About,...), Queue, v menu bude historie
from PyQt5.QtCore import QUrl, Qt
from PyQt5.QtGui import QPalette, QColor, QIcon
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QFileDialog, QWidget, QLabel, QVBoxLayout, QHBoxLayout, \
QPushButton, QStyle, QSlider
from ui import UI
class QueueWin(QMainWindow, UI):
def __init__(self, *args, **kwargs):
super(QueueWin, self).__init__(*args, **kwargs)
self.ui(self)
self.setWindowTitle('MPx') # nastavi title
self.setWindowIcon(QIcon('media-player-5.ico')) # nastavi ikonku
p = self.palette()
p.setColor(QPalette.Window, QColor(52, 51, 51)) # nastavi barvu okna
self.setPalette(p) # aplikuje barvu
self.open_file_act.triggered.connect(self.open_file)
#self.history_act.triggered.connect(self.open_history)
self.history_act.triggered.connect(self.videopop)
#self.open_act.triggered.connect(self.clr_history)
self.mediaPlayer.stateChanged.connect(self.mediastate_changed) #mení ikonku na tlačítku play
self.mediaPlayer.positionChanged.connect(self.update_position)
self.mediaPlayer.durationChanged.connect(self.duration_changed)
self.slider.valueChanged.connect(self.mediaPlayer.setPosition)
self.audioSlider.valueChanged.connect(self.mediaPlayer.setVolume)
def open_history (self):
h = History(self)
h.show()
def videopop(self):
v = VideoWindow(self)
v.show()
def credits(self):
w = Credits(self)
w.show()
def open_file(self):
filename, _ = QFileDialog.getOpenFileName(self, "Open Video")
if filename != '':
self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(filename)))
self.playBtn.setEnabled(True)
self.mediaPlayer.play()
mime = magic.Magic(mime=True) # check zda je soubor video
videocheck = mime.from_file(filename)
if videocheck.find('video') != -1:
self.videopop()
print('it is video')
def position_changed(self, position):
self.slider.setValue(position)
def duration_changed(self,duration):
self.slider.setRange(0, duration)
def update_position(self, position):
self.slider.blockSignals(True)
self.slider.setValue(position)
self.slider.blockSignals(False)
def play_video(self):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
def mediastate_changed(self, state):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
else:
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
class VideoWindow(QMainWindow, UI):
def __init__(self, *args, **kwargs):
super(VideoWindow, self).__init__(*args, **kwargs)
self.videoui(self)
self.setWindowTitle('Video') #nastavi title
self.setWindowIcon(QIcon('media-player-5.ico')) #nastavi ikonku
# http://www.iconseeker.com/search-icon/isabi/media-player-5.html <-- odkaz na ikonku
self.setGeometry(710, 290, 500, 500) #xMistoOtevreni, yMistoOtevreni, xVelikost, yVelikost
self.setMinimumSize(400, 400) # xMinimalniVelikost, yMinimalniVelikost
p = self.palette()
p.setColor(QPalette.Window, QColor(52, 51, 51)) #nastavi barvu okna
self.setPalette(p) #aplikuje barvu
class History(QMainWindow):
def __init__(self, parent=None):
super(History, self).__init__(parent)
self.setWindowTitle('Historie') # nastavi title
self.setWindowIcon(QIcon('media-player-5.ico')) # nastavi ikonku
# http://www.iconseeker.com/search-icon/isabi/media-player-5.html <-- odkaz na ikonku
self.setGeometry(710, 290, 500, 200) # xMistoOtevreni, yMistoOtevreni, xVelikost, yVelikost
self.setMinimumSize(200, 200) # xMinimalniVelikost, yMinimalniVelikost
p = self.palette()
p.setColor(QPalette.Window, QColor(52, 51, 51)) # nastavi barvu okna
self.setPalette(p) # aplikuje barvu
class Credits(QMainWindow):
def __init__(self, parent=None):
super(Credits, self).__init__(parent)
self.setWindowTitle('O programu') # nastavi title
self.setWindowIcon(QIcon('media-player-5.ico')) # nastavi ikonku
# http://www.iconseeker.com/search-icon/isabi/media-player-5.html <-- odkaz na ikonku
self.setGeometry(710, 290, 500, 200) # xMistoOtevreni, yMistoOtevreni, xVelikost, yVelikost
self.setMinimumSize(200, 200) # xMinimalniVelikost, yMinimalniVelikost
p = self.palette()
p.setColor(QPalette.Window, QColor(52, 51, 51)) # nastavi barvu okna
self.setPalette(p) # aplikuje barvu
self.label = QLabel('Autor: Tomáš Gabriel, 3.B OAUH<br>Napsáno pomocí Pythonu a PyQt5', self)
self.label.setStyleSheet("color: yellow")
self.label.adjustSize()
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = QueueWin()
mainWin.show()
sys.exit(app.exec_())
file: UI.py
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtMultimedia import QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import QPushButton, QSlider, QStyle, QHBoxLayout, QVBoxLayout, QMenuBar
class UI(object):
def ui(self, QueueWin):
QueueWin.setObjectName("QueueWin")
QueueWin.resize(600, 500)
_translate = QtCore.QCoreApplication.translate
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.centralWidget = QtWidgets.QWidget(QueueWin)
#sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
#sizePolicy.setHorizontalStretch(0)
#sizePolicy.setVerticalStretch(0)
#sizePolicy.setHeightForWidth(self.centralWidget.sizePolicy().hasHeightForWidth())
#self.centralWidget.setSizePolicy(sizePolicy)
self.openFileBtn = QPushButton("Otevrit soubor...", self.centralWidget)
self.openFileBtn.clicked.connect(self.open_file)
self.playBtn = QPushButton(self.centralWidget) # vytvori tlacitko "play"
self.playBtn.setEnabled(False)
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.playBtn.clicked.connect(self.play_video)
self.slider = QSlider(Qt.Horizontal, self.centralWidget) # vytvori slider
self.slider.setRange(0, 0)
self.audioSlider = QSlider(Qt.Horizontal, self.centralWidget)
self.audioSlider.setMaximum(100)
self.audioSlider.setProperty("value", 100)
QueueWin.setCentralWidget(self.centralWidget)
# self.queue = QListView(self)
# self.queue.setAcceptDrops(True)
# self.queue.setAlternatingRowColors(True)
self.hbox = QHBoxLayout(self.centralWidget) # umisti tlacitka, slidery,... do UI
self.hbox.setContentsMargins(11, 11, 11, 11)
self.hbox.addWidget(self.openFileBtn)
self.hbox.addWidget(self.playBtn)
self.hbox.addWidget(self.slider)
self.hbox.addWidget(self.audioSlider)
self.vbox = QVBoxLayout(self.centralWidget)
#self.vbox.addWidget(videowidget)
# vbox.addWidget(self.queue)
self.vbox.addLayout(self.hbox)
#self.mediaPlayer.setVideoOutput(videowidget)
#self.mediaPlayer.positionChanged.connect(self.update_position)
#self.mediaPlayer.durationChanged.connect(self.duration_changed)
self.menuBar = QMenuBar(QueueWin)
QueueWin.setMenuBar(self.menuBar)
self.open = QtWidgets.QMenu(self.menuBar)
self.open_file_act = QtWidgets.QAction(QueueWin)
self.open.addAction(self.open_file_act)
self.open_file_act.setText(_translate("QueueWin", "Otevřít..."))
self.menuBar.addAction(self.open.menuAction())
self.open.setTitle(_translate("QueueWin", "Soubor"))
self.history = QtWidgets.QMenu(self.menuBar)
self.history_act = QtWidgets.QAction(QueueWin)
self.history.addAction(self.history_act)
self.history_act.setText(_translate("QueueWin", "Otevřít historii"))
self.menuBar.addAction(self.history.menuAction())
self.history.setTitle(_translate("QueueWin", "Historie"))
self.historyClr_act = QtWidgets.QAction(QueueWin)
self.history.addAction(self.historyClr_act)
self.historyClr_act.setText(_translate("QueueWin", "Vymazat historii"))
self.historyClr_act.setShortcut('ALT+H')
#about = self.menuBar.addMenu('Autor')
#about_act = QAction('O autorovi...', self)
#about_act.setShortcut('CTRL+A')
#about_act.triggered.connect(lambda: self.credits())
#about.addAction(about_act)
QtCore.QMetaObject.connectSlotsByName(QueueWin)
def videoui(self, VideoWindow):
VideoWindow.setObjectName("QueueWin")
VideoWindow.resize(600, 500)
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.centralWidget = QtWidgets.QWidget(VideoWindow)
self.videowidget = QVideoWidget(self.centralWidget)
VideoWindow.setCentralWidget(self.centralWidget)
self.hbox = QHBoxLayout(self.centralWidget) # umisti tlacitka, slidery,... do UI
self.hbox.setContentsMargins(11, 11, 11, 11)
self.hbox.addWidget(self.videowidget)
self.vbox = QVBoxLayout(self.centralWidget)
self.vbox.addLayout(self.hbox)
QtCore.QMetaObject.connectSlotsByName(VideoWindow)
You're not setting the video output, and the QMediaPlayer in VideoWindow is completely useless (so you can remove that), while you should use the one created for QueueWin and set the video output with the QVideoWidget of the other window.
The "basic" solution would be the following:
if videocheck.find('video') != -1:
videoWindow = VideoWindow(self)
self.mediaPlayer.setVideoOutput(videoWindow.videowidget)
videoWindow.show()
Be aware, though, that your code has some important issues that would require fixing.
First of all, files generated by the pyuic utility should never, ever be manually modified (read how to properly use those files in the official guidelines about using Designer).
Then, while technically working, creating a new QMainWindow with another one as parent is not suggested; you also continuously create a new window everytime the function is called, while you should reuse any existing window. A more proper approach would be to create the child windows when creating the main one, and show them when required, in this way you can already set up the video output for the separate video widget.
Also note that you don't need to use an external module to check if the media actually has video: just use the videoAvailableChanged signal and show the video window if the argument is True (you cannot just check isVideoAvailable() right after setMedia(), as that function is asynchronous).
Finally, if you're not using any of the QMainWindow features (menu, toolbars, dock widgets, status bar), just use a basic QWidget instead.
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).
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