laura laura - 4 months ago 26
C++ Question

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

Answer

This can be done neatly using a proxy filter model. Below is a self-contained example that runs on both Qt 4 and 5.

screenshot

#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 = nullptr) : 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 QVariant{};
      const Vehicle & 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{};
      };
   }
   QVariant headerData(int section, Qt::Orientation orientation, int role) const override {
      if (orientation != Qt::Horizontal) return QVariant{};
      if (role != Qt::DisplayRole) return QVariant{};
      switch (section) {
      case 0: return "Make";
      case 1: return "Model";
      case 2: return "Reg.#";
      default: return QVariant{};
      }
   }
   void append(const Vehicle & vehicle) {
      beginInsertRows(QModelIndex{}, 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(Vehicle{"Volvo", "240", "SQL8941"});
      m_model.append(Vehicle{"Volvo", "850", QString{}});
      m_model.append(Vehicle{"Volvo", "940", "QRZ1321"});
      m_model.append(Vehicle{"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();
}