PDA

View Full Version : QTreeWidgetItem - widget with layout collapsing



enkidu
25th July 2009, 20:03
Hello! I have troubles with creating userlist in my IM. Because I need to display some data in one Item. I decided to embed QFrame into widget, make layout and insert labels into that layout. Here is code of my function used to create item (pointer is returned, needed by userlist manager)


QTreeWidgetItem* QTlenRosterBox::addRosterItem(QString name,
QTlenPresence type,
QString desc,
QString jid,
QPixmap pxAvatar,
QTreeWidgetItem* node)
{
QTreeWidgetItem* item = new QTreeWidgetItem(node);
QFrame* frame = new QFrame(this);
QHBoxLayout* hLayout = new QHBoxLayout(frame);
hLayout->setAlignment(Qt::AlignTop);
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setAlignment(Qt::AlignTop);
QLabel* statusIcon = new QLabel(frame);
statusIcon->setFixedSize(16,16);
switch (type)
{
case Online:
statusIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/icons/16x16/online.png")));
break;
case Chatty:
statusIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/icons/16x16/chatty.png")));
break;
case Away:
statusIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/icons/16x16/away.png")));
break;
case XA:
statusIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/icons/16x16/xa.png")));
break;
case DND:
statusIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/icons/16x16/dnd.png")));
break;
case Offline:
statusIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/icons/16x16/offline.png")));
break;
}

hLayout->addWidget(statusIcon);
hLayout->addLayout(vLayout);
QLabel* nameLabel = new QLabel(name, frame);
QFont nameFont;
nameFont.setBold(true);
nameLabel->setFont(nameFont);
vLayout->addWidget(nameLabel);
if (!desc.isEmpty() && settings->value("/roster/show_descriptions", false).toBool())
{
QLabel* descLabel = new QLabel(desc, frame);
QFont descFont;
descFont.setPointSize(7);
descFont.setItalic(true);
descLabel->setWordWrap(true);
descLabel->setFont(descFont);
vLayout->addWidget(descLabel);
}
//prowizorka
if(settings->value("/roster/show_avatars", false).toBool())
{
QLabel* avatar = new QLabel();
if(pxAvatar.isNull())
avatar->setPixmap(QPixmap(QString::fromUtf8(":/icons/icons/64x64/default_av.png")));
else
avatar->setPixmap(pxAvatar);
avatar->setAlignment(Qt::AlignRight);
hLayout->addWidget(avatar);
}
frame->setLayout(hLayout);
setItemWidget(item, 0, frame);
return item;
}

and here is roster with only one item visible and with two visible; height is calculated properly, but layout is collapsed.

how to fix that in best way?
thanks in advance.

wysota
25th July 2009, 20:17
The best way is not to embed widgets but use delegates or at least report a proper size hint from the items.

And please don't link images from 3-rd party sites, they tend to vanish over time making the thread useless. Attach images to your post instead.

enkidu
25th July 2009, 20:33
so I should try with QTreeView and QItemDelegate? I wanted to avoid that.

Images are located on my server, so wont disappear without my knowledge ;)

wysota
25th July 2009, 20:43
so I should try with QTreeView and QItemDelegate? I wanted to avoid that.
You can keep using QTreeWidget but you should implement your own item delegate.


Images are located on my server, so wont disappear without my knowledge ;)

But they will disappear without my knowledge. I don't care if you remove them willingly or not, it's a fact that if they disappear, others won't benefit from this thread.

enkidu
9th August 2009, 23:50
After making some other task I decided to reimplement item delegate. I modified StarDelegate example and finally got this:
qtlenrosteritemdelegate.hpp

#ifndef QTLENROSTERITEMDELEGATE_HPP
#define QTLENROSTERITEMDELEGATE_HPP
#include <QtGui>
#include "qtlenrosteritem.hpp"
class QTlenRosterItemDelegate: public QStyledItemDelegate
{
Q_OBJECT
public:
QTlenRosterItemDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {}
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const;

};

#endif // QTLENROSTERITEMDELEGATE_HPP
qtlenrosteritemdelegate.cpp

void QTlenRosterItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (qVariantCanConvert<QTlenRosterItem>(index.data())) {
QTlenRosterItem rosterItem = qVariantValue<QTlenRosterItem>(index.data());
bool checked = false;
if (option.state & QStyle::State_Selected)
{
painter->fillRect(option.rect, option.palette.highlight());
checked = true;
}
rosterItem.setHeight(rosterItem.paint(painter, option.rect, option.palette, checked));
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}

QSize QTlenRosterItemDelegate::sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (qVariantCanConvert<QTlenRosterItem>(index.data())) {

QTlenRosterItem rosterItem = qVariantValue<QTlenRosterItem>(index.data());
return rosterItem.sizeHint(option);
} else {
return QStyledItemDelegate::sizeHint(option, index);
}
}
qtlenrosteritem.hpp

#ifndef QTLENROSTERITEM_HPP
#define QTLENROSTERITEM_HPP
#include "defines.h"
#include <QtGui>
class QTlenRosterItem
{
public:
QTlenRosterItem(QString name = "", QTlenPresence type = Offline, QString desc="", QString jid="", QPixmap pxAvatar=QPixmap());
int paint(QPainter *painter, const QRect &rect,
const QPalette &palette, bool checked) const;
QSize sizeHint(const QStyleOptionViewItem &option) const;
int height;
void setHeight(int);
private:
QString name;
QPixmap presence;
QString desc;
QString jid;
QPixmap avatar;
};
Q_DECLARE_METATYPE(QTlenRosterItem)
#endif // QTLENROSTERITEM_HPP
qtlenrosteritem.cpp

#include "qtlenrosteritem.hpp"

QTlenRosterItem::QTlenRosterItem(QString name, QTlenPresence type, QString desc, QString jid, QPixmap pxAvatar)
{
this->name = name;
this->desc = desc;
this->jid = jid;
this->avatar = pxAvatar;
height = 68;
switch (type)
{
case Online:
presence=QPixmap(QString::fromUtf8(":/icons/icons/16x16/online.png"));
break;
case Chatty:
presence=QPixmap(QString::fromUtf8(":/icons/icons/16x16/chatty.png"));
break;
case Away:
presence=QPixmap(QString::fromUtf8(":/icons/icons/16x16/away.png"));
break;
case XA:
presence=QPixmap(QString::fromUtf8(":/icons/icons/16x16/xa.png"));
break;
case DND:
presence=QPixmap(QString::fromUtf8(":/icons/icons/16x16/dnd.png"));
break;
case Offline:
presence=QPixmap(QString::fromUtf8(":/icons/icons/16x16/offline.png"));
break;
}
}

int QTlenRosterItem::paint(QPainter *painter, const QRect &rect, const QPalette &palette, bool checked) const
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
if (checked) painter->setBrush(palette.highlightedText());
else
painter->setBrush(palette.foreground());
//name
QRect nameRect;
nameRect.setLeft(rect.left()+20);
nameRect.setRight(rect.right()-68);
nameRect.setTop(rect.top()+2);
nameRect.setBottom(rect.bottom()-2);
QRect *nameBR = new QRect;
QFont nameFont;
nameFont.setBold(true);
painter->setFont(nameFont);
painter->drawText(nameRect, (int)(Qt::AlignLeft | Qt::TextWordWrap), name, nameBR);
//jid
QRect jidRect;
jidRect.setLeft(rect.left()+20);
jidRect.setRight(rect.right()-68);
jidRect.setTop(nameBR->bottom()+2);
jidRect.setBottom(nameBR->bottom()+18);
QRect *jidBR = new QRect;
nameFont.setBold(false);
painter->setFont(nameFont);
painter->drawText(jidRect, (int)(Qt::AlignLeft | Qt::TextWordWrap), jid, jidBR);
//desc
QRect descRect;
descRect.setLeft(rect.left()+20);
descRect.setRight(rect.right()-68);
descRect.setTop(jidBR->bottom()+2);
descRect.setBottom(rect.bottom()-2);
QRect *descBR = new QRect;
QFont descFont;
descFont.setPointSize(7);
descFont.setItalic(true);
painter->setFont(descFont);
painter->drawText(descRect, (int)(Qt::AlignLeft | Qt::TextWordWrap), desc, descBR);
int textHeight = nameBR->height() + jidBR->height() + descBR->height()+8;
int theight;
if (textHeight > 68)
theight = textHeight;
else
theight = 68;
//setHeight(68);
painter->drawPixmap(rect.left()+2, rect.top()-8+rect.height()/2,16, 16, presence);
painter->drawPixmap(rect.right()-66, rect.top()+2, 64, 64, avatar);
painter->restore();
return theight;
}

QSize QTlenRosterItem::sizeHint(const QStyleOptionViewItem &option) const
{
qDebug(QByteArray::number(option.rect.right()));
QFont descFont;
descFont.setPointSize(7);
descFont.setItalic(true);
QFontMetrics fm(descFont);
QRect rect = fm.boundingRect(0,0,80,160, (int)(Qt::AlignLeft | Qt::TextWordWrap), desc);
int h = rect.height();
if ((h + 30) < 68)
return QSize(160, 68);
return QSize(160, h+30);
}

void QTlenRosterItem::setHeight(int h)
{
height = h;
}

What I found, that in calculating SizeHint option.rect is not providing width, so I cannot calculate width of text blocks properly. This results in improper calculated height (only few pixels fortunately). Other thing, that is strange for me (maybe I'm misunderstanding docs) is that painter->setBrush(palette.highlightedText()); doesn't work as expected - text is still black (as if I used painter->setBrush(palette.foreground());) while for selected items should be white (or other colour derived from style). Any ideas? Thanks in advance.