PDA

View Full Version : QTableView, Model/View, QCheckBox



lxman
6th July 2011, 00:31
I have been re-writing this project from the bottom up to "correctly" use the Model/View framework. I have a database with several tables. I have set up two views in a QMainWindow, along with a QSqlTableModel for each table I need, and when I need, I just swap a view to a different model and reconfigure columns, etc. That is all working out nicely. I have also managed to subclass QStyledItem to create a combobox to present a list of text entries, display the chosen text entry in the cell, but store its corresponding "id" number in the backend table/database.

I am now attempting to set up some columns with QCheckBox for boolean values, and I am close, but not quite there.

Here's what I have.

cbitemdelegate.h


#ifndef CBITEMDELEGATE_H
#define CBITEMDELEGATE_H

#include <QItemDelegate>

class CBItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit CBItemDelegate(QObject *parent = 0);
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;
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index) const;
void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const;
};

#endif //CBITEMDELEGATE_H


cbitemdelegate.cpp


#include "cbitemdelegate.h"
#include <QApplication>
#include <QtGui>

CBItemDelegate::CBItemDelegate(QObject *parent)
: QItemDelegate(parent)
{
}

QWidget *CBItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const {
QCheckBox *editor = new QCheckBox(parent);
editor->installEventFilter(const_cast<CBItemDelegate*>(this));
return editor;
}

void CBItemDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const {
int value = index.model()->data(index, Qt::DisplayRole).toInt();

QCheckBox *checkBox = static_cast<QCheckBox*>(editor);
if(value == 1)
checkBox->setCheckState(Qt::Checked);
else
checkBox->setCheckState(Qt::Unchecked);
}

void CBItemDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const {
QCheckBox *checkBox = static_cast<QCheckBox*>(editor);
int value;
if(checkBox->checkState() == Qt::Checked)
value = 1;
else
value = 0;

model->setData(index, value);
}

void CBItemDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index) const {
editor->setGeometry(option.rect);
}

void CBItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const {
bool data = index.model()->data(index, Qt::DisplayRole).toBool();
QStyleOptionButton checkboxstyle;
QRect checkbox_rect = QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle);
checkboxstyle.rect = option.rect;
checkboxstyle.rect.setLeft(option.rect.x() + option.rect.width()/2 - checkbox_rect.width()/2);
if(data)
checkboxstyle.state = QStyle::State_On|QStyle::State_Enabled;
else
checkboxstyle.state = QStyle::State_Off|QStyle::State_Enabled;

QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkboxstyle, painter);
}


I then create an instance to a CBItemDelegate, and use setItemDelegateForColumn to install it to the view.

When I first bring the table up, it looks wonderful, all of the checkboxes are checked/unchecked appropriately, and they are all centered horizontally and vertically in their cells, thanks to the paint procedure.

Then when I click to edit the cells things start to go wrong. The checkbox jumps to the side of the cell, it responds, it is just on the side. I presume it is because in the createEditor function I am creating an actual QCheckBox with its attached (blank) QLabel, and that throws things off. Could somebody please help me past this point?

lxman
6th July 2011, 16:52
Bump.

Could someone verify that I am taking the "proper" approach with what I am doing here?

I have experimented with moving the widget in the updateEditorGeometry call, but when I do, it seems that the coordinate system suddenly switches from the cell the item is in, to the table the item is in (i.e. if I move the widget in the third row to (10, 2) it does move, but to (10, 2) relative to the view window, down in cell 0, 0). I could set up to calculate the proper global position based on row/column, etc. But it seems to me that there ought to be a cleaner way.

d_stranz
6th July 2011, 19:16
Won't QAbstractItemView::visualRect( const QModelIndex & ) return you the rect you need, without having to compute the location yourself? The table cells themselves aren't widgets, and when the editor is created it is positioned relative to the table (likely using visualRect()).

lxman
6th July 2011, 21:47
Yep, that did the trick! Thanks d_stranz. For the record, I couldn't call QAbstractItemView::visualRect(const QModelIndex &) as a static function (which I tried first). So in the constructor for the delegate, I am passing in the QTableView instance as parent, then I can call visualRect(const QModelIndex &) as I need to on it.

Works beautifully!

d_stranz
6th July 2011, 23:19
I didn't mean to imply that QAbstractItemView::visualRect() was a static method; I gave the full declaration so you would know where it could be found in the class inheritance hierarchy.

lxman
7th July 2011, 01:53
And my implication wasn't meant to imply that you were implying that QAbstractItemView::visualRect() was a static method. :D I was the one who misunderstood. I just put a note so that any other newbie's like myself might be able to avoid the same mistake. :o That's all.

Again, thanks much for the help.