qml tableview 数据库,Qml 2.0 TableView与QAbstractItemModel和上下文菜单

这篇博客介绍了如何在QML中创建一个与QAbstractItemModel连接的TableView,并实现右键菜单功能,允许用户通过菜单项调用模型中对象的方法。文章详细阐述了如何在Delegate中引用模型项,以及如何利用Q_OBJECT和Q_PROPERTY来实现对象的信号和槽,从而在菜单触发时更新特定行的数据。此外,还展示了如何创建一个QObject列表模型,该模型能够自动通知其包含的QObjects的属性变化,并且在批量更改时合并通知。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

I have TableView from Qml 2.0 controls that is connected to QAbstractItemModel. I want to make a context menu that can change the properties or simply call methods of a concrete object from the model.

Example:

QAbstractItemModel has an std::vector. Person has a method alter() which makes some changes (any changes, it does not matter which ones exactly are changes, the point is that the we are able to call the method).

When there is a right click on the row, the menu appears with an item Alter.

All I was able to find is how to make the menu.

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()

}

}

}

But I still don't know how to connect the menu with an exact object of the row.

解决方案

Within the delegate, you can refer to the item by using the role.property convention.

The default role would be display. Of course the Person has to derive from QObject, and must be registered with the QML Engine.

The code below demonstrates how to:

Create a sensibly behaving ObjectListModel for storage of QObjects, usable from QML.

Create a QObject-derived class that keeps your data.

Access the properties and invokable methods of the data objects from a pop-up menu shown on the delegate.

The model can be set to automatically notify about changes to the properties of the contained QObjects. Such notifications, if resulting from bulk changes (say done in a loop) are coalesced and sent off as a single dataChanged event.

Unfortunately, the user property of a QObject acquires no special meaning - you still need to use the .property selector to access it.

Proper behavior of the model can be observed directly, since there are two lists hooked to the same model - they better showed the same thing.

The ObjectListModel could also implement a mapping between the roles and the properties. Currently, both the display and edit roles select the entire object, not any particular property of it.

If the storage of QObjects is too high of an overhead, an alternative model implementation could create QObject adapters to POD types on-the-fly.

uUy4J.png

main.cpp

#include

#include

#include

#include

#include

#include

#include

#include

#include

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 m_factory;

bool m_tracking;

QBasicTimer m_notifyTimer;

QMap 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(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 & 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();

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(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();

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(topLevel);

window->show();

return app.exec();

}

#include "main.moc"

main.qrc

main.qml

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)

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值