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?

scopchanov
  • 7,966
  • 10
  • 40
  • 68
Bahramdun Adil
  • 5,907
  • 7
  • 35
  • 68
  • 1
    For my log view, I chose a different option: I use a [`QTextEdit`](https://doc.qt.io/qt-5/qtextedit.html) 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. – Scheff's Cat Nov 01 '18 at 16:56
  • 2
    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. – Scheff's Cat Nov 01 '18 at 17:00

1 Answers1

20

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

Community
  • 1
  • 1
scopchanov
  • 7,966
  • 10
  • 40
  • 68
  • 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. – Bahramdun Adil Nov 02 '18 at 14:35
  • 2
    @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. – scopchanov Nov 02 '18 at 16:04
  • 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. – Bahramdun Adil Dec 03 '18 at 14:41