简体   繁体   English

QT/QML 数据 Model

[英]QT/QML Data Model

I want to create a Qt data model with a structure like below to use in Python and QML. If any values or keys are changed, added, or deleted in Python or QML I would need the values to be updated on the other side (QML or Python).我想创建一个 Qt 数据 model,其结构如下所示,用于 Python 和 QML。如果在 Python 或 QML 中更改、添加或删除了任何值或键(我需要在另一侧更新 QML 值)或蟒蛇)。 This would ideally be the model used in a ListView and I would only display certain fields for the ListView.理想情况下,这将是 ListView 中使用的 model,我只会显示 ListView 的某些字段。 But I would use this data model to store all my information.但我会使用这个数据 model 来存储我所有的信息。 When a test is performed for memory in Python I would want to write that test log information to this data model and display that in QML. I have read about QAbstractListModel, but I am unsure if I can make nested objects or lists and whether they would update automatically or not.当在 Python 中对 memory 执行测试时,我想将该测试日志信息写入此数据 model 并在 QML 中显示。我已经阅读了 QAbstractListModel,但我不确定我是否可以制作嵌套对象或列表以及它们是否会自动更新与否。

System: {
    Processor: {
        name: 'Processor',
        description: 'Intel i7 6600k',
        iconSource: '/resources/images/chip.svg',
        progressValue: 100,
        pageSource: '/resources/qml/Processor.qml',
        details: {
            "badge": "Intel® Core i5 processor",
            "cache": "6144 KB",
            "clock": "4200000"
        }
        testLog: [
            'Starting Cpu Test',
            'Detected Intel CPU',
            'Performing intense calculations',
            'Processing calculations still',
            'Cleaning up',
            'Test Passed'
        ]
    }    
    Memory: {
        name: 'Memory',
        description: 'Kingston 16GB DDR3',
        iconSource: '/resources/images/ram.svg',
        progressValue: 50,
        pageSource: '/resources/qml/Processor.qml',
        details: {
            "device_locator_string": "ChannelB-DIMM1",
            "device_set": 0,
            "error_handle": 65534,
            "extended_size": 0,
            "form_factor": "Unknown"
        },
        testLog: [
            'Starting Memory Test',
            'Detected 2 x RAM modules',
            'Performing intense calculations',
            'Processing calculations still',
            'Cleaning up',
            'Test Failed'
        ]
    }
}

There are several options in this case such as:在这种情况下有几种选择,例如:

  • Create a model based on QAbstractItemModel where you provide the properties through roles.基于 QAbstractItemModel 创建一个 model,您可以在其中通过角色提供属性。

  • Create a QObject Device that has the desired properties as qproperties and expose it through a qproperty associated with a signal from another QObject, the QObject Device list and use that list as a model.创建一个 QObject 设备,它具有所需的属性作为 qproperties,并通过与来自另一个 QObject 的信号关联的 qproperty 公开它,QObject 设备列表并将该列表用作 model。

  • Create a model as a QAbstractListModel (or QStandardItemModel) and expose the QObject through a role.创建一个 model 作为 QAbstractListModel(或 QStandardItemModel)并通过角色公开 QObject。

  • Create a QObject that exposes a list of QObjects Device through ListProperty.创建一个 QObject,通过 ListProperty 公开 QObjects Device 列表。

In this case I have chosen the first option for a demo:在这种情况下,我选择了第一个演示选项:

main.py主程序

from dataclasses import dataclass
import sys
from typing import Callable

from PySide2.QtCore import (
    Property,
    QCoreApplication,
    QObject,
    QVariantAnimation,
    Qt,
    QUrl,
)
from PySide2.QtGui import QGuiApplication, QStandardItem, QStandardItemModel
from PySide2.QtQml import QQmlApplicationEngine


@dataclass
class item_property:
    role: int
    function: Callable = None

    def __call__(self, function):
        self.function = function
        return self


class item_property_impl(property):
    def __init__(self, role, function):
        super().__init__()
        self._role = role
        self._function = function

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        if hasattr(obj, "_initial"):
            obj.setData(self._function(obj), self._role)
            delattr(obj, "_initial")
        return obj.data(self._role)

    def __set__(self, obj, value):
        obj.setData(value, self._role)


class ItemMeta(type(QStandardItem), type):
    def __new__(cls, name, bases, attrs):
        for key in attrs.keys():
            attr = attrs[key]
            if not isinstance(attr, item_property):
                continue
            new_prop = item_property_impl(attr.role, attr.function)
            attrs[key] = new_prop
            if not hasattr(cls, "attrs"):
                cls._names = []
            cls._names.append(key)

        obj = super().__new__(cls, name, bases, attrs)
        return obj

    def __call__(cls, *args, **kw):
        obj = super().__call__(*args, **kw)
        obj._initial = True
        for key in cls._names:
            getattr(obj, key)
        return obj


class Item(QStandardItem, metaclass=ItemMeta):
    pass


keys = (b"name", b"description", b"icon", b"progress", b"source", b"details", b"log")
ROLES = (
    NAME_ROLE,
    DESCRIPTION_ROLE,
    ICON_ROLE,
    PROGRESS_ROLE,
    SOURCE_ROLE,
    DETAILS_ROLE,
    LOG_ROLE,
) = [Qt.UserRole + i for i, _ in enumerate(keys)]


class Device(Item):
    @item_property(role=NAME_ROLE)
    def name(self):
        return ""

    @item_property(role=DESCRIPTION_ROLE)
    def description(self):
        return ""

    @item_property(role=ICON_ROLE)
    def icon(self):
        return ""

    @item_property(role=PROGRESS_ROLE)
    def progress(self):
        return 0

    @item_property(role=SOURCE_ROLE)
    def source(self):
        return ""

    @item_property(role=DETAILS_ROLE)
    def details(self):
        return dict()

    @item_property(role=LOG_ROLE)
    def log(self):
        return list()


class DeviceManager(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._model = QStandardItemModel()
        self._model.setItemRoleNames(dict(zip(ROLES, keys)))

    def get_model(self):
        return self._model

    model = Property(QObject, fget=get_model, constant=True)

    def add_device(self, *, name, description, icon, progress, source, details, log):
        dev = Device()
        dev.name = name
        dev.description = description
        dev.icon = icon
        dev.progress = progress
        dev.source = source
        dev.details = details
        dev.log = log
        self.model.appendRow(dev)
        return dev


def main():
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    manager = DeviceManager()
    engine.rootContext().setContextProperty("device_manager", manager)

    url = QUrl("main.qml")

    def handle_object_created(obj, obj_url):
        if obj is None and url == obj_url:
            QCoreApplication.exit(-1)

    engine.objectCreated.connect(handle_object_created, Qt.QueuedConnection)
    engine.load(url)

    processor = manager.add_device(
        name="Processor",
        description="Intel i7 6600k",
        icon="/resources/images/chip.svg",
        progress=10,
        source="resources/qml/Processor.qml",
        details={
            "badge": "Intel® Core i5 processor",
            "cache": "6144 KB",
            "clock": "4200000",
        },
        log=[
            "Starting Cpu Test",
            "Detected Intel CPU",
            "Performing intense calculations",
            "Processing calculations still",
            "Cleaning up",
            "Test Passed",
        ],
    )

    memory = manager.add_device(
        name="Memory",
        description="Kingston 16GB DDR3",
        icon="/resources/images/ram.svg",
        progress=50,
        source="resources/qml/Memory.qml",
        details={
            "device_locator_string": "ChannelB-DIMM1",
            "device_set": 0,
            "error_handle": 65534,
            "extended_size": 0,
            "form_factor": "Unknown",
        },
        log=[
            "Starting Memory Test",
            "Detected 2 x RAM modules",
            "Performing intense calculations",
            "Processing calculations still",
            "Cleaning up",
            "Test Failed",
        ],
    )

    def update_progress(value):
        processor.progress = value

    animation = QVariantAnimation(
        startValue=processor.progress, endValue=100, duration=3 * 1000
    )
    animation.valueChanged.connect(update_progress)
    animation.start()

    ret = app.exec_()
    sys.exit(ret)


if __name__ == "__main__":
    main()

main.qml主要.qml

import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    id: root

    visible: true
    width: 400
    height: 400

    ListView {
        id: view

        property url currentSource: ""

        model: device_manager.model
        width: parent.width / 2
        height: parent.height
        spacing: 10
        clip: true
        flickableDirection: Flickable.VerticalFlick
        boundsBehavior: Flickable.StopAtBounds
        currentIndex: -1

        ScrollBar.vertical: ScrollBar {
        }

        highlight: Rectangle {
            color: "lightsteelblue"
            radius: 5
        }

        delegate: Rectangle {
            id: rect

            color: "transparent"
            border.color: ListView.isCurrentItem ? "red" : "green"
            height: column.height
            width: ListView.view.width

            Column {
                id: column

                Text {
                    text: model.name
                }

                ProgressBar {
                    from: 0
                    to: 100
                    value: model.progress
                }

                Label {
                    text: "Log:"
                    font.bold: true
                    font.pointSize: 15
                }

                Text {
                    text: model.log.join("\n")
                }

            }

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    rect.ListView.view.currentIndex = index;
                    rect.ListView.view.currentSource = model.source;
                }
            }

        }

    }

    Rectangle {
        x: view.width
        width: parent.width / 2
        height: parent.height
        color: "salmon"

        Loader {
            anchors.centerIn: parent
            source: view.currentSource
        }

    }

}

Processor.qml处理器.qml

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle{
    color: "red"
    width: 100
    height: 40
    Text{
        text: "Processor"
        anchors.centerIn: parent
    }
}

Memory.qml Memory.qml

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle{
    color: "blue"
    width: 100
    height: 40
    Text{
        text: "Memory"
        anchors.centerIn: parent
    }
}
├── main.py
├── main.qml
└── resources
    └── qml
        ├── Memory.qml
        └── Processor.qml

在此处输入图像描述

I want to create a Qt data model with a structure like below to use in Python and QML.我想创建一个具有如下结构的 Qt 数据 model 用于 Python 和 ZC477ABFB2FD4959A20ZD5.E0 If any values or keys are changed, added, or deleted in Python or QML I would need the values to be updated on the other side (QML or Python).如果在 Python 或 QML 中更改、添加或删除任何值或键,我需要在另一侧(QML 或 Python)更新这些值。 This would ideally be the model used in a ListView and I would only display certain fields for the ListView.理想情况下,这将是 ListView 中使用的 model,我只会显示 ListView 的某些字段。 But I would use this data model to store all my information.但我会使用这些数据 model 来存储我的所有信息。 When a test is performed for memory in Python I would want to write that test log information to this data model and display that in QML.当对 Python 中的 memory 执行测试时,我想将该测试日志信息写入此数据 model 并在 ZC4DZ7AB02FD4444444D4DZ7AB02FD4 中显示I have read about QAbstractListModel, but I am unsure if I can make nested objects or lists and whether they would update automatically or not.我已经阅读了 QAbstractListModel,但我不确定我是否可以制作嵌套对象或列表以及它们是否会自动更新。

System: {
    Processor: {
        name: 'Processor',
        description: 'Intel i7 6600k',
        iconSource: '/resources/images/chip.svg',
        progressValue: 100,
        pageSource: '/resources/qml/Processor.qml',
        details: {
            "badge": "Intel® Core i5 processor",
            "cache": "6144 KB",
            "clock": "4200000"
        }
        testLog: [
            'Starting Cpu Test',
            'Detected Intel CPU',
            'Performing intense calculations',
            'Processing calculations still',
            'Cleaning up',
            'Test Passed'
        ]
    }    
    Memory: {
        name: 'Memory',
        description: 'Kingston 16GB DDR3',
        iconSource: '/resources/images/ram.svg',
        progressValue: 50,
        pageSource: '/resources/qml/Processor.qml',
        details: {
            "device_locator_string": "ChannelB-DIMM1",
            "device_set": 0,
            "error_handle": 65534,
            "extended_size": 0,
            "form_factor": "Unknown"
        },
        testLog: [
            'Starting Memory Test',
            'Detected 2 x RAM modules',
            'Performing intense calculations',
            'Processing calculations still',
            'Cleaning up',
            'Test Failed'
        ]
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM