Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. How can I achieve that a class with a Decorator in a separated python file is recognized by QML (registered and installed)?

How can I achieve that a class with a Decorator in a separated python file is recognized by QML (registered and installed)?

Scheduled Pinned Locked Moved Solved Qt for Python
qt for pythonpyside
4 Posts 3 Posters 539 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Offline
    T Offline
    tobias.hochguertel
    wrote on 16 Jan 2024, 07:30 last edited by
    #1

    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 the main.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
    
    1 Reply Last reply
    0
    • C Offline
      C Offline
      CristianMaureira
      wrote on 16 Jan 2024, 10:55 last edited by
      #3

      You need to import the CpuLoadModel class in main.py in order to be recognized by QML, otherwise you cannot have a reference

      T 1 Reply Last reply 16 Jan 2024, 12:51
      1
      • F Online
        F Online
        friedemannkleint
        wrote on 16 Jan 2024, 10:49 last edited by
        #2

        :qml, :src indicate resources (.qrc) files, that is probably not intended (although it might be advisable to use .qrc files)?

        1 Reply Last reply
        0
        • C Offline
          C Offline
          CristianMaureira
          wrote on 16 Jan 2024, 10:55 last edited by
          #3

          You need to import the CpuLoadModel class in main.py in order to be recognized by QML, otherwise you cannot have a reference

          T 1 Reply Last reply 16 Jan 2024, 12:51
          1
          • T tobias.hochguertel has marked this topic as solved on 16 Jan 2024, 12:49
          • C CristianMaureira
            16 Jan 2024, 10:55

            You need to import the CpuLoadModel class in main.py in order to be recognized by QML, otherwise you cannot have a reference

            T Offline
            T Offline
            tobias.hochguertel
            wrote on 16 Jan 2024, 12:51 last edited by
            #4

            @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
            
            1 Reply Last reply
            0

            2/4

            16 Jan 2024, 10:49

            • Login

            • Login or register to search.
            2 out of 4
            • First post
              2/4
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved