我正在创建烤箱监控程序,该程序可以通过Modbus TCP到达某些PID控制器。我正在尝试实现一个电子邮件警报部件,该部件将监视温度以及温度是否在允许范围内。如果超出容忍范围,则发送一封电子邮件,然后每隔10分钟再发送一封电子邮件,然后再超出容忍范围。我确定我的工作范围有些混乱,但是对于我的一生,我无法弄清楚是什么。当烤箱超出公差范围时,我的“ inTolerance”功能将起作用。它发送1封电子邮件,应该启动计时器,但我的“ is_alive”调用未返回true。因此,当“ inTolerance”再次呼叫时,它会发送另一封电子邮件,然后炸裂,因为我认为它会尝试启动另一个“ t”计时器。
任何帮助和健全性检查将非常有帮助和赞赏。
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_())
您的问题的主要原因是您正在调用cancel()
,这会导致计时器使其自身无效[[即使尚未启动],并且结果是在此之后计时器将永远不再实际开始。此后还有另一个问题:线程对象只能启动一次,并且每次需要再次启动它时都需要创建一个新的Timer对象。
也就是说,您不应混合使用计时器对象,在处理线程时,通常最好使用Qt提供的内容。
在您的情况下,解决方案是使用QElapsedTimer,该对象可以返回自(重新)启动以来经过的时间。
请注意,我使用的是QWidget类(仅在此示例中简化了ui),有关代码之后的内容更多。
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_())
如上所述,我正在使用QWidget类。似乎您正在使用pyuic实用程序的输出来创建程序,您应该这样做。您应该在单独的脚本中创建程序,并将生成的文件用作导入的模块。从pyuic创建的never
py
文件应NEVER进行编辑。在这种情况下,我完全通过代码创建了接口,但是您仍然可以在设计器中重新创建ui并使用生成的py文件,如the documentation中所述(第三个方法,即多重继承方法,通常是最好的)。