标题可能有点令人困惑,但我的意图非常简单:
我有一个[Repeater/Instantiator]
,可以创建任意委托的多个实例。我想对委托实例的所有属性更改(只有第一级,因此没有属性属性),调用函数
function update(index, propertyName)
这似乎很容易,但我失败了。这是我的代码
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
}
}
}
}
}
它成功地连接了某些东西,但我无法正确锁定key
。无论我改变哪个属性,我总是得到信息,我在我的例子中更改了属性p3
。
如何锁定密钥,以便在使用相应名称更改属性时获得propertyName
?
答案 0 :(得分:1)
你想要做的是内省对象&#39;属性在运行时更改。因此,多亏了Qt强大的元系统,使用QObject中的元对象更为内在。
主要思想是为QML使用自定义C ++扩展,它可以分析和收集QML对象中的所有属性,并将这些属性的notifySignals连接到我们的自定义插槽(如果有的话)。下面是一些代码片段,完整的演示可以在我的Github repo中找到:https://github.com/cjmdaixi/PropertySpy
定义PropertySpy
class PropertySpy : public QObject
{
Q_OBJECT
public:
explicit PropertySpy(QObject *parent = 0);
Q_INVOKABLE void spy(QObject * object);
public slots:
void onPropertyChanged();
};
执行间谍:
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);
}
}
onPropertyChanged的实现。在演示中,我们只需打印属性及其值。实际上你可以做任何你想做的事:
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);
}
}
}
将PropertySpy注册到main.cpp中的qml引擎
qmlRegisterType<PropertySpy> ("PropertySpy", 1, 0, "PropertySpy");
在qml中使用PropertySpy,通常在您感兴趣的一些qml对象的Component.onCompleted中。例如,以下代码显示了如何反省MouseArea属性的更改:
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)
}
}
答案 1 :(得分:0)
一种可能的解决方案是,创建一个具有该功能的对象,然后只分配此功能
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!*/}
}
}
但也许有更好的解决方案等待披露?
答案 2 :(得分:0)
“键”始终为p3的原因是,所有创建的函数都引用相同的“键” 变量(它们是“键”的闭包),因此,当调用其中的任何一个时,他们在“ key”(OP原始代码中的p3)中找到了最近存储的值。
代码看起来就像它每次在循环中创建一个单独的本地 key var一样,但是在Javascript中不是这样,因为在JS中,所有var声明都是“悬挂”在其包含范围的顶部(这就是为什么您可以在声明var之前引用它的原因-声明是“为您”移动的);不幸的是,在Javascript中,除了 function 范围外,没有其他本地范围。在这种情况下,“ var key”声明被提升到onItemHandled
块的顶部(qml编译为anon函数)。
可能的解决方案:在另一个函数中创建处理程序函数,以便处理程序引用的“键”是包装器函数的参数,包装器函数是 单独创建的每次通话:
item[key + 'Changed'].connect(
(function(k,i) {
return function() { console.log('Property',k,'of',i,'changed') }
})(key,index)
);