活动期间 GUI 没有变化? (Python3.6,PyQt5)

Posted

技术标签:

【中文标题】活动期间 GUI 没有变化? (Python3.6,PyQt5)【英文标题】:No changes of GUI during event? (Python3.6, PyQt5) 【发布时间】:2019-09-09 11:13:08 【问题描述】:

我正在尝试在操作期间更改按钮的样式。

当按钮被按下时,我想让它们变成蓝色,然后读取 rfid 卡,然后通过 ID 将按钮的颜色更改为绿色或红色(代码中只是红色以便更容易)。

问题在于将按钮样式更改为蓝色无济于事(绿色和红色都可以正常工作)。 它正在等待完成“clicked()”方法吗? 如何告诉 PyQt5 “现在就做!” ?

编辑:我将伪代码修改为可重现的示例。

#!/usr/bin/python3
import sys
import time
from PyQt5.QtWidgets import *

class MainScreen(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.button = QPushButton("TEXT")
        self.button.clicked.connect(self.clicked)
        self.changeButtonColor("black")  # set default style
        self.grid = QGridLayout(self)
        self.grid.addWidget(self.button)
        self.show()

    def changeButtonColor(self, color):
        self.button.setStyleSheet("QPushButton \
                color: "+color+"; font: bold 18px;")

    def clicked(self):
        self.changeButtonColor("blue")  # nothing happening (this is my problem!)
        uid = self.readCard()  # reading ID from rfid card
        self.changeButtonColor("red")

    def readCard(self):
        #return rfid.read() # in real case
        time.sleep(2)
        return "12345678"

def main():
    app = QApplication(sys.argv)
    window = MainScreen()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

【问题讨论】:

请发帖minimal reproducible example 【参考方案1】:

void QTimer::singleShot(int msec, const QObject *receiver, const char *member)

这个静态函数在给定的时间间隔后调用一个槽。

使用此功能非常方便,因为您无需费心使用 timerEvent 或创建本地 QTimer 对象。

import sys
import time
from PyQt5.QtWidgets import *
from PyQt5 import QtCore

class MainScreen(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.button = QPushButton("TEXT")
        self.button.clicked.connect(self.clicked)
        self.changeButtonColor("black")             
        self.grid = QGridLayout(self)
        self.grid.addWidget(self.button)
        self.show()

    def changeButtonColor(self, color):
        self.button.setStyleSheet("QPushButton \
                color: "+color+"; font: bold 18px;")

    def clicked(self):
        self.changeButtonColor("blue")  # nothing happening (this is my problem!)
#        time.sleep(2)                  # reading ID from rfid card
#        self.changeButtonColor("red")
        QtCore.QTimer.singleShot(2000, lambda: self.changeButtonColor("red"))             # <---

def main():
    app = QApplication(sys.argv)
    window = MainScreen()
    sys.exit(app.exec_())


更新

import sys
import time
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *

class Worker(QtCore.QObject):                              # +++

    started  = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()
    data     = QtCore.pyqtSignal(str)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.running = False
        self.stop = 5

    @QtCore.pyqtSlot()
    def read_data_from_sensor(self):
        self.started.emit()
        time.sleep(1)                                  # We simulate the blocking process
        while self.running and self.stop:
            dt  = time.strftime("%Y-%m-%d %H:%M:%S")
            self.data.emit(dt)
            time.sleep(1)                              # We simulate the blocking process
            self.stop -= 1
            
        self.finished.emit()


class MainScreen(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.button = QPushButton("TEXT Start")
        self.button.clicked.connect(self.clicked)
        self.changeButtonColor("black") 
        
        self.label_data = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)
        self.label_data.setText('Pending')
        
        self.grid = QGridLayout(self)
        self.grid.addWidget(self.label_data)
        self.grid.addWidget(self.button)
        self.show()
### vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv       
        self._worker = Worker()
        self._worker.started.connect(self.on_started)
        self._worker.finished.connect(self.on_finished)
        self._worker.data.connect(self.update_label)

        self._thread = QtCore.QThread(self)
        self._thread.start()
        self._worker.moveToThread(self._thread)      

    @QtCore.pyqtSlot()
    def on_started(self):
        self.label_data.setText("Start to read")
        self.button.setText("TEXT Stop")
        self.button.setEnabled(True) 
        self._worker.stop = 5
        
    @QtCore.pyqtSlot()
    def on_finished(self):
        self.label_data.setText("Pending")
        self.button.setText("TEXT Start")
        self.button.setEnabled(True)
        self.changeButtonColor("red")                      # <---
        self._worker.running = False
        self._worker.stop = 5
        
    @QtCore.pyqtSlot(str)
    def update_label(self, data):
        self.label_data.setText(data)
### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        

    def changeButtonColor(self, color):
        self.button.setStyleSheet("QPushButton \
                color: "+color+"; font: bold 18px;")

    def clicked(self):
        self.changeButtonColor("blue")  # nothing happening (this is my problem!)
#        time.sleep(2)                  # reading ID from rfid card
#        self.changeButtonColor("red")
#        QtCore.QTimer.singleShot(2000, lambda: self.changeButtonColor("red"))             # <---

### vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv       
        if self._worker.running:
            self._worker.running = False
        else:
            self._worker.running = True
            QtCore.QTimer.singleShot(0, self._worker.read_data_from_sensor)
        self.button.setEnabled(False)        
### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^       
        

def main():
    app = QApplication(sys.argv)
    window = MainScreen()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

【讨论】:

QTimer 不错,但在我的情况下 'time.sleep()' 实际上是一个读取 rfid 卡的函数,所以我不知道用户连接卡需要多长时间 【参考方案2】:

安装quamash和qtawesome

#!/usr/bin/python3
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QSize
import asyncio
from quamash import QEventLoop
import qtawesome as qt


class MainScreen(QWidget):
    oop = None

    def __init__(self, _loop):
        try:
            QWidget.__init__(self)
            self.loop = _loop
            self.button = QPushButton("TEXT")
            self.button.clicked.connect(self.synced_click)
            self.button.setStyleSheet("color: black;")
            self.grid = QGridLayout(self)
            self.grid.addWidget(self.button)
            self.show()
        except Exception as e:
            print(e)

    def synced_click(self):
        try:
            asyncio.ensure_future(self.clicked(), loop=self.loop)
        except Exception as e:
            print('error')
            print(e)

    @asyncio.coroutine
    async def clicked(self):
        try:
            spin_icon = qt.icon('fa5s.spinner', color='red', animation=qt.Spin(self.button))
            self.loop.call_soon_threadsafe(self.button.setIconSize, QSize(12, 12))
            self.loop.call_soon_threadsafe(self.button.setIcon, spin_icon)
            self.loop.call_soon_threadsafe(self.button.setText, "progress")
            self.loop.call_soon_threadsafe(self.button.setStyleSheet, "color: red;")
            await asyncio.sleep(3)
            tick_icon = qt.icon('fa5s.check-circle', color='blue')
            self.loop.call_soon_threadsafe(self.button.setStyleSheet, "color:blue;")
            self.loop.call_soon_threadsafe(self.button.setText, "done")
            self.loop.call_soon_threadsafe(self.button.setIcon, tick_icon)
            await asyncio.sleep(1)
        except Exception as e:
            print('error')
            print(e)


if __name__ == "__main__":
    import sys

    loop = QEventLoop(QApplication(sys.argv))
    asyncio.set_event_loop(loop=loop)
    with loop:
        window = MainScreen(loop)
        loop.run_forever()

【讨论】:

以上是关于活动期间 GUI 没有变化? (Python3.6,PyQt5)的主要内容,如果未能解决你的问题,请参考以下文章

基于Python3.6+OpenCV行人检测(GUI界面)

用PyQt5和python3.6做一个最简单的GUI的程序

构建代码期间的 Python 版本不匹配

python3.6新变化'async for'与枚举不兼容吗

Python入门 Python3.6安装

Swing GUI 在数据处理期间不更新