我从Qml 2.0控件TableView
连接到QAbstractItemModel
。我想创建一个可以更改属性的上下文菜单,或者只是从模型中调用具体对象的方法。
示例:
QAbstractItemModel
有一个std::vector<Person>
。 Person
有一个方法alter()
进行一些更改(任何更改,哪些更改都无关紧要,重点是我们可以调用该方法)。
如果右键单击该行,则会显示一个菜单Alter
。
我能找到的就是如何制作菜单。
rowDelegate: Item {
Menu {
id: myContextMenu
MenuItem {text: "Alter"; onTriggered: {} }
}
MouseArea {
id: longPressArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button == Qt.RightButton)
myContextMenu.popup()
}
}
}
但我仍然不知道如何将菜单与行的确切对象连接起来。
答案 0 :(得分:5)
在委托中,您可以使用role.property
约定来引用该项目。
默认角色为display
。当然,Person
必须派生自QObject
,并且必须在QML引擎中注册。
以下代码演示了如何:
为存储ObjectListModel
创建一个明智的QObjects
行为,可以从QML中使用。
创建一个保存数据的QObject
派生类。
从代理上显示的弹出菜单中访问数据对象的属性和可调用方法。
可以将模型设置为自动通知有关包含的QObject的属性的更改。如果由批量更改(例如在循环中完成)产生的此类通知将合并,并作为单个dataChanged
事件发送。
不幸的是,QObject
的用户属性没有特殊含义 - 您仍然需要使用.property
选择器来访问它。
可以直接观察模型的正确行为,因为有两个链接到同一模型的列表 - 它们更好地展示了相同的东西。
ObjectListModel
还可以实现角色和属性之间的映射。目前,显示和编辑角色都选择整个对象,而不是它的任何特定属性。
如果QObject
的存储空间过高,则替代模型实现可以在运行中为POD类型创建QObject
适配器。
<强>的main.cpp 强>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QAbstractListModel>
#include <QQmlContext>
#include <QtQml>
#include <QSet>
#include <QBasicTimer>
#include <functional>
class Person : public QObject {
Q_OBJECT
Q_PROPERTY(QString name NOTIFY nameChanged MEMBER m_name)
QString m_name;
public:
Q_INVOKABLE Person(QObject * parent = 0) : QObject(parent) { setRandomName(); }
Q_INVOKABLE Person(QString name, QObject * parent = 0) :
QObject(parent), m_name(name) {}
Q_SIGNAL void nameChanged(const QString &);
Q_INVOKABLE void setRandomName() {
static const QString names = "Badger,Shopkeeper,Pepperpots,Gumbys,Colonel";
static const QStringList nameList = names.split(',');
QString newName = nameList.at(qrand() % nameList.length());
if (newName != m_name) {
m_name = newName;
emit nameChanged(m_name);
}
}
};
class ObjectListModel : public QAbstractListModel {
Q_OBJECT
Q_DISABLE_COPY(ObjectListModel)
//! Whether changes to underlying objects are exposed via `dataChanged` signals
Q_PROPERTY(bool elementChangeTracking
READ elementChangeTracking WRITE setElementChangeTracking
NOTIFY elementChangeTrackingChanged)
QObjectList m_data;
std::function<QObject*()> m_factory;
bool m_tracking;
QBasicTimer m_notifyTimer;
QMap<int, char> m_notifyIndexes;
//! Updates the property tracking connections on given object.
void updateTracking(QObject* obj) {
const int nIndex = metaObject()->indexOfSlot("propertyNotification()");
QMetaMethod const nSlot = metaObject()->method(nIndex);
const int props = obj->metaObject()->propertyCount();
if (m_tracking) for (int i = 0; i < props; ++i) {
const QMetaProperty prop = obj->metaObject()->property(i);
if (prop.hasNotifySignal()) connect(obj, prop.notifySignal(), this, nSlot);
} else {
disconnect(obj, 0, this, 0);
}
}
//! Receives property notification changes
Q_SLOT void propertyNotification() {
int i = m_data.indexOf(sender());
if (i >= 0) m_notifyIndexes.insert(i, 0);
// All of the notifications will be sent as a single signal from the event loop.
if (!m_notifyTimer.isActive()) m_notifyTimer.start(0, this);
}
protected:
//! Emits the notifications of changes done on the underlying QObject properties
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_notifyTimer.timerId()) return;
emit dataChanged(index(m_notifyIndexes.begin().key()),
index((m_notifyIndexes.end()-1).key()),
QVector<int>(1, Qt::DisplayRole));
m_notifyTimer.stop();
m_notifyIndexes.clear();
}
public:
//! A model that creates instances via a given metaobject
ObjectListModel(const QMetaObject * mo, QObject * parent = 0) :
QAbstractListModel(parent),
m_factory([mo, this](){
return mo->newInstance(Q_ARG(QObject*, this));
}),
m_tracking(false)
{}
//! A model that creates instances using a factory function
ObjectListModel(const std::function<QObject*()> & factory,
QObject * parent = 0) :
QAbstractListModel(parent), m_factory(factory), m_tracking(false)
{}
~ObjectListModel() {
qDeleteAll(m_data);
}
bool elementChangeTracking() const { return m_tracking; }
void setElementChangeTracking(bool tracking) {
if (m_tracking == tracking) return;
for (QObject* obj : m_data) updateTracking(obj);
emit elementChangeTrackingChanged(m_tracking = tracking);
}
Q_SIGNAL void elementChangeTrackingChanged(bool);
int rowCount(const QModelIndex &) const Q_DECL_OVERRIDE {
return m_data.count();
}
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE {
if (role == Qt::DisplayRole || role == Qt::EditRole) {
return QVariant::fromValue(m_data.at(index.row()));
}
return QVariant();
}
bool setData(const QModelIndex &index, const QVariant &value, int role)
Q_DECL_OVERRIDE {
Q_UNUSED(role);
QObject* object = value.value<QObject*>();
if (!object) return false;
if (object == m_data.at(index.row())) return true;
delete m_data.at(index.row());
m_data[index.row()] = object;
emit dataChanged(index, index, QVector<int>(1, role));
return true;
}
Q_INVOKABLE bool insertRows(int row, int count,
const QModelIndex &parent = QModelIndex())
Q_DECL_OVERRIDE {
Q_UNUSED(parent);
beginInsertRows(QModelIndex(), row, row + count - 1);
for (int i = row; i < row + count; ++ i) {
QObject * object = m_factory();
Q_ASSERT(object);
m_data.insert(i, object);
updateTracking(object);
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
}
endInsertRows();
return true;
}
Q_INVOKABLE bool removeRows(int row, int count,
const QModelIndex &parent = QModelIndex())
Q_DECL_OVERRIDE {
Q_UNUSED(parent);
beginRemoveRows(QModelIndex(), row, row + count - 1);
while (count--) delete m_data.takeAt(row);
endRemoveRows();
return true;
}
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<Person>();
ObjectListModel model1(&Person::staticMetaObject);
model1.setElementChangeTracking(true);
model1.insertRows(0, 1);
engine.rootContext()->setContextProperty("model1", &model1);
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"
<强> main.qrc 强>
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
<强> main.qml 强>
import QtQuick 2.0
import QtQml.Models 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
width: 300; height: 300
Row {
width: parent.width
anchors.top: parent.top
anchors.bottom: row2.top
Component {
id: commonDelegate
Rectangle {
width: view.width
implicitHeight: editor.implicitHeight + 10
border.color: "red"
border.width: 2
radius: 5
TextInput {
id: editor
anchors.margins: 1.5 * parent.border.width
anchors.fill: parent
text: edit.name // "edit" role of the model, to break the binding loop
onTextChanged: {
display.name = text; // set the name property of the data object
}
}
Menu {
id: myContextMenu
MenuItem { text: "Randomize"; onTriggered: display.setRandomName() }
MenuItem { text: "Remove"; onTriggered: model1.removeRows(index, 1) }
}
MouseArea {
id: longPressArea
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: myContextMenu.popup()
}
}
}
spacing: 2
ListView {
id: view
width: (parent.width - parent.spacing)/2
height: parent.height
model: DelegateModel {
id: delegateModel1
model: model1
delegate: commonDelegate
}
spacing: 2
}
ListView {
width: (parent.width - parent.spacing)/2
height: parent.height
model: DelegateModel {
model: model1
delegate: commonDelegate
}
spacing: 2
}
}
Row {
id: row2
anchors.bottom: parent.bottom
Button {
text: "Add Page";
onClicked: model1.insertRows(delegateModel1.count, 1)
}
}
}
答案 1 :(得分:0)
我创建了一个基于Kuba Ober答案的新项目。我还创建了一个名为QQuickList的商品类,它继承了QList并集成了一个QObjectListModel。享受。