Sunday, July 25, 2010

How to customize Listview in Qt using Delegates.





By default listview in Qt takes a text and an icon. What if you want a Listview with more then one icon, what if you want listview with more then one text or both? To come up from this problem, Qt has concept called Delegates.

Before knowing about Delegates, first we shall see the model and view concept in Qt.

What is model and view class?
Model class is responsible for storing the data, view class is responsible for showing data.
Model and view can be separated, they are independent.

Listview in Qt:
Listview in Qt takes model and view class, first data should be filled in the model class and you need to tell view to show data, but how the data present in model should be displayed in view?
The answer is Delegates, delegates tell where and how the data should be displayed in view.

Sample inbox example, shows how to use delegate in Qt
The Requirement is displaying a image, One bigger text and a sub-text of small size.

To make delegate class, you need to override couple of functions,
sizeHint(const QStyleOptionViewItem & option ,
const QModelIndex & index) const

sizeHint return you the item width. Which can be either predefined or you can calculate it using the data elements.

paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const

In paint you need to tell, how and were data should be displayed in view. i.e, where image should be displayed, where the text should come and the size of the text.

here how the delegate cpp and header file looks.
/*
 * ListviewDelegate.cpp
 *
 *  Created on: Jul 17, 2010
 *      Author: http://qt-articles.blogspot.com
 */

#include "ListviewDelegate.h"

ListviewDelegate::ListviewDelegate()
    {
    // TODO Auto-generated constructor stub

    }

ListviewDelegate::~ListviewDelegate()
    {
    // TODO Auto-generated destructor stub
    }

//alocate each item size in listview.
QSize ListviewDelegate::sizeHint(const QStyleOptionViewItem &  option ,
                              const QModelIndex & index) const
{
    QIcon icon = qvariant_cast<QIcon>(index.data(IconRole));
    QSize iconsize = icon.actualSize(option.decorationSize);
    QFont font = QApplication::font();
    QFontMetrics fm(font);
    
    return(QSize(iconsize.width(),iconsize.height()+fm.height() +8 ));

}
void ListviewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                           const QModelIndex &index) const
 {
    QStyledItemDelegate::paint(painter,option,index);
    
    painter->save();
    
    QFont font = QApplication::font();
    QFont SubFont = QApplication::font();
    //font.setPixelSize(font.weight()+);
    font.setBold(true);
    SubFont.setWeight(SubFont.weight()-2);
    QFontMetrics fm(font);
    
    QIcon icon = qvariant_cast<QIcon>(index.data(IconRole));
    QString headerText = qvariant_cast<QString>(index.data(headerTextRole));
    QString subText = qvariant_cast<QString>(index.data(subHeaderTextrole));
    
    QSize iconsize = icon.actualSize(option.decorationSize);
    
    QRect headerRect = option.rect;
    QRect subheaderRect = option.rect;
    QRect iconRect = subheaderRect;
    
    iconRect.setRight(iconsize.width()+30);
    iconRect.setTop(iconRect.top()+5);
    headerRect.setLeft(iconRect.right());
    subheaderRect.setLeft(iconRect.right());
    headerRect.setTop(headerRect.top()+5);
    headerRect.setBottom(headerRect.top()+fm.height());
    
    subheaderRect.setTop(headerRect.bottom()+2);
    
    
    //painter->drawPixmap(QPoint(iconRect.right()/2,iconRect.top()/2),icon.pixmap(iconsize.width(),iconsize.height()));
    painter->drawPixmap(QPoint(iconRect.left()+iconsize.width()/2+2,iconRect.top()+iconsize.height()/2+3),icon.pixmap(iconsize.width(),iconsize.height()));
    
    painter->setFont(font);
    painter->drawText(headerRect,headerText);
    
    
    painter->setFont(SubFont);
    painter->drawText(subheaderRect.left(),subheaderRect.top()+17,subText);
    
    painter->restore();
    
 }



/*
 * ListviewDelegate.h
 *
 *  Created on: Jul 17, 2010
 *      Author: http://qt-articles.blogspot.com
 */

#ifndef ListviewDelegate_H_
#define ListviewDelegate_H_
#include <QtGui>

class ListviewDelegate : public QStyledItemDelegate
    {
public:
    ListviewDelegate();
    virtual ~ListviewDelegate();
    
    enum datarole {headerTextRole = Qt::UserRole + 100,subHeaderTextrole = Qt::UserRole+101,IconRole = Qt::UserRole+102};
    
    void paint(QPainter *painter, const QStyleOptionViewItem &option,
               const QModelIndex &index) const;

    QSize sizeHint(const QStyleOptionViewItem &option,
                   const QModelIndex &index ) const;

    };

#endif /* ListviewDelegate_H_ */


How to connect Model,view and Delegates in Listview.

 QListView *view;
 QStandardItemModel *model;
 ListviewDelegate *listdelegate;
       
 view = new QListView();
 model = new QStandardItemModel();
 listdelegate = new ListviewDelegate();
           
  view->setItemDelegate(listdelegate); //connect the delegate to view
  view->setModel(model);//connect the model to view.


How to insert an item to model,
QStandardItem *item = new QStandardItem();
           QIcon icon(":/new/prefix1/about.png");
           
           item->setData("Inbox",ListviewDelegate::headerTextRole);
           item->setData("10 Messages",ListviewDelegate::subHeaderTextrole);
           item->setData(icon,ListviewDelegate::IconRole);
           model->appendRow(item);


This is how the entire code of inserting item to view looks

 

//http://qt-articles.blogspot.com
#include "ListDeligates.h"  
 #include <QtGui>  
 #include <QApplication>  
 #include "ListviewDelegate.h"  
 int main(int argc, char *argv[])  
 {  
   QApplication a(argc, argv);  
   QListView *view;  
   QStandardItemModel *model;  
   ListviewDelegate *listdelegate;  
   view = new QListView();  
   model = new QStandardItemModel();  
   listdelegate = new ListviewDelegate();  
   view->setItemDelegate(listdelegate);  
   view->setModel(model);  
          QStandardItem *item = new QStandardItem();  
          QIcon icon(":/new/prefix1/about.png");  
          item->setData("Inbox",ListviewDelegate::headerTextRole);  
          item->setData("10 Messages",ListviewDelegate::subHeaderTextrole);  
          item->setData(icon,ListviewDelegate::IconRole);  
          model->appendRow(item);  
          QStandardItem *item1 = new QStandardItem();  
          item1->setData("Draft",ListviewDelegate::headerTextRole);  
          item1->setData("5 Messages",ListviewDelegate::subHeaderTextrole);  
          item1->setData(icon,ListviewDelegate::IconRole);  
          item1->setEditable(false);  
          model->appendRow(item1);  
          QStandardItem *item2 = new QStandardItem();  
      item2->setData("Sent Items",ListviewDelegate::headerTextRole);  
      item2->setData("5 Messages",ListviewDelegate::subHeaderTextrole);  
      item2->setData(icon,ListviewDelegate::IconRole);  
      item2->setEditable(false);  
      model->appendRow(item2);  
      QStandardItem *item3 = new QStandardItem();  
      item3->setData("Message Settings",ListviewDelegate::headerTextRole);  
      item3->setData("",ListviewDelegate::subHeaderTextrole);  
      item3->setData(icon,ListviewDelegate::IconRole);  
      item3->setEditable(false);  
      model->appendRow(item3);  
      QStandardItem *item4 = new QStandardItem();  
      item4->setData("Delivery Reports",ListviewDelegate::headerTextRole);  
      item4->setData("",ListviewDelegate::subHeaderTextrole);  
      item4->setData(icon,ListviewDelegate::IconRole);  
      item4->setEditable(false);  
           model->appendRow(item4);  
     view->showMaximized();  
     return a.exec();  
 }  

Here is the output




3 comments:

  1. very helpful, thanks a lot.

    ReplyDelete
  2. Can we download this code as a project ? its hard for beginners to follow without a working poject

    ReplyDelete