Существует несколько методов для изменения свойства элемента QML из python / C ++, и у каждого есть свои преимущества и недостатки.
1.Получение ссылок из QML
- Получение объекта QML через findChildren через другой объект.
- Измените или получите доступ к свойству с помощью
setProperty()
или property()
соответственно или с помощью QQmlProperty.
main.qml (qml для следующих 2 .py)
import QtQuick 2.11
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
ApplicationWindow {
visible: true
width: Screen.width/2
height: Screen.height/2
Rectangle {
id: rectangle
x: 187
y: 92
width: 200
height: 200
color: "blue"
objectName: "foo_object"
}
}
1.1 setProperty (), property ().
import os
import sys
from PyQt5 import QtCore, QtGui, QtQml
from functools import partial
def testing(r):
import random
w = r.property("width")
h = r.property("height")
print("width: {}, height: {}".format(w, h))
r.setProperty("width", random.randint(100, 400))
r.setProperty("height", random.randint(100, 400))
def run():
myApp = QtGui.QGuiApplication(sys.argv)
myEngine = QtQml.QQmlApplicationEngine()
directory = os.path.dirname(os.path.abspath(__file__))
myEngine.load(QtCore.QUrl.fromLocalFile(os.path.join(directory, 'main.qml')))
if not myEngine.rootObjects():
return -1
r = myEngine.rootObjects()[0].findChild(QtCore.QObject, "foo_object")
timer = QtCore.QTimer(interval=500)
timer.timeout.connect(partial(testing, r))
timer.start()
return myApp.exec_()
if __name__ == "__main__":
sys.exit(run())
1.2 QQmlProperty.
import os
import sys
from PyQt5 import QtCore, QtGui, QtQml
from functools import partial
def testing(r):
import random
w_prop = QtQml.QQmlProperty(r, "width")
h_prop = QtQml.QQmlProperty(r, "height")
print("width: {}, height: {}".format(w_prop.read(), w_prop.read()))
w_prop.write(random.randint(100, 400))
h_prop.write(random.randint(100, 400))
def run():
myApp = QtGui.QGuiApplication(sys.argv)
myEngine = QtQml.QQmlApplicationEngine()
directory = os.path.dirname(os.path.abspath(__file__))
myEngine.load(QtCore.QUrl.fromLocalFile(os.path.join(directory, 'main.qml')))
if not myEngine.rootObjects():
return -1
r = myEngine.rootObjects()[0].findChild(QtCore.QObject, "foo_object")
timer = QtCore.QTimer(interval=500)
timer.timeout.connect(partial(testing, r))
timer.start()
return myApp.exec_()
if __name__ == "__main__":
sys.exit(run())
Недостатком этого метода является то, что если связь объекта с корневым объектом является сложной (иногда объекты, находящиеся в других QML, труднодоступ с помощью findChild) часть доступа к объекту становится сложной, а иногда и невозможной, поэтому этот метод завершится ошибкой.Другая проблема заключается в том, что при использовании objectName в качестве основных данных поиска существует высокая зависимость уровня Python от уровня QML, поскольку, если objectName изменяется в QML, необходимо изменить логику в python.Другим недостатком является то, что, не управляя жизненным циклом объекта QML, его можно устранить без знания Python, чтобы получить доступ к неверной ссылке, что приведет к неожиданному завершению работы приложения.
2.Отправка ссылок на QML
- Создание объекта QObject с такими же типами свойств.
- Экспорт в QML с использованием setContextProperty.
- Создание привязки между свойствами объектаQObject и свойства элемента.
main.qml
import QtQuick 2.11
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
ApplicationWindow {
visible: true
width: Screen.width/2
height: Screen.height/2
Rectangle {
id: rectangle
x: 187
y: 92
width: r_manager.width
height: r_manager.height
color: "blue"
}
}
main.py
import os
import sys
from PyQt5 import QtCore, QtGui, QtQml
from functools import partial
class RectangleManager(QtCore.QObject):
widthChanged = QtCore.pyqtSignal(float)
heightChanged = QtCore.pyqtSignal(float)
def __init__(self, parent=None):
super(RectangleManager, self).__init__(parent)
self._width = 100
self._height = 100
@QtCore.pyqtProperty(float, notify=widthChanged)
def width(self):
return self._width
@width.setter
def width(self, w):
if self._width != w:
self._width = w
self.widthChanged.emit(w)
@QtCore.pyqtProperty(float, notify=heightChanged)
def height(self):
return self._height
@height.setter
def height(self, h):
if self._height != h:
self._height = h
self.heightChanged.emit(h)
def testing(r):
import random
print("width: {}, height: {}".format(r.width, r.height))
r.width = random.randint(100, 400)
r.height = random.randint(100, 400)
def run():
myApp = QtGui.QGuiApplication(sys.argv)
myEngine = QtQml.QQmlApplicationEngine()
manager = RectangleManager()
myEngine.rootContext().setContextProperty("r_manager", manager)
directory = os.path.dirname(os.path.abspath(__file__))
myEngine.load(QtCore.QUrl.fromLocalFile(os.path.join(directory, 'main.qml')))
if not myEngine.rootObjects():
return -1
timer = QtCore.QTimer(interval=500)
timer.timeout.connect(partial(testing, manager))
timer.start()
return myApp.exec_()
if __name__ == "__main__":
sys.exit(run())
Недостаток в том, что вам нужно написать еще немного кода.Преимущество состоит в том, что объект доступен всем QML, поскольку он использует setContextProperty, еще одно преимущество состоит в том, что, если объект QML удаляется, он не создает проблем, поскольку устраняется только привязка.И, наконец, без использования objectName зависимость не существует.
Поэтому я предпочитаю использовать второй метод, для получения дополнительной информации читайте Взаимодействие с QML из C ++ .