PDA

View Full Version : QListView word wrap



serega
28th May 2007, 18:28
Hi All,
my first on this forum is regarding QListView.
I have strings of arbitrary length in my model and I want them to wrap in a QListView when the length of the string is longer than the width of the QListView.
QListView::setWordWrap(true) method does not seem to do anything at all.
I created a custom QItemDelegate and reimplemented sizeHint(). I had to make QListView a parent of QItemDelegate, so I can get a width of the QListView.
It did help though. When I drag a border of QListView and make the width smaller, the height of the item increases, based on sizeHint() and the string automatically wraps to the next line. Now when I drag a border of QListView to the other side and increase the width, the strings unwrap, but the height of the item stay the same, leaving empty space. The height of the item area only grows, but never gets smaller despite of the returning value of sizeHint().

Is the any way to fix this behaviour?

serega.

marcel
28th May 2007, 18:44
Of course it is.
Could we see your implementation of sizeHint?

serega
28th May 2007, 19:01
Here it is:


QSize PartOfSpeechItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QAbstractItemView *view = dynamic_cast<QAbstractItemView*>(parent());
QFontMetrics fontMetrics = view->fontMetrics();
QRect rect = fontMetrics.boundingRect(index.data().toString());
int proportion = (rect.width() / (view->width() + 4));
if (proportion == 0 || rect.width() > view->width())
proportion++;

return QSize(view->width() - 4, rect.height() * proportion);
}


serega.

marcel
28th May 2007, 19:31
The code seems OK.
Have you tried setting the list view's resize mode to QListView::Adjust, with setResizeMode?

This causes the items to be laid out again at resize.
You might be experiencing this due to items position/size caching.

Regards

serega
29th May 2007, 01:02
I already had resizeMode set to Adjust.

I also noticed that it works when there is enough items for a vertical scrollbar.:)

But when the QListView is not fully filled, and does not show the scrollbar the height of individual items only grows when QListView gets squeezed. Enlarging QListView does not change the height of items.

serega.

marcel
29th May 2007, 04:49
What if you override the paint() for QItemDelegate too?
Then you have complete control in this aspect.
Shouldn't be that hard, because you're drawing only text, after all.

Regards

serega
29th May 2007, 13:14
Yes, I will try that. But I am wondering..., I can paint on the area specified by the QStyleOptionViewItem.rect property, which I cannot control directly. I can only provide sizeHint().


serega.

marcel
29th May 2007, 13:21
sizeHint means the size the item prefers to have.
There is no guarantee that it will have that size - but most of the times will.

The style option rect will be identical with the sizeHint, as it represents the current size of the item.

Regards

serega
30th May 2007, 12:44
sizeHint means the size the item prefers to have.
There is no guarantee that it will have that size - but most of the times will.
[/QUOTE/
That is the problem, there is no quarantee.
[QUOTE]
The style option rect will be identical with the sizeHint, as it represents the current size of the item.

I verified and that is true, but the area that QListView allocates for items does not always match the sizeHint(). As you pointed out before there is probably some caching going on. May be I can subclass QListView and do something there...

serega.

serega
31st May 2007, 03:42
I subclassed QListView and reimplemented resizeEvent().
I simply call reset() when new width is greater than the old width of the QListView.
It seems to be a brute solution, but it fixed the problem.

serega.

jiveaxe
4th August 2007, 14:40
Here it is:


QSize PartOfSpeechItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QAbstractItemView *view = dynamic_cast<QAbstractItemView*>(parent());
QFontMetrics fontMetrics = view->fontMetrics();
QRect rect = fontMetrics.boundingRect(index.data().toString());
int proportion = (rect.width() / (view->width() + 4));
if (proportion == 0 || rect.width() > view->width())
proportion++;

return QSize(view->width() - 4, rect.height() * proportion);
}


serega.

what is the parent() function on line 3 in the above code? Maybe an error?

Thanks

marcel
4th August 2007, 14:46
It is the parent list view.
When you set the delegate with QAbstractItemView::setItemDelegate, the view takes ownership of the delegate and reparents it.

What you see in that code is actually QObject::parent().

Regards

jiveaxe
4th August 2007, 14:56
Is this equivalent?


QSize PartOfSpeechItemDelegate::sizeHint(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QAbstractItemView *view = dynamic_cast<QAbstractItemView*>(parent);
QFontMetrics fontMetrics = view->fontMetrics();
QRect rect = fontMetrics.boundingRect(index.data().toString());
int proportion = (rect.width() / (view->width() + 4));
if (proportion == 0 || rect.width() > view->width())
proportion++;

return QSize(view->width() - 4, rect.height() * proportion);
}

marcel
4th August 2007, 15:05
Is PartOfSpeechItem delegate a QAbstractItemDelegate?
Because the signature of QAbstractItemDelegate::sizeHint differs from your implementation. It does not have a QWidget* parameter.


Regards

jiveaxe
4th August 2007, 15:15
I was trying using this sizeHint() code to solve my problem (http://www.qtcentre.org/forum/f-qt-programming-2/t-word-wrap-in-qlistview-8371.html). Using this code like it is give me an error (‘parent’ was not declared in this scope) while changing the signature like in my previous post, program compile without error, even if it doesn't solve the problem.

Bye

marcel
4th August 2007, 15:28
Well, I don't know if it is a typo, but in your code should be parent() not parent.
You must call the function. There is not parent object.

Regards

jiveaxe
4th August 2007, 15:45
Sorry marcel if i ask you in this manner, but can you review the sizeHint() in this post (http://www.qtcentre.org/forum/p-word-wrap-in-qlistview-post44841/postcount4.html) and explain me a way for changing row height if text wraps ? (with that code the rows height are always one line independently if text wraps, and the wrapped text overlaps the rows below).

Regards

serega
30th August 2007, 03:13
I haven't played with my application for a while, now I came back to it and to sizeHint(). Below is my current implementation, which provides may be not perfect, but very good results.
There was some questions above regarding parent(). Ideally QAbstractItemDelegate do not need to know about QAbstractItemView, but in my case I have to set the view as a parent of the delegate because I need the width of the view to determine the height.



QSize PartOfSpeechItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QAbstractItemView *view = dynamic_cast<QAbstractItemView*>(parent());
QFontMetricsF fontMetrics(view->fontMetrics());
QString str = index.data().toString().trimmed();
QStringList words = str.split(QChar(' '));
QRectF boundingRect = fontMetrics.boundingRect(str);
int width = view->viewport()->width() - 6;
int times = 0;
while (words.size() > 0) {
times++;
qreal lineWidth = 0;
bool enoughSpace = true;
do {
QString word = words.first();
qreal wordWidth = fontMetrics.width(word);
if (wordWidth + lineWidth < width) {
lineWidth += wordWidth;
lineWidth += fontMetrics.width(QChar(' '));
words.removeFirst();
} else
enoughSpace = false;

} while (enoughSpace && words.size() > 0);
}
return QSize(width, boundingRect.height() * times - times + 1);
}

void PartOfSpeechItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QRect rect(option.rect.topLeft(), option.rect.bottomRight());
if (option.state & QStyle::State_Selected)
painter->fillRect(rect, option.palette.highlight());

painter->save();
painter->setPen(QPen(Qt::gray));
painter->drawLine(rect.topLeft(), rect.topRight());
painter->restore();

rect.setLeft(rect.left() + 4);
rect.setTop(rect.top() + 2);
rect.setRight(rect.right() - 2);
painter->drawText(rect, Qt::TextWordWrap , index.data().toString());
}




My view:


void PartOfSpeechItemView::resizeEvent(QResizeEvent *resizeEvent)
{
reset();
QListView::resizeEvent(resizeEvent);
}


See the attachment how it looks like.

serega.