PDA

View Full Version : QTableView, Model/View, QCheckBox



lxman
15th July 2011, 00:39
I have subclassed QItemDelegate to display a column of QCheckBox items for boolean values. I am using a bit of manipulation, but things are working out the way that I want them. I get a column of checkboxes that are centered in the cells and when I click on one, the appearance changes ever so slightly and the check state changes immediately without having to click to bring up the editor then click again to change the state.

Here is my code to accomplish this.

CBItemDelegate.h:


#ifndef CBITEMDELEGATE_H
#define CBITEMDELEGATE_H

#include <QItemDelegate>
#include "checkbox.h"
#include <QTableView>

class CBItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit CBItemDelegate(QTableView *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;
virtual bool editorEvent(QEvent *event,
QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index);

private:
QTableView *my_table;

private slots:
void slot_clicked(int row, Qt::CheckState cs);

signals:
void sig_clicked(int row, Qt::CheckState cs);

};

#endif //CBITEMDELEGATE_H


CBItemDelegate.cpp:


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

CBItemDelegate::CBItemDelegate(QTableView *parent)
: QItemDelegate(parent)
{
my_table = parent;
}

QWidget *CBItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &,
const QModelIndex &index) const {
CheckBox *editor = new CheckBox(index.row(), parent);
editor->installEventFilter(const_cast<CBItemDelegate*>(this));
editor->setFixedSize(24, 24);
editor->setFocusPolicy(Qt::StrongFocus);
editor->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
connect(editor, SIGNAL(pressed(int,Qt::CheckState)),
this, SLOT(slot_clicked(int,Qt::CheckState)),
Qt::DirectConnection);
QRect rect = my_table->visualRect(index);
QPoint draw_at(rect.left() + (rect.width() / 2) - (editor->width() / 2),
rect.top() + (rect.height() / 2) - (editor->height() / 2));
editor->move(draw_at);
return editor;
}

void CBItemDelegate::slot_clicked(int row, Qt::CheckState cs) {
emit sig_clicked(row, cs);
}

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

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

void CBItemDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const {
CheckBox *checkBox = static_cast<CheckBox*>(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 {
QRect r;
r.setSize(QSize(24, 24));
r.moveTo((option.rect.width() / 2) - (editor->width() / 2),
(option.rect.height() / 2) - (editor->height() / 2));
editor->setGeometry(r);
QRect rect = my_table->visualRect(index);
QPoint draw_at(rect.left() + (rect.width() / 2) - (editor->width() / 2),
rect.top() + (rect.height() / 2) - (editor->height() / 2));
editor->move(draw_at);
}

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);
}

bool CBItemDelegate::editorEvent(QEvent *event,
QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index) {
Q_ASSERT(event);
Q_ASSERT(model);

// make sure that the item is checkable
Qt::ItemFlags flags = model->flags(index);
if (!(flags & Qt::ItemIsUserCheckable) || !(flags & Qt::ItemIsEnabled))
return false;
// make sure that we have a check state
QVariant value = index.data(Qt::CheckStateRole);
if (!value.isValid())
return false;
// make sure that we have the right event type
if (event->type() == QEvent::MouseButtonRelease) {
const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
QRect checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
check(option, option.rect, Qt::Checked).size(),
QRect(option.rect.x() + textMargin, option.rect.y(),
option.rect.width() - (2 * textMargin), option.rect.height()));
if (!checkRect.contains(static_cast<QMouseEvent*>(event)->pos()))
return false;
} else if (event->type() == QEvent::KeyPress) {
if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
&& static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
return false;
} else {
return false;
}
Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
? Qt::Unchecked : Qt::Checked);

QMessageBox::information(0,
QString((state == Qt::Checked) ? "Qt::Checked" : "Qt::Unchecked"),
QString("[%1/%2]").arg(index.row()).arg(index.column()));

return model->setData(index, state, Qt::CheckStateRole);
}


All of this works well. Now, however, I need to get a clicked() signal from the checkboxes in a column. Try as I might, I cannot seem to figure out where to go to lock in on this one.

As you may note, I have also subclassed QCheckBox as well attempting to manipulate the signals/slots from there - no joy.

You may also note in the createEditor method that I connect a signal from the created editor, from which I get one signal, and then nothing else. I am just blind guessing, but it seems that maybe the view makes a copy of the editor and then destroys the original thus breaking the signal/slot connection(?)

I really don't know. Could someone help me figure out where to look to get some sort of clicked() signal from these checkbox delegates? It doesn't even matter to me what information is passed. When I get the clicked signal, I can go find the information I need. I just need a clicked() (or pressed() etc.) signal that works.

Any ideas? Or am I attacking this in a basically incorrect fashion, perhaps?

lxman
15th July 2011, 04:45
Okay, found a way.

Note line 16 of CBItemDelegate.cpp

I derived a custom event filter and installed it to catch the mouse events. From there I am able to draw the information out and do what I need.