I have a GUI written in qml and c++. There are 2 comboboxes (qt control 5.1). Second combobox has to update at runtime whenever the value of the first one is changed.
我有一个用qml和c++编写的GUI。有两个组合盒(qt控制5.1)。每当第一个combobox的值发生变化时,第二个combobox必须在运行时进行更新。
maincontext->setContextProperty("typemodel", QVariant::fromValue(m_typemodel));
maincontext->setContextProperty("unitmodel", QVariant::fromValue(m_unitmodel));
These are 2 models that I give to qml from c++.
这是我从c++中给qml的两个模型。
ComboBox {
id: typebox
anchors.left: text1.right
anchors.leftMargin: 5
signal changed(string newtext)
width: 70
height: 23
anchors.top: parent.top
anchors.topMargin: 37
model: typemodel
onCurrentTextChanged: {
mainwin.unitGenerator(typebox.currentText);
}
This is the first combobox. As you see, the c++ model of second combobox is updated every time the value of the first is changed (mainwin.unitGenerator(typebox.currentText)). But it does not seem to update the combobox's model.
这是第一个组合框。正如您所看到的,每次修改第一个combobox的值(mainwin.unitGenerator(typebox.currentText))时,都会更新第二个combobox的c++模型。但它似乎并没有更新combobox的模型。
How can I update qml's model on runtime?
如何在运行时更新qml的模型?
2 个解决方案
#1
30
To even begin to address your issue, we'd need to see what the unitGenerator
method does. If you're using a custom model, it's almost certain that you're not correctly implementing the notifications. My bet at the moment would be that you're not signaling the model reset.
为了开始解决您的问题,我们需要了解unitGenerator方法的作用。如果您正在使用自定义模型,几乎可以肯定您没有正确地实现通知。我敢打赌,你并不是在发出信号,表明模型正在重置。
Below is a complete code example that shows how you can tie a QStringListModel
to an editable ListView
and to ComboBox
es. The second ComboBox
's model is regenerated based on the selection from the first one. This presumably approximates your desired functionality.
下面是一个完整的代码示例,它展示了如何将QStringListModel绑定到可编辑的ListView和ComboBoxes。第二个ComboBox的模型是根据第一个的选择重新生成的。这大概是您想要的功能。
Note the specific handling of roles done by the QStringListModel
. The model treats the display and edit roles almost the same: they both are mapped to the string value in the list. Yet when you update a particular role's data, the dataChanged
signal carries only the role that you've changed. This can be used to break a binding loop that might be otherwise present in the model editor item (TextInput). When you use a custom model, you may need to implement similar functionality.
注意QStringListModel对角色的特定处理。模型对显示和编辑角色的处理几乎相同:它们都映射到列表中的字符串值。然而,当您更新特定角色的数据时,dataChanged信号只携带您已更改的角色。这可以用来中断绑定循环,否则可能会出现在模型编辑器项(TextInput)中。当您使用自定义模型时,您可能需要实现类似的功能。
The display
role is used to bind the combo boxes to the model. The edit
role is used to pre-populate the editor objects. The editor's onTextChanged
signal handler is updating the display
role, and this doesn't cause a binding loop to itself. If the handler was updating the edit
role, it would cause a binding loop via the text
property.
显示角色用于将组合框绑定到模型。编辑角色用于预填充编辑器对象。编辑器的onTextChanged信号处理程序正在更新显示角色,这不会导致对自身的绑定循环。如果处理程序正在更新编辑角色,它将通过文本属性导致绑定循环。
On Models in QML
There are various kinds of "models" in QML. Internally, QML will wrap almost "anything" in a model. Anything that is internally not a QObject yet can still be a model (say a QVariant
), won't be notifying anyone about anything.
QML中有各种各样的“模型”。在内部,QML将在模型中包装几乎“任何东西”。任何内部不是QObject的东西仍然可以是一个模型(比如一个q变体),它不会通知任何人任何事情。
For example, a "model" based on QVariant
that wraps an int
will not issue notifications, because QVariant
is not a QObject
that could signal changes.
例如,基于包装int的QVariant的“模型”将不会发出通知,因为QVariant不是可以通知更改的QObject。
Similarly, if your "model" is tied to a property value of a class derived from QObject
, but you fail to emit
the property change notification signal, it also won't work.
类似地,如果您的“模型”与从QObject派生的类的属性值绑定,但是您无法发出属性更改通知信号,那么它也不会工作。
Without knowing what your model types are, it's impossible to tell.
如果不知道模型类型是什么,就不可能知道。
main.qml
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.0
ApplicationWindow {
width: 300; height: 300
ListView {
id: view
width: parent.width
anchors.top: parent.top
anchors.bottom: column.top
model: model1
spacing: 2
delegate: Component {
Rectangle {
width: view.width
implicitHeight: edit.implicitHeight + 10
color: "transparent"
border.color: "red"
border.width: 2
radius: 5
TextInput {
id: edit
anchors.margins: 1.5 * parent.border.width
anchors.fill: parent
text: edit // "edit" role of the model, to break the binding loop
onTextChanged: model.display = text
}
}
}
}
Column {
id: column;
anchors.bottom: parent.bottom
Text { text: "Type"; }
ComboBox {
id: box1
model: model1
textRole: "display"
onCurrentTextChanged: generator.generate(currentText)
}
Text { text: "Unit"; }
ComboBox {
id: box2
model: model2
textRole: "display"
}
}
}
main.cpp
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QStringListModel>
#include <QQmlContext>
class Generator : public QObject
{
Q_OBJECT
QStringListModel * m_model;
public:
Generator(QStringListModel * model) : m_model(model) {}
Q_INVOKABLE void generate(const QVariant & val) {
QStringList list;
for (int i = 1; i <= 3; ++i) {
list << QString("%1:%2").arg(val.toString()).arg(i);
}
m_model->setStringList(list);
}
};
int main(int argc, char *argv[])
{
QStringListModel model1, model2;
Generator generator(&model2);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QStringList list;
list << "one" << "two" << "three" << "four";
model1.setStringList(list);
engine.rootContext()->setContextProperty("model1", &model1);
engine.rootContext()->setContextProperty("model2", &model2);
engine.rootContext()->setContextProperty("generator", &generator);
engine.load(QUrl("qrc:/main.qml"));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
window->show();
return app.exec();
}
#include "main.moc"
#2
3
This is actually more of an answer/comment to the answer of @KubaOber.
这实际上更像是对@KubaOber的回答的回答。
I found that it is actually not necessary to do any special tricks using multiple roles if you bind to the correct event:
我发现,如果您绑定到正确的事件,实际上不需要使用多个角色进行任何特殊的技巧:
onAccepted: model.edit = text
works just fine and does not create any update loop (as it is only called on "human"/input modification).
工作正常,不创建任何更新循环(因为只调用“human”/input修改)。
#1
30
To even begin to address your issue, we'd need to see what the unitGenerator
method does. If you're using a custom model, it's almost certain that you're not correctly implementing the notifications. My bet at the moment would be that you're not signaling the model reset.
为了开始解决您的问题,我们需要了解unitGenerator方法的作用。如果您正在使用自定义模型,几乎可以肯定您没有正确地实现通知。我敢打赌,你并不是在发出信号,表明模型正在重置。
Below is a complete code example that shows how you can tie a QStringListModel
to an editable ListView
and to ComboBox
es. The second ComboBox
's model is regenerated based on the selection from the first one. This presumably approximates your desired functionality.
下面是一个完整的代码示例,它展示了如何将QStringListModel绑定到可编辑的ListView和ComboBoxes。第二个ComboBox的模型是根据第一个的选择重新生成的。这大概是您想要的功能。
Note the specific handling of roles done by the QStringListModel
. The model treats the display and edit roles almost the same: they both are mapped to the string value in the list. Yet when you update a particular role's data, the dataChanged
signal carries only the role that you've changed. This can be used to break a binding loop that might be otherwise present in the model editor item (TextInput). When you use a custom model, you may need to implement similar functionality.
注意QStringListModel对角色的特定处理。模型对显示和编辑角色的处理几乎相同:它们都映射到列表中的字符串值。然而,当您更新特定角色的数据时,dataChanged信号只携带您已更改的角色。这可以用来中断绑定循环,否则可能会出现在模型编辑器项(TextInput)中。当您使用自定义模型时,您可能需要实现类似的功能。
The display
role is used to bind the combo boxes to the model. The edit
role is used to pre-populate the editor objects. The editor's onTextChanged
signal handler is updating the display
role, and this doesn't cause a binding loop to itself. If the handler was updating the edit
role, it would cause a binding loop via the text
property.
显示角色用于将组合框绑定到模型。编辑角色用于预填充编辑器对象。编辑器的onTextChanged信号处理程序正在更新显示角色,这不会导致对自身的绑定循环。如果处理程序正在更新编辑角色,它将通过文本属性导致绑定循环。
On Models in QML
There are various kinds of "models" in QML. Internally, QML will wrap almost "anything" in a model. Anything that is internally not a QObject yet can still be a model (say a QVariant
), won't be notifying anyone about anything.
QML中有各种各样的“模型”。在内部,QML将在模型中包装几乎“任何东西”。任何内部不是QObject的东西仍然可以是一个模型(比如一个q变体),它不会通知任何人任何事情。
For example, a "model" based on QVariant
that wraps an int
will not issue notifications, because QVariant
is not a QObject
that could signal changes.
例如,基于包装int的QVariant的“模型”将不会发出通知,因为QVariant不是可以通知更改的QObject。
Similarly, if your "model" is tied to a property value of a class derived from QObject
, but you fail to emit
the property change notification signal, it also won't work.
类似地,如果您的“模型”与从QObject派生的类的属性值绑定,但是您无法发出属性更改通知信号,那么它也不会工作。
Without knowing what your model types are, it's impossible to tell.
如果不知道模型类型是什么,就不可能知道。
main.qml
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.0
ApplicationWindow {
width: 300; height: 300
ListView {
id: view
width: parent.width
anchors.top: parent.top
anchors.bottom: column.top
model: model1
spacing: 2
delegate: Component {
Rectangle {
width: view.width
implicitHeight: edit.implicitHeight + 10
color: "transparent"
border.color: "red"
border.width: 2
radius: 5
TextInput {
id: edit
anchors.margins: 1.5 * parent.border.width
anchors.fill: parent
text: edit // "edit" role of the model, to break the binding loop
onTextChanged: model.display = text
}
}
}
}
Column {
id: column;
anchors.bottom: parent.bottom
Text { text: "Type"; }
ComboBox {
id: box1
model: model1
textRole: "display"
onCurrentTextChanged: generator.generate(currentText)
}
Text { text: "Unit"; }
ComboBox {
id: box2
model: model2
textRole: "display"
}
}
}
main.cpp
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QStringListModel>
#include <QQmlContext>
class Generator : public QObject
{
Q_OBJECT
QStringListModel * m_model;
public:
Generator(QStringListModel * model) : m_model(model) {}
Q_INVOKABLE void generate(const QVariant & val) {
QStringList list;
for (int i = 1; i <= 3; ++i) {
list << QString("%1:%2").arg(val.toString()).arg(i);
}
m_model->setStringList(list);
}
};
int main(int argc, char *argv[])
{
QStringListModel model1, model2;
Generator generator(&model2);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QStringList list;
list << "one" << "two" << "three" << "four";
model1.setStringList(list);
engine.rootContext()->setContextProperty("model1", &model1);
engine.rootContext()->setContextProperty("model2", &model2);
engine.rootContext()->setContextProperty("generator", &generator);
engine.load(QUrl("qrc:/main.qml"));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
window->show();
return app.exec();
}
#include "main.moc"
#2
3
This is actually more of an answer/comment to the answer of @KubaOber.
这实际上更像是对@KubaOber的回答的回答。
I found that it is actually not necessary to do any special tricks using multiple roles if you bind to the correct event:
我发现,如果您绑定到正确的事件,实际上不需要使用多个角色进行任何特殊的技巧:
onAccepted: model.edit = text
works just fine and does not create any update loop (as it is only called on "human"/input modification).
工作正常,不创建任何更新循环(因为只调用“human”/input修改)。