QTableView with model containing QComboBox and QCheckBox
Hi!
I want to make a table which contains in first column QComboBox and in last QCheckBox, but centered in a cell and without text.
I've followed few topics and everywhere the behaviour of such components is strange - sometimes QComboBox/QCheckBox is invisible - it appears only after DOUBLE clicking the cell where it should be, or it appears all the time, but still double click is needed.
Can it be done better? By better I mean showing control all over the time and let it react after single click?
And the other thing I am thinking about:
I want QComboBox to load data to the rest of its row cells. So checking option in QComboBox sets the values of the whole row, allowing user to change only one cell. Should I make it in TableView? This was my first idea, but now I am thinking about creating my own row that has nothing in common with QTableView and propagate it x times on my layout. One thing - the data will be finally exported to csv file. If I should stay with QComboBox can you tell me how to set other cells in a row after choosing QComboBox option? By signal - slot mechanism or in model's function?
Here is my current code, where the QCheckBox doesn't appear in the last column (never - even after double clicking a cell) and QComboBox shows up after double click.
datamodel.h
Code:
#ifndef DATAMODEL_H
#define DATAMODEL_H
#include <QAbstractTableModel>
#include <QStringList>
{
Q_OBJECT
public:
explicit DataModel
(QObject *parent
= 0);
QVariant headerData
(int section, Qt
::Orientation orientation,
int role
) const;
private:
signals:
void editCompleted
(const QString &);
public slots:
};
#endif // DATAMODEL_H
datamodel.cpp
Code:
#include "datamodel.h"
#include <QStringList>
DataModel
::DataModel(QObject *parent
) :{
headers_ << "Nazwa, opis" << "Kod towaru" << "Ilość" << "JM" << "Cena netto" << "Wartość netto" << "Refaktura";
}
int DataModel
::rowCount(const QModelIndex & /*parent*/) const {
return 8;
}
int DataModel
::columnCount(const QModelIndex & /*parent*/) const {
return 7;
}
QVariant DataModel
::headerData(int section, Qt
::Orientation orientation,
int role
) const {
if (orientation == Qt::Horizontal) {
if( role == Qt::DisplayRole) {
return headers_.at(section);
}
} else {
if( role == Qt::DisplayRole) {
return section + 1;
}
}
}
{
if (role == Qt::DisplayRole)
{
return dataBase[index.row()][index.column()];
}
}
{
if (role == Qt::EditRole)
{
//save value from editor to member m_gridData
dataBase[index.row()][index.column()] = value.toString();
//for presentation purposes only: build and emit a joined string
for(int row= 0; row < 8; row++)
{
for(int col= 0; col < 7; col++)
{
result += dataBase[row][col] + " ";
}
}
emit editCompleted( result );
}
return true;
}
Qt
::ItemFlags DataModel
::flags(const QModelIndex &index
) const{
if (index.column() == 0 || index.column() == 2 || index.column() == 6)
else
}
comboboxdelegate.h (name is confusing - it should be rather mytabledelegate as it supports both QComboBox and QCheckBox)
Code:
#ifndef COMBOBOXDELEGATE_H
#define COMBOBOXDELEGATE_H
#include <QStyledItemDelegate>
class ComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ComboBoxDelegate
(QObject *parent
= 0);
private:
signals:
public slots:
};
#endif // COMBOBOXDELEGATE_H
comboboxdelegate.cpp
Code:
#include "comboboxdelegate.h"
#include <QComboBox>
#include <QCheckBox>
#include <QRect>
#include <QApplication>
ComboBoxDelegate
::ComboBoxDelegate(QObject *parent
) : QStyledItemDelegate(parent)
{
}
{
// ComboBox ony in column 1
if(index.column() == 0) {
// Create the combobox and populate it
return cb;
} else if (index.column() == 6) {
return cb;
} else
return QStyledItemDelegate::createEditor(parent, option, index);
}
{
if(QComboBox *cb
= qobject_cast<QComboBox
*>
(editor
)) { // get the index of the text in the combobox that matches the current value of the itenm
QString currentText
= index.
data(Qt
::EditRole).
toString();
int cbIndex = cb->findText(currentText);
// if it is valid, adjust the combobox
if(cbIndex >= 0)
cb->setCurrentIndex(cbIndex);
} if (QCheckBox *cb
= qobject_cast<QCheckBox
*>
(editor
)) { int val = index.data(Qt::DisplayRole).toInt();
if (val == 1)
cb->setCheckState(Qt::Checked);
else
cb->setCheckState(Qt::Unchecked);
} else {
QStyledItemDelegate::setEditorData(editor, index);
}
}
{
if(QComboBox *cb
= qobject_cast<QComboBox
*>
(editor
)) // save the current text of the combo box as the current value of the item
model->setData(index, cb->currentText(), Qt::EditRole);
else if (QCheckBox *cb
= qobject_cast<QCheckBox
*>
(editor
)) { int value;
if (cb->checkState())
value = 1;
else
value = 0;
model->setData(index, value);
} else
QStyledItemDelegate::setModelData(editor, model, index);
}
QRect check_box_rect
= QApplication::style()->subElementRect
( QStyle::SE_CheckBoxIndicator,
&check_box_style_option
);
QPoint check_box_point
(view_item_style_options.
rect.
x() + view_item_style_options.
rect.
width() / 2 - check_box_rect.
width() / 2,
view_item_style_options.rect.y() + view_item_style_options.rect.height() / 2 -
check_box_rect.height() / 2);
return QRect(check_box_point, check_box_rect.
size());
}
if (index.column() == 6) {
int value = index.model()->data(index, Qt::DisplayRole).toInt();
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 = ComboBoxDelegate::CheckBoxRect(option);
QApplication::style()->drawControl
(QStyle::CE_CheckBox,
&check_box_style_option, painter
);
} else
QStyledItemDelegate::paint(painter, option, index);
}
Looks like the problem with showing QCheckBox is that index is always invalid, so it never steps into the condition (index.column() == 6)