在 Python 中线程化一个类 [英] Threading a class in Python
问题描述
我目前正在使用在 Qt 中完成的 UI 在 python 中编写程序.这个 UI 包括对一些数据进行编程,并在每 200 毫秒点击一个按钮后发送它们.我的问题是,如果我在主类中执行此操作,程序将完全崩溃(冻结),因此我必须在另一个类或函数中执行此操作.经过一番研究,我遇到了线程,我不知道它是否适合我,但它应该在另一个线程(在后台)中运行我的类,直到我按下停止按钮而不会使我的主窗口崩溃.第二个类需要有一个循环,直到我按下停止按钮.
这是我的代码示例:
导入面板从 PyQt5 导入 QtWidgets、QtCore导入系统从线程导入线程进口螺纹导入时间类 MainWindow(QtWidgets.QMainWindow):def __init__(self, parent=None):super(MainWindow, self).__init__(parent)self.ui = Panel.Ui_MainWindow()self.ui.setupUi(self)self.ui.B_START.clicked.connect(self.StartBtnClicked)self.ui.B_STOP.clicked.connect(self.StopBtnClicked)#..#其他连接和功能#..def StartBtnClicked(self):全局启动开始 btn = 1我的类A()def StopBtnClicked(self):全局启动开始 btn = 0类 myClassA(线程):def __init__(self):Thread.__init__(self)打印(myClassA")self.daemon = 真self.run()定义运行(自我):而 Startbtn == 1:打印(完成")#做事时间.睡眠(0.2)如果 __name__ == '__main__':导入系统app = QtWidgets.QApplication(sys.argv)w = 主窗口()w.show()sys.exit(app.exec_())
以及用于测试的 UI:
from PyQt5 import QtCore, QtGui, QtWidgets类 Ui_MainWindow(对象):def setupUi(self, MainWindow):MainWindow.setObjectName("MainWindow")MainWindow.resize(197, 310)字体 = QtGui.QFont()font.setPointSize(11)font.setBold(True)font.setWeight(75)MainWindow.setFont(字体)MainWindow.setStyleSheet("background-color: rgb(252, 252, 252);\n""")self.centralwidget = QtWidgets.QWidget(MainWindow)self.centralwidget.setStyleSheet("")self.centralwidget.setObjectName("centralwidget")self.B_START = QtWidgets.QPushButton(self.centralwidget)self.B_START.setGeometry(QtCore.QRect(50, 50, 71, 61))字体 = QtGui.QFont()font.setPointSize(11)font.setBold(True)font.setWeight(75)self.B_START.setFont(字体)self.B_START.setFocusPolicy(QtCore.Qt.ClickFocus)self.B_START.setStyleSheet("background-color: rgb(65, 218, 78);\n""\n"""\n""\n"""")self.B_START.setObjectName("B_START")self.B_STOP = QtWidgets.QPushButton(self.centralwidget)self.B_STOP.setGeometry(QtCore.QRect(50, 190, 71, 61))字体 = QtGui.QFont()font.setPointSize(11)font.setBold(True)font.setWeight(75)self.B_STOP.setFont(字体)self.B_STOP.setStyleSheet("背景颜色:rgb(255, 0, 0);")self.B_STOP.setObjectName("B_STOP")self.B_SET = QtWidgets.QPushButton(self.centralwidget)self.B_SET.setGeometry(QtCore.QRect(50, 140, 71, 23))字体 = QtGui.QFont()font.setPointSize(11)font.setBold(True)font.setWeight(75)self.B_SET.setFont(字体)self.B_SET.setStyleSheet("background-color: rgb(255,255,255);\n""\n"""border-width: 1px;\n"""\n""")self.B_SET.setObjectName("B_SET")MainWindow.setCentralWidget(self.centralwidget)self.menubar = QtWidgets.QMenuBar(MainWindow)self.menubar.setGeometry(QtCore.QRect(0, 0, 197, 26))字体 = QtGui.QFont()font.setPointSize(11)font.setBold(True)font.setWeight(75)self.menubar.setFont(字体)self.menubar.setObjectName("menubar")self.menulk = QtWidgets.QMenu(self.menubar)字体 = QtGui.QFont()font.setPointSize(11)font.setBold(True)font.setWeight(75)self.menulk.setFont(字体)self.menulk.setObjectName("menulk")MainWindow.setMenuBar(self.menubar)self.statusbar = QtWidgets.QStatusBar(MainWindow)self.statusbar.setObjectName("statusbar")MainWindow.setStatusBar(self.statusbar)self.menubar.addAction(self.menulk.menuAction())self.retranslateUi(MainWindow)QtCore.QMetaObject.connectSlotsByName(MainWindow)def retranslateUi(self, MainWindow):_translate = QtCore.QCoreApplication.translateMainWindow.setWindowTitle(_translate(MainWindow", Panel"))self.B_START.setText(_translate("MainWindow", "START"))self.B_STOP.setText(_translate("MainWindow", "STOP"))self.B_SET.setText(_translate("MainWindow", "SET"))self.menulk.setTitle(_translate("MainWindow", "TEST"))如果 __name__ == __main__":导入系统app = QtWidgets.QApplication(sys.argv)MainWindow = QtWidgets.QMainWindow()ui = Ui_MainWindow()ui.setupUi(主窗口)MainWindow.show()sys.exit(app.exec_())
您只需将它放在同一个文件中并命名 ui:Pannel.py";我采用的解决方案是好的解决方案吗?如果是,为什么按开始时主窗口仍然崩溃.
通常你会创建一个 QObject 并将它移动到一个
如您所见 +- 每 200 毫秒随机生成一个字符串发送回主窗口.
I'm currently doing a program in python using an UI done in Qt. This UI consists of programming some data and sending them after a click of a button every 200ms. My problem is that if I do this in my main class, the program completely crashes (freeze) so I have to do this in another class or function. After a bit of research, I came across threading, I don't know if it's the right solution for me but it's supposed to run my class in another thread(in the background) until I press the stop button without making my main window crash. The second class needs to have a loop until i press the stop button.
Here is an example of my code:
import Panel
from PyQt5 import QtWidgets, QtCore
import sys
from threading import Thread
import threading
import time
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.ui = Panel.Ui_MainWindow()
self.ui.setupUi(self)
self.ui.B_START.clicked.connect(self.StartBtnClicked)
self.ui.B_STOP.clicked.connect(self.StopBtnClicked)
#..
#Other connect and function
#..
def StartBtnClicked(self):
global Startbtn
Startbtn = 1
myClassA()
def StopBtnClicked(self):
global Startbtn
Startbtn = 0
class myClassA(Thread):
def __init__(self):
Thread.__init__(self)
print("myClassA")
self.daemon = True
self.run()
def run(self):
while Startbtn == 1:
print("DONE")
#Doing stuff
time.sleep(0.2)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
And the UI to test it with:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(197, 310)
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
MainWindow.setFont(font)
MainWindow.setStyleSheet("background-color: rgb(252, 252, 252);\n""")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setStyleSheet("")
self.centralwidget.setObjectName("centralwidget")
self.B_START = QtWidgets.QPushButton(self.centralwidget)
self.B_START.setGeometry(QtCore.QRect(50, 50, 71, 61))
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.B_START.setFont(font)
self.B_START.setFocusPolicy(QtCore.Qt.ClickFocus)
self.B_START.setStyleSheet("background-color: rgb(65, 218, 78);\n""\n""\n""\n""")
self.B_START.setObjectName("B_START")
self.B_STOP = QtWidgets.QPushButton(self.centralwidget)
self.B_STOP.setGeometry(QtCore.QRect(50, 190, 71, 61))
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.B_STOP.setFont(font)
self.B_STOP.setStyleSheet("background-color: rgb(255, 0, 0);")
self.B_STOP.setObjectName("B_STOP")
self.B_SET = QtWidgets.QPushButton(self.centralwidget)
self.B_SET.setGeometry(QtCore.QRect(50, 140, 71, 23))
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.B_SET.setFont(font)
self.B_SET.setStyleSheet("background-color: rgb(255,255,255);\n""\n""border-width: 1px;\n""\n""")
self.B_SET.setObjectName("B_SET")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 197, 26))
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.menubar.setFont(font)
self.menubar.setObjectName("menubar")
self.menulk = QtWidgets.QMenu(self.menubar)
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.menulk.setFont(font)
self.menulk.setObjectName("menulk")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menulk.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Panel"))
self.B_START.setText(_translate("MainWindow", "START"))
self.B_STOP.setText(_translate("MainWindow", "STOP"))
self.B_SET.setText(_translate("MainWindow", "SET"))
self.menulk.setTitle(_translate("MainWindow", "TEST"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
You just have to put it in the same file and name the ui : "Pannel.py" Is the solution i went with is the good one? If yes, why is main window is still crashing when I press Start.
Usually you would create a QObject and move it into a QThread, in order to make it run in another thread, preventing the main thread to freeze:
import sys
import random
import string
import datetime
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QPushButton, QPlainTextEdit
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QThread, QObject, QTimer
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle("My Awesome App")
layout1 = QVBoxLayout()
layout2 = QHBoxLayout()
self.startButton = QPushButton('Start Thread 1')
self.stopButton = QPushButton('Stop Thread 1')
self.startButton.clicked.connect(self._startThread)
self.stopButton.clicked.connect(self._stopThread)
layout2.addWidget(self.startButton)
layout2.addWidget(self.stopButton)
layout1.addLayout( layout2 )
self.textArea = QPlainTextEdit()
self.textArea.setPlaceholderText("")
layout1.addWidget(self.textArea)
widget = QWidget()
widget.setLayout(layout1)
self.setCentralWidget(widget)
# setup threads
self._threads = []
worker = Worker()
self.wThread = QThread()
self.wThread.setObjectName('worker')
self._threads.append((self.wThread, worker)) # you need to keep track of the threads and instances.
worker.moveToThread(self.wThread)
worker.sendData.connect(self._showData) # connect the signal from the thread to a function to to something with the data.
self.wThread.started.connect(worker.start)
def _showData(self, data):
msg = f"[{datetime.datetime.now()}] {data}"
self.textArea.appendPlainText(msg)
def _startThread(self):
self.wThread.start()
def _stopThread(self):
self.wThread.quit()
class Worker(QObject):
sendData = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__()
@pyqtSlot()
def start(self):
self.timer = QTimer()
self.timer.setInterval(200) # 200ms!
self.timer.setSingleShot(False)
self.timer.timeout.connect(self.run)
self.timer.start()
def run(self):
data = ''.join(random.choice(string.ascii_letters) for x in range(24))
self.sendData.emit(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Screenshot:
As you can see +- every 200ms a randomly generated string is sent back to the main window.
这篇关于在 Python 中线程化一个类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!