Событие GPIO не отображается в QTreeModel / QWidget / QMainWindow - PullRequest
1 голос
/ 15 апреля 2019

Мой код, приведенный ниже, работает на Pi, и кнопка «Scroll» на экране работает, визуально прокручивая содержимое экрана. Затем я присоединяю нажатие кнопки GPIO к тому же методу прокрутки, и пока код запускается при нажатии аппаратной кнопки, экран не обновляется, пока мышь не наведут на область, которая должна быть обновлена.

Я пробовал разные способы обновления экрана, но ни один из них не работал:

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

Минимальный пример кода приведен ниже. Любая помощь с благодарностью получена.

Большое спасибо

Kevin

#!/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_())

1 Ответ

0 голосов
/ 15 апреля 2019

Обратный вызов, вызванный add_event_detect, выполняется во вторичном потоке, и в Qt запрещено обновлять графический интерфейс из другого потока. Таким образом, хитрость заключается в обновлении графического интерфейса, используя QTimer.singleShot(0, ...) с 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
...