QTableView, Model/View, QCheckBox
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:
Code:
#ifndef CBITEMDELEGATE_H
#define CBITEMDELEGATE_H
#include <QItemDelegate>
#include "checkbox.h"
#include <QTableView>
{
Q_OBJECT
public:
void setEditorData
(QWidget *editor,
void updateEditorGeometry
(QWidget *editor,
virtual bool editorEvent
(QEvent *event,
private:
private slots:
void slot_clicked(int row, Qt::CheckState cs);
signals:
void sig_clicked(int row, Qt::CheckState cs);
};
#endif //CBITEMDELEGATE_H
CBItemDelegate.cpp:
Code:
#include "cbitemdelegate.h"
#include <QApplication>
#include <QAbstractItemView>
#include <QtGui>
CBItemDelegate
::CBItemDelegate(QTableView *parent
){
my_table = parent;
}
CheckBox *editor = new CheckBox(index.row(), parent);
editor->installEventFilter(const_cast<CBItemDelegate*>(this));
editor->setFixedSize(24, 24);
editor->setFocusPolicy(Qt::StrongFocus);
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,
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,
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,
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,
bool data = index.model()->data(index, Qt::DisplayRole).toBool();
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;
}
bool CBItemDelegate
::editorEvent(QEvent *event,
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);
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?
Re: QTableView, Model/View, QCheckBox
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.