QML 仪表未从 Python 更新

QML gauges not updating from Python

我还在努力掌握 QT...我制作了一个 python 文件和一个 QML 文件。 Python 文件根据通过 UDP 获取的数据更新仪表值。

虽然这只有效一次...第一个 UDP 数据包进入并更新仪表,但是当它收到下一个数据包时,尽管值更新,仪表本身却没有。

QML

 CircularGauge {
        id: circularGauge
        x: 30
        y: 30
        value: itt1value
        minimumValue: 0
        maximumValue: 1200
        tickmarksVisible: false
        style: CircularGaugeStyle {
            maximumValueAngle: 400
            minimumValueAngle: 90
        }
    }

Python:

def configureApplication():

    # Set up the application window
    app = QGuiApplication(sys.argv)
    view = QQuickView()
    view.setResizeMode(QQuickView.SizeRootObjectToView)
    view.setTitle("my title")

    # Load the QML file
    qml_file = os.path.join(os.path.dirname(__file__), "maingui.qml")
    view.setSource(QUrl.fromLocalFile(os.path.abspath(qml_file)))

    # load the slots into the QML file
    view.rootContext().setContextProperty("itt1value", 0)


    t = threading.Thread(target=receivedata, args=(view,))
    t.start()

    # Show the window
    if view.status() == QQuickView.Error:
        sys.exit(-1)
    view.show()

    # execute and cleanup
    app.exec_()
    del view

在线程方法 receivedata() 中,我从 UDP 获取数据,处理它,然后像这样将它发送到仪表:

view.rootContext().setContextProperty("itt1value", itt)

receivedata() 中有一个包含上述详细信息的 while 循环,但仪表实际上只更新一次。如果我在 QML 文件中放置一个语句来显示 itt1value,它总是有正确的值,那么我是否需要放置一个方法来检测这个值的变化并重新绘制仪表?

编辑:有人问我 receivedata() 的细节,所以我把它附在这里:

def receivedata(view):
    print("Starting UDP server...")
    UDP_IP = "192.168.0.14"
    UDP_PORT = 49000
    sock = socket.socket(socket.AF_INET,  # Internet
                         socket.SOCK_DGRAM)  # UDP
    sock.bind((UDP_IP, UDP_PORT))
    olditt = 0
    loopruns = 0 # for debugging

    while True:
        rawstring = sock.recv(1024)
        hexarray = []

        #lots of irrelevent formatting here, result is int(value)


        itt = float(hextoint(value, olditt))
        olditt = itt

        itt = format(itt, '.3f')

        current = str(loopruns) # for debugging
        view.setTitle(current) # for debugging
        view.rootContext().setContextProperty("itt1value", itt)
        loopruns = loopruns + 1
        print(itt)

您有以下错误:

  • 您不能直接从另一个线程修改 GUI。

  • 可以使用setContextProperty()再次导出值,除非重新加载QML,否则不会更改以前的值。

  • 如果你想"itt"修改QML中的任何值,它必须是兼容类型,在这种情况下,CircularGauge的值是"real",因此数据类型python 支持的是 float。

考虑到以上情况,我创建了一个 QObject,因为它是线程安全的,可以通过信号通知更改,并导出使用 Connections 建立连接的 QObject。

main.py

import os
import random
import sys
import threading
import time

from PySide2.QtCore import QObject, QUrl, Signal
from PySide2.QtGui import QGuiApplication
from PySide2.QtQuick import QQuickView


class Connections(QObject):
    titleChanged = Signal(str, arguments=["title"])
    valueChanged = Signal(float, arguments=["value"])


def receivedata(connector):
    # configurations

    loopruns = 0
    while True:
        # other stuff
        time.sleep(0.1)
        itt = random.uniform(0.0, 1200.0)
        connector.valueChanged.emit(itt)
        connector.titleChanged.emit(str(loopruns))
        loopruns += 1


def main(args):
    app = QGuiApplication(args)
    view = QQuickView(title="my title", resizeMode=QQuickView.SizeRootObjectToView)
    connector = Connections()
    connector.titleChanged.connect(view.setTitle)
    view.rootContext().setContextProperty("connector", connector)
    # Load the QML file
    qml_file = os.path.join(os.path.dirname(__file__), "maingui.qml")
    view.setSource(QUrl.fromLocalFile(os.path.abspath(qml_file)))
    # start thread
    threading.Thread(target=receivedata, args=(connector,)).start()
    # Show the window
    if view.status() == QQuickView.Error:
        return -1
    view.show()
    # execute and cleanup
    ret = app.exec_()
    del view
    return ret


if __name__ == "__main__":
    sys.exit(main(sys.argv)) 

maingui.qml

import QtQml 2.13
import QtQuick.Extras 1.4
import QtQuick.Controls.Styles 1.4

CircularGauge {
    id: circularGauge
    value: 100
    minimumValue: 0
    maximumValue: 1200
    tickmarksVisible: false
    style: CircularGaugeStyle {
        maximumValueAngle: 400
        minimumValueAngle: 90
    }

    Connections{
        target: connector
        onValueChanged: circularGauge.value = value
    }
}