PDA

View Full Version : How to create a QStyledItemDelegate made of two QDoubleSpinBox widgets



abgeos
19th March 2013, 01:14
Hello,

I am trying to use an editor made of two QDoubleSpinBox when subclassing QStyledItemDelegate. I have no problem connecting the delegate and editor to the model but I failed to have my editor to show up in my view. I had no such problem with a simpler editor (e.g. a single QSpinBox).

Constructor for the Editor


Data_editor::Data_editor( QWidget *parent)
:QFrame(parent)
{
lower_bound_spin_ = new QDoubleSpinBox(this);
upper_bound_spin_ = new QDoubleSpinBox(this);

QHBoxLayout* layout = new QHBoxLayout(this);
layout->addWidget(new QLabel("Lower", this));
layout->addWidget(lower_bound_spin_);
layout->addStretch();
layout->addWidget(new QLabel("Upper", this));
layout->addWidget(upper_bound_spin_);

this->setLayout(layout);

this->setFocusPolicy( Qt::StrongFocus );
}


Implementation of the delegate


QWidget *Data_delegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const

{
Data_editor* editor = new Data_editor( parent);

return editor;

}

void Data_delegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
}


QSize Data_delegate::sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
return QStyledItemDelegate::sizeHint(option, index);

}

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


I played a bit with eventFilter and try to record the log of the events passing through


bool Data_delegate::eventFilter(QObject *object, QEvent *event)
{
Data_editor *editor = qobject_cast<Data_editor*>(object);
if (!editor)
return false;

QWidget *w_focused = QApplication::focusWidget();
QEvent::Type t = event->type();

std::string class_name = (w_focused!=0)?w_focused->metaObject()->className():std::string("000");
ostream_<<class_name <<" "<< QString("%1").arg(t).toStdString()+"\n";

if (event->type() == QEvent::KeyPress) {
switch (static_cast<QKeyEvent *>(event)->key()) {
case Qt::Key_Tab:
emit commitData(editor);
emit closeEditor(editor, QAbstractItemDelegate::EditNextItem);
return true;
case Qt::Key_Backtab:
emit commitData(editor);
emit closeEditor(editor, QAbstractItemDelegate::EditPreviousItem);
return true;
case Qt::Key_Enter:
case Qt::Key_Return:

QMetaObject::invokeMethod(this, "_q_commitDataAndCloseEditor",
Qt::QueuedConnection, Q_ARG(QWidget*, editor));
return false;
case Qt::Key_Escape:
// don't commit data
emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
break;
default:
return false;
}
if (editor->parentWidget())
editor->parentWidget()->setFocus();
return true;
} else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) {
//the Hide event will take care of he editors that are in fact complete dialogs
//if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
if (!editor->isActiveWindow() || (w_focused != editor)) {
QWidget *w = w_focused;
//QWidget *w = QApplication::focusWidget();
while (w) { // don't worry about focus changes internally in the editor
if (w == editor)
return false;
w = w->parentWidget();
}
emit commitData(editor);
emit closeEditor(editor, NoHint);
}
} else if (event->type() == QEvent::ShortcutOverride) {
if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
event->accept();
return true;
}
}
return false;
}


Which output (first column widget className, second Qcolumn Event type):
Data_tree_view 75
Data_tree_view 70
Data_tree_view 70
Data_tree_view 70
Data_tree_view 70
Data_tree_view 69
Data_tree_view 69
Data_tree_view 69
Data_tree_view 69
Data_tree_view 13
Data_tree_view 14
Data_tree_view 17
Data_tree_view 26
Data_editor 8
Data_editor 67
Data_editor 74
Data_editor 12
Data_editor 10
Data_editor 11
Data_editor 12
Data_tree_view 9

The editor is painted (event 12) but is never shown. The last event (which probably occurred when I moved the cursor) is the FocusOut event which commit the data and close the editor.

So what is missing in my delegate/editor such the editor will be shown in my view? Online comments seem to point out to a focus issue but I cannot get it right.

Thanks

ChrisW67
19th March 2013, 03:38
The default behaviour will be looking to commit and close the editor when the widget you provided as the editor loses focus. I expect that widget will lose focus immediately when the focus shifts into one of its child spin boxes. You should look specifically at that event and whether you need to block it from propagating.

Cannot say I have done a lot of event filtering myself so I will be interested in the other responses.

abgeos
19th March 2013, 16:16
Thanks,

It is the first time that I work explicitly with the child-parent relationship of widgets as well as with event filtering. So this is not obvious to me.

I was under the impression that the eventFilter member function of the delegate would have taken care of the out of focus event within a child of the delegate widget (line 45 of the last code snippet in my original post):


QWidget *w = QApplication::focusWidget();
while (w) { // don't worry about focus changes internally in the editor
if (w == editor)
return false;
w = w->parentWidget();
}

It seems that this is not the case. I'll run more tests.

Thanks

norobro
19th March 2013, 19:45
. . . I failed to have my editor to show up in my view.
Try setting the layout margins in Data_editor to 0:
layout->setMargin(0);

abgeos
19th March 2013, 20:56
norobro: Right on!

Thank you so much I would have never tried that. If you have a minute would you care to explain why it works?

The editor show up nicely with the two QDoubleSpinBox and I can interact with them at will. However the display of the model remains visible while the editor is active. Any idea how to remove, blank or hide the model display for that item while the editor is active.

Thanks.

norobro
19th March 2013, 21:48
You're welcome. According to the docs QLayout::setMargin() is obsolete so you should use QLayout::setContentsMargins() instead.

I encountered this once upon a time and as I recall the row height for a QTreeView with the default font (9 pt) on my system is 13 and the default margin is 11. The upper and lower margins take up more space than is available on the row so there is no space to paint the layout contents. Try incrementing the margins up from zero and I think you will see your spinboxes disappearing.



Any idea how to remove, blank or hide the model display for that item while the editor is active.
Put the following statement in the Data_editor ctor:
setAutoFillBackground(true);

abgeos
20th March 2013, 17:50
Thanks for the explanation. Everything is now working properly.

sunny27710
28th January 2014, 16:21
Hey abgeos,

can you post your source code? I am wanting to do a slider and spinbox instead of 2 spinbox and having some issues.

Appreciate your help!
Thanks,
Sunny