PDA

View Full Version : How to create collapsing type contact list



The Storm
20th November 2007, 00:42
Hi all,

I'm trying to create a nice contact list for a chat program and I very like the contact list like in Skype and some other chat clients that when you click on some contact he will expand giving you more options to do with it and info. I wonder how it can be done, what classes I need to use and how actually I can make similar effect. I will be very happy if You can give me some instructions how to do it. :)

Regards,
The Storm

wysota
20th November 2007, 07:12
Currently only possible way to reliably mimic the effect is to make the "expansion" part of the item a subitem of the base item and make the view a tree. Then you need to reimplement QTreeView::drawTree to make it look good.

Kostanev
20th November 2007, 12:51
Hello all,

This is really good idea. I'm sorry for this question, but can someone give me a litle example how to do this ?

Thanks!

wysota
20th November 2007, 13:33
Which part?

The Storm
20th November 2007, 14:16
For me this part is not very clear


to make the "expansion" part of the item a subitem of the base item and make the view a tree

If you can give a simple example what actually I must reimplent and change(except the painter part) it will be very nice. :)

wysota
20th November 2007, 14:32
You must implement your list as a tree. For that you need to use QTreeView instead of QListView. Each contact should consist of two items - one is the basic stuff only and the other is everything that is not in the first one. So looking at skype, you'd place the icon and the contact name in the first item and things like photo, phone number, action icons, address, etc. in the second one. Then make the second one child of the first one and you'll see what I mean. A mockup:

QTreeWidget *tw = new QTreeWidget;
for(int i=0;i<10;i++){
QTreeWidgetItem *basic = new QTreeWidgetItem(tw);
basic->setText(0, "name");
basic->setIcon(0, QPixmap(":/online.png"));
QTreeWidgetItem *extra = new QTreeWidgetItem(basic);
extra->setText(0, "Some extra data for a contact");
}
tw->show();

The Storm
20th November 2007, 19:37
Thanks but you are using QTreeWidget, must I use it too or to use QTreeView ?

wysota
20th November 2007, 19:52
No, you can use QTreeView. QTreeWidget is a convenience widget - I don't need to create a model to use it, I just wanted to show you the concept.

The Storm
20th November 2007, 20:43
Ok I inherited QTreeView and added some items using the QStandardItemModel, they show up well but my reimplementation of drawTree() is not called at all, but paintEvent() is called non-stop... So I can't customize this thing at all.

wysota
20th November 2007, 22:02
Check if the signature of the method is correct.

The Storm
20th November 2007, 22:40
Sorry I'm a bit noob, what signature I must check and how? Sorry for all those questions but I really want to do it. :)

wysota
20th November 2007, 22:44
The signature of the method you reimplement. It should be exactly the same as the original method:

void MyTreeView::drawTree ( QPainter * painter, const QRegion & region ) const{
//...
}

Note the "const" keyword at the end, it has to be there.

Baah... sorry, my mistake... for QTreeView you should reimplement drawBranches() and drawRow(), not drawTree...

The Storm
20th November 2007, 22:48
Yes the signature is correct, I even directly copied the function from qtreeview.h, here is the reimplentation:



class ContactView : public QTreeView
{
public:
ContactView(QWidget *parent = 0) : QTreeView(parent) {}
~ContactView() {};

protected:
void paintEvent(QPaintEvent *event)
{
QTreeView::paintEvent(event);
}
void drawTree(QPainter *painter, const QRegion &region) const
{
QMessageBox::information(0, QString("Draw Tree"), QString("Called"));
QTreeView::drawTree(painter, region);
}
};


Edit: Oh thats other thing, thanks. :) I will try them tomorrow.

The Storm
21st November 2007, 13:28
Ok this two functions are calling perfect and I already done some primitive drawing. :P
Now I want to ask is there a way with the Painter to draw a widgets that are already created, lets say I have a QFrame with some buttons on it and I want to show it as item in to the modified QTreeView, so if there is a function for drawing already created widgets can you tell me what is its name and how to use it. Thanks. :)

wysota
21st November 2007, 18:54
Now I want to ask is there a way with the Painter to draw a widgets that are already created, lets say I have a QFrame with some buttons on it and I want to show it as item in to the modified QTreeView, so if there is a function for drawing already created widgets can you tell me what is its name and how to use it. Thanks. :)

Call the base class implementation of the overloaded methods. They probably handle that.

The Storm
21st November 2007, 19:14
I'm very sorry but I didn't understand you clearly again... What is that base class implentation ? Very sorry for my noobish quesiotns...

wysota
21st November 2007, 19:33
void MyClass::method(int x, int y){
BaseClass::method(x,y); // <--
}
This is the base class implementation provided that MyClass inherits BaseClass.

The Storm
22nd November 2007, 13:30
Maybe you understand me wrong or I unserstand you wrong. :P I need to draw a totaly other qwidget than the QTreeView base class rows in my case. I want to draw some widget that I had created already from a QFrame with some some buttons on it, I want to draw it on the place where the row must be drawed instead of the real QTreeView rows, so calling BaseClass::method will not do the thing that I want to do. :) So that was my previous question, can I draw my own widget with other widgets placed on it instead of the rows using directly the painter. I hope you understand me right this time. :)

wysota
22nd November 2007, 13:39
Items are drawn using drawRow(), which will in turn call the delegate to draw each item. Now you have two possible solutions. Either fake a widget drawing all the contents yourself or use index widgets (these are real widgets) and then widgets will draw themselves. The latter will slow your application down significantly if you have more than a few of them, so the first solution is advised. If you want a list of contacts, I doubt you have to use real widgets, so painting in the delegate's paint method is the way to go. Search the forum for more details on this.

The Storm
11th January 2008, 18:56
Hi again :)

I have been busy with other things but now I'm back to coding my messenger, after all readed again and again I do understand that I need to use a real widgets in to the TreeView, because of the functionality of each user in the list with only clicking on the widget. As you told me this will slow down the application but from what I see in other messengers they do have widgets in the contact list but only one in a time. I meant that general the list of users in the list looks like a totaly normal items for treeview with some icon for status, but when you select someone he become a widget with some or other options on it like in Skype for example, when you select other user the previous selected become a normal item again and the other user become in widget and etc... So I do know how to instert widgets in TreeView (I think, there is some from the examples doing it) and I do know how to instert normal items, but I really don't know how to do the change like the messengers like Skype do - only the selected user become a widget and in this way the program is not slowing down. Do you have any idea for some kind of way that I can do this too ? :)

Thanks a lot for your help but new people in Qt like me needs more and more... Sorry if you are bored of my questions. :)

wysota
11th January 2008, 23:29
They have a tree and the "expanded" view is simply a child item of the "base" view item.

The Storm
12th January 2008, 17:25
My english is not very good so maybe I can't fully understand you, can you show me little example pleace. :)

wysota
12th January 2008, 19:51
Skype is your example :)

A single "contact" consists of two items where one is a child of the other. When the contact is "active" you see both items and when it is "collapsed" you only see the parent item. At this moment the child is hidden.

The Storm
12th January 2008, 20:56
Ok but QTreeView doesn't accept my QItemDelegate modification that I copied one to one from the examples(SpinBoxDelegate). The example use QTableView... Am I forced to use QTableView too and is QTableView able to hold a normal items like QTreeView? Or is there a better easy way to do the stuff like inserting a widget in to the view without QItemDelegate ?

wysota
12th January 2008, 23:14
What does "doesn't accept" mean?

The Storm
12th January 2008, 23:45
It meant that nothing is showed but the mistake was mine. :) The bad thing here is that the item size is very small in to the treeview, is there a way I can resize it and is there other way to show a widget without an ItemDelegate and if not how the parent item can be normal and the child to be widget? :)

wysota
13th January 2008, 10:32
Set a SizeHintRole for your item.

The Storm
13th January 2008, 12:53
Thanks you, this done the job. :) But now other problem exist - why the widget is showing only when I doble click on the item, I want to be showed always and on one click to expand his child, is there any flags or something to do this ?

uglykid
13th January 2008, 14:21
Hi,

simply take a look into the QTreeView base class documentation and check that you reimplement the itemClicked event to expand the related item!

BTW: I realized the effect of expanding contact items by simply calling setItemWidget on a childitem of the item that was clicked.

since there is always only one of these items expanded there is no performance issue arising. Simply be sure to detach the ItemWidget as soon as you collapse the parent item.

the widget you assign to the child item will be displayed below the expanded item....quite fast way realizing your plans if you ask me...

-uglykid

The Storm
13th January 2008, 14:43
So I don't need from QItemDelegate(actually I will not edit anything) so your way seems very good, About the detach is there a function for that or I must do setItemWidget with NULL pointer ? :)

Edit: Actually QTreeView doesn't not have setItemWidget() so maybe I will stick with setIndexWidget() witch must do the same I think, I will play a bit. :)

Edit2: Ok only one problem left now, yay. :) How I can remove the "+" and "-", for the top level items I use setRootIsDecorated(false) but for the childs what I must use to remove the "+" and "-" ?

The Storm
13th January 2008, 17:37
Here what I meant(please don't tell me that I must use the paintEvent()).

wysota
13th January 2008, 19:48
Reimplement QTreeView::drawBranches() and leave it empty.

The Storm
13th January 2008, 21:05
Thanks but the item still have some empty space, I'm attaching new picture. I tryed to reimplent drawTree() and leave it empty too but the result was same.

wysota
13th January 2008, 21:20
Set the indentation property of the tree to 0.

uglykid
14th January 2008, 18:20
Hi Storm,

if you use the QTreeWidget as base class for your tree implementation instead you will have
setItemWidget and removeItemWidget which makes live much more easy!

To my opinion there is no reason to implement your own treewidget functions based on QtreeView if you have the qtreewidget!

-frank

wysota
15th January 2008, 00:14
To my opinion there is no reason to implement your own treewidget functions based on QtreeView if you have the qtreewidget!

Note that QTreeWidget is derived from QTreeView, so you are building upon tree view anyway.

andytork
10th January 2009, 11:53
Thanks but the item still have some empty space, I'm attaching new picture. I tryed to reimplent drawTree() and leave it empty too but the result was same.

This looks very similiar to what I am trying to do, do you have any sample code you could send or attach

Thanks

Andy

Lykurg
12th January 2009, 14:09
Hi,

I have created a new wiki page related to this issue: Expanding_list. Fell free to improve it..

Lykurg

zuck
10th October 2009, 13:34
Use this:



QAbstractItemModel* model = const_cast<QAbstractItemModel*>(index.model());
QMetaObject* mobj = const_cast<QMetaObject*>(model->metaObject());
if (mobj)
{
mobj->invokeMethod(model, "layoutChanged");
return;
}


instead of:



CustomItemModel *model = const_cast<CustomItemModel*> (dynamic_cast<const CustomItemModel*>(index.model()));
if (model)
{
model->wantsUpdate();
return;
}


and no changes to model (i.e. CustomItemModel) or view are required!! ;)