在 Python 中线程化一个类 [英] Threading a class in Python

查看:44
本文介绍了在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆