QListView only showing a single item in the view



  • I am utilizing the Model View Delegate framework used by Qt for displaying lists of objects with custom 'views' or layouts.

    Background:

    I require showing a country flag, country name, city name and an optional 'premium' rating star in a list, which can be selected by a user.

    To achieve this, I use the following components:

    • Model - a QStandardItemModel, see doc page, which holds all the data of the QListView and interaction concerns, doc page

    • Delegate - For drawing/displaying the data in a custom layout fashion, I use a QStyledItemDelegate, see doc page.

    • View - For the view, a simple QListView will suffice, as this is a single column list containing a collection of objects with a custom layout.

    Tutorials and Help:

    Using the following tutorial(s),

    1. a messageviewer application showing how to implement a detailed model-delegate-view concept in accordance with,
    2. the basics of a simple messaging menu system for Nokia smartphones, I have been able to create, with relative ease, the desired layout for my QListView.

    Problem:

    I am required to add QStandardItem items to my model, which will be added to my view. I do so, and it is confirmed by the delegate where each item is drawn by the paint override method.

    However, during runtime, the QListView only displays 1 item. But I can use my up/down arrow keys to select various other items in the list.

    Please see code below:

    Setting up MVC(delegate):

    mainwindow.h
    //...
    QStandardItemModel modelServers;
    ServerDelegate
    serverDelegate;
    //...

    mainwindow.cpp

    modelServers = new QStandardItemModel(0);
    serverDelegate = new ServerDelegate(0);
    
    ui->listServers->setModel(modelServers);
    ui->listServers->setItemDelegate(serverDelegate);
    

    and adding items (QStandardItem's) to list:

    for (int i = 0; i < someList->length(); ++i) {
    	Server server = someList->value(i);
    	QStandardItem *item = new QStandardItem();
    	item->setData(server.getCountryName,         item->setData(QPixmap(Global::getCountryFlagFromCache(v.country)), ServerDelegate::DataRole::CountryFlag);
    	item->setData(server.getCountry(), ServerDelegate::DataRole::CountryText);
    	item->setData(server.getCity(), ServerDelegate::DataRole::CityText);
    	item->setData(i, ServerDelegate::ListIndex);
    	//...
    	modelServer->appendRow(item)
    }
    

    The finally the list displays the data, however the list is only populated with items, but only the first has visible text and images.

    note: when scrolling down, only the top item is visible, see images below for an example.

    e.g.

    Initial Loaded list:

    enter image description here

    One scroll down

    enter image description here

    Selecting an item with no text/images:

    enter image description here

    Additional Code below:

    ServerDelegate class handling the custom layout

    ServerDelegate.h

    #ifndef SERVERDELEGATE_H
    #define SERVERDELEGATE_H
    
    #include <QApplication>
    #include <QtGui>
    #include <QStyledItemDelegate>
    #include <QtWidgets>
    #include <qglobal.h>
    
    #include "global.h"
    
    class ServerDelegate : public QStyledItemDelegate
    {
        Q_OBJECT
    public:
        ServerDelegate(QStyledItemDelegate* parent = 0);
        virtual ~ServerDelegate();
    
        enum DataRole{
            CountryText = Qt::UserRole + 100,
            CityText = Qt::UserRole+101,
            CountryFlag = Qt::UserRole+102,
            SideIconFlag = Qt::UserRole+103,
            ListIndex = Qt::UserRole+105
        };
    
        void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    
        QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
    
    private:
        QFont fontCountry, fontCity;
    };
    
    #endif // SERVERDELEGATE_H
    

    ServerDelegate.cpp

    #include "serverdelegate.h"
    
    ServerDelegate::ServerDelegate(QStyledItemDelegate *parent)
        : QStyledItemDelegate(parent)
    {
        fontCountry = QApplication::font();
        fontCountry.setBold(true);
        fontCountry.setPointSize(QApplication::font().pointSize() + 3);
    
        fontCity = QApplication::font();
        fontCity.setItalic(true);
        fontCity.setPointSize(QApplication::font().pointSize() - 1);
    }
    
    ServerDelegate::~ServerDelegate(){
    }
    
    QSize ServerDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const{
        Q_UNUSED(index)
        QSize totalCountrySize = Global::getCountryFlagFromCache(index.data(DataRole::CountryText).toString()).size();
        QSize totalSideIcon = QPixmap(":/res/images/premium", "PNG").size();
    
        QFontMetrics fmCountry(fontCountry);
        QFontMetrics fmCity(fontCity);
    
        int fontHeight = (2 * AppGlobal::Style_List_Seperator_Width) + (2 * AppGlobal::Style_List_Text_Item_Margin) + fmCountry.height() + fmCity.height();
        int iconHeight = (2 * AppGlobal::Style_List_Seperator_Width) + (totalCountrySize.height() > totalSideIcon.height() ? totalCountrySize.height() : totalSideIcon.height());
        int height = (fontHeight > iconHeight) ? fontHeight : iconHeight;
    
        int width = option.rect.width();
        QSize size = QSize(width, height);
    
        return size;
    }
    
    void ServerDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
        QStyledItemDelegate::paint(painter, option, index);
    
        QRect rec = option.rect;
    
        painter->save();
        painter->setClipRect(rec);
    
        QString countryText = index.data(DataRole::CountryText).toString();
        QString cityText = index.data(DataRole::CityText).toString();
        QPixmap countryFlag = QPixmap(qvariant_cast<QPixmap>(index.data(DataRole::CountryFlag)));
        QPixmap sideIcon = qvariant_cast<QPixmap>(index.data(DataRole::SideIconFlag));
    
        // Get a rectangle by size x, y.
        // With cooridinates [0,0]; [x,0]; [x,y]; [0,y]
        QRect topLine = option.rect,
                bottomLine = option.rect;
    
        // create top 'seperator' of X px's width, green in color;
        topLine.setTop(0);
        topLine.setLeft(0);
        topLine.setRight(option.rect.width());
        topLine.setBottom(AppGlobal::Style_List_Seperator_Width); // 1px down
    
        painter->setPen(AppGlobal::Style_List_Seperator_Color);
        painter->fillRect(topLine, AppGlobal::Style_List_Seperator_Color);
        painter->drawRect(topLine);
    
        // create bottom 'seperator' of X px's width, green in color;
        bottomLine.setTop(option.rect.height() - AppGlobal::Style_List_Seperator_Width);
        bottomLine.setLeft(0);
        bottomLine.setRight(option.rect.width());
        bottomLine.setBottom(option.rect.height()); // 1px down
    
        painter->setPen(AppGlobal::Style_List_Seperator_Color);
        painter->fillRect(bottomLine, AppGlobal::Style_List_Seperator_Color);
        painter->drawRect(bottomLine);
    
        // create background rectangle
        QRect content(0, topLine.bottom(), option.rect.width(), bottomLine.top());
    
        painter->setPen(AppGlobal::Style_List_Background_Color);
        painter->fillRect(content, AppGlobal::Style_List_Background_Color);
        painter->drawRect(content);
    
        // create content rectangles from content container.
    
        QRect rectCountryFlag = content,
                rectSideIcon = content;
    
        //    create country icon rectangle
        QSize countryFlagSize = countryFlag.size();
        int cFPos = (rectCountryFlag.height() / 2) - (countryFlagSize.height() / 2) - 8;
        rectCountryFlag.setTop(cFPos);
        rectCountryFlag.setBottom(content.height() - cFPos);
        rectCountryFlag.setLeft(AppGlobal::Style_List_Left_Item_Margin - 8);
        rectCountryFlag.setRight(AppGlobal::Style_List_Left_Item_Margin + 16 + countryFlagSize.width());
    
        painter->drawPixmap(rectCountryFlag, countryFlag);
    
        //    create side icon rectangle
        QSize sideIconSize = sideIcon.size();
        int siPos = (rectSideIcon.height() / 2) - (sideIconSize.height() / 2) - 4;
        rectSideIcon.setTop(siPos);
        rectSideIcon.setBottom(content.height() - siPos);
        rectSideIcon.setLeft(rec.width() - (AppGlobal::Style_List_Right_Item_Margin + 8 + sideIconSize.width()));
        rectSideIcon.setRight(rec.width() - AppGlobal::Style_List_Right_Item_Margin);
    
        painter->drawPixmap(rectSideIcon, sideIcon);
    
        const QRect textContent(rectCountryFlag.right() + AppGlobal::Style_List_Text_Item_Margin + AppGlobal::Style_List_Left_Item_Margin, content.top() + AppGlobal::Style_List_Text_Item_Margin,
                                rectSideIcon.left() - AppGlobal::Style_List_Text_Item_Margin, content.bottom() - AppGlobal::Style_List_Text_Item_Margin);
    
        // create country text rectangle
    
        QRect rectCountryText = content,
                rectCityText = content;
    
        rectCountryText.setLeft(textContent.left());
        rectCountryText.setTop(textContent.top());
        rectCountryText.setRight(textContent.right());
        rectCountryText.setBottom(qRound(textContent.height() * 0.6) - AppGlobal::Style_List_Text_Item_Margin);
    
        painter->setPen(AppGlobal::Style_Heading_Color);
        painter->setFont(fontCountry);
        painter->drawText(rectCountryText, countryText);
    
        // create city text rectangle
    
        rectCityText.setLeft(textContent.left() + ( 2 * AppGlobal::Style_List_Text_Item_Margin));
        rectCityText.setTop(rectCountryText.bottom() + AppGlobal::Style_List_Text_Item_Margin);
        rectCityText.setRight(textContent.right());
        rectCityText.setBottom(textContent.height());
    
        painter->setPen(AppGlobal::Style_SubText_Color);
        painter->setFont(fontCity);
        painter->drawText(rectCityText, cityText);
    
        // restore painter
        painter->restore();
    }
    

  • Lifetime Qt Champion

    Hi,

    What version of Qt are you using ?
    On what platform ?



  • @CybeX
    After answering @SGaist's question....

    In ServerDelegate::sizeHint(), what values do fontHeight & iconHeight respectively actually produce?



  • This is proper odd...

    Can you try, temporarily, setting CountryText = Qt::EditRole, and commenting out ui->listServers->setItemDelegate(serverDelegate); just to understand if the problem is in the delegate (as I suspect)?



  • @SGaist
    Platform - Linux x86_64
    Qt Version - 5.9.3

    @JNBarchan
    fontHeight produced a integer value +/- 56
    iconHeight produced an integer value ( < fontHeight)

    @VRonin
    The Role's where not the problem. Please see my updated delegate below (kudos for calling it, the delegate being the problem)


    Well this is embarrasing. I did not fully realize what I was coding until I had battled with the delegate all night.

    I understood that I was coding a template, and assumed that on each item which was added, the origin remained at [0,0]. This was not the case as there was an offset added of value specified in the sizeHint (in my case, 56). Thus all my QListView's QStandardItem's were in fact there, but drawn over each other.

    After changing values and experimenting over a long period (a number of hours), I finally came to the desired result, shown below.

    Also, I need to thank scopchanov for his hint into this offset problem

    StackoverFlow question as a reference

    Updated ServerDelegate.cpp

    #include "serverdelegate.h"
    
    ServerDelegate::ServerDelegate(QStyledItemDelegate *parent)
        : QStyledItemDelegate(parent)
    {
        fontCountry = QApplication::font();
        fontCountry.setBold(true);
        fontCountry.setPointSize(QApplication::font().pointSize() + 3);
    
        fontCity = QApplication::font();
        fontCity.setItalic(true);
        fontCity.setPointSize(QApplication::font().pointSize() - 1);
    }
    
    ServerDelegate::~ServerDelegate(){
    }
    
    QSize ServerDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const{
        Q_UNUSED(index)
        QSize totalCountrySize = Global::getCountryFlagFromCache(index.data(DataRole::CountryText).toString()).size();
        QSize totalSideIcon = QPixmap(":/res/images/premium", "PNG").size();
    
        QFontMetrics fmCountry(fontCountry);
        QFontMetrics fmCity(fontCity);
    
        int fontHeight = (2 * AppGlobal::Style_List_Seperator_Width) + (2 * AppGlobal::Style_List_Text_Item_Margin) + fmCountry.height() + fmCity.height();
        int iconHeight = (2 * AppGlobal::Style_List_Seperator_Width) + (totalCountrySize.height() > totalSideIcon.height() ? totalCountrySize.height() : totalSideIcon.height());
        int height = (fontHeight > iconHeight) ? fontHeight : iconHeight;
    
        int width = option.rect.width();
        QSize size = QSize(width, height);
    
        return size;
    }
    
    void ServerDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
        QStyledItemDelegate::paint(painter, option, index);
    
        QFontMetrics fmCountry(fontCountry);
        QFontMetrics fmCity(fontCity);
    
        QRect rec = option.rect;
    
        painter->save();
        painter->setClipRect(rec);
    
        QString countryText = index.data(DataRole::CountryText).toString();
        QString cityText = index.data(DataRole::CityText).toString();
        QPixmap countryFlag = QPixmap(qvariant_cast<QPixmap>(index.data(DataRole::CountryFlag)));
        QPixmap sideIcon = qvariant_cast<QPixmap>(index.data(DataRole::SideIconFlag));
    
        // Get a rectangle by size x, y.
        // With cooridinates [0,0]; [x,0]; [x,y]; [0,y]
        QRect topLine = option.rect,
                bottomLine = option.rect;
    
        // create top 'seperator' of X px's width, green in color;
        topLine.setTop(rec.top());
        topLine.setLeft(rec.left());
        topLine.setRight(rec.right());
        topLine.setBottom(rec.top() + AppGlobal::Style_List_Seperator_Width); // 1px down
    
        painter->setPen(AppGlobal::Style_List_Seperator_Color);
        painter->fillRect(topLine, AppGlobal::Style_List_Seperator_Color);
        painter->drawRect(topLine);
    
        // create bottom 'seperator' of X px's width, green in color;
        bottomLine.setTop(rec.bottom() - AppGlobal::Style_List_Seperator_Width);
        bottomLine.setLeft(rec.left());
        bottomLine.setRight(rec.right());
        bottomLine.setBottom(rec.bottom()); // 1px down
    
        painter->setPen(AppGlobal::Style_List_Seperator_Color);
        painter->fillRect(bottomLine, AppGlobal::Style_List_Seperator_Color);
        painter->drawRect(bottomLine);
    
        // create background rectangle
        QRect content(rec.left(), topLine.bottom(), (rec.right() - rec.left()), (bottomLine.top() - topLine.bottom()));
    
        painter->setPen(AppGlobal::Style_List_Background_Color);
        painter->fillRect(content, ((option.state & QStyle::State_MouseOver) ? AppGlobal::Style_List_Hover_Color : AppGlobal::Style_List_Background_Color ));
        painter->drawRect(content);
    
        // create content rectangles from content container.
    
        QRect rectCountryFlag = content,
                rectSideIcon = content;
    
        //    create country icon rectangle
        QSize countryFlagSize = countryFlag.size();
        int cFPos = ((rectCountryFlag.bottom() - rectCountryFlag.top()) / 2) - (countryFlagSize.height() / 2) - 8;
        rectCountryFlag.setTop(rectCountryFlag.top() + cFPos);
        rectCountryFlag.setBottom(content.bottom() - cFPos);
        rectCountryFlag.setLeft(AppGlobal::Style_List_Left_Item_Margin - 8);
        rectCountryFlag.setRight(AppGlobal::Style_List_Left_Item_Margin + 16 + countryFlagSize.width());
    
        painter->drawPixmap(rectCountryFlag, countryFlag);
    
        //    create side icon rectangle
        QSize sideIconSize = sideIcon.size();
        int siPos = ((rectSideIcon.bottom() - rectSideIcon.top()) / 2) - (sideIconSize.height() / 2) - 4;
        rectSideIcon.setTop(rectSideIcon.top() + siPos);
        rectSideIcon.setBottom(content.bottom() - siPos);
        rectSideIcon.setLeft(rec.width() - (AppGlobal::Style_List_Right_Item_Margin + 8 + sideIconSize.width()));
        rectSideIcon.setRight(rec.width() - AppGlobal::Style_List_Right_Item_Margin);
    
        painter->drawPixmap(rectSideIcon, sideIcon);
    
        int textContentLeft = rectCountryFlag.right() + AppGlobal::Style_List_Text_Item_Margin + AppGlobal::Style_List_Left_Item_Margin,
                textContentTop = content.top() + AppGlobal::Style_List_Text_Item_Margin;
    
        const QRect textContent( textContentLeft , textContentTop,
                                (rectSideIcon.left() - AppGlobal::Style_List_Text_Item_Margin) - textContentLeft, (content.bottom() - AppGlobal::Style_List_Text_Item_Margin) - textContentTop);
    
        // create country text rectangle
    
        QRect rectCountryText = content,
                rectCityText = content;
    
        rectCountryText.setLeft(textContent.left());
        rectCountryText.setTop(textContent.top());
        rectCountryText.setRight(textContent.right());
        rectCountryText.setBottom(textContent.top() + fmCountry.height());
    
        painter->setPen(AppGlobal::Style_Heading_Color);
        painter->setFont(fontCountry);
        painter->drawText(rectCountryText, countryText);
    
        // create city text rectangle
    
        rectCityText.setLeft(textContent.left() + ( 2 * AppGlobal::Style_List_Text_Item_Margin));
        rectCityText.setTop(rectCountryText.bottom());
        rectCityText.setRight(textContent.right());
        rectCityText.setBottom(textContent.bottom() + fmCity.height());
    
        painter->setPen(AppGlobal::Style_SubText_Color);
        painter->setFont(fontCity);
        painter->drawText(rectCityText, cityText);
    
        // restore painter
        painter->restore();
    }
    
    


Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.