PDA

View Full Version : Custom Item Delegate (QDateEdit) and Size Hints



ChrisW67
27th May 2009, 09:13
G'day,

After a discussion in another thread I am experimenting with a custom delegate (QStyledItemDelegate) to handle "date" fields coming from an Sqlite database. I've built the delegate and, for the most part, got it working. I have called QTableView::resizeColumnsToContents() and set QHeaderView::setResizeMode(QHeaderView::ResizeToCo ntents) so the display mode has the date column resized to fit.

The problem comes when I go into edit mode. The text string ISO data coming from Sqlite is converted to QDate and passed to the QDateEdit control the delegate has set up to edit it. The editor is rendered in the QTableView cell but the spin box controls invariably overlay the right end of the date.

I am struggling to find how I can have the column resize to fit the QDateEdit spin box controls and still have the date displayed without truncation. I'm guessing that some magic is required in the delegate's size hint but cannot see how I can determine a good hint. There must be some way to get at the size of the edit control (no pointer to this is passed to sizeHint()) to add to the string size but I'm not familiar enough with the style options stuff to locate it.

Any nudges in the right direction would be greatly appreciated.
Chris W

QT4.5.1

wysota
27th May 2009, 10:03
You can try emitting a sizeHintChanged() signal from the delegate once you open the editor and reutrn a proper (bigger) size hint from the respective method in the delegate when asked for it.

ChrisW67
27th May 2009, 11:04
Thanks for the suggestion wysota.

I can't emit sizeHintChanged() or set a member variable to the sizeHint() of the editor while inside the createEditor() or setEditorData() methods because these actions violate the method const-ness. In the sizeHint() method itself I don't have access to the editor widget to ask for a "sane" sizeHint() to pass back, which is why I was looking to capture the size at the point of widget creation. I can do something like:

QSize s = QStyledItemDelegate::sizeHint(option, index);
s.setWidth(s.width() * 1.5);
return s; in the sizeHint() method and the cell does get bigger but the result is not related to the size the editor thinks it should be. Is there a better way to determine a good size?

wysota
27th May 2009, 17:14
I can't emit sizeHintChanged() or set a member variable to the sizeHint() of the editor while inside the createEditor() or setEditorData() methods because these actions violate the method const-ness.

But you can use invokeMethod() to schedule a delayed signal emission from a custom slot. Use "mutable" if necessary.

ChrisW67
28th May 2009, 00:36
Thanks again.

I'm still not quite there though. My createEditor() looks like:

QWidget * DateDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (index.data().canConvert(QVariant::Date)) {
QDateEdit *editor = new QDateEdit(parent);
editor->setDisplayFormat("dd MMM yyyy");

// Capture the size hint for later
DateDelegate *obj = const_cast<DateDelegate *>(this);
QSize size = editor->sizeHint();
QMetaObject::invokeMethod(obj, "captureSizeHint", Qt::QueuedConnection,
Q_ARG(QModelIndex, index),
Q_ARG(QSize, size) );

connect(editor, SIGNAL(editingFinished()),
this, SLOT(commitAndCloseEditor()));
return editor;
}
else
return QStyledItemDelegate::createEditor(parent, option, index);
} and the custome slot captureSizeHint():
void DateDelegate::captureSizeHint(const QModelIndex & index, QSize hint)
{
m_size = hint;
emit sizeHintChanged(index);
} The sizeHint() returns m_size if it is valid otherwise it just hands off to the default implementation.

The code works but the column does not resize until the editor is closed. Tracing the execution I see the editor size hint being captured at creation time, but the delegate's sizeHint() method is not called again until after the editor closes. Using Qt::DirectConnection makes no difference.

Forgive me if I'm missing the blindingly obvious but I am in the middle of a steep C++ and Qt learning curve.

wysota
28th May 2009, 07:50
If it doesn't work this way then there is no easy way of obtaining what you want. I would reimplement QAbstractItemView::edit() slot (you have to redeclare it as a slot in the subclass) and there I would do the trick with the sizeHintChanged() signal and only after the signal is emitted I would call the base class implementation of edit(). I can't guarantee it will work but there is a chance it will.

There is also an alternative - get rid of the arrows in the spinbox or set a smaller font for the editor.

ChrisW67
28th May 2009, 08:16
Thanks for your time. I'll ponder what to do over the next day or two.

ChrisW67
28th May 2009, 23:49
What I'm doing for now is creating a QDateEdit with the right display format on the stack in the delegate's constructor. I then store its sizeHint() in a member variable before it goes out of scope. The delegate's sizeHint() returns the cached size hint, and can do so the first time the table asks for it (in display mode).

This approach has some downsides in terms of allowing the user to set a date format in a QSettings value or the like: the delegate has to be reconstructed to pick up the change. This is not a show stopper for me though.