Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()

时光总嘲笑我的痴心妄想 提交于 2021-02-07 17:29:22

问题


I'm trying to change Image with Pushbutton (GPIO PINs) stored inside a folder using QML with PyQt5
Python Code:

from gpiozero import Button
from signal import pause

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtQml import *
import os, time, sys


def btn_pressed():
    global r
    return lambda: r.setProperty("source", "/home/pi/Desktop/example/sample/img/img1.jpg")

button1 = Button(20)        
myApp = QGuiApplication([])
myEngine = QQmlApplicationEngine()
directory = os.path.dirname(os.path.abspath(__file__))
myEngine.load(QUrl.fromLocalFile(os.path.join(directory, 'simple1.qml')))
if not myEngine.rootObjects():
    print("root object not found")

r = myEngine.rootObjects()[0].findChild(QObject, "DisplayImage")
dir(r)
print("Main Thead id ",myApp.thread())
updateUI = UpdateUI()

button1.when_pressed = btn_pressed()   
myEngine.quit.connect(myApp.quit)
sys.exit(myApp.exec_())

QML:

import QtQuick 2.10
import QtQuick.Controls 1.6
import QtQuick.Window 2.2

ApplicationWindow {
    id : main
    title: qsTr("Test")
    width: 640
    height: 480
    visible: true

    Rectangle{
        width: parent.width
        height: parent.height

        Image {
            id: img
            source: ""
            width : main.width;
            fillMode : Image.PreserveAspectFit
            objectName: "DisplayImage"
        }
    }
}

When I Pressed the Push Button Connected to GPIO 20 in raspberry Pi 4 , I'm Getting below error message.

QObject: Cannot create children for a parent that is in a different thread. 
(Parent is QQmlApplicationEngine(0x10f5ba0), parent's thread is QThread(0xf6f7c0), current thread is QThread(0xa98007c8)
Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()

I also tried creating class with a method changing Image Source Property and then calling the same method(via Class Object) upon PushButton press but it shows same error message

Is there a way to set Image's Source Property in QML - from parent thread in Python.

In Winforms we can Avoid "Cross-Thread Violation Error" by using Delegates.

Can we Use Signal and Slot to Solve this Problem in PyQt5.


回答1:


gpiozero uses threads to be able to monitor the gpio so as not to block the main GUI, so the associated function when_pressed will be executed in that thread but Qt prohibits updating GUI elements such as the image from another thread.

The solution is to create a QObject that emits the signal in the method associated with when_pressed since the signals are thread-safe.

On the other hand it is not good to modify the QML elements from C++/Python, it is better to export the QObject to QML and make the connections in that scope.

import os
import sys

from gpiozero import Button

from PyQt5.QtCore import pyqtSignal, QObject, QUrl
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine


class ButtonManager(QObject):
    pressed = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self._button = Button(20)
        self._button.when_pressed = self._on_when_pressed

    def _on_when_pressed(self):
        self.pressed.emit()


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    button_manager = ButtonManager()
    engine = QQmlApplicationEngine()
    engine.rootContext().setContextProperty("button_manager", button_manager)
    current_dir = os.path.dirname(os.path.abspath(__file__))
    engine.load(QUrl.fromLocalFile(os.path.join(current_dir, "simple1.qml")))
    if not engine.rootObjects():
        print("root object not found")
        sys.exit(-1)
    engine.quit.connect(app.quit)
    sys.exit(app.exec_())

simple1.qml

import QtQuick 2.10
import QtQuick.Controls 1.6
import QtQuick.Window 2.2

ApplicationWindow {
    id : main
    title: qsTr("Test")
    width: 640
    height: 480
    visible: true
    Rectangle{
        anchors.fill: parent
        Image {
            id: img 
            width : main.width
            fillMode : Image.PreserveAspectFit
        }
    }
    Connections{
        target: button_manager
        onPressed: img.source = "/home/pi/Desktop/example/sample/img/img1.jpg"
    }
}


来源:https://stackoverflow.com/questions/59678470/updates-can-only-be-scheduled-from-gui-thread-or-from-qquickitemupdatepaintnod

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!