PDA

View Full Version : QtabkeView, QStandardItemModel and QSortFilterProxyModel problem.



nehk018
23rd December 2023, 20:21
Hello all,

I want to do a QTableView with a custom calculate cell. In the first column I have the name of the device. In the second column I have millisecond field and in the third column I have the rest of the previous millisecond field row and the actual row and write in the second column. Also a have a filter to select the device.

I have a QStandardItemModel for update the data, and a QSortFilterProxyModel for the filter.

My code is:



this->model = new QStandardItemModel();
this->model->setColumnCount(3);
this->model->setHeaderData(0, Qt::Horizontal, QObject::tr("Time"));
this->model->setHeaderData(1, Qt::Horizontal, QObject::tr("Milliseconds"));
this->model->setHeaderData(2, Qt::Horizontal, QObject::tr("Spend Time"));

this->dataTable = new QTableView();
this->dataTable->setEditTriggers(QAbstractItemView::NoEditTriggers) ;
this->dataTable->setSortingEnabled(false);
this->dataTable->sortByColumn(1, Qt::AscendingOrder);


QRegExp filterString = QRegExp("ARM1", Qt::CaseInsensitive);
this->filterModel->setFilterRegExp(filterString);
this->filterModel->setFilterKeyColumn(-1);
this->dataTable->setModel(filterModel);




When I don't use the filter, I can write in the data model without problem like this:



QStandardItemModel *auxModel = (QStandardItemModel*)this->dataTable->model();
int rows = auxModel->rowCount();
for (int i = 0; i < rows; i++) {
if (i == 0) {
auxModel->setItem(i, 2, new QStandardItem("0"));
} else {
unsigned long aux1 = auxModel->data(auxModel->index(i,1)).toString().toLong();
unsigned long aux2 = auxModel->data(auxModel->index(i-1,1)).toString().toLong();
QString s = QString::number(aux1 - aux2);
auxModel->setItem(i, 2, new QStandardItem(s));
}
}


But when I use the filter I get a segmentation fault and I cannot found a solution.

Can anyone hellp me?
Thanks

ecanela
24th December 2023, 04:49
seems to you need call the sourceModel() fuction in proxyModel, and then set the model in the TableView.



this->filterModel->setSourceMode(this->model) //<--- need to setSourceModel in filterProxy
this->dataTable->setModel(this->filterModel);


if this is not correct, please post the minimum code needed to run the program.

nehk018
24th December 2023, 10:56
Than you for your reply ecanela,

After some test I think the problem is edit a cell from the QSortFilterProxyModel.

My .h code is:




#include <QDialog>
#include <QCloseEvent>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QCheckBox>
#include <QTableView>
#include <QStandardItemModel>
#include <QSignalMapper>
#include <QSortFilterProxyModel>
#include <QHeaderView>
#include <QMenu>
#include <QScrollBar>
#include <QFileDialog>
#include <QItemSelectionModel>

#include <fstream>

#include "VariablesBuffer.h"
#include "StringFunctions.h"
#include "CommandsBuffer.h"

#include "WindowInclude.h"

#include <unistd.h> /* Unix standard function definitions */

class WindowTimeMonitoring : public WindowInclude {
Q_OBJECT
private:
QHBoxLayout * layoutMonitoring;

CommandsBuffer * commands;
VariablesBuffer * variables;

/** @brief Reference to the signal mapper for the filter */
QSignalMapper* signalMapper;

/** @brief Reference to the filter data */
QSortFilterProxyModel *filterModel = NULL;

/** @brief Reference to the header of the table to show data */
QHeaderView *tableHeader = NULL;

std::map<std::string, QCheckBox *> mapAviables;

std::map<std::string, QCheckBox *> mapFilter;

QTableView * dataTable;

/** @brief Reference to model with data */
QStandardItemModel *model;


public slots:
void filterAction();
void clearSlot();
void exportSlot();
void udpateTimeSlot();

public:
WindowTimeMonitoring(VariablesBuffer * variables, CommandsBuffer * commands, QWidget *parent = nullptr);
void addCommand(Command * command);
virtual ~WindowTimeMonitoring();
};





And my Cpp code is






seems to you need call the sourceModel() fuction in proxyModel, and then set the model in the TableView.



this->filterModel->setSourceMode(this->model) //<--- need to setSourceModel in filterProxy
this->dataTable->setModel(this->filterModel);


if this is not correct, please post the minimum code needed to run the program.




#include "WindowTimeMonitoring.h"

WindowTimeMonitoring::WindowTimeMonitoring(QWidget *parent) : QDialog(parent) {

this->setWindowTitle("Time Monitoring");
this->resize(800,600);

std::string modules("ARM1, ARM2, ARM3");

QVBoxLayout * layoutMain = new QVBoxLayout();
std::vector<std::string> listaModulos = strFunctions::split(modules, ',');

QHBoxLayout * layoutButtons = new QHBoxLayout();
QPushButton *buttonClear = new QPushButton("Clear table");
QObject::connect(buttonClear, SIGNAL(clicked()), this, SLOT(clearSlot()));

QPushButton *buttonClose = new QPushButton("Close");
QObject::connect(buttonClose, SIGNAL(clicked()), this, SLOT(closeSlot()));

QPushButton *buttonExport = new QPushButton("Export to file");
QObject::connect(buttonExport, SIGNAL(clicked()), this, SLOT(exportSlot()));

QPushButton *buttonRecal = new QPushButton("Calculate spend time");
QObject::connect(buttonRecal, SIGNAL(clicked()), this, SLOT(udpateTimeSlot()));

layoutButtons->addWidget(buttonRecal);
layoutButtons->addWidget(buttonClear);
layoutButtons->addWidget(buttonClose);
layoutButtons->addWidget(buttonExport);

QGroupBox *groupFilter = new QGroupBox("Filter Monitoring:");
QHBoxLayout * layoutFilter = new QHBoxLayout();
for(auto item: listaModulos) {

mapFilter[item] = new QCheckBox(QString::fromStdString(item));
QObject::connect(mapFilter[item], SIGNAL(clicked()), this, SLOT(filterAction()));

mapFilter[item]->setChecked(true);

layoutFilter->addWidget(mapFilter[item]);
}
groupFilter->setLayout(layoutFilter);

this->model = new QStandardItemModel();
this->model->setColumnCount(5);

this->filterModel = new QSortFilterProxyModel();

model->setHeaderData(0, Qt::Horizontal, QObject::tr("Time"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Milliseconds"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Time Spend"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Module"));
model->setHeaderData(4, Qt::Horizontal, QObject::tr("Message"));

QGroupBox *groupTimeMonitoring = new QGroupBox("Time Monitoring:");
layoutMonitoring = new QHBoxLayout();
dataTable = new QTableView();
this->dataTable->setEditTriggers(QAbstractItemView::NoEditTriggers) ;
this->dataTable->setSortingEnabled(false);
this->dataTable->sortByColumn(1, Qt::AscendingOrder);
this->dataTable->setModel(this->model);
dataTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

layoutMonitoring->addWidget(dataTable);
groupTimeMonitoring->setLayout(layoutMonitoring);

layoutMain->addWidget(groupAviable);
layoutMain->addWidget(groupFilter);
layoutMain->addWidget(groupTimeMonitoring);
layoutMain->addLayout(layoutButtons);

this->setLayout(layoutMain);

}

void WindowTimeMonitoring::udpateTimeSlot() {

QStandardItemModel *auxModel = (QStandardItemModel*)this->dataTable->model();

int rows = auxModel->rowCount();

for (int i = 0; i < rows; i++) {
if (i == 0) {

auxModel->setItem(i, 2, new QStandardItem("0"));

} else {
unsigned long aux1 = auxModel->data(auxModel->index(i,1)).toString().toLong();
unsigned long aux2 = auxModel->data(auxModel->index(i-1,1)).toString().toLong();

QString s = QString::number(aux1 - aux2);
auxModel->setItem(i, 2, new QStandardItem(s));

}
}
}

void WindowTimeMonitoring::clearSlot() {
model->clear();
model->removeRows( 0, model->rowCount());


}

void WindowTimeMonitoring::exportSlot() {

QString aux_filename = QFileDialog::getSaveFileName(this,
tr("Select File"), "", tr("All Files (*)"));
if (! aux_filename.isEmpty()) {

QStandardItemModel *auxModel = (QStandardItemModel*)this->dataTable->model();

QString textData("Time,Milliseconds,Time Spend, Module, Message");
int rows = auxModel->rowCount();
int columns = auxModel->columnCount();

for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {

textData += auxModel->data(auxModel->index(i,j)).toString();
textData += ", "; // for .csv file format
}
textData += "\n"; // (optional: for new line segmentation)
}

std::ofstream MyFile(aux_filename.toStdString());
MyFile << textData.toStdString();
MyFile.close();

}
}
/** ************************************************** ************************
* void WindowShowBufferPackaged::filterAction(QString cadena)
* @brief Filter table buffer data.
* @details Filter the table with the buffer data.
*
* @param cadena Conditions to filter the table. The first character is
* the column to apply the filter.
************************************************** **************************/
void WindowTimeMonitoring::filterAction() {
std::vector<std::string> lista;

for(auto item: mapFilter) {
if (item.second->isChecked()) {
lista.push_back(item.first);
}
}

if (lista.size() == mapFilter.size()) {
this->dataTable->setModel(model);
//QRegExp filterString = QRegExp("", Qt::CaseInsensitive);
//filterModel->setFilterRegExp(filterString);

} else {

QString cadena("^(");
QString aux;
for (std::string item: lista) {
cadena += aux + QString(item.c_str());

aux = "|";
}
cadena += ")$";

this->filterModel->setSourceModel(model);

//QRegExp filterString = QRegExp("", Qt::CaseInsensitive);
//filterModel->setFilterRegExp(filterString);



QRegExp filterString = QRegExp(cadena, Qt::CaseInsensitive);
filterModel->setFilterRegExp(filterString);
filterModel->setFilterKeyColumn(-1);
this->dataTable->setModel(filterModel);
}

}

void WindowTimeMonitoring::addCommand(Command * command) {
QStandardItem *item;

this->model->setColumnCount(5);

QList<QStandardItem *> items;
item = new QStandardItem(QString(command->getTimeStr().c_str()));
items.append(item);

item = new QStandardItem(QString::number(command->getMiliSeconds()));
items.append(item);

item = new QStandardItem(QString(""));
items.append(item);

item = new QStandardItem(QString(command->origen.c_str()));
items.append(item);

item = new QStandardItem(QString(command->params.c_str()));
items.append(item);

this->model->appendRow(items);
}

WindowTimeMonitoring::~WindowTimeMonitoring() {
// TODO Auto-generated destructor stub
}

ecanela
26th December 2023, 04:21
please provide a short self-contained correct example, the code is extense and dont provide some classes.

https://stackoverflow.com/help/minimal-reproducible-example

commandBuffer and VariableBuffer classes are not provided, and maybe not necesary for the example code.

nehk018
26th December 2023, 16:32
Dear Ecanela,

sorry for my mistake. I try to do quickly and I don't read the rules. Sorry again.

Ok, I do a short self-cointaned example. The example has a QTimer to add elements for the table. There is a button ("Calculate spend time") to recalculate the difference between the rows. To reproduce the error, you can unselect one of the elements of the filter and then recalculate the difference between rows.

The Cpp class.



#include "WindowTimeMonitoringShort.h"

WindowTimeMonitoringShort::WindowTimeMonitoringSho rt(QWidget *parent) : QDialog(parent) {

this->setWindowTitle("Time Monitoring");
this->resize(800,600);

QTimer * clock =new QTimer(this);

/* Conections */
connect(clock, SIGNAL(timeout()), this, SLOT(addCommand()));
clock->start(20000); /* Check every 20 second */

QVBoxLayout * layoutMain = new QVBoxLayout();


listaModulos.push_back("ARM1");
listaModulos.push_back("ARM2");
listaModulos.push_back("ARM3");
listaModulos.push_back("ARM4");

QHBoxLayout * layoutButtons = new QHBoxLayout();

QPushButton *buttonClose = new QPushButton("Close");
QObject::connect(buttonClose, SIGNAL(clicked()), this, SLOT(closeSlot()));

QPushButton *buttonRecal = new QPushButton("Calculate spend time");
QObject::connect(buttonRecal, SIGNAL(clicked()), this, SLOT(udpateTimeSlot()));

layoutButtons->addWidget(buttonRecal);
layoutButtons->addWidget(buttonClose);

QGroupBox *groupFilter = new QGroupBox("Filter Monitoring:");
QHBoxLayout * layoutAviables = new QHBoxLayout();
QHBoxLayout * layoutFilter = new QHBoxLayout();
for(auto item: listaModulos) {
mapFilter[item] = new QCheckBox(QString::fromStdString(item));
QObject::connect(mapFilter[item], SIGNAL(clicked()), this, SLOT(filterAction()));

mapFilter[item]->setChecked(true);
layoutFilter->addWidget(mapFilter[item]);
}
groupFilter->setLayout(layoutFilter);

this->model = new QStandardItemModel();
this->model->setColumnCount(5);

this->filterModel = new QSortFilterProxyModel();

model->setHeaderData(0, Qt::Horizontal, QObject::tr("Time"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Milliseconds"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Time Spend"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Module"));
model->setHeaderData(4, Qt::Horizontal, QObject::tr("Message"));

QGroupBox *groupTimeMonitoring = new QGroupBox("Time Monitoring:");
layoutMonitoring = new QHBoxLayout();
dataTable = new QTableView();
this->dataTable->setEditTriggers(QAbstractItemView::NoEditTriggers) ;
this->dataTable->setSortingEnabled(false);
this->dataTable->sortByColumn(1, Qt::AscendingOrder);
this->dataTable->setModel(this->model);
this->dataTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

this->dataTable->horizontalHeader()->resizeSection(0, 120);
this->dataTable->horizontalHeader()->resizeSection(1, 120);
this->dataTable->horizontalHeader()->resizeSection(2, 100);
this->dataTable->horizontalHeader()->resizeSection(3, 100);

layoutMonitoring->addWidget(dataTable);
groupTimeMonitoring->setLayout(layoutMonitoring);

layoutMain->addWidget(groupFilter);
layoutMain->addWidget(groupTimeMonitoring);
layoutMain->addLayout(layoutButtons);

this->setLayout(layoutMain);

}


/**
* Ths method recalculate the differences between the rows.
*/
void WindowTimeMonitoringShort::udpateTimeSlot() {

QStandardItemModel *auxModel = (QStandardItemModel*)this->dataTable->model();

int numRows = auxModel->rowCount();
for (int i = 0; i < numRows; i++) {
if (i == 0) { // In the frist line I put a 0.
auxModel->setItem(i, 2, new QStandardItem("0"));
} else {
unsigned long aux1 = auxModel->data(auxModel->index(i,1)).toString().toLong();
unsigned long aux2 = auxModel->data(auxModel->index(i-1,1)).toString().toLong();

QString s = QString::number(aux1 - aux2);
auxModel->setItem(i, 2, new QStandardItem(s));
}
}
}


/** ************************************************** ************************
* This method filter the model to show in the table.
*
* In this case, if there are all modules selected, I select the original model without
* the filter. In this case, I can execute the udpateTimeSlot to recalculate the difference
* between rows. If I eliminate a select of the filter and execute the updateTimeSlot,
* I get the error.
************************************************** **************************/
void WindowTimeMonitoringShort::filterAction() {
std::vector<std::string> lista;

for(auto item: mapFilter) {
if (item.second->isChecked()) {
lista.push_back(item.first);
}
}

if (lista.size() == mapFilter.size()) {
this->dataTable->setModel(model);
} else {

QString cadena("^(");
QString aux;
// COn esta funciona "^(ARM1_RENDER|POLAR|ARM1)$"
for (std::string item: lista) {
cadena += aux + QString(item.c_str());

aux = "|";
}
cadena += ")$";

this->filterModel->setSourceModel(model);

//QRegExp filterString = QRegExp("", Qt::CaseInsensitive);
//filterModel->setFilterRegExp(filterString);



QRegExp filterString = QRegExp(cadena, Qt::CaseInsensitive);
filterModel->setFilterRegExp(filterString);
filterModel->setFilterKeyColumn(-1);
this->dataTable->setModel(filterModel);
}

}


/**
* Slot to add a row per module list items to the table with the basic information
*
*/
void WindowTimeMonitoringShort::addCommand() {
QStandardItem *item;


for(auto it: listaModulos) {

auto nowDate = std::chrono::system_clock::now();


unsigned long long milliseconds_since_epoch =
std::chrono::duration_cast<std::chrono::milliseconds>(nowDate.time_since_epoch()).count();



QList<QStandardItem *> items;
item = new QStandardItem(QString(date::format("%T", nowDate).c_str()));
items.append(item);

item = new QStandardItem(QString::number(milliseconds_since_e poch));
items.append(item);

item = new QStandardItem(QString(""));
items.append(item);

item = new QStandardItem(QString(it.c_str()));
items.append(item);

item = new QStandardItem(QString(it.c_str()));
items.append(item);

this->model->appendRow(items);

usleep(2000);
}
}

WindowTimeMonitoringShort::~WindowTimeMonitoringSh ort() {
// TODO Auto-generated destructor stub
}



and de h



#ifndef SRC_POLAR_GUI_QT_WINDOWS_WindowTimeMonitoringShort _H_
#define SRC_POLAR_GUI_QT_WINDOWS_WindowTimeMonitoringShort _H_

#include <QDialog>
#include <QCloseEvent>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QCheckBox>
#include <QTableView>
#include <QStandardItemModel>
#include <QSignalMapper>
#include <QSortFilterProxyModel>
#include <QHeaderView>
#include <QMenu>
#include <QScrollBar>
#include <QFileDialog>
#include <QItemSelectionModel>
#include <QTimer>

#include <fstream>
#include "date.h"
#include <unistd.h> /* Unix standard function definitions */

class WindowTimeMonitoringShort : public QDialog {
Q_OBJECT
private:
QHBoxLayout * layoutMonitoring;

/** @brief Reference to the signal mapper for the filter */
QSignalMapper* signalMapper;

/** @brief Reference to the filter data */
QSortFilterProxyModel *filterModel = NULL;

std::vector<std::string> listaModulos;

/** @brief Reference to the header of the table to show data */
QHeaderView *tableHeader = NULL;

std::map<std::string, QCheckBox *> mapAviables;

std::map<std::string, QCheckBox *> mapFilter;

QTableView * dataTable;

/** @brief Reference to model with data */
QStandardItemModel *model;


public slots:
void filterAction();
void udpateTimeSlot();
void addCommand();
public:
WindowTimeMonitoringShort(QWidget *parent = nullptr);

virtual ~WindowTimeMonitoringShort();
};

#endif /* SRC_POLAR_GUI_QT_WINDOWS_WindowTimeMonitoring_H_ */




Thank you in advance, and sorry again for my mistake.

nehk018
27th December 2023, 22:12
Hello everybody,

I get a solution. It isn't the best solution, but it's work. I use a QTableWidget to show the data from the QSortFilterProxyModel.

Thanks to everybody.