PDA

View Full Version : tree view displaying widgets as items



paksas
16th May 2010, 10:05
Hi

I need to make a tree of widgets.

I tried using a QTreeView and a subclassed QAbstractItemModel, but I coulnd't figure out how the tree view woould know how to draw my widgets, since the model only provides it with QVariant data.

Then I read this post: inserting custom Widget to listview (http://www.qtcentre.org/threads/26916-inserting-custom-Widget-to-listview?highlight=tree+of+widgets), and I ust admit it made me really confused.

Could you please point me to an example or give me some pointers how to achieve my goal.

I must add, that I will need to dsplay all kinds of widgets in the tree - not only labels and buttons.

Thanks,
Paksas

tbscope
16th May 2010, 10:20
Here's another example:
http://doc.qt.nokia.com/4.6/itemviews-stardelegate.html

Can you describe what makes you confused or what you do not understand?

paksas
16th May 2010, 11:46
Here's another example:
http://doc.qt.nokia.com/4.6/itemviews-stardelegate.html

Can you describe what makes you confused or what you do not understand?

The example you quoted also shows a non-widget class being edited by an external editor (derrived from a qwidget). The delegate operating on it accepts a QVariant data - to which a StarRating class instance can be cast.

What I want is to embed any QWidget directly, and with this approach it raises a few questions:
- where can I get an editor from for let's say a QLabel or a QTextBox?
- how can I cast the widgets to a QVariant?

Here's the delegate class I wrote - but it doesn't work because of the problems I mentioned.



#include <QStyledItemDelegate.h>
#include <QPainter.h>
#include <QWidget.h>

class WidgetItemDelegate : public QStyledItemDelegate
{
public:
/**
* Constructor.
*
* @param parent parent widget
*/
WidgetItemDelegate( QWidget* parent = NULL );

// -------------------------------------------------------------------------
// QStyledItemDelegate
// -------------------------------------------------------------------------
void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const;
QWidget* createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
void setEditorData( QWidget *editor, const QModelIndex &index ) const;
void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const;

private slots:
void commitAndCloseEditor();
};

///////////////////////////////////////////////////////////////////////////////

WidgetItemDelegate::WidgetItemDelegate( QWidget* parent )
{
}

///////////////////////////////////////////////////////////////////////////////

void WidgetItemDelegate::paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
if ( qVariantCanConvert< QWidget* >( index.data() ) )
{
QWidget* widget = qVariantValue< QWidget* >( index.data() );

if ( option.state & QStyle::State_Selected )
{
painter->fillRect( option.rect, option.palette.highlight() );
}

widget->render( painter );
}
else
{
QStyledItemDelegate::paint( painter, option, index );
}
}

///////////////////////////////////////////////////////////////////////////////

QSize WidgetItemDelegate::sizeHint( const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
if ( qVariantCanConvert< QWidget* >( index.data() ) )
{
QWidget* widget = qVariantValue< QWidget* >( index.data() );
return widget->sizeHint();
}
else
{
return QStyledItemDelegate::sizeHint(option, index);
}
}

///////////////////////////////////////////////////////////////////////////////

QWidget* WidgetItemDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
return QStyledItemDelegate::createEditor( parent, option, index );
}

///////////////////////////////////////////////////////////////////////////////

void WidgetItemDelegate::setEditorData( QWidget *editor,
const QModelIndex &index ) const
{
QStyledItemDelegate::setEditorData(editor, index);
}

///////////////////////////////////////////////////////////////////////////////

void WidgetItemDelegate::setModelData( QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index ) const
{
QStyledItemDelegate::setModelData( editor, model, index );
}

///////////////////////////////////////////////////////////////////////////////

void WidgetItemDelegate::commitAndCloseEditor()
{
QWidget* widget = qobject_cast< QWidget* >( sender( ));

emit commitData(widget);
emit closeEditor(widget);
}

///////////////////////////////////////////////////////////////////////////////



Thanks for your help :)
Paksas

paksas
16th May 2010, 12:57
I resolved the problem with embedding widgets in the delegates, but another one arose: I can't draw them correctly. I'm trying to embed them in a QTreeWidget, but they are displayed in wrong places, and the collapsed items are still drawn even thought they shouldn't be.

Here's my delegate's paint method:


///////////////////////////////////////////////////////////////////////////////

void WidgetItemDelegate::paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
if ( qVariantCanConvert< QObject* >( index.data() ) )
{
QWidget* widget = dynamic_cast< QWidget* >( qVariantValue< QObject* >( index.data() ) );

if ( option.state & QStyle::State_Selected )
{
painter->fillRect( option.rect, option.palette.highlight() );
}

widget->move( QPoint( option.rect.left(), option.rect.top() ) );
widget->render( painter );
}
else
{
QStyledItemDelegate::paint( painter, option, index );
}
}


And here are the results:
4639

when I use the following items setup:

m_editorsTree = new QTreeWidget( mainWindow );
m_editorsTree->setItemDelegate( new WidgetItemDelegate() );

// setup TEST data
{
QTreeWidgetItem* item1 = new QTreeWidgetItem( m_editorsTree );
QObject* label1 = new QLabel( "label 1", m_editorsTree );
item1->setData( 0, Qt::DisplayRole, qVariantFromValue( label1 ) );

QTreeWidgetItem* item2 = new QTreeWidgetItem( item1 );
QObject* label2 = new QLabel( "label 2", m_editorsTree );
item2->setData( 0, Qt::DisplayRole, qVariantFromValue( label2 ) );

m_editorsTree->addTopLevelItem( item1 );
}


If I don't set thye parent of the labels, they don't get drawn at all, which brought me to conclussion that the parent's possition is taken into account by the QWidget::render method. However - I can't set the parent to a QTreeWidgetItem instance, since it's not a QWidget...

Can someone please help?

Thanks,
Paksas

faldzip
16th May 2010, 16:45
Better way is to render widget to a QPixmap and then draw this pixmap. Other thing is that you can set Qt::WA_DontShowOnScreen attribute so the widgets are not drawn at all - you have to draw them manually.

But the thing is: what type of widgets you want to show in tree view? Because QWidget::render() just renders actual state, so it gives you just an image of real widget - rendered button is not a real button, but just a picture so you can't click this button.

If you want to labels the your whole idea is wrong as you dont need widgets. If you want to show tree of buttons or other simple widgets, then use QStyle or QStylePainter to draw "fake" widgets not using real widgets. Keeping tons of real widgets in memory is rather bad and really slow solution - I checked this: three views with something around 20 widgets on each view gives 80% CPU usage on both cores (don't know why both?) on windows vista/7 while rendering widgets constantly to have such features like vista's progressbar blinking and buttons highlighting.

Even having QScrollArea with QVBoxLayout with hundred of simple widgets (button + blinking vista progressbar) makes your qscrollarea consume a lot of CPU and works slowly - users would not be happy while using such application.

paksas
16th May 2010, 17:02
But the thing is: what type of widgets you want to show in tree view? Because QWidget::render() just renders actual state, so it gives you just an image of real widget - rendered button is not a real button, but just a picture so you can't click this button.

That's the thing - I want to put any widget I want in there - be it a button, a label, or a whole tree or a table. And ideally - I'd like it to exhibit its regular behavior - without me needing to click it before I can use it ...

Is there a way to get that?

I've no problem with having many widgets around - at least for now :D Let's just say that performance is not an issue.

Aparna charugundla
28th January 2013, 13:13
Hi, Did you find the solution for this?
I have similar requirement where i have to place a combobox or text edit whenever i click that particular grid in Treeview.Please suggest how this can be done?
I Thought of using delegate but I found that i have to apply that delegate for full view/row/column but i need to apply it only for particular grid.How to do that?

Thanks in Advance...

paksas
28th January 2013, 13:18
Yes - I used QGraphicsProxyWidget. Works really well and allows you to embed any QWidget you like.

Cheers,
Paksas

Aparna charugundla
28th January 2013, 13:36
If you dont mind, can you post sample code please.It will be really helpful for me.

Thanks in Advance...

paksas
28th January 2013, 13:58
There's not much to see there - you just instantiate a QGraphicsProxyWidget in your QGraphicsItem and that's it.
Oh - and I use it only for the widgets rendered in a QGraphicsScene. I actually left that custom tree thing, 'cause I didn't find anything suitable.