qml listview does not update information after datachanged() signal - c++

I have a model written based on this example. This model is connected to a listview to display dynamic data. I have made sure the model's datachanged() signal is emitted whenever the source data is being changed. The list view does not update on the datachanged() signal. Here is my code:
DataModel.h:
class DataList {
public:
DataList(const QString &name, const QString &value, const QString &category);
QString name() const;
QString value() const;
QString category() const;
private:
QString m_name;
QString m_value;
QString m_category;
};
class DataModel : public QAbstractListModel {
Q_OBJECT
public:
enum AVRoles {
NameRole = Qt::UserRole + 1,
ValueRole,
CategoryRole
};
DataModel(QAbstractItemModel *parent = 0);
virtual ~DataModel() {}
void addData(const DataList &datalist);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role=Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<DataList> m_datalist;
public slots:
void onInitialPolygonAreaChanged(qreal);
};
DataModel.cpp:
DataList::DataList(const QString &name, const QString &value, const QString &category) {
m_name = name;
m_value = value;
m_category = category;
}
QString DataList::name() const {
return m_name;
}
QString DataList::value() const {
return m_value;
}
QString DataList::category() const {
return m_category;
}
DataModel::DataModel(QAbstractItemModel *parent) :
QAbstractListModel(parent) {
}
void DataModel::addData(const DataList &datalist){
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_datalist << datalist;
endInsertRows();
}
int DataModel::rowCount(const QModelIndex &parent) const{
Q_UNUSED(parent);
return m_datalist.count();
}
QVariant DataModel::data(const QModelIndex &index, int role) const {
if (index.row() < 0 || index.row() >= m_datalist.count())
return QVariant();
const DataList &datalist = m_datalist[index.row()];
if(role ==NameRole )
return datalist.name();
else if(role == ValueRole)
return datalist.value();
else if(role == CategoryRole)
return datalist.category();
return QVariant();
}
QHash<int, QByteArray> DataModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[ValueRole] = "value";
roles[CategoryRole] = "category";
return roles;
}
void DataModel::onInitialPolygonAreaChanged(qreal ) {
//0 is the index of iPA
qDebug() << "IPA from model" ;
QVector<int> roleVector;
roleVector.append(ValueRole);
roleVector.append(NameRole);
roleVector.append(CategoryRole);
roleVector.append(Qt::DisplayRole);
emit dataChanged(index(0),index(0),roleVector);
}
Mainwindow.cpp:
model.addData(DataList(QString("Initial Polygon Area"),QString::number(av1.initialPolygonArea()) + " sq.km",QString("Area")));
View->rootContext()->setContextProperty("AVModel",&model);
connect(&av1,SIGNAL(initialPolygonAreaChanged(qreal)),&model,SLOT(onInitialPolygonAreaChanged(qreal)));
view.qml:
ListView {
id: view
anchors.top: parent.top
anchors.bottom: parent.bottom
width: parent.width
model: AVModel
delegate: Text { text: name + " " + value; font.pixelSize: 18 ; color: "white"}
}

Related

Displaying Json data with qml ListView

I am currently working on a ticker client, that polls data from a web api, hands it to a ListModel, which is then used to display the data in a qml ListView.
class TickerClient : public QObject
{
Q_OBJECT
public:
explicit TickerClient(QObject *parent = nullptr);
QList<QVariantMap> items() const;
protected:
void registerErrorHandlers(QNetworkReply *reply);
signals:
void statusChanged(QNetworkAccessManager::NetworkAccessibility acc);
void dataChanged();
void preItemRefresh();
void postItemRefresh();
public slots:
void fetch(int start = 0, int limit = 100);
protected slots:
void onReceive(QNetworkReply *reply);
protected:
QSharedPointer<QNetworkAccessManager> mNetMan;
QList<QVariantMap> mItems;
};
class TickerModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(TickerClient *client READ client WRITE setClient)
public:
explicit TickerModel(QObject *parent = nullptr);
enum {
IdRole = Qt::UserRole + 1,
NameRole,
SymbolRole,
...
};
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
TickerClient *client() const;
void setClient(TickerClient *client);
private:
TickerClient *mClient;
};
The ticker client does not only fetch, but also handle the fetched data and expose it to the surrounding ListModel as a list of QVariantMaps.
void TickerClient::onReceive(QNetworkReply *reply)
{
if (!reply)
return;
if(reply->error()) {
qCritical() << "Error: "
<< reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString();
return;
}
// Read all data as json document.
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll());
if(!jsonDoc.isArray()) {
qCritical() << "Error: Expected array";
return;
}
emit preItemRefresh();
mItems.clear();
QJsonArray currencies = jsonDoc.array();
for(int i = 0; i < currencies.count(); i++) {
QJsonObject currency = currencies[i].toObject();
mItems.append(currency.toVariantMap());
}
emit postItemRefresh();
reply->deleteLater();
}
Both, the TickerClient and the TickerModel are exposed to qml:
qmlRegisterType<TickerModel>("Ticker", 1, 0, "TickerModel");
qmlRegisterUncreatableType<TickerClient>("Ticker", 1, 0, "TickerClient",
QStringLiteral("MarketCapProvider should not be created in QML"));
TickerClient tickerClient;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("tickerClient", &tickerClient);
The exposed client is then handed to the model and refreshed every 5 seconds:
ListView {
id: page
Timer {
interval: 5000
running: true
repeat: true
triggeredOnStart: true
onTriggered: {
var pos = scrollBar.position
tickerClient.fetch()
scrollBar.position = pos
}
}
ScrollBar.vertical: ScrollBar {
id: scrollBar
}
model: TickerModel {
client: tickerClient
}
delegate: RowLayout {
width: parent.width
spacing: 10
Label {
text: "#" + model.rank
padding: 5
}
Label {
text: model.name
padding: 5
}
}
}
However, the fetching of the data doesn't seem to work well as the wrong data is returned. So if I ask for model.name, I might end of with model.rank. The following code is used to fetch the entry for a given pair of index and role.
QVariant TickerModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || !mClient)
return QVariant();
auto it = roleNames().find(role);
if(it != roleNames().end())
return mClient->items().at(index.row())[it.value()];
else
qDebug() << "Didn't match a role";
return QVariant();
}
QHash<int, QByteArray> TickerModel::roleNames() const
{
static const QHash<int, QByteArray> names{
{ IdRole, "id" },
{ NameRole, "name" },
...
};
return names;
}
TickerClient *TickerModel::client() const
{
return mClient;
}
void TickerModel::setClient(TickerClient *client)
{
beginResetModel();
if(mClient) {
mClient->disconnect(this);
}
mClient = client;
if(mClient) {
connect(mClient, &TickerClient::preItemRefresh, this, [=]() {
beginResetModel();
});
connect(mClient, &TickerClient::postItemRefresh, this, [=]() {
endResetModel();
});
}
endResetModel();
}
What, am I doing wrong and how can I extend this solution for more complex Json objects?
You are clearing mItems and then appending new items to it in onReceive method. You are not showing if model is listening to postItemRefresh signal. Anyway, you should call beginResetModel and endResetModel.
E.g. add following slot to your TickerModel and connect it to postItemRefresh signal to get your model synched witht the new data:
void TickerModel::reset()
{
beginResetModel();
endResetModel();
}
Edit:
You should add prints or go through with the debugger with what parameters your model data() is called when things get screwed. Also, make sure beginResetModel and endResetModel are called when items are updated.
Is your rowCount returning mClient->items().count() ?
To handle more complex json objects, instead of:
QJsonArray currencies = jsonDoc.array();
for(int i = 0; i < currencies.count(); i++) {
QJsonObject currency = currencies[i].toObject();
mItems.append(currency.toVariantMap());
}
You don't handle QVariantMap but parse more complex json objects to your own class which provides getters for data:
QJsonArray currencies = jsonDoc.array();
for(int i = 0; i < currencies.count(); i++) {
QJsonObject currency = currencies[i].toObject();
ComplexCurrency c;
c.read(currency);
mItems.append(c);
}
Your class:
class ComplexCurrency
{
public:
int id() const;
QString name() const;
bool enabled() const;
QList<QVariantMap> items();
void read(const QJsonObject &json);
private:
int m_id;
QString m_name;
bool m_enabled;
QList<QVariantMap> m_items;
...
};
void ComplexCurrency::read(const QJsonObject &json)
{
m_id = json["id"].toInt();
m_name = json["name"].toString();
m_enabled = json["enabled"].toBool();
// parse items array here
}
And then in model data():
QVariant TickerModel::data(const QModelIndex &index, int role) const
{
if ((index.row() < 0 || index.row() >= mClient->items().count()) || !mClient)
return QVariant();
const ComplexCurrency &c = mClient->items().at(index.row());
if (role == NameRole)
return c.name();
else if (role == SomethingRole)
return c.something();
return QVariant();
}

QIdentityProxyModel display empty rows

I've implemented QIdentityProxyModel like this:
class ProxyModel : public QIdentityProxyModel
{
Q_OBJECT
public:
ProxyModel(QObject *parent)
{
entriesPerPage = 3;
page = 1;
}
inline int getEntriesPerPage() const
{ return entriesPerPage; }
inline void setEntriesPerPage(int value)
{ entriesPerPage = value; }
inline qint64 getPage() const
{ return page; }
inline void setPage(const qint64 &value)
{ page = value; }
inline int rowCount(const QModelIndex &parent = QModelIndex()) const override{
Q_UNUSED(parent)
if(!sourceModel())
return 0;
return entriesPerPage * page <= sourceModel()->rowCount()
? entriesPerPage
: sourceModel()->rowCount() - entriesPerPage * (page - 1);
}
inline int columnCount(const QModelIndex &parent = QModelIndex()) const override{
Q_UNUSED(parent);
return 6;
}
QModelIndex ProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
if(!sourceModel() && !proxyIndex.isValid())
return QModelIndex();
return sourceIndex.isValid()
? createIndex(sourceIndex.row() % entriesPerPage, sourceIndex.column(), sourceIndex.internalPointer())
: QModelIndex();
}
QModelIndex ProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
if(!sourceModel() && !proxyIndex.isValid())
return QModelIndex();
QModelIndex remapped = createIndex(proxyIndex.row() ,
proxyIndex.column(),
proxyIndex.internalPointer());
return QIdentityProxyModel::mapToSource(remapped);
}
private:
int entriesPerPage;
qint64 page;
};
When I insert a row in the sourceModel() with index more then entriesPerPage, view displays empty rows, so that row number is more then entriesPerPage, although rowCount() return number equal to entriesPerPage.
How can I get rid of empty rows?
First. It is a bad practice to override rowCount/mapFromSource for QIdentityProxyModel. I propose you to use QAbstractProxyModel to have more clear code.
Main. Your problem is in getEntriesPerPage/setPage methods. You need to call beginResetModel/endResetModel after updating such data.
inline void setPage(const qint64 &value)
{
beginResetModel();
page = value;
endResetModel();
}
Offtopic: it is pretty cool, that you have to code with Qt in BSUIR. Who is your teacher?

Qt: undefined reference on constructor of customer class

I'm using a custom class, GpibDevicesModel, inheriting from QAbstractTableModel, and the compiler says that there is an undefined reference on the constructor. But I don't understand why.
Here is the code:
MainWindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
//...
private:
GpibDevicesModel *m_gpibDevicesModel;
};
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
//...
//undefined reference is located here
m_gpibDevicesModel = new GpibDevicesModel;
//...
}
GpibDevicesModel.h
#ifndef GPIBDEVICESMODEL_H
#define GPIBDEVICESMODEL_H
#include <QAbstractTableModel>
class MeasureDevice;
class GpibDevicesModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum Columns{
NameCol = 0,
AddressCol,
IdCol,
TypeCol,
ConnectedCol,
EndCol //Should always be at end
};
//constructor and destructor
explicit GpibDevicesModel(QObject *parent = 0);
~GpibDevicesModel();
protected:
//Reimplemented from QAbstractTableModel
QVariant data(const QModelIndex &index, int role) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
//Data list getter and setter
QList<MeasureDevice *> measureDevices() const;
void setMeasureDevices(const QList<MeasureDevice *> &measureDevices);
private:
QList<MeasureDevice *> m_measureDevices;
};
#endif // GPIBDEVICESMODEL_H
GpibDevicesModel.cpp
#include "gpibdevicesmodel.h"
#include "measuredevice.h"
#include <QIcon>
GpibDevicesModel::GpibDevicesModel(QObject *parent)
: QAbstractTableModel(parent)
{}
GpibDevicesModel::~GpibDevicesModel()
{}
QVariant GpibDevicesModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid() || index.row() >= m_measureDevices.size())
return QVariant();
switch(role){
//This is roles for ComboBox
case Qt::DisplayRole:
switch(index.column()){
case NameCol: return m_measureDevices.at(index.row())->displayName();
case AddressCol:return m_measureDevices.at(index.row())->gpibName();
case IdCol: return m_measureDevices.at(index.row())->property("internal_id").toString();
case TypeCol: return m_measureDevices.at(index.row())->typeString();
}
break;
case Qt::DecorationRole:
switch(index.column()){
case ConnectedRole: {
MeasureDevice *md = m_measureDevices.at(index.row());
if(md->isConnected()) return QIcon(":/Icons/green_led.png");
else return QIcon(":/Icons/red_led.png");
}
}
break;
}
return QVariant();
}
int GpibDevicesModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_measureDevices.size();
}
int GpibDevicesModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return EndCol;
}
QVariant GpibDevicesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
static QStringList headers = QStringList() << tr("Name") << tr("Address") << tr("Id")
<< tr("Type") << tr("Connected") ;
if(role == Qt::DisplayRole && orientation == Qt::Vertical)
return QVariant(); //return section; //uncomment here to displayed row number
if(section >= headers.size())
return QVariant();
switch(role){
case Qt::DisplayRole: return headers.at(section);
}
return QVariant();
}
QList<MeasureDevice *> GpibDevicesModel::measureDevices() const
{
return m_measureDevices;
}
void GpibDevicesModel::setMeasureDevices(const QList<MeasureDevice *> &measureDevices)
{
beginResetModel();
m_measureDevices = measureDevices;
endResetModel();
}
And here is the error message from the compiler
mainwindow.cpp:1430: undefined reference to GpibDevicesModel::GpibDevicesModel(QObject*)'
I think that will be a stupid thing, as always... But I cannot figure out this error. I'm using another custom model in the same way and I don't have any problem.
As I guessed, it was stupid...
I resolved the problem by removing manually the build folder and the Makefiles (makefile, makefile.debug and makefile.release), then I run again qmake -> build and it was ok. So this was not a problem from code, maybe the makefiles was in read-only for an unknown reason.
Thanks for your help and your time Frogatto and Frank Osterfeld.

(Qt) LineEdit with suggestion delegate that read options from model (not working)

I'm trying to set up a delegate that has a lineedit with suggestions that are read from a model.
So, I tried passing the model that delegate should read the information from, this is not working since after the delgate is constructed, the model that the QCompleter is looking at doesn't not show the external modifications made to recived_model. So it does only work making the right suggestions when the recived_model hasn't changed...
Considering that the delegates is being used to modify the contents of recived_model. I think there should be a way to access the data of the column I want the completer to get suggestions from. I think this could be done using a "for" and the data function to set up a new QStringList that constains only the information from the column that I wanted the completer to use as suggestions. But this show be redone any time other item in the view is accessed. I'm same as start, no idea how to achieve this.
Here is my code... Hope you can help:
//header
class myDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
myDelegate(QWidget* parent=0,QSqlTableModel *recived_model, int col_pr);
QWidget* createEditor(QWidget* parent, QStyleOptionViewItem const& option, QModelIndex const& index) const;
void setEditorData(QWidget* editor, QModelIndex const& index) const;
void setModelData(QWidget*editor, QAbstractItemModel* model, QModelIndex const& index) const;
void updateEditorGeometry(QWidget* editor, QStyleOptionViewItem const& option, QModelIndex const& index ) const;
ModelWithoutDuplicatesProxy *model_pr;
int col_proxy;//Only column from the recived
//model that remains
};
and the implementation:
//cpp
myDelegate::myDelegate(QWidget* parent,QSqlTableModel *recived_model, int col_pr)
: QStyledItemDelegate(parent)
{
model_pr = new ModelWithoutDuplicatesProxy(parent,col_pr);
model_pr->setSourceModel(recived_model);
col_proxy = col_pr;
}
QWidget* myDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& option, QModelIndex const& index) const
{
QLineEdit *editor = new QLineEdit(parent);
QCompleter *completer = new QCompleter(model_pr);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setCompletionColumn(3);
completer->setFilterMode(Qt::MatchContains);
//completer->setMaxVisibleItems()
completer->setModelSorting(QCompleter::UnsortedModel);
editor->setCompleter(completer);
return editor;
}
void myDelegate::setEditorData(QWidget* editor, QModelIndex const& index) const
{
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(editor);
QString currentText = index.model()->data(index, Qt::EditRole).toString();
lineEdit->setText(currentText);
}
void myDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, QModelIndex const& index) const
{
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(editor);
model->setData(index, lineEdit->text(), Qt::EditRole);
}
void myDelegate::updateEditorGeometry(QWidget* editor, QStyleOptionViewItem const& option, QModelIndex const& index) const
{
editor->setGeometry(option.rect);
}
.
.
.
ModelWithoutDuplicatesProxy
class ModelWithoutDuplicatesProxy : public QSortFilterProxyModel
{
Q_OBJECT
public:
ModelWithoutDuplicatesProxy(QObject* parent=0,int column=0);
void setSourceModel(QAbstractItemModel* sourceModel);
protected:
bool filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const;
private slots:
void clearCache() { cache.clear(); }
private:
int Column;
mutable QStringList cache;
};
Implementation:
#include "modelwithoutduplicatesproxy.h"
#include <QDebug>
ModelWithoutDuplicatesProxy::ModelWithoutDuplicatesProxy(QObject* parent,int column)
: QSortFilterProxyModel(parent)
{
Column = column;
//setDynamicSortFilter(true);
}
bool ModelWithoutDuplicatesProxy::filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const
{
qDebug() << "filterAcceptsRow!";
QModelIndex index = sourceModel()->index(sourceRow,
Column,sourceParent);
const QString &text = sourceModel()->data(index, Qt::EditRole).toString();
//qDebug() << "El texto es:" << text << ".\n";
if (cache.contains(text,Qt::CaseInsensitive))
return false;
else
{
cache << text;
qDebug() << "Else: El texto es:" << text << ".\n";
return true;
}
}
void ModelWithoutDuplicatesProxy::setSourceModel(
QAbstractItemModel *sourceModel)
{
connect(sourceModel, SIGNAL(modelReset()),
this, SLOT(clearCache()));
QSortFilterProxyModel::setSourceModel(sourceModel);
}
There it is the code of the duplicates deleter.

Using QTableView with a model

I have the QVector cars that I want to filter basing on the car's registration number. I want to create a new filtered vector. I don't think that this is ok because i'm iterating 2 vectors, copying from the first one to the second one. Am I doing this right?
void MainWindow::on_actionBy_registration_number_triggered()
{
myDialog = new MyDialog(this);
myDialog->exec();
QString toSearchString = myDialog->getRegistrationNumber();
QVector<Vehicle> founded;
QVectorIterator<Vehicle> iterator(cars);
while(iterator.hasNext()){
Vehicle car = iterator.next();
QString num = car.getRegistration().getRegistrationNumber();
if(num.contains(toSearchString,Qt::CaseInsensitive)){
founded.append(car);
}
}
model = new QStandardItemModel(founded.size(),5,this);
//create header
createHeader(model);
int j = 0; //row
QVectorIterator<Vehicle> iter(founded);
while(iter.hasNext()){
Vehicle output = iter.next();
//set car
QString makeAndModel = output.getGeneralData().getMake() + output.getGeneralData().getModel();
QStandardItem *mAndM = new QStandardItem(QString(makeAndModel));
model->setItem(j,0,mAndM);
//set type
QStandardItem *type = new QStandardItem(QString(output.getGeneralData().getType()));
model->setItem(j,1,type);
//set mileage
QString mileageString = QString::number(output.getGeneralData().getMileage());
QStandardItem *mileage = new QStandardItem(QString(mileageString));
model->setItem(j,2,mileage);
//set year
QString yearString = QString::number(output.getGeneralData().getYear());
QStandardItem *year = new QStandardItem(QString(yearString));
model->setItem(j,3,year);
//set registration
QString regString = VehicleHelper::convertBoolToString(output.getRegistration().isRegistered());
QStandardItem *regDate = new QStandardItem(QString(regString));
model->setItem(j,4,regDate);
j++;
}
ui->tableView->setModel(model);
ui->tableView->setEnabled(false);
}
This can be done neatly using a proxy filter model. Below is a self-contained example that runs on both Qt 4 and 5.
// https://github.com/KubaO/stackoverflown/tree/master/questions/filter-18964377
#include <QtGui>
#if QT_VERSION_MAJOR > 4
#include <QtWidgets>
#endif
class Vehicle {
QString m_make, m_model, m_registrationNumber;
public:
Vehicle(const QString & make, const QString & model, const QString & registrationNumber) :
m_make{make}, m_model{model}, m_registrationNumber{registrationNumber} {}
QString make() const { return m_make; }
QString model() const { return m_model; }
QString registrationNumber() const { return m_registrationNumber; }
bool isRegistered() const { return !m_registrationNumber.isEmpty(); }
};
class VehicleModel : public QAbstractTableModel {
QList<Vehicle> m_data;
public:
VehicleModel(QObject * parent = {}) : QAbstractTableModel{parent} {}
int rowCount(const QModelIndex &) const override { return m_data.count(); }
int columnCount(const QModelIndex &) const override { return 3; }
QVariant data(const QModelIndex &index, int role) const override {
if (role != Qt::DisplayRole && role != Qt::EditRole) return {};
const auto & vehicle = m_data[index.row()];
switch (index.column()) {
case 0: return vehicle.make();
case 1: return vehicle.model();
case 2: return vehicle.registrationNumber();
default: return {};
};
}
QVariant headerData(int section, Qt::Orientation orientation, int role) const override {
if (orientation != Qt::Horizontal || role != Qt::DisplayRole) return {};
switch (section) {
case 0: return "Make";
case 1: return "Model";
case 2: return "Reg.#";
default: return {};
}
}
void append(const Vehicle & vehicle) {
beginInsertRows({}, m_data.count(), m_data.count());
m_data.append(vehicle);
endInsertRows();
}
};
class Widget : public QWidget {
QGridLayout m_layout{this};
QTableView m_view;
QPushButton m_button{"Filter"};
VehicleModel m_model;
QSortFilterProxyModel m_proxy;
QInputDialog m_dialog;
public:
Widget() {
m_layout.addWidget(&m_view, 0, 0, 1, 1);
m_layout.addWidget(&m_button, 1, 0, 1, 1);
connect(&m_button, SIGNAL(clicked()), &m_dialog, SLOT(open()));
m_model.append({"Volvo", "240", "SQL8941"});
m_model.append({"Volvo", "850", {}});
m_model.append({"Volvo", "940", "QRZ1321"});
m_model.append({"Volvo", "960", "QRZ1628"});
m_proxy.setSourceModel(&m_model);
m_proxy.setFilterKeyColumn(2);
m_view.setModel(&m_proxy);
m_dialog.setLabelText("Enter registration number fragment to filter on. Leave empty to clear filter.");
m_dialog.setInputMode(QInputDialog::TextInput);
connect(&m_dialog, SIGNAL(textValueSelected(QString)),
&m_proxy, SLOT(setFilterFixedString(QString)));
}
};
int main(int argc, char *argv[])
{
QApplication a{argc, argv};
Widget w;
w.show();
return a.exec();
}