更新只能从 GUI 线程或从 QQuickItem::updatePaintNode() 安排 [英] Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()

查看:224
本文介绍了更新只能从 GUI 线程或从 QQuickItem::updatePaintNode() 安排的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用带有 PyQt5 的 QML 更改存储在文件夹中的按钮(GPIO PIN)图像
Python代码:

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"
        }
    }
}

当我在 raspberry Pi 4 中按下连接到 GPIO 20 的按钮时,我收到以下错误消息.

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

有没有办法在 QML 中设置图像的 Source 属性 - 从 Python 中的父线程.

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

在 Winforms 中,我们可以通过使用 Delegates 来避免跨线程违规错误".

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

我们能否在 PyQt5 中使用 Signal and Slot 来解决这个问题.

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

推荐答案

使用线程来监视 gpio 以免阻塞主 GUI,因此关联函数 when_pressed 将在该线程中执行,但 Qt 禁止从另一个线程更新 GUI 元素,例如图像.

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.

解决方案是创建一个 QObject,它在与 when_pressed 关联的方法中发出信号,因为信号是线程安全的.

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

另一方面,从 C++/Python 修改 QML 元素是不好的,最好将 QObject 导出到 QML 并在该范围内建立连接.

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"
    }
}

这篇关于更新只能从 GUI 线程或从 QQuickItem::updatePaintNode() 安排的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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