I want to use a QTableView. This is a result of some tests.
As you can see, there are some boxes in every cell, before the content "123". What are these boxes and how can I remove these?
I think I need to change some properties of the QTableView, but I did not found a property related to these misterious boxes.
Here some code I used:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
//...
TVLDataModel* model = new TVLDataModel();
ui->uxTVLView->setModel(model);
}
TVLDataModel (inherits QAbstractTableModel)
int TVLDataModel::rowCount(const QModelIndex &parent) const
{
return 2;
}
int TVLDataModel::columnCount(const QModelIndex &parent) const
{
return 2;
}
QVariant TVLDataModel::data(const QModelIndex &index, int role) const
{
return 123;
}
you should change your QVariant TVLDataModel::data(const QModelIndex &index, int role) const function to indicate the role you are using. for example Qt::EditRole, Qt::BackgroundRole, etc.
for example :
QVariant TVLDataModel::data(const QModelIndex &index, int role) const
{
switch(role){
case Qt::EditRole :
case Qt::DisplayRole :
return 123;
default : break;
}
return QVariant();
}
Otherwise you would return 123 for every ItemDataRole.
Those "strange" boxes are checkboxes. Your model indicates that each item is checkable.
Related
I have two MySQL tables with one-to-many relations.
Table si:
id integer primary key auto_increment,
...
cur_verify_date date,
...
Table verify:
...
si_id integer, -- id in 'si'
verify_date date,
...
So I need a combobox delegate for a table si in QTableView to choose items of verify_date only with si_id for corresponding id, different items in each row. However, the delegates can have, as I suppose, only identical values for each row. Is there a way to do it in the way I need?
Yes, you can do that with QItemDelegate, I would say that's your main solution when you want to show combobox in QTableView.
Check is signature of createEditor method from QAbstractItemDelegate:
https://doc.qt.io/qt-5/qabstractitemdelegate.html#createEditor
You can use QModelIndex parameter to define custom values for each row in your combobox.
While actual implementation will depend on your implementation of your model, you can check this simple example of that idea.
#include <QAbstractTableModel>
#include <QApplication>
#include <QTableView>
#include <QComboBox>
#include <QStyledItemDelegate>
class DummyModel : public QAbstractTableModel {
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
return 5;
}
int columnCount(const QModelIndex& parent = QModelIndex()) const override
{
return 2;
}
Qt::ItemFlags flags(const QModelIndex& index) const override
{
return __super::flags(index) | Qt::ItemIsEditable;
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
{
if (role == Qt::DisplayRole && index.column() == 0)
return index.row();
return {};
}
};
class ComboBoxItemDelegate : public QStyledItemDelegate {
public:
ComboBoxItemDelegate(QObject* parent = nullptr)
: QStyledItemDelegate(parent) {};
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
QComboBox* cb = new QComboBox(parent);
//place to query database for actual values
const auto value = index.model()->index(index.row(), 0).data(Qt::DisplayRole).toString();
cb->addItem(value);
cb->addItem(value);
return cb;
}
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
DummyModel model;
QTableView table_view;
table_view.setModel(&model);
ComboBoxItemDelegate cb_delegate;
table_view.setItemDelegateForColumn(1, &cb_delegate);
table_view.show();
return app.exec();
}
An editable table view is connected to a custom model (a subclass of QAbstractTableModel) that populates it from a database. The table view's edited values are temporarily held in a 2D QVector.
The goal is for a push button below the table view to trigger an update of the database.
The problem is that I can't seem to figure out the SIGNAL&SLOT mechanism for this. It seems to be a problem of the scope of the push button, I think, but I don't know how to fix it.
First two files just for overview, the problem is (probably) in the third, in setData().
I'm still a beginner in this framework so apologies for a potentially silly question.
a tableView("menuTable") and a pushButton("menuTableUpdateButton") are made in Design Mode.
mainwindow.cpp (opening a db conection, querying db, placing result into QVector and initializing and assigning the model)
...
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//database connection
...
//opening database
if(db.open())
{
...
//2D array to hold the query to pass into model
QVector< QVector<QString> > sqlQueryVector;
...
//querying and storing the result
...
//initialize QAbstractTableModel and pass it into the menu table
//passing an id(int) and a 2D QVector that holds the sql query result to the model
menuTableModel* modelMenu = new menuTableModel(id, sqlQueryVector);
ui->menuTable->setModel(modelMenu);
}
else
{
...
}
}
menutablemodel.h (basic initializations)
#ifndef MENUTABLEMODEL_H
#define MENUTABLEMODEL_H
#include <QAbstractTableModel>
#include <QVector>
class menuTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit menuTableModel(const int &monId, const QVector< QVector<QString> > &qry, QObject *parent = 0)
: QAbstractTableModel(parent), mondayId(monId),query(qry) {}
int rowCount(const QModelIndex &parent = QModelIndex()) const ;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex & index) const ;
void setQuery(const QVector< QVector<QString> > &query);
void setId(const int &stId);
private:
int mondayId;
QVector< QVector<QString> > query;
signals:
void editCompleted(const QString &);
slots:
void updateDatabase();
};
#endif // MENUTABLEMODEL_H
menutablemodel.cpp (assigning data into table, editing and SAVING TO DATABASE)
#include "menutablemodel.h"
#include <QDebug>
#include <QSqlQuery>
int menuTableModel::rowCount(const QModelIndex & /*parent*/) const {...}
int menuTableModel::columnCount(const QModelIndex & /*parent*/) const {...}
QVariant menuTableModel::data(const QModelIndex &index, int role) const {...}
QVariant menuTableModel::headerData(int section, Qt::Orientation orientation, int role) const {...}
//edit and save into database
bool menuTableModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
if (role == Qt::EditRole)
{
query[index.row()][index.column()] = value.toString();
QString result;
for(int row= 0; row < query.size(); row++)
{
for(int col= 0; col < query[0].size(); col++)
{
result += query[row][col] + " ";
}
}
emit editCompleted( result );
qDebug() << "result is: " << result;
//update database here with connect??
//connect(menuTableUpdateButton, SIGNAL (clicked()), this, SLOT (updateDatabase()));
}
return true;
}
Qt::ItemFlags menuTableModel::flags(const QModelIndex & /*index*/) const {...}
void menuTableModel::setQuery(const QVector< QVector<QString> > &qry) {
query = qry;
}
void menuTableModel::setId(const int &stId) {...}
void menuTableModel::updateDatabase() {
//write SqlQuery here
...
}
My main question is where and how do I define the SIGNAL&SLOT command to connect the button being clicked to executing an SQL query to the database?
In your MainWindow constructor, you have the model and the button, so that's the best place to connect them:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//opening database
if(db.open()) {
//...
menuTableModel* modelMenu = new menuTableModel(id, sqlQueryVector);
ui->menuTable->setModel(modelMenu);
// Connect button click to model update
connect(ui->menuTableUpdateButton, SIGNAL(clicked()),
modelMenu, SLOT(updateDatabase()));
}
}
Due to lack of any other Qt demo, I am making use of Qt widgets's SimpleTreeModeldemo to implement C++ model for my QML TreeView. I have defined roles so QML can use it but I am having trouble connecting them with actual model data.
What I also find interesting is that widgets (C++) demo works fine but TreeModeldoesn't seem to store the data as its member variable..leaves me scratching my head. I figured this out, every TreeItem store all its child items and TreeModel has only one rootItem which in turn stores all data as its child.
The TreeItem class
class TreeItem
{
public:
explicit TreeItem(const QList<QVariant> &data, TreeItem *parentItem = 0);
~TreeItem();
void appendChild(TreeItem *child);
TreeItem *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
int row() const;
TreeItem *parentItem();
private:
QList<TreeItem*> m_childItems;
QList<QVariant> m_itemData;
TreeItem *m_parentItem;
};
The TreeModel class
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum DisplayRoles {
TitleRole = Qt::UserRole + 1,
SummaryRole
};
explicit TreeModel(const QString &data, QObject *parent = 0);
~TreeModel();
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QHash<int, QByteArray> TreeModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[TitleRole] = "title";
roles[SummaryRole] = "summary";
return roles;
}
private:
void setupModelData(const QStringList &lines, TreeItem *parent);
TreeItem *rootItem;
};
The model loads the data from a default.txt
TreeModel::TreeModel(const QString &data, QObject *parent)
: QAbstractItemModel(parent)
{
QList<QVariant> rootData;
rootData << "Title" << "Summary";
rootItem = new TreeItem(rootData);
setupModelData(data.split(QString("\n")), rootItem);
}
void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
{
QList<TreeItem*> parents;
QList<int> indentations;
parents << parent;
indentations << 0;
int number = 0;
while (number < lines.count()) {
int position = 0;
while (position < lines[number].length()) {
if (lines[number].mid(position, 1) != " ")
break;
position++;
}
QString lineData = lines[number].mid(position).trimmed();
if (!lineData.isEmpty()) {
// Read the column data from the rest of the line.
QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
QList<QVariant> columnData;
for (int column = 0; column < columnStrings.count(); ++column)
columnData << columnStrings[column];
if (position > indentations.last()) {
// The last child of the current parent is now the new parent
// unless the current parent has no children.
if (parents.last()->childCount() > 0) {
parents << parents.last()->child(parents.last()->childCount()-1);
indentations << position;
}
} else {
while (position < indentations.last() && parents.count() > 0) {
parents.pop_back();
indentations.pop_back();
}
}
// Append a new item to the current parent's list of children.
parents.last()->appendChild(new TreeItem(columnData, parents.last()));
}
++number;
}
}
My problem is in this function, how do I connect the roles with the data which is stored in rootItem?. Note titleString and summaryStringare possible proposed functions (if needed) but I don't know what to write in them get access the data!
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.isValid() && role >= TitleRole) {
switch (role) {
case TitleRole:
return QVariant(titleString(rootItem(index))); // get title through rootItem?
case SummaryRole:
return QVariant(summaryString(rootItem(index))); // get summary through rootItem?
}
}
if (role != Qt::DisplayRole)
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->data(index.column());
}
The default.txthas the following data but the same is available in the Qt Creator demo itself as well.
Getting Started How to familiarize yourself with Qt Designer
Launching Designer Running the Qt Designer application
The User Interface How to interact with Qt Designer
Designing a Component Creating a GUI for your application
Creating a Dialog How to create a dialog
Composing the Dialog Putting widgets into the dialog example
Creating a Layout Arranging widgets on a form
Signal and Slot Connections Making widget communicate with each other
Using a Component in Your Application Generating code from forms
The Direct Approach Using a form without any adjustments
The Single Inheritance Approach Subclassing a form's base class
The Multiple Inheritance Approach Subclassing the form itself
Automatic Connections Connecting widgets using a naming scheme
A Dialog Without Auto-Connect How to connect widgets without a naming scheme
A Dialog With Auto-Connect Using automatic connections
Form Editing Mode How to edit a form in Qt Designer
Managing Forms Loading and saving forms
Editing a Form Basic editing techniques
The Property Editor Changing widget properties
The Object Inspector Examining the hierarchy of objects on a form
Layouts Objects that arrange widgets on a form
Applying and Breaking Layouts Managing widgets in layouts
Horizontal and Vertical Layouts Standard row and column layouts
The Grid Layout Arranging widgets in a matrix
Previewing Forms Checking that the design works
Using Containers How to group widgets together
General Features Common container features
Frames QFrame
Group Boxes QGroupBox
Stacked Widgets QStackedWidget
Tab Widgets QTabWidget
Toolbox Widgets QToolBox
Connection Editing Mode Connecting widgets together with signals and slots
Connecting Objects Making connections in Qt Designer
Editing Connections Changing existing connections
My output shows the same number of lines as in widgets demo except that there is no text. It seems like it is just not connected to the roles properly or roles are not connected to day. I am attaching the screen shot of my output.
Just so if anyone is trying to do the same thing, I figured it out and here is the answer. The data method of the model should read the following.
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (index.isValid() && role >= TitleRole) {
switch (role) {
case TitleRole:
return item->data(0);
case SummaryRole:
return item->data(1);
}
}
if (role != Qt::DisplayRole)
return QVariant();
return item->data(index.column());
}
TreeItem could be changed in real application to store specific data in which case the above method will point to that data instead of column number approach.
I derived a custem model from QAbstractItemModel and implemented drag&drop support by
overloading a flags() method that returns Qt::ItemIsDragEnabled where applicable,
overloading supportedDropActions and
implementing mimeData() to properly return a QMimeData object with some string set for the text/plain format.
When I setup this model in a QTreeView and enable dragging via setDragEnabled(true), I can pick items from the tree view and drag them. For example, I can drag an item and drop it onto a QLineEdit, which in turn will display the string that the model returned via mimeData().
However, when I drag an item and drop it onto another application's widgets (such as notepad or simply a second instance of my application) an empty string gets dropped.
I found another clue using the Drop site example shipped with Qt: When I drag an item over it, it correctly shows that there is text/plain available and it even displays the string associated with it. Yet, when I drop the item I get an empty string again in the dropEvent()...
Sourcecode
ssce.pro:
QT += core gui widgets
TARGET = ssce
TEMPLATE = app
SOURCES += main.cpp
main.cpp:
#include <QtCore>
#include <QtWidgets>
struct Model: public QAbstractItemModel {
int rowCount(const QModelIndex &parent) const {
return parent.isValid() ? 0 : 1;
}
int columnCount(const QModelIndex &parent) const {
return 1;
}
QModelIndex index(int row, int column, const QModelIndex &parent) const {
return parent.isValid() ? QModelIndex() : createIndex(row, column, static_cast<void *>(0));
}
QModelIndex parent(const QModelIndex &child) const {
return QModelIndex();
}
Qt::ItemFlags flags(const QModelIndex &index) const {
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
}
QVariant data(const QModelIndex &index, int role) const {
return (role == Qt::DisplayRole) ? "Test item" : QVariant();
}
QStringList mimeTypes() const {
return QStringList("text/plain");
}
QMimeData *mimeData(const QModelIndexList &indexes) const {
QMimeData *d = new QMimeData();
d->setText("hello world");
return d;
}
Qt::DropActions supportedDropActions() const {
return Qt::CopyAction;
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QDialog d;
QVBoxLayout l;
QLineEdit e;
l.addWidget(&e);
QTreeView v;
Model m;
v.setModel(&m);
v.setDragEnabled(true);
l.addWidget(&v);
d.setLayout(&l);
d.show();
return a.exec();
}
Description:
Run this and you can drag the item from the tree view into the line edit, it will display hello world then. Drop the item anywhere else, including the line edit of a second instance of the sample application, and nothing will happen.
How can I build the Layout used by QtCreator in Tools->Options for my own application? It should look like this, but with another content:
How can I do this using C++/Qt using the QtDesigner integrated in QtCreator?
I just found the solution! I needed to use a QListView with a QStringListModel in it. To set the Item Size and Icons manually, I needed to subclass QStringListModel and QStyledItemDelegate:
// ItemDelegate to set item size manually
class MyItemDelegate : public QStyledItemDelegate
{
public:
virtual QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QSize suggestion = QStyledItemDelegate::sizeHint(option, index);
return QSize(suggestion.width(), 30);
}
};
// StringListModel to allow setting images
class MyStringListModel : public QStringListModel
{
public:
virtual QVariant data (const QModelIndex &index, int role) const
{
if (role == Qt::DecorationRole)
{
QString value = stringList().at(index.row());
if (value == "new")
return add;
else
return db;
}
else
return QStringListModel::data(index, role);
}
private:
QIcon db = QIcon::fromTheme("server-database");
QIcon add = QIcon::fromTheme("list-add");
};
Then, I could update my QListView (that was created by Qt):
MyStringListModel * model = new MyStringListModel();
model->setStringList(QStringList() << "test" << "new");
ui->databases->setModel(model);
ui->databases->setItemDelegate(new MyItemDelegate());