首页 > 解决方案 > GPIO 事件未显示在 QTreeModel / QWidget / QMainWindow

问题描述

我下面的代码在 Pi 上运行,屏幕上的“滚动”按钮可以工作,明显滚动屏幕的内容。然后,我将按下 GPIO 按钮附加到相同的 Scroll 方法,当按下硬件按钮时代码运行时,屏幕不会更新,直到鼠标移到应该更新的区域上。

我尝试了各种更新屏幕的方法,但都没有奏效:

    self.IQM.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
    self.IQM.layoutChanged.emit()
    self.update()

最小的示例代码如下。感激地收到任何帮助。

非常感谢

凯文

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from PyQt5.QtWidgets import QMainWindow, QWidget, QGridLayout
from PyQt5.QtWidgets import QTreeView, QApplication, qApp, QPushButton

from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtCore import Qt, pyqtSlot

import sys

import RPi.GPIO as GPIO


class StartMarshall(QMainWindow):

    def __init__(self):
        super().__init__()

        self.data = ['XXX' for _ in range(10)]

        # Build Central Widget
        self.widget = QWidget()
        self.setCentralWidget(self.widget)

        # build buttons
        scrollButton = self.createButton(self.scroll, 'Scroll', 'Scroll action')
        exitButton = self.createButton(qApp.quit, 'Exit', 'Exit action')

        # Setup RPI GPIO Hardware Buttons
        # Use Broadcom GPIO numbers (BCM)
        GPIO.setmode(GPIO.BCM)
        pinStartGate = 4
        # Setup pinSG as an Input with a pull up resistor attached (ie pull down for press)
        GPIO.setup(pinSG, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        # Call self.scroll on button press
        GPIO.add_event_detect(pinSG, GPIO.FALLING, callback=self.scroll, bouncetime=300)

        grid = QGridLayout()
        grid.setSpacing(10)

        # intialize view of data
        self.IQ = IQ = QTreeView()

        # Prepopulate View Models
        self.IQM = self.prepModel(IQ)

        self.fillModel(self.IQM, self.data[2:len(self.data)])

        # include the widgets
        grid.addWidget(IQ, 3, 1, -1, -1)

        grid.addWidget(scrollButton, 5, 0)
        grid.addWidget(exitButton, 7, 0)

        self.widget.setLayout(grid)

        # Show QMainWindow
        self.show()
        #self.showFullScreen()
        #self.showMaximized()

    def createButton(self, on_click, btn_txt='button title', btn_tip='this is a button hint'):
        button = QPushButton(btn_txt, self)
        button.setToolTip(btn_tip)
        button.clicked.connect(on_click)
        return button

    def prepModel(self, widget):
        # initialize a model
        model = QStandardItemModel()

        # remove indentation and headers
        widget.setIndentation(0)
        widget.setHeaderHidden(1)

        # add (data) model to widget
        widget.setModel(model)
        return model

    def fillModel(self, model, data):
        # for refilling model data
        for i, d in enumerate(data):
            model.setItem(i, QStandardItem(d))
        return

    @pyqtSlot()
    def scroll(self, event=None):
        print("Scroll")
        # when scroll button is clicked
        if self.data[1:2] == '':  # if second data is blank then no need to scroll
            return

        # remove the first element from data
        self.data.pop(0)

        # add the padded (blank) element from data
        self.data.append('')

        # show the full queue (-1 doesnt show last racer?)
        self.fillModel(self.IQM, self.data[2:len(self.data)])

        return

# Main
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = StartMarshall()
    sys.exit(app.exec_())

标签: pythonpyqtraspberry-pipyqt5gpio

解决方案


add_event_detect 调用的回调在辅助线程上执行,并且在 Qt 中禁止从另一个线程更新 GUI。所以诀窍是使用QTimer.singleShot(0, ...)with更新 GUI functools.partial

from functools import partial

# ...

class StartMarshall(QMainWindow):
    # ...

    @pyqtSlot(QStandardItemModel, list)
    def fillModel(self, model, data):
        # for refilling model data
        for i, d in enumerate(data):
            model.setItem(i, QStandardItem(d))
        return

    def scroll(self, event=None):
        print("Scroll")
        # when scroll button is clicked
        # if second data is blank then no need to scroll
        if self.data[1:2] == "":
            return
        # remove the first element from data
        self.data.pop(0)
        # add the padded (blank) element from data
        self.data.append("")
        # show the full queue (-1 doesnt show last racer?)
        wrapper = partial(self.fillModel, self.IQM, self.data[2:])
        QtCore.QTimer.singleShot(0, wrapper)
        return

推荐阅读