问题
The title might be a bit confusing, but my intent is pretty simple:
I have a [Repeater/Instantiator]
which creates multiple instances of a arbitrary delegate. I want to react to all changes of the properties (only first-level, so no properties of properties) of the instances of the delegate, calling a function
function update(index, propertyName)
This seems to be easy, but I fail. This is my code
TestObj.qml
Repeater {
onItemAdded: {
var keys = Object.keys(item)
console.log(keys)
for (var k = 0; k < keys.length; k++) {
if (item[keys[k] + 'Changed'] !== undefined && keys[k] !== 'objectName') {
var key = keys[k]
var str = "function() { console.log('Property, " + key + " of " + index + " changed') }"
console.log(str)
item[key + 'Changed'].connect(function() { console.log('Property', key, 'of', index, 'changed') })
}
}
}
}
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQml 2.2
import QtQuick.Controls 1.4 as Ctrl
import '.'
import 'test.js' as Test
ApplicationWindow {
id: root
visible: true
minimumWidth: 500
minimumHeight: 500
property var blub: []
Column {
spacing: 5
TestObj {
model: 5
delegate: Row {
spacing: 2
property int p1: 0
property int p2: 2
property int p3: 4
Button {
text: parent.p1
onClicked: parent.p1++
}
Button {
text: parent.p2
onClicked: parent.p2 *= 2
}
Button {
text: parent.p3
onClicked: parent.p3 *= parent.p3
}
}
}
}
}
It succesfully connects something, but I can't get the key
properly locked. Whichever property I change, I always get the info, I had changed property p3
in my example.
How can I lock the key, so that I get propertyName
when I change the the property with the corresponding name?
回答1:
What you want to do is to introspect objects' property changing at runtime. Therefore thanks to Qt's powerful meta-system, it is more intrinsic to do this using the metaobjects in the QObject.
The main idea is to use a custom C++ extension for QML which can analyze and gather all the properties in a QML object, and connect the notifySignals of these properties to our custom slots if any. Below are some code snippets, and the complete demo can be found in my Github repo: https://github.com/cjmdaixi/PropertySpy
define PropertySpy
class PropertySpy : public QObject { Q_OBJECT public: explicit PropertySpy(QObject *parent = 0); Q_INVOKABLE void spy(QObject * object); public slots: void onPropertyChanged(); };
the implementation of spy:
void PropertySpy::spy(QObject *object) { auto slotIndex = metaObject()->indexOfSlot("onPropertyChanged()"); if(slotIndex == -1){ qDebug()<<"The index of onPropertyChanged is invalid!"; return; } auto slotMethod = metaObject()->method(slotIndex); if(!slotMethod.isValid()){ qDebug()<<"cannot find onPropertyChanged!"; return; } for(auto i = 0; i != object->metaObject()->propertyCount(); ++i){ auto prop = object->metaObject()->property(i); auto sig = prop.notifySignal(); if(!sig.isValid()) continue; connect(object, sig, this, slotMethod); } }
the implementation of onPropertyChanged. Here in the demo, we simply print the property and it's value. Actually you can do whatever you want:
void PropertySpy::onPropertyChanged() { auto senderObj = sender(); auto signalIndex = senderSignalIndex(); for(auto i = 0; i != senderObj->metaObject()->propertyCount(); ++i){ auto prop = senderObj->metaObject()->property(i); if(prop.notifySignalIndex() == signalIndex){ qDebug()<<prop.name()<<prop.read(senderObj); } } }
register PropertySpy to the qml engine in main.cpp
qmlRegisterType<PropertySpy> ("PropertySpy", 1, 0, "PropertySpy");
use PropertySpy in qml, usually in Component.onCompleted of some qml objects that you are interested. For example, the following codes show how to introspect the changes of MouseArea's properties:
PropertySpy{ id: propSpy } Rectangle{ id: rect anchors.centerIn: parent color: "red" width: 100 height: 50 radius: 8 MouseArea{ anchors.fill: parent id: mouseArea Component.onCompleted: propSpy.spy(mouseArea) } }
Then you can get notified when any property is changed like below:
回答2:
One possible solution is, to create an object that has the function, and then just assign this function
Repeater {
onItemAdded: {
var keys = Object.keys(item)
for (var k = 0; k < keys.length; k++) {
if (item[keys[k] + 'Changed'] !== undefined && keys[k] !== 'objectName') {
var key = keys[k]
var f = __funproto.createObject(this, { i: index, n: key })
item[key + 'Changed'].connect(f.fun)
}
}
}
}
Component {
id: __funproto
QtObject {
property int i
property string n
function fun() { /*do something with it!*/}
}
}
But maybe there is a even better solution waiting to be disclosed?
来源:https://stackoverflow.com/questions/43121850/connect-dynamically-to-signals-of-dynamically-created-objects