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.
Related
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
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
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.
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);
}
I was working on the code which in simple word displays a list of items.
i am able to populate the QlistView but its a empty model.The model i have created is empty.
I mean i have added some four Items.but i cannot see it in the output.Please help.
Main.cpp
#include <QtGui/QApplication>
#include <QDeclarativeContext>
#include <QKeyEvent>
#include <QAbstractItemModel>
#include <QListView>
#include <QDebug>
#include "qmlapplicationviewer.h"
#include "listmodel.h"
#include "songitem.h"
#include "mainwindow.h"
#include "song.h"
#include "songs.h"
#include "songitem.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
ListModel *model = new ListModel(new SongItem, qApp);
model->appendRow(new SongItem( "Michel Telo","Ai Se Eu Te Pego","Ai Se Eu Te Pego", model));
SongItem test = new SongItem( "Michel Telo","Ai Se Eu Te Pego","Ai Se Eu Te Pego", model);
qDebug() << test.data(NameRole);
QModelIndex index = model->index(0, 2, QModelIndex());
qDebug() << model->data(index);
Songs songz;
Song s;
vector<Song> songCollection;
songCollection = songz.all(3);
int ii;
for(ii=0; ii < songCollection.size(); ii++)
{
QString qstr = QString::fromStdString(songCollection[ii].album);
model->appendRow(new SongItem( qstr , qstr, qstr, model));
}
QListView *view = new QListView;
view->setWindowTitle("Model Data");
view->setModel(model);
view->setStyleSheet("color: black");
view->show();
return app.exec();
}
ListModel.cpp
#include <QDebug>
#include "listmodel.h"
int i=0;
ListModel::ListModel(ListItem* prototype, QObject *parent) :
QAbstractListModel(parent), m_prototype(prototype)
{
setRoleNames(m_prototype->roleNames());
}
int ListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_list.size();
}
QVariant ListModel::data(const QModelIndex &index, int role) const
{
if(index.row() < 0 || index.row() >= m_list.size())
return QVariant();
return m_list.at(index.row())->data(role);
}
ListModel::~ListModel() {
delete m_prototype;
clear();
}
void ListModel::appendRow(ListItem *item)
{
appendRows(QList<ListItem*>() << item);
//qDebug() << "Test";
//qDebug() << item;
}
void ListModel::appendRows(const QList<ListItem *> &items)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount()+items.size()-1);
foreach(ListItem *item, items) {
connect(item, SIGNAL(dataChanged()), SLOT(handleItemChange()));
m_list.append(item);
}
endInsertRows();
}
void ListModel::insertRow(int row, ListItem *item)
{
beginInsertRows(QModelIndex(), row, row);
connect(item, SIGNAL(dataChanged()), SLOT(handleItemChange()));
m_list.insert(row, item);
endInsertRows();
}
void ListModel::handleItemChange()
{
ListItem* item = static_cast<ListItem*>(sender());
QModelIndex index = indexFromItem(item);
if(index.isValid())
emit dataChanged(index, index);
}
ListItem * ListModel::find(const QString &id) const
{
foreach(ListItem* item, m_list) {
if(item->id() == id) return item;
}
return 0;
}
QModelIndex ListModel::indexFromItem(const ListItem *item) const
{
Q_ASSERT(item);
for(int row=0; row<m_list.size(); ++row) {
if(m_list.at(row) == item) return index(row);
}
return QModelIndex();
}
void ListModel::clear()
{
qDeleteAll(m_list);
m_list.clear();
}
bool ListModel::removeRow(int row, const QModelIndex &parent)
{
Q_UNUSED(parent);
if(row < 0 || row >= m_list.size()) return false;
beginRemoveRows(QModelIndex(), row, row);
delete m_list.takeAt(row);
endRemoveRows();
return true;
}
bool ListModel::removeRows(int row, int count, const QModelIndex &parent)
{
Q_UNUSED(parent);
if(row < 0 || (row+count) >= m_list.size()) return false;
beginRemoveRows(QModelIndex(), row, row+count-1);
for(int i=0; i<count; ++i)
{
delete m_list.takeAt(row);
}
endRemoveRows();
return true;
}
ListItem * ListModel::takeRow(int row)
{
beginRemoveRows(QModelIndex(), row, row);
ListItem* item = m_list.takeAt(row);
endRemoveRows();
return item;
}
SongItem.h
#ifndef SONGITEM_H
#define SONGITEM_H
#include "listmodel.h"
class SongItem : public ListItem
{
Q_OBJECT
public:
enum Roles {
NameRole = Qt::UserRole+1,
ArtistRole,
TrackRole
};
public:
SongItem(QObject *parent = 0): ListItem(parent){}
explicit SongItem(const QString &name, const QString &artist, const QString &track, QObject *parent = 0);
QVariant data(int role) const;
QHash<int, QByteArray> roleNames() const;
inline QString id() const { return m_name; }
inline QString name() const { return m_name; }
inline QString artist() const { return m_artist; }
inline QString track() const { return m_track; }
private:
QString m_name;
QString m_artist;
QString m_track;
};
#endif // SONGITEM_H
SongItem.cpp
#include "songitem.h"
#include <QDebug>
SongItem::SongItem(const QString &name, const QString &artist, const QString &track, QObject *parent) :
ListItem(parent), m_name(name), m_artist(artist), m_track(track)
{
}
QHash<int, QByteArray> SongItem::roleNames() const
{
QHash<int, QByteArray> names;
names[NameRole] = "name";
names[ArtistRole] = "artist";
names[TrackRole] = "track";
return names;
}
QVariant SongItem::data(int role) const
{
switch(role) {
case NameRole:
return name();
case ArtistRole:
return artist();
case TrackRole:
return track();
default:
return QVariant();
}
}
A QListView will fetch data with the Qt::DisplayRole role for display, so you have to adapt SongItem::data to return something for this role, e.g.
QVariant SongItem::data(int role) const
{
switch(role) {
case Qt::DisplayRole:
return name();
case NameRole:
return name();
case ArtistRole:
return artist();
case TrackRole:
return track();
default:
return QVariant();
}
}
It might be that you took some examples related to QML, where it works quite differently.