Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to create QListView item with different heights



  • I am trying to create a ListView which the item does not have a specific height, 64~n, so when adding a new Item to the list also needs to give the height also, but I don't know the exact height of the text which is going be shown in the list.

    Here is when add new Item to the list:

    void LogListView::addMessage(const QJsonObject &msg, const bool append)
    {
        static int id = 1; // unique id for log items
        auto *item = new QStandardItem();
        item->setEditable(false);
        item->setData(QString("%1").arg(id++, 5, 10, QChar('0')), LogListItemDelegate::DT_Id);
        item->setData(msg["icon"], LogListItemDelegate::DT_ICON);
        item->setData(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"), LogListItemDelegate::DT_Timestamp);
        item->setData(msg["title"], LogListItemDelegate::DT_Title);
        item->setData(msg["subtitle"], LogListItemDelegate::DT_Subtitle);
        item->setData(msg["details"], LogListItemDelegate::DT_Details);
        item->setData(false, LogListItemDelegate::DT_Expanded);
        //item->setSizeHint(QSize(0, 64)); // here is the exact height cannot be calculated
       
       static_cast<QStandardItemModel *>(model())->appendRow(item);
      scrollToBottom();
    }
    

    And here is when drawing the data on List Item:

    void LogListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        QStyleOptionViewItem opt = option;
        QString icon = index.data(DT_ICON).toString();
        if(icon == "1") {           // normal
            icon = ":/icons/status-ok.png";
        } else if(icon == "2") {    // warning
            icon = ":/icons/status-warn.png";
        } else {                    // error
            icon = ":/icons/status-error.png";
        }
        QString id = index.data(DT_Id).toString();
        QString timestamp = index.data(DT_Timestamp).toString();
        QString title = index.data(DT_Title).toString();
        QString subtitle = index.data(DT_Subtitle).toString();
    
        bool hovered = opt.state & QStyle::State_MouseOver;
    
        QFont f(opt.font);
        f.setPointSize(10);
    
        painter->save();
    
        // draw hover
        if(hovered) {
            painter->fillRect(opt.rect.adjusted(0, 0, 0, 0), "#E6E6E6");
        }
    
        // Draw message icon
        painter->drawPixmap(8, opt.rect.y() + 8, QIcon(icon).pixmap(32, 32));
    
        // Draw id
        painter->setFont(f);
        painter->setPen(opt.palette.text().color());
        painter->drawText(opt.rect.adjusted(48, 8, 0, 0), Qt::TextSingleLine, id);
    
        // Draw timestamp
        painter->setFont(f);
        int wTimestamp = painter->fontMetrics().boundingRect(timestamp).width();
        painter->setPen(opt.palette.text().color());
        painter->drawText(opt.rect.adjusted((opt.rect.width()/2)-(wTimestamp/2), 8, 0, 0), Qt::TextSingleLine, timestamp);
    
        // Draw title
        QFont fntTitle(opt.font);
        fntTitle.setBold(true);
        fntTitle.setPointSize(12);
        painter->setFont(fntTitle);
        painter->setPen(opt.palette.windowText().color());
        painter->drawText(opt.rect.adjusted(48, 32, 0, 0), Qt::TextSingleLine, title);
    
        // Draw subtile
        int wTitle = painter->fontMetrics().boundingRect(title).width();
        QFont fntSubTitle(opt.font);
        fntTitle.setPointSize(10);
        painter->setFont(fntSubTitle);
        painter->setPen(opt.palette.windowText().color());
        painter->drawText(opt.rect.adjusted(48+wTitle+12, 32, 0, 0), Qt::TextSingleLine, subtitle);
    
        //expand icon
        QString details = index.data(DT_Details).toString();
    
        QFont fntDetials(opt.font);
        painter->setFont(fntDetials);
        fntDetials.setPointSize(12);
        painter->setPen(opt.palette.windowText().color());
        painter->drawText(opt.rect.adjusted(48, 32+32, 0, 0), Qt::TextWordWrap, details);
    
        painter->restore();
    }
    

    And here is when returning the sizeHint():

    QSize LogListItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
    //    bool expanded = index.data(DT_Expanded).toBool();
    //    QFont fntDetials = option.font;
    //    fntDetials.setPointSize(12);
    //    QRect r = option.rect;
    //    QFontMetrics fm(fntDetials);
    //    QString details = index.data(DT_Details).toString();
    //    QRect br = fm.boundingRect(r, Qt::TextWordWrap, details);
    //    return QSize(option.rect.width(), br.height()+64);
    
        QSize result = QStyledItemDelegate::sizeHint(option, index);
        result.setHeight(result.height()*2);
        return result;
    
        //QSize s = QStyledItemDelegate::sizeHint(option, index);
    //    QFont fntDetials = option.font;
    //    fntDetials.setPointSize(12);
    //    QRect r = option.rect;
    //    QFontMetrics fm(fntDetials);
    //    QString details = index.data(DT_Details).toString();
    //    QRect br = fm.boundingRect(r, Qt::TextWordWrap, details);
    //    s.setHeight(br.height());
        //return s;
    }
    

    But the result always not correct, sometimes it is very small to fit the content and sometimes is very large for a small content, and so on...
    And also when resizing the window, also will cause content to not fit the height.

    I really want to ask: Why Qt decided to create such kind of ListView which is really very hard to use when there is a very complex data to show, all things need to draw by myself, calculate a lot of x,y positions, padding, .... etc.
    Why there is not something like Android ListView, which only needs to set the Item layout and give the data to populate on. And the ListView itself will take care of everything inside the item, and also by using the view holder recycle the items view, which solves the memory waste problem. BUT Qt makes it very hard to use, everything must be done by myself, the ListView does nothing.? And also there is no any good tutorial to show how to use for different cases such as specific, non-specific height and width, resizing and so on...

    Thanks!

    Note: this problem wastes me a lot of time, till now I am not able to solve.


Log in to reply