How can I achieve that a class with a Decorator in a separated python file is recognized by QML (registered and installed)?
-
I use the QMLElement Decorator approach to register a class as a QML Component, instead of the qmlRegisterType approach.
When I try to run the application, I receive the following error message
module "PsUtils" is not installed
:QQmlApplicationEngine failed to load component file:///Users/tobiashochgurtel/work-dev/rpi-hmi/tandoor-kitchen/qml/main.qml:6:1: module "PsUtils" is not installed
When I put the class
CpuLoadModel
with the Decorator@QmlElement
into the same file as themain.py
file, it works. I understand that this is because the class is loaded / parsed or so.But when I split the application into multiple files, and have the class in a separated file, it gets not loaded and, as I understand, not registered or installed. I thought on
engine.addImportPath(":/src")
but that didn't work.How can I achieve that a class with a Decorator in a separated python file is recognized by QML (registered and installed)?
I have a simple application which is structured as following:
❯ tree --gitignore -I images . ├── qml │ └── main.qml └── src ├── CpuLoadModel.py └── main.py
File:
qml/main.qml
import QtQuick import QtQuick.Window import QtQuick.Controls import Generators import PsUtils ApplicationWindow { id: window width: 640 height: 480 visible: true title: qsTr("Tandoor Viewer") ListView { anchors.fill: parent model: CpuLoadModel { } delegate: Rectangle { id: delegate required property int display width: parent.width height: 30 color: "white" Rectangle { id: bar width: parent.width * delegate.display / 100.0 height: 30 color: "green" } Text { anchors.verticalCenter: parent.verticalCenter x: Math.min(bar.x + bar.width + 5, parent.width - width) text: delegate.display + "%" } } } }
File:
src/main.py
class NumberGeneratorSignalbased(QObject): nextNumber = Signal(int, arguments=['number']) def __init__(self): QObject.__init__(self) @Slot() def giveNumber(self): self.nextNumber.emit(random.randint(0, 99)) if __name__ == "__main__": recipes = load_recipes() # Set up the application window app = QGuiApplication(sys.argv) QQuickStyle.setStyle("iOS") engine = QQmlApplicationEngine() qDebug("Default path >> " + engine.offlineStoragePath()) engine.addImportPath(":/qml") engine.addImportPath(":/src") qmlRegisterType(NumberGeneratorSignalbased, 'Generators', 1, 0, 'NumberGenerator') qml_file = Path(__file__).resolve().parent / "../qml" / "main.qml" engine.load(qml_file) if not engine.rootObjects(): sys.exit(-1) sys.exit(app.exec())
File:
src/CpuLoadModel.py
from typing import Union, Any import PySide6 import psutil from PySide6.QtCore import QAbstractListModel, QTimer, Qt from PySide6.QtQml import QmlElement QML_IMPORT_NAME = "PsUtils" QML_IMPORT_MAJOR_VERSION = 1 QML_IMPORT_MINOR_VERSION = 0 # Optional @QmlElement class CpuLoadModel(QAbstractListModel): def __init__(self): QAbstractListModel.__init__(self) self.__cpu_count = psutil.cpu_count() self.__cpu_load = [0] * self.__cpu_count self.__update_timer = QTimer(self) self.__update_timer.setInterval(1000) self.__update_timer.timeout.connect(self.__update) self.__update_timer.start() # The first call returns invalid data psutil.cpu_percent(percpu=True) def __update(self): self.__cpu_load = psutil.cpu_percent(percpu=True) self.dataChanged.emit(self.index(0, 0), self.index(self.__cpu_count - 1, 0)) def rowCount(self, parent: Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex] = ...) -> int: return self.__cpu_count def data(self, index: Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex], role: int = ...) -> Any: if (role == Qt.DisplayRole and index.row() >= 0 and index.row() < len(self.__cpu_load) and index.column() == 0): return self.__cpu_load[index.row()] else: return None
-
You need to import the
CpuLoadModel
class inmain.py
in order to be recognized by QML, otherwise you cannot have a reference -
:qml, :src indicate resources (.qrc) files, that is probably not intended (although it might be advisable to use .qrc files)?
-
You need to import the
CpuLoadModel
class inmain.py
in order to be recognized by QML, otherwise you cannot have a reference -
-
@CristianMaureira Thanks
I do it now like you said it, and added a comment for PyCharm IDE to not remote these imports when I run the Optimize Imports Refactoring tool.
# noinspection PyUnresolvedReferences from src.examples import CpuLoadModel, NumberGeneratorSignalbased, NumberGeneratorPropertiesbased, UserModel # noinspection PyUnresolvedReferences from src import RecipesModel