Skip to content

Instantly share code, notes, and snippets.

@mitchcurtis
Last active October 11, 2020 17:44
Show Gist options
  • Save mitchcurtis/42e50d378d24e35a8396607e94a08f97 to your computer and use it in GitHub Desktop.
Save mitchcurtis/42e50d378d24e35a8396607e94a08f97 to your computer and use it in GitHub Desktop.
ProxyModelNoneEntry with QAbstractProxyModel
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QAbstractProxyModel>
#include <QDebug>
class MyModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit MyModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Q_INVOKABLE void addRow();
private:
QVector<QString> mData;
};
MyModel::MyModel(QObject *parent) :
QAbstractListModel(parent)
{
for (int i = 0; i < 10; ++i)
mData.append(QString::fromLatin1("Item %1").arg(i + 1));
}
int MyModel::rowCount(const QModelIndex &) const
{
return mData.size();
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() > rowCount()) {
return QVariant();
}
qDebug() << objectName() << "asking for entry at row index" << index.row();
switch (role) {
case Qt::DisplayRole:
return mData.at(index.row());
}
return QVariant();
}
void MyModel::addRow()
{
qDebug() << "adding Test row";
beginInsertRows(QModelIndex(), mData.size(), mData.size());
mData.append("Test");
endInsertRows();
}
// Based on https://stackoverflow.com/a/29275072
class ProxyModelNoneEntry : public QAbstractProxyModel
{
Q_OBJECT
public:
ProxyModelNoneEntry(QString entryText = tr("(None)"), QObject *parent = nullptr);
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
/* lessThan() is not necessary for this model to work, but can be
implemented in a derived class if a custom sorting method is required. */
// bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
private:
QString mEntryText;
};
ProxyModelNoneEntry::ProxyModelNoneEntry(QString entryText, QObject *parent) :
QAbstractProxyModel(parent)
{
mEntryText = entryText;
}
int ProxyModelNoneEntry::columnCount(const QModelIndex &/*parent*/) const
{
return 1;
}
int ProxyModelNoneEntry::rowCount(const QModelIndex &parent) const
{
qDebug() << "ProxyModelNoneEntry rowCount" << sourceModel()->rowCount(parent) + 1;
return sourceModel() ? sourceModel()->rowCount(parent) + 1 : 0;
}
QModelIndex ProxyModelNoneEntry::mapFromSource(const QModelIndex &sourceIndex) const
{
if (!sourceIndex.isValid())
return QModelIndex();
else if (sourceIndex.parent().isValid())
return QModelIndex();
return createIndex(sourceIndex.row() + 1, sourceIndex.column());
}
QModelIndex ProxyModelNoneEntry::mapToSource(const QModelIndex &proxyIndex) const
{
if (!proxyIndex.isValid())
return QModelIndex();
else if (proxyIndex.row() == 0)
return QModelIndex();
return sourceModel()->index(proxyIndex.row() - 1, proxyIndex.column());
}
QVariant ProxyModelNoneEntry::data(const QModelIndex &index, int role) const
{
if (!sourceModel() || !index.isValid())
return QVariant();
qDebug() << "ProxyModelNoneEntry data" << index.row() << role;
if (index.row() == 0) {
if (role == Qt::DisplayRole)
return mEntryText;
else
return QVariant();
}
const QModelIndex sourceIndex = mapToSource(index);
if (index.isValid() && !sourceIndex.isValid())
return QVariant();
return sourceModel()->data(sourceIndex, role);
}
Qt::ItemFlags ProxyModelNoneEntry::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
if (index.row() == 0)
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
QModelIndex sourceIndex = mapToSource(index);
return sourceModel()->flags(sourceIndex);
}
QModelIndex ProxyModelNoneEntry::index(int row, int column, const QModelIndex &/*parent*/) const
{
if (row > rowCount())
return QModelIndex();
return createIndex(row, column);
}
QModelIndex ProxyModelNoneEntry::parent(const QModelIndex &/*child*/) const
{
return QModelIndex();
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<ProxyModelNoneEntry>("App", 1, 0, "ProxyModelNoneEntry");
qmlRegisterType<MyModel>("App", 1, 0, "MyModel");
qmlRegisterAnonymousType<QAbstractItemModel>("App", 1);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
#include "main.moc"
import QtQuick 2.15
import QtQuick.Controls 2.15
import App 1.0
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Timer {
running: true
interval: 1000
onTriggered: {
comboBox.model.sourceModel.addRow()
}
}
ComboBox {
id: comboBox
textRole: "display"
model: ProxyModelNoneEntry {
sourceModel: MyModel {}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment