How to call a model function and update the data in a QTableView?
-
Hi everyone, I am working a on game which can be represented as a 2D grid, which made the table view useful for this. When a user selects a column (wherever inside the column, rows are not important), I want a signal to be sent to the model, which will update the board accordingly.
For now, I have the following inside a main.cpp, mainwindow is the default one, nothing was changed there.
I implemented my own model which has a game attribute, the game has all the relevant data and logic within. In the code here under, nothing happens when I click on a cell and the "buttons" (the pieces of the game) are not moved around. Did I mess up the connection?
#include "mainwindow.h" #include "qtmodel.h" #include <QApplication> #include <QTableView> int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window; QTableView tableView; Game *gamePointer{new Game}; QtModel model; model.setGame(gamePointer); tableView.setModel(&model); tableView.setSelectionBehavior(QTableView::SelectColumns); // problematic connection here? QTableView::connect(&tableView, SIGNAL(clicked(const QModelIndex &)), &model, SLOT(moveStacksOnColumnClicked(const QModelIndex &))); tableView.setStyleSheet( "selection-background-color: rgba(137, 196, 244, 250)"); window.setCentralWidget(&tableView); window.resize(1400, 600); window.setWindowTitle("MyGAME!"); window.show(); int ret = app.exec(); delete gamePointer; return ret; }
The model is as follows:
It doesn't do anything fancy aside from changing the background colors of cells depending on some game enumeration, the header numbering is not that important but the important functions aredata
andmoveStackOnColumnClicked
which calls a method of the game that moves the "buttons" of my game around, it takes a single integer as parameter, which is the index of the column where the player wants to act.#include "qtmodel.h" #include <QBrush> QtModel::QtModel(QObject *parent) : QAbstractTableModel(parent) {} // See https://doc.qt.io/qt-6/modelview.html#2-1-a-read-only-table QVariant QtModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Vertical) { for (int i = 0; i < ButtonStack::N_BUTTONS; i++) { const std::string rowHeader = std::to_string(ButtonStack::N_BUTTONS - i); if (i == section) { return QString(rowHeader.c_str()); } } } if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { for (int i = 0; i < ButtonStack::N_BUTTONS; i++) { const std::string colHeader = std::to_string(i + 1); if (i == section) { return QString(colHeader.c_str()); } } } return QVariant(); } int QtModel::rowCount(const QModelIndex & /*parent*/) const { return 9; } int QtModel::columnCount(const QModelIndex & /*parent*/) const { return 9; } QVariant QtModel::data(const QModelIndex &index, int role) const { int row = index.row(); int col = index.column(); if (role == Qt::BackgroundRole) { Button dat = (*m_game)[col][ButtonStack::N_BUTTONS - row - 1]; switch (dat) { case Button::EMPTY: return QBrush(QColorConstants::Svg::lightgray); case Button::RED: return QBrush(QColorConstants::Svg::orangered); case Button::BLACK: return QBrush(QColorConstants::Svg::black); case Button::WHITE: return QBrush(QColorConstants::Svg::ghostwhite); } } return QVariant(); } void QtModel::setGame(Game *game) { m_game = game; } void QtModel::moveStacksOnColumnClicked(const QModelIndex &idx) { if (idx.isValid()) { m_game->moveStack(idx.column()); } }
Thanks for the help.
-
Hi everyone, I am working a on game which can be represented as a 2D grid, which made the table view useful for this. When a user selects a column (wherever inside the column, rows are not important), I want a signal to be sent to the model, which will update the board accordingly.
For now, I have the following inside a main.cpp, mainwindow is the default one, nothing was changed there.
I implemented my own model which has a game attribute, the game has all the relevant data and logic within. In the code here under, nothing happens when I click on a cell and the "buttons" (the pieces of the game) are not moved around. Did I mess up the connection?
#include "mainwindow.h" #include "qtmodel.h" #include <QApplication> #include <QTableView> int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window; QTableView tableView; Game *gamePointer{new Game}; QtModel model; model.setGame(gamePointer); tableView.setModel(&model); tableView.setSelectionBehavior(QTableView::SelectColumns); // problematic connection here? QTableView::connect(&tableView, SIGNAL(clicked(const QModelIndex &)), &model, SLOT(moveStacksOnColumnClicked(const QModelIndex &))); tableView.setStyleSheet( "selection-background-color: rgba(137, 196, 244, 250)"); window.setCentralWidget(&tableView); window.resize(1400, 600); window.setWindowTitle("MyGAME!"); window.show(); int ret = app.exec(); delete gamePointer; return ret; }
The model is as follows:
It doesn't do anything fancy aside from changing the background colors of cells depending on some game enumeration, the header numbering is not that important but the important functions aredata
andmoveStackOnColumnClicked
which calls a method of the game that moves the "buttons" of my game around, it takes a single integer as parameter, which is the index of the column where the player wants to act.#include "qtmodel.h" #include <QBrush> QtModel::QtModel(QObject *parent) : QAbstractTableModel(parent) {} // See https://doc.qt.io/qt-6/modelview.html#2-1-a-read-only-table QVariant QtModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Vertical) { for (int i = 0; i < ButtonStack::N_BUTTONS; i++) { const std::string rowHeader = std::to_string(ButtonStack::N_BUTTONS - i); if (i == section) { return QString(rowHeader.c_str()); } } } if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { for (int i = 0; i < ButtonStack::N_BUTTONS; i++) { const std::string colHeader = std::to_string(i + 1); if (i == section) { return QString(colHeader.c_str()); } } } return QVariant(); } int QtModel::rowCount(const QModelIndex & /*parent*/) const { return 9; } int QtModel::columnCount(const QModelIndex & /*parent*/) const { return 9; } QVariant QtModel::data(const QModelIndex &index, int role) const { int row = index.row(); int col = index.column(); if (role == Qt::BackgroundRole) { Button dat = (*m_game)[col][ButtonStack::N_BUTTONS - row - 1]; switch (dat) { case Button::EMPTY: return QBrush(QColorConstants::Svg::lightgray); case Button::RED: return QBrush(QColorConstants::Svg::orangered); case Button::BLACK: return QBrush(QColorConstants::Svg::black); case Button::WHITE: return QBrush(QColorConstants::Svg::ghostwhite); } } return QVariant(); } void QtModel::setGame(Game *game) { m_game = game; } void QtModel::moveStacksOnColumnClicked(const QModelIndex &idx) { if (idx.isValid()) { m_game->moveStack(idx.column()); } }
Thanks for the help.
- Have you verified whether your
moveStacksOnColumnClicked()
slot actually gets called (and with a valid index)? - Your
data()
returnsQVariant()
for the data for any index. Is this an issue?
- Have you verified whether your
-
- Have you verified whether your
moveStacksOnColumnClicked()
slot actually gets called (and with a valid index)? - Your
data()
returnsQVariant()
for the data for any index. Is this an issue?
I'm not used to Qt at all so I'm not sure how I would do 1), that is how to ensure the function is called?
As for the second question, I don't know what it means to be honest, I populate my 9x9 grid entirely with the pieces of the game which looks like this
Gray = empty, the other colors signify a "button" (a piece of the game) that will be moved around (they will be stacked onto each other to create little stacks of "buttons", hence the name of the function).
Does that answer the question?
P.S: I'm somewhat limited in how often I can reply because of reputation apparently?
- Have you verified whether your
-
I'm not used to Qt at all so I'm not sure how I would do 1), that is how to ensure the function is called?
As for the second question, I don't know what it means to be honest, I populate my 9x9 grid entirely with the pieces of the game which looks like this
Gray = empty, the other colors signify a "button" (a piece of the game) that will be moved around (they will be stacked onto each other to create little stacks of "buttons", hence the name of the function).
Does that answer the question?
P.S: I'm somewhat limited in how often I can reply because of reputation apparently?
@NullFunction
It's hard to be sure, but I think you are saying you expectmoveStacksOnColumnClicked()
to be called to do some stuff but you do not see the intended result?So first put a
qDebug()
statement abovem_game->moveStack(idx.column());
to ensure that gets called, and then I guess show whatever that does if you are saying it does not have the intended effect. -
@NullFunction
It's hard to be sure, but I think you are saying you expectmoveStacksOnColumnClicked()
to be called to do some stuff but you do not see the intended result?So first put a
qDebug()
statement abovem_game->moveStack(idx.column());
to ensure that gets called, and then I guess show whatever that does if you are saying it does not have the intended effect.I realized that in the header of
QtModel
, the functionmoveStacksOnColumnClicked
was not underpublic slots:
so it did not register and nothing happened.Now the function actually works but it's terribly slow to update (on a 9x9 grid this should be really fast). I suspect I should add
emit dataChanged(....)
in themoveStacksOnColumnClicked
? I'd like to redraw the 9x9 grid anytime it's called.I'm not so sure about how to do that because
dataChanged
require specific indices, i just need to redraw the whole grid though.EDIT:
This was actually sufficient:
void QtModel::moveStacksOnColumnClicked(const QModelIndex &idx) { if (idx.isValid()) { m_game->moveStack(idx.column()); emit dataChanged(index(0, 0), index(9, 9)); } }