Qt 5.6.2(msvc2015-32bit) model/view-readonly example unexpected behaviour - c++

I experiment with the 'readonly' model/view example:
examples/widgets/tutorials/modelview/1_readonly/mymodel.cpp
When I change it to this ...
#include <QDebug>
#include "mymodel.h"
MyModel::MyModel(QObject *parent)
:QAbstractTableModel(parent)
{
}
int MyModel::rowCount(const QModelIndex & /*parent*/) const
{
return 2;
}
int MyModel::columnCount(const QModelIndex & /*parent*/) const
{
return 22000000;
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
qDebug() << QString("R%1C%2")
.arg(index.row() + 1)
.arg(index.column() +1);
if (role == Qt::DisplayRole)
{
return QString("R%1C%2")
.arg(index.row() + 1)
.arg(index.column() +1);
}
return QVariant();
}
... QTableView from the main.c keeps asking for 'index' beyond the cells it is showing :
However, when I change return value to 21000000
int MyModel::columnCount(const QModelIndex & /*parent*/) const
{
return 21000000;
}
... it works as expected :
What is so special about the number 22000000 ?
Thank you.

Related

Large set of data in QTableView

I am trying to add large sets of data in QTableView. As there is huge set of data, in order to avoid freezing the application, I am trying to use a QAbstractTableModel to not showthe whole dataset immediately, but only what is necessary. Based on the QT example for this (HERE), I have the following class:
FileListModel.cpp
#include "filelistmodel.h"
#include <QGuiApplication>
#include <QDir>
#include <QPalette>
#include "qdebug.h"
FileListModel::FileListModel(QObject *parent)
: QAbstractTableModel(parent), fileCount(0) //edit
{}
int FileListModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : fileCount;
}
QVariant FileListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (index.row() >= fileList.size() || index.row() < 0)
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
return fileList.at(index.row());
}
return QVariant();
}
bool FileListModel::canFetchMore(const QModelIndex &parent) const
{
if (parent.isValid())
{
return false;
}
return (fileCount < fileList.size());
}
int FileListModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : colCount;
}
void FileListModel::fetchMore(const QModelIndex &parent)
{
if (parent.isValid())
{
return;
}
int remainder = fileList.size() - fileCount;
int itemsToFetch = qMin(100, remainder);
if (itemsToFetch <= 0)
{
return;
}
beginInsertRows(QModelIndex(), fileCount, fileCount + itemsToFetch - 1);
qDebug()<< "Qmodelindex "<< QModelIndex()<< "filecount "<< fileCount <<"filecount + itemtofetch "<<fileCount + itemsToFetch - 1;
fileCount += itemsToFetch;
endInsertRows();
}
void FileListModel::setColumnNumber(const int x) //edit
{
colCount = x;
}
void FileListModel::setDataToList(const QList<float> &data)
{
beginResetModel();
fileList = data;
fileCount = 0;
endResetModel();
}
FileListModel.h
#ifndef FILELISTMODEL_H
#define FILELISTMODEL_H
#include <QAbstractTableModel>
#include <QStringList>
class FileListModel : public QAbstractTableModel //edit
{
Q_OBJECT
public:
FileListModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override; //edit
void setColumnNumber(const int );
public slots:
void setDataToList(const QList<float>&);
protected:
bool canFetchMore(const QModelIndex &parent) const override;
void fetchMore(const QModelIndex &parent) override;
private:
QList<float> fileList;
int fileCount;
int colCount;//edit
};
#endif // FILELISTMODEL_H
In my window, I have a QTableView and 5 QList<float>, each representing a column.
I proceed this way to add the data in my QTableView:
FileListModel *model=new FileListModel(this);
model->setColumnNumber(5); //edit
model->setDataToList(list1);
model->setDataToList(list2);
model->setDataToList(list3);
model->setDataToList(list4);
model->setDataToList(list5);
tableview->setModel(model)
However, the last list added (list5) replace the previous one and I have only the same text in all the columns. I am aware that I need to write the needed code to add columns. But to be honest, I am not knowing very well the QAbstract classes, and have no idea how to proceed. I would be grateful if you could provide me some hints or an example on how to modify my code in order to add columns in my model.
I found how to proceed by storing my data in a QList>.
Please find below a working code that can probably be improved:
CPP FILE
#include "filelistmodel.h"
#include <QGuiApplication>
#include <QDir>
#include <QPalette>
#include "qdebug.h"
FileListModel::FileListModel(QObject *parent)
: QAbstractTableModel(parent), rowNumber(0)
{}
int FileListModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : rowNumber;
}
int FileListModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : colNumber;
}
void FileListModel::setColumnNumber(const int x)
{
colNumber = x;
}
QVariant FileListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (index.row() >= fileList[0].size() || index.row() < 0)
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
return fileList[index.column()][index.row()];
}
return QVariant();
}
bool FileListModel::canFetchMore(const QModelIndex &parent) const
{
if (parent.isValid())
{
return false;
}
return (rowNumber < fileList[0].size());
}
void FileListModel::fetchMore(const QModelIndex &parent)
{
if (parent.isValid())
{
return;
}
int remainder = fileList[0].size() - rowNumber;
int itemsToFetch = qMin(100, remainder);
if (itemsToFetch <= 0)
{
return;
}
beginInsertRows(QModelIndex(), rowNumber, rowNumber + itemsToFetch - 1);
rowNumber += itemsToFetch;
endInsertRows();
}
void FileListModel::setDataToTable(const QList<QList<float>> &data)
{
beginResetModel();
fileList = data;
rowNumber = 0;
endResetModel();
}
H FILE
#ifndef FILELISTMODEL_H
#define FILELISTMODEL_H
#include <QAbstractListModel>
#include <QStringList>
class FileListModel : public QAbstractTableModel
{
Q_OBJECT
public:
FileListModel(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 setColumnNumber(const int );
void setDataToTable(const QList<QList<float>>&);
protected:
bool canFetchMore(const QModelIndex &parent) const override;
void fetchMore(const QModelIndex &parent) override;
private:
QList<QList<float>> fileList;
int rowNumber;
int colNumber;
};
#endif // FILELISTMODEL_H
BUILDING MODEL
FileListModel *pModel =new FileListModel(this);
QList<QList<float>> a;
pModel->setColumnNumber(5);
a.append(qlist1);
a.append(qlist2);
a.append(qlist3);
a.append(qlist4);
a.append(qlist5);
pModel->setDataToTable(a);

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

Changing data in Qt table view

I went through previous questions on stackoverflow regarding updating table view and Qt tutorial but I still cannot get my code to work. I want to update table view data with setData method when the push button is pressed. The method insertData inside my custom model TableModel gets called but data does not get changed (new rows are added). Where is the problem? I'm not sure if this is relevant but my table view is on qwidget which is on tab widget.
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "tablemodel.h"
#include <iostream>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->dataFetcher = new DataFetcher();
tm = new TableModel(0);
ui->tableView->setModel(tm);
ui->tableView->show();
this->setPushButtonHandlers();
}
MainWindow::~MainWindow()
{
delete ui;
delete this->dataFetcher;
}
void MainWindow::setPushButtonHandlers()
{
connect(this->ui->refreshDataButton, SIGNAL (clicked()), this, SLOT (refreshDataHandler()));
}
void MainWindow::refreshDataHandler()
{
this->tm->insertData();
}
TableModel.cpp
#include "tablemodel.h"
#include <iostream>
TableModel::TableModel(QObject *parent): QAbstractTableModel(parent)
{
}
int TableModel::rowCount(const QModelIndex &parent) const
{
return 2;
}
int TableModel::columnCount(const QModelIndex &parent) const
{
return 16;
}
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
{
return QString("Row%1, Column%2")
.arg(index.row() + 1)
.arg(index.column() +1);
}
return QVariant();
}
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole)
{
if (orientation == Qt::Horizontal) {
switch (section)
{
case 0:
return QString("Symbol");
case 1:
return QString("Name");
case 2:
return QString("Stock Exchange");
case 3:
return QString("Trade Price");
case 4:
return QString("Ask");
case 5:
return QString("Bid");
case 6:
return QString("Change");
case 7:
return QString("Change %");
case 8:
return QString("Previous close");
case 9:
return QString("Open");
case 10:
return QString("Days Low");
case 11:
return QString("Days High");
case 12:
return QString("Year Low");
case 13:
return QString("Year High");
case 14:
return QString("Change % From Year Low");
case 15:
return QString("Change % From Year High");
}
} else if (orientation == Qt::Vertical) {
return QVariant(QString::number(section+1));
}
}
return QVariant();
}
void TableModel::insertData()
{
beginInsertRows(QModelIndex(), 3, 100);
endInsertRows();
QModelIndex rv = this->index(1, 1);
setData(rv, 9999);
emit dataChanged(rv, rv);
QString s = data(rv).toString();
std::cout << s.toUtf8().constData() << std::endl;
}
At first you should re-implement insertRows virtual method of QAbstractItemModel. Next, you should refactor your code as following:
void TableModel::insertData()
{
insertRows(rowCount(), 1, QModelIndex());
}
bool TableModel::insertRows(int row, int count, const QModelIndex &index)
{
beginInsertRows();
items.push_back("");
endInsertRows();
return true;
}
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if (index.isValid() && role == Qt::DisplayRole) {
return items.at(index.row());
}
return QVariant();
}
int TableModel::rowCount(const QModelIndex &parent) const
{
return items.size();
}
Also, add the data actually:
class TableModel {
// ...your code...
private:
std::vector<QString> items;
};
This code is just an example code - when moving this to your production code you absolutely should do some changes.

Display enabled QCheckBox as disabled

I use a QTreeView with a TreeModel (that inherits QAbstractItemModel). The TreeModel::flags() function returns Qt::ItemIsUserCheckable and therefore a checkbox is displayed besides every item in the view.
What I need to do is display the checkbox as disabled when the parent item is not checked but the user must be able to interact with it normally (the checkbox must be enabled).
I tried to implement a custom QStyledItemDelegate and draw the checkbox myself. However I don't know how to use custom images when drawing the checkbox at the draw() function of the delegate. I don't even know if this is the right way to do it.
I also thought of using stylesheets somehow, but the display of the checkbox depends on the model data (if the parent is checked or not).
Any Ideas?
This is my ItemDelegate::paint() function.
void ItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::DisplayRole).toInt();
QStyleOptionButton check_box_style_option;
check_box_style_option.state |= QStyle::State_Enabled;
if (value == 1)
check_box_style_option.state |= QStyle::State_On;
else
check_box_style_option.state |= QStyle::State_Off;
check_box_style_option.rect = option.rect;
QApplication::style()->drawControl(QStyle::CE_CheckBox,
&check_box_style_option, painter);
}
[Edit] TreeModel code. The tree model is based on the "simpletreemodel" example. I changed flags() to also return Qt::ItemIsUserCheckable and data(), setData() to enable the checkboxes to be checked and unchecked.
#include <QtGui>
#include <QDebug>
#include "treeitem.h"
#include "treemodel.h"
TreeModel::TreeModel(Container *data, QObject *parent)
: QAbstractItemModel(parent)
{
root_item_ = new TreeItem("");
SetupModelData(data, root_item_);
}
TreeModel::~TreeModel()
{
delete root_item_;
}
int TreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
else
return root_item_->columnCount();
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if(role == Qt::CheckStateRole && index.column() == 0)
return static_cast<int>(item->checked()) ? Qt::Checked : Qt::Unchecked;
if(role != Qt::DisplayRole)
return QVariant();
return item->data(index.column());
}
bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if(role == Qt::CheckStateRole)
item->set_checked(!item->checked());
if(role == Qt::EditRole)
{
qDebug() << "value:" << value.toString();
item->set_data(value.toString());
}
emit dataChanged(index, index);
return true;
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
}
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return root_item_->data(section);
return QVariant();
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
TreeItem *parentItem;
if (!parent.isValid())
parentItem = root_item_;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
TreeItem *parentItem = childItem->parent();
if (parentItem == root_item_)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = root_item_;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
return parentItem->childCount();
}
void TreeModel::SetupModelData(Container *data, TreeItem *parent)
{
TreeItem *new_item = new TreeItem(data->id(), parent);
data->set_tree_item(new_item);
parent->appendChild(new_item);
foreach(Container *child, data->children())
SetupModelData(child, new_item);
}