PDA

View Full Version : QTableView with model containing QComboBox and QCheckBox



karlkar
29th June 2013, 10:39
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


#ifndef DATAMODEL_H
#define DATAMODEL_H

#include <QAbstractTableModel>
#include <QStringList>

class DataModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit DataModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const ;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex & index) const ;

private:
QStringList headers_;
QString dataBase[8][7];

signals:
void editCompleted(const QString &);

public slots:

};

#endif // DATAMODEL_H

datamodel.cpp

#include "datamodel.h"
#include <QStringList>

DataModel::DataModel(QObject *parent) :
QAbstractTableModel(parent)
{
headers_ = QStringList();
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;
}
}
}

QVariant DataModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
{
return dataBase[index.row()][index.column()];
}
return QVariant();
}

bool DataModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
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
QString result;
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)
return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
else
return QAbstractTableModel::flags(index);
}


comboboxdelegate.h (name is confusing - it should be rather mytabledelegate as it supports both QComboBox and QCheckBox)

#ifndef COMBOBOXDELEGATE_H
#define COMBOBOXDELEGATE_H

#include <QStyledItemDelegate>

class ComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ComboBoxDelegate(QObject *parent = 0);

virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
QRect CheckBoxRect(const QStyleOptionViewItem &view_item_style_options) const;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;

private:

signals:

public slots:

};

#endif // COMBOBOXDELEGATE_H


comboboxdelegate.cpp

#include "comboboxdelegate.h"
#include <QComboBox>
#include <QCheckBox>
#include <QRect>
#include <QApplication>

ComboBoxDelegate::ComboBoxDelegate(QObject *parent) :
QStyledItemDelegate(parent)
{
}

QWidget* ComboBoxDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
// ComboBox ony in column 1
if(index.column() == 0) {

// Create the combobox and populate it
QComboBox *cb = new QComboBox(parent);
cb->addItem(QString("Produkt 1"));
cb->addItem(QString("Produkt 2"));
cb->addItem(QString("Produkt 3"));
return cb;
} else if (index.column() == 6) {
QCheckBox *cb = new QCheckBox(parent);
return cb;
} else
return QStyledItemDelegate::createEditor(parent, option, index);
}

void ComboBoxDelegate::setEditorData ( QWidget *editor, const QModelIndex &index ) const
{
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);
}
}

void ComboBoxDelegate::setModelData ( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
{
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 ComboBoxDelegate::CheckBoxRect(const QStyleOptionViewItem &view_item_style_options) const {

QStyleOptionButton check_box_style_option;
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());
}

void ComboBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {

if (index.column() == 6) {
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 = 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)