How to display data inside QtTableView from a Model [duplicate] - c++

This question already has answers here:
Scope vs. Lifetime of Variable
(6 answers)
Classes and variables scope in C++
(6 answers)
Closed 3 years ago.
I am new to Qt5 and trying to create a QTableView widget using QtCreator. As for as I understand QTableView can be connected to a model via a setModel method and Model must be an instance QtAbstractTableModel
#ifndef FILELIST_H
#define FILELIST_H
#include <QAbstractTableModel>
namespace Ui {
class FileList;
}
class FileList : public QAbstractTableModel
{
Q_OBJECT
public:
FileList(QObject *parent=nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};
#endif // FILELIST_H
Here is the Implementation FileList.cpp
#include <QFont>
#include <QBrush>
#include <QDebug>
#include "FileList.h"
FileList::FileList(QObject *parent) : QAbstractTableModel(parent)
{
qDebug() << QString("Constructor...");
}
int FileList::rowCount(const QModelIndex &parent) const
{
return 2;
}
int FileList::columnCount(const QModelIndex &parent) const
{
return 3;
}
QVariant FileList::data(const QModelIndex &index, int role) const
{
int row = index.row();
int col = index.column();
// generate a log message when this method gets called
qDebug() << QString("row %1, col%2, role %3")
.arg(row).arg(col).arg(role);
switch (role) {
case Qt::DisplayRole:
if (row == 0 && col == 1) return QString("<--left");
if (row == 1 && col == 1) return QString("right-->");
return QString("Row%1, Column%2")
.arg(row + 1)
.arg(col +1);
case Qt::FontRole:
if (row == 0 && col == 0) { //change font only for cell(0,0)
QFont boldFont;
boldFont.setBold(true);
return boldFont;
}
break;
case Qt::BackgroundRole:
if (row == 1 && col == 2) //change background only for cell(1,2)
return QBrush(Qt::red);
break;
case Qt::TextAlignmentRole:
if (row == 1 && col == 1) //change text alignment only for cell(1,1)
return Qt::AlignRight + Qt::AlignVCenter;
break;
case Qt::CheckStateRole:
if (row == 1 && col == 0) //add a checkbox to cell(1,0)
return Qt::Checked;
break;
}
return QVariant();
}
Now when I attach the QTableView widget to my main window the data does not show up in the table view and I get a blank empty TableView
CloudSync.cpp
#include <QDebug>
#include "CloudSync.h"
#include "ui_cloudsync.h"
#include "FileList.h"
CloudSync::CloudSync(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::CloudSync)
{
ui->setupUi(this);
FileList fileList;
qDebug() << QString("row %1, col%2, role %3");
ui->listView->setModel(&fileList);
//ui->listView->show();
setCentralWidget(ui->listView);
}
CloudSync::~CloudSync()
{
delete ui;
}
and main.cpp
#include "CloudSync.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CloudSync w;
w.show();
return a.exec();
}
Now what I am doing wrong here? The qDebug inside the ::data method does not output anything into the console which makes me the thing that the model is not properly connected
Final Output

Related

How to keep row selected after adding new one in the begining of table?

My model designed to add row in the begining of table. Problem is that if I select second row (for example) selection still will be on this row (second) after adding new row, but original row moved down.
I figured out that QStandardItemModel provides propely behaviour. How to provide same one in my inherited QAbstractTableModel?
Now there is exapmle code for QStandardItemModel. You can select row and see what's going on.
#include <QApplication>
#include <QMainWindow>
#include <QStandardItemModel>
#include <QTableView>
#include <QTimer>
void timeout(QTimer &timer, QStandardItemModel &tblModel)
{
static int step = 0;
tblModel.insertRow(0);
QStandardItem *pItem = new QStandardItem(QString("row %0").arg(step));
tblModel.setItem(0, 1, pItem);
step++;
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QMainWindow win;
QStandardItemModel tblModel(0, 1);
QTableView tblView;
tblView.setModel(&tblModel);
win.setCentralWidget(&tblView);
win.show();
QTimer timer;
timer.setInterval(1000);
QObject::connect(&timer, &QTimer::timeout,
[&timer, &tblModel]() { timeout(timer, tblModel); });
timer.start();
return app.exec();
}
And simplified version of my model. You can see that in my version selected row doesen't move.
#include <QApplication>
#include <QMainWindow>
#include <QStandardItemModel>
#include <QTableView>
#include <QTimer>
#include <QString>
class MyModel : public QAbstractTableModel
{
public:
MyModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void insertRow(int data);
private:
QVector<int> table;
};
MyModel::MyModel(QObject *parent)
: QAbstractTableModel(parent) {}
int MyModel::rowCount(const QModelIndex & /*parent*/) const
{
return table.count();
}
int MyModel::columnCount(const QModelIndex & /*parent*/) const
{
return 1;
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::DisplayRole) {
return table[table.count() - 1 - index.row()];
}
return QVariant();
}
void MyModel::insertRow(int data) {
beginInsertRows(QModelIndex(), rowCount(), rowCount());
table.push_back(data);
endInsertRows();
}
void timeout(QTimer &timer, MyModel * tblModel)
{
static int step = 0;
tblModel->insertRow(step);
step++;
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QMainWindow win;
MyModel * model = new MyModel();
QTableView tblView;
tblView.setModel(model);
win.setCentralWidget(&tblView);
win.show();
QTimer timer;
timer.setInterval(1000);
QObject::connect(&timer, &QTimer::timeout,
[&timer, model]() { timeout(timer, model); });
timer.start();
return app.exec();
}
I didn't find any information in QAbstractTableModel documentation. After that I tried to read QStandardItemModel source code but it's really difficult for understanding.

QTableView drag & drop rows not working properly

I am trying to move rows of a QTableView. I build a small MVCE according to this article. I can successfully move rows, however a strange effect happens as soon as I drop the row, see below print screen:
A new row5 gets created instead of pushing two to Row0 and three to Row1
So the correct result should be:
instead I get the following incorrect result:
I have been trying to solve this problem reading this source, which was useful to build and test the model. In addition to that this was useful to understand the parameters to give to the QTableView but did not totally solved the problem. Also from here a small description of the process was useful to read but still my problem persists. Finally I found this interesting source that seems to describe to address Qt::ItemFlags as possible solution but could not properly relate it to a possible solution.
See below the most important part of the code:
main.cpp
#include <QApplication>
#include <QtGui>
#include <QAbstractItemModel>
#include <QTableView>
#include <QListView>
#include <QAbstractItemView>
#include "newmodel.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStringList numbers;
numbers << "one" << "two" << "three" << "four" << "five";
QAbstractItemModel *model = new NewModel(numbers);
QTableView *tableView = new QTableView;
tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
tableView->dragDropOverwriteMode();
tableView->setDragEnabled(true);
tableView->setAcceptDrops(true);
tableView->setDropIndicatorShown(true);
tableView->setModel(model);
tableView->setDefaultDropAction(Qt::MoveAction);
tableView->show();
QListView *listView = new QListView;
listView->setDragEnabled(true);
listView->setAcceptDrops(true);
listView->setModel(model);
listView->setDefaultDropAction(Qt::MoveAction);
listView->show();
return a.exec();
}
newmodel.cpp
#include "newmodel.h"
#include <QStringListModel>
#include <QDebug>
NewModel::NewModel(const QStringList &strings, QObject *parent)
: QAbstractListModel(parent)
, stringList(strings)
{}
int NewModel::rowCount(const QModelIndex &parent) const
{
return stringList.count();
Q_UNUSED(parent);
}
QVariant NewModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(index.row() >= stringList.size())
return QVariant();
if(role == Qt::DisplayRole || role == Qt::EditRole)
return stringList.at(index.row());
else
return QVariant();
}
QVariant NewModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role != Qt::DisplayRole)
return QVariant();
if(orientation == Qt::Horizontal)
return QString("Column %1").arg(section);
else
return QString("Row %1").arg(section);
}
Qt::ItemFlags NewModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
if(index.isValid())
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
else
return Qt::ItemIsDropEnabled | defaultFlags;
}
bool NewModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if(index.isValid() && role == Qt::EditRole) {
stringList.replace(index.row(), value.toString());
emit dataChanged(index, index);
return true;
}
return false;
}
bool NewModel::insertRows(int position, int rows, const QModelIndex &parent)
{
beginInsertRows(QModelIndex(), position, position+rows-1);
for(int row = 0; row < rows; row++) {
stringList.insert(position, "");
}
endInsertRows();
return true;
Q_UNUSED(parent);
}
bool NewModel::removeRows(int position, int rows, const QModelIndex &parent)
{
beginRemoveRows(QModelIndex(), position, position+rows-1);
for(int row = 0; row < rows; ++row) {
stringList.removeAt(position);
}
endRemoveRows();
return true;
Q_UNUSED(parent);
}
Qt::DropActions NewModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
QStringList NewModel::mimeTypes() const
{
QStringList types;
types << "application/vnd.text.list";
return types;
}
QMimeData *NewModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData();
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
foreach(const QModelIndex &index, indexes) {
if(index.isValid()) {
QString text = data(index, Qt::DisplayRole).toString();
stream << text;
}
}
mimeData->setData("application/vnd.text.list", encodedData);
return mimeData;
}
bool NewModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
qDebug() << action;
if(action == Qt::IgnoreAction)
return true;
if(!data->hasFormat("application/vnd.text.list"))
return false;
if(column > 0)
return false;
int beginRow;
if(row != -1)
beginRow = row;
else if(parent.isValid())
beginRow = parent.row();
else
beginRow = rowCount(QModelIndex());
QByteArray encodedData = data->data("application/vnd.text.list");
QDataStream stream(&encodedData, QIODevice::ReadOnly);
QStringList newItems;
int rows = 0;
while(!stream.atEnd()) {
QString text;
stream >> text;
newItems << text;
++rows;
}
insertRows(beginRow, rows, QModelIndex());
foreach(const QString &text, newItems) {
QModelIndex idx = index(beginRow, 0, QModelIndex());
setData(idx, text);
beginRow++;
}
return true;
}
bool NewModel::dragDropOverwtiteMode() const
{
return false;
}
Thank you very much for pointing in the right direction and trying to shed light on this matter
In NewModel::dropMimeData() you are inserting the row at the specified location. What you are not doing is:
check if the action is a CopyAction or MoveAction
For me I always get a CopyAction even with view.setDefaultDropAction(Qt::MoveAction). By now I'm suspecting a bug in QT5 that ignores the default drop action.
For a MoveAction remove the original row
I'm not sure how to do this right for moves from one view to another or even harder between applications. But as you are moving inside the same table you can just remove the old row in the drop method.
Actually models support moving rows directly instead of remove + insert. See https://doc.qt.io/qt-5/qabstractitemmodel.html#beginMoveRows

Qt custom tree model display correctly but buggy and slow

I'm trying to implement a model in which different first-level parents have a child tables of different sizes. In this code there is only one first level parent index and child table of 8 by 8, and the application when displaying clearly buggy and slow, although the data is correct. What am I doing wrong?
testqtmodel.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = testqtmodel
TEMPLATE = app
SOURCES += main.cpp
main.cpp
#include <QApplication>
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QTableView>
class TreeModel : public QAbstractItemModel
{
public:
explicit TreeModel(QObject *parent = 0)
: QAbstractItemModel(parent)
{
for (int i = 0; i < 8; ++i)
for (int j = 0; j < 8; ++j)
foo[i][j] = i + j;
}
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole)
return QVariant();
if (index.internalId() == 0)
return QVariant();
if (index.internalId() == 1)
return foo[index.row()][index.column()];
}
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE
{
if (!parent.isValid())
return createIndex(row, column, (quint64)0);
else
return createIndex(row, column, (quint64)1);
}
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE
{
if (index.internalId() == 0)
return QModelIndex();
if (index.internalId() == 1)
return createIndex(0, 0, (quint64)0);
}
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE
{
if (!parent.isValid())
return 1;
if (parent.internalId() == 0)
return 8;
}
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE
{
if (!parent.isValid())
return 1;
if (parent.internalId() == 0)
return 8;
}
private:
int foo[8][8];
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTableView *v = new QTableView();
TreeModel *m = new TreeModel();
v->setModel(m);
v->setRootIndex(m->index(1, 1));
v->show();
return a.exec();
}
I'm definitely a fool
v->setRootIndex(m->index(1, 1));
should be
v->setRootIndex(m->index(0, 0));

Qt background color of row in ListStringModel

i try set color of row with this code and it doesnt work.
QColor dataColor =Qt::red;
row = seznamChyb->rowCount();
seznamChyb->insertRows(row,1);
QModelIndex index = seznamChyb->index(row);
ui->listView->setCurrentIndex(index);
seznamChyb->setData(index,dataColor, Qt::BackgroundRole);
seznamChyb->setData(index,data);
I'm not sure what really type of seznamChyb and ListStringModel. But Changing the background color of row required the implementation of method QAbstractItemModel::data().
I write a example for this question, and it does work well:
#include <QApplication>
#include <QTableView>
#include <QAbstractListModel>
class StringListModel : public QAbstractListModel
{
public:
StringListModel(const QStringList &strings, QObject *parent = 0)
: QAbstractListModel(parent), stringList(strings) {}
int rowCount(const QModelIndex &) const
{
return stringList.count();
}
QVariant data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
return stringList.at(index.row());
if (role == Qt::BackgroundColorRole && index.row() == 0) // Look this.
return QBrush(Qt::red);
else
return QVariant();
}
private:
QStringList stringList;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTableView tableView;
QStringList list;
list << "a" << "b" << "c";
StringListModel model(list);
tableView.setModel(&model);
tableView.show();
return a.exec();
}
Please check your program again.

Spanning horizontal header in Qt

I want to merge(span) horizontal headers in QTableWidget. I tried googling for the same, but no luck and hence posting it. Please guide me.
You can subclass QHeaderView and create one section for each of the groups of columns/rows you would like to span and connect signals and slots to have them react to the different columns/rows.
The following example is for spanning the horizontal header:
#include <QtGui>
class MyHeaderModel : public QAbstractItemModel
{
public:
MyHeaderModel(QObject *parent = 0) : QAbstractItemModel(parent) {}
int columnCount(const QModelIndex &parent = QModelIndex()) const { return 2; }
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { return QVariant(); }
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const { return QModelIndex(); }
QModelIndex parent(const QModelIndex &index) const { return QModelIndex(); }
int rowCount(const QModelIndex &parent = QModelIndex()) const { return 0; }
};
class MyHeader : public QHeaderView
{
Q_OBJECT
public:
MyHeader(QHeaderView *header, QWidget *parent = 0) : QHeaderView(Qt::Horizontal, header), mainHeader(header)
{
setModel(new MyHeaderModel(this));
// This example uses hardcoded groups, you can extend
// this yourself to save the groups
// Group 1 is 0-2 and Group 2 is 3-4
resizeSection(0, getSectionSizes(0, 2));
resizeSection(1, getSectionSizes(3, 4));
connect(this, SIGNAL(sectionResized(int,int,int)), this, SLOT(updateSizes()));
connect(((QTableWidget *)(mainHeader->parentWidget()))->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updateOffset()));
setGeometry(0, 0, header->width(), header->height());
updateOffset();
mainHeader->installEventFilter(this);
}
public slots:
void updateSizes()
{
setOffset(mainHeader->offset());
mainHeader->resizeSection(2, mainHeader->sectionSize(2) + (sectionSize(0) - getSectionSizes(0, 2)));
mainHeader->resizeSection(4, mainHeader->sectionSize(4) + (sectionSize(1) - getSectionSizes(3, 4)));
}
void updateOffset()
{
setOffset(mainHeader->offset());
}
protected:
bool eventFilter(QObject *o, QEvent *e)
{
if (o == mainHeader) {
if (e->type() == QEvent::Resize) {
setOffset(mainHeader->offset());
setGeometry(0, 0, mainHeader->width(), mainHeader->height());
}
return false;
}
return QHeaderView::eventFilter(o, e);
}
private:
int getSectionSizes(int first, int second)
{
int size = 0;
for (int a=first;a<=second;++a)
size += mainHeader->sectionSize(a);
return size;
}
QHeaderView *mainHeader;
};
#include "main.moc"
int main(int argc, char **argv)
{
QApplication a(argc, argv);
QWidget w;
QVBoxLayout *vbox = new QVBoxLayout;
QTableWidget *tw = new QTableWidget(5, 5);
MyHeader *h = new MyHeader(tw->horizontalHeader());
vbox->addWidget(tw);
w.setLayout(vbox);
w.show();
return a.exec();
}