Обновления могут быть запланированы только из потока GUI или из QQuickItem :: updatePaintNode () - PullRequest
4 голосов
/ 10 января 2020

Я пытаюсь изменить изображение с помощью кнопки (PIN-коды GPIO), хранящейся в папке, используя QML с PyQt5
Python Код:

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

Когда я нажал кнопку Pu sh, подключенную к GPIO 20 в Raspberry Pi 4, я получаю сообщение об ошибке ниже.

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()

Я также попытался создать класс с методом, изменяющим свойство Image Source и затем вызывает тот же метод (через Class Object) при нажатии PushButton, но он показывает то же сообщение об ошибке

Есть ли способ установить свойство Source Image в QML - из родительского потока в Python.

В Winforms мы можем избежать «ошибки межпотокового нарушения», используя Delegates.

Можем ли мы использовать Signal and Slot для решения этой проблемы в PyQt5.

1 Ответ

1 голос
/ 11 января 2020

использует потоки, чтобы иметь возможность отслеживать gpio, чтобы не блокировать основной GUI, поэтому связанная функция when_pressed будет выполняться в этом потоке, но Qt запрещает обновление элементов GUI, таких как как изображение из другого потока.

Решение состоит в том, чтобы создать объект QObject, который испускает сигнал в методе, связанном с when_pressed, поскольку сигналы являются потокобезопасными.

С другой стороны, это нехорошо модифицировать элементы QML из C ++ / Python, лучше экспортировать QObject в QML и выполнить соединения в этой области.

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