2

I do not create the elements dynamically from python, I just intend to access existing elements already declared in the qml file.

I use findChild to get a QObject reference and connect to signals. This works fine, but when I try to be more specific and get a widget (not a QObject) like a QComboBox, I always get None. Am I missing something or findChild is not meant to be used with widgets?

This is my simple qml code:

Window {
    visible:true
    width:600
    height:400

    Button {
        id: clickMe
        objectName: "clickMe"
        x: 244
        y: 263
        text: qsTr("click me!")
    }
   ComboBox {
       id: comboBox
       objectName: "comboBox"
       x: 199
       y: 157
       width: 200
   }
}

And this is my python code:

# qt imports
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtWidgets import QApplication, QWidget, QInputDialog, QLineEdit, QMessageBox
from PyQt5.QtWidgets import QComboBox, QPushButton
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QObject

app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.load('main.qml')
win = engine.rootObjects()[0]
win.show()


# this does work because it is QObject:
clickMe = win.findChild(QObject, "clickMe")
clickMe.clicked.connect(Foo)

# this does not work, I get None so can't add items to the combobox:
comboBox = win.findChild(QComboBox, "comboBox")
comboBox.addItem("a")


sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
dumbcode
  • 33
  • 1
  • 2

1 Answers1

1

First of all, the QML Item Combobox is not a QtWidgets QComboBox so you should not filter using that class, that's why your attempt fails. It is also bad practice to access QML elements from python (or C++) since the life cycle is not managed (for example objects of the same "id" can be deleted and recreated without notifying), instead you must create QObject that allow exchanging information, for example for the QComboBox you can create a model:

main.py

import os
import sys

# qt imports
from PyQt5.QtCore import pyqtProperty, pyqtSlot, QObject, QUrl
from PyQt5.QtGui import QGuiApplication, QStandardItem, QStandardItemModel
from PyQt5.QtQml import QQmlApplicationEngine

CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


class Manager(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._model = QStandardItemModel()

    @pyqtProperty(QObject, constant=True)
    def model(self):
        return self._model

    @pyqtSlot()
    def foo(self):
        print("clicked")


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

    manager = Manager()

    engine = QQmlApplicationEngine()
    engine.rootContext().setContextProperty("manager", manager)

    filename = os.path.join(CURRENT_DIR, "main.qml")

    engine.load(QUrl.fromLocalFile(filename))

    item = QStandardItem("a")
    manager.model.appendRow(item)

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

main.qml

import QtQuick.Window 2.15
import QtQuick.Controls 2.15


Window {
    visible:true
    width:600
    height:400

    Button {
        id: clickMe
        x: 244
        y: 263
        text: qsTr("click me!")
        onClicked: manager.foo()
    }
   ComboBox {
       id: comboBox
       x: 199
       y: 157
       width: 200
       model: manager.model
       textRole: "display"
   }
}

I recommend you also read my other answers:

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • I checked a lot of posts on SO, many of them answered by you, but I guess I was actively trying to avoid too much extra code and wanted to keep it simple, but now I understand my method wasn't correct, will use this instead, thank you for the quick response – dumbcode Oct 23 '20 at 19:24