Is it possible to add a custom widget into a QListView?
Asked Answered
Y

1

8

I have a large log data (100, 1000, 100000, ... records) and I want to visualize it in the following manner:

enter image description here enter image description here enter image description here

Which widget (e.g. QListView, QListWidget) should I use and how, in order to stay away from performance and memory problems?

Yvette answered 1/11, 2018 at 16:24 Comment(2)
For my log view, I chose a different option: I use a QTextEdit with readOnly(true). As it supports rich-texts, I can start the log output lines with the appropriate icons. If the log becomes very long, then it could be an option to make a bit more effort and cache only part of the data while loading more when near top/bottom. However, this can cause some fiddling to adjust scrollbars properly.Gapin
Concerning QListWiget vs. QListView: The former is derived from the latter. While QListWidget might be more convenient to use, it might cause a certain overhead.Gapin
S
22

Is it possible to add a custom widget into a QListView?

Please, read about:

How to display a scrollable list with a substantial amount of widgets as items in a Qt C++ app?


I want to show every log message in the above format

Solution

To achieve the desired result and stay away from performance issues, even with a very long data log, use a QListView with a custom delegate:

  1. Create a subclass of QStyledItemDelegate, say Delegate

  2. Reimplement the QStyledItemDelegate::paint method to do the custom drawing

  3. Reimplement the QStyledItemDelegate::sizeHint to report the correct size of the items in the list

  4. Use the custom delegate in the view by calling QAbstractItemView::setItemDelegate

Example

I have prepared a working example for you in order to demonstrate how the proposed solution could be implemented and used in an application.

The essential part of the example is the way the delegate paints the items in the list view:

void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                     const QModelIndex &index) const
{
    QStyleOptionViewItem opt(option);
    initStyleOption(&opt, index);

    const QPalette &palette(opt.palette);
    const QRect &rect(opt.rect);
    const QRect &contentRect(rect.adjusted(m_ptr->margins.left(),
                                               m_ptr->margins.top(),
                                               -m_ptr->margins.right(),
                                               -m_ptr->margins.bottom()));
    const bool lastIndex = (index.model()->rowCount() - 1) == index.row();
    const bool hasIcon = !opt.icon.isNull();
    const int bottomEdge = rect.bottom();
    QFont f(opt.font);

    f.setPointSize(m_ptr->timestampFontPointSize(opt.font));

    painter->save();
    painter->setClipping(true);
    painter->setClipRect(rect);
    painter->setFont(opt.font);

    // Draw background
    painter->fillRect(rect, opt.state & QStyle::State_Selected ?
                          palette.highlight().color() :
                          palette.light().color());

    // Draw bottom line
    painter->setPen(lastIndex ? palette.dark().color()
                              : palette.mid().color());
    painter->drawLine(lastIndex ? rect.left() : m_ptr->margins.left(),
                      bottomEdge, rect.right(), bottomEdge);

    // Draw message icon
    if (hasIcon)
        painter->drawPixmap(contentRect.left(), contentRect.top(),
                            opt.icon.pixmap(m_ptr->iconSize));

    // Draw timestamp
    QRect timeStampRect(m_ptr->timestampBox(opt, index));

    timeStampRect.moveTo(m_ptr->margins.left() + m_ptr->iconSize.width()
                         + m_ptr->spacingHorizontal, contentRect.top());

    painter->setFont(f);
    painter->setPen(palette.text().color());
    painter->drawText(timeStampRect, Qt::TextSingleLine,
                      index.data(Qt::UserRole).toString());

    // Draw message text
    QRect messageRect(m_ptr->messageBox(opt));

    messageRect.moveTo(timeStampRect.left(), timeStampRect.bottom()
                       + m_ptr->spacingVertical);

    painter->setFont(opt.font);
    painter->setPen(palette.windowText().color());
    painter->drawText(messageRect, Qt::TextSingleLine, opt.text);

    painter->restore();
}

The complete code of the example is available on GitHub.

Result

As written, the given example produces the following result:

Window with a message logger

Strang answered 1/11, 2018 at 22:37 Comment(4)
Thanks for the reply! But I think Qt makes it very complex to achieve this functioanilty, It is not a good Model and View concept, all things need to be done by a lot of code, all painting is done manually. For a simple list, you need to write a lot of codes and files. I think it should be like Android ListView, which only needs a list of Model and the items view, all the things are done inside the ListView engine. Which is largely decreasing the complexity of code and less chance of the BUG.Yvette
@BahramdunAdil, Qt widgets make it easy to create a natively looking GUI. When you want a custom looking one, you either use custom delegates for the item views (as in my answer), or create a custom style, or you go with QML. On SO there is a lot of information about each approach. However, to discuss them all in one topic is out of the scope of the site. Your question was too broad in the first place and it was a challenge to narrow it down to something answerable, which I did, plus providing a working example of how to achieve what you want. Please, do not make the question even broader.Strang
How do you calculate the hight if the details message does not have a specific length? QSize(index.data(Qt::SizeHintRole).toSize().width(), 64); Here you hardcoded 64, but it is not something I want, because log message can be very long or very short depends on system what log it sends.Yvette
GTK4's model/view system did this far better, AFAICT. There, a model emits signals thru which the user can specify how to setup/bind/teardown a widget per visible model item - so we keep all functionality of labels, buttons, layout mgrs, etc. - rather than having to drop to manual drawing and lose (or reinvent) normal widgets' layout, theming, etc. Sad!Eduction

© 2022 - 2025 — McMap. All rights reserved.