Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. Qt Contribution
  4. Ideas to optimise QAbstractItemView::dataChanged
Forum Updated to NodeBB v4.3 + New Features

Ideas to optimise QAbstractItemView::dataChanged

Scheduled Pinned Locked Moved Qt Contribution
23 Posts 3 Posters 10.3k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • VRoninV Offline
    VRoninV Offline
    VRonin
    wrote on last edited by VRonin
    #21

    @kshegunov said in Ideas to optimise QAbstractItemView::dataChanged:

    Okay, this is my bad then.

    I see what you mean. if a view only reimplemented paintEvent it might break due to this change. fair point probably a note for future releses only then

    To summarise where we are, the current "idea" of QAbstractItemView::dataChanged is:

    void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
    {
        Q_UNUSED(roles)
        // Single item changed
        Q_D(QAbstractItemView);
        if (topLeft == bottomRight && topLeft.isValid()) {
            const QEditorInfo &editorInfo = d->editorForIndex(topLeft);
            //we don't update the edit data if it is static
            if (!editorInfo.isStatic && editorInfo.widget) {
                QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft);
                if (delegate) {
                    delegate->setEditorData(editorInfo.widget.data(), topLeft);
                }
            }
            if (isVisible() && !d->delayedPendingLayout) {
                // otherwise the items will be update later anyway
                update(topLeft);
            }
        } else {
            d->updateEditorData(topLeft, bottomRight);
            if (isVisible() && !d->delayedPendingLayout)  {
                const QRect viewportRect = d->viewport->rect(); 
                const QModelIndex parent = topLeft.parent();
                const QRect cornerRect = visualRect(topLeft) | visualRect(bottomRight);
                // If dirty is 1/4th or more of the viewport rect, just trigger a full update
                const QRect cornerRectViewPort = cornerRect & viewportRect;
                if(cornerRectViewPort.width() * cornerRectViewPort.height() * 4 > viewportRect.width() * viewportRect.height())  {
                    d->viewport->update();
                }
                else  { // Just fall back to iterating over the model indices
                        QRect dirty(cornerRect);
                        const int maxRow = bottomRight.row();
                        for (int i = topLeft.row(); i < maxRow; ++i)  {
                            const int maxColumn = bottomRight.column() - ((i==maxRow-1) ? 1:0);
                            for (int j = topLeft.column()+ ((i==topLeft.row()) ? 1:0); j < maxColumn; ++j)  {
                                dirty |= visualRect(d->model->index(i, j, parent));
                            }
                        }
                        dirty &= viewportRect;
                        if (!dirty.isEmpty())
                            d->viewport->update(dirty);
                }
            }
        }
    #ifndef QT_NO_ACCESSIBILITY
        if (QAccessible::isActive()) {
            QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::DataChanged);
            accessibleEvent.setFirstRow(topLeft.row());
            accessibleEvent.setFirstColumn(topLeft.column());
            accessibleEvent.setLastRow(bottomRight.row());
            accessibleEvent.setLastColumn(bottomRight.column());
            QAccessible::updateAccessibility(&accessibleEvent);
        }
    #endif
        d->updateGeometry();
    }
    

    EDIT:

    Avoid calculating visualRect for the corners twice in the iteration case

    "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
    ~Napoleon Bonaparte

    On a crusade to banish setIndexWidget() from the holy land of Qt

    kshegunovK 1 Reply Last reply
    1
    • Christian EhrlicherC Christian Ehrlicher

      If someone has a good idea how to improve the default implementation in QAbstractItemModel - I'm open for suggestions

      kshegunovK Offline
      kshegunovK Offline
      kshegunov
      Moderators
      wrote on last edited by
      #22

      @Christian-Ehrlicher
      Even though it's not strictly the model API we were discussing, have you any thoughts on the proposed snippet? would it be worth changing like this?

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      1
      • VRoninV VRonin

        @kshegunov said in Ideas to optimise QAbstractItemView::dataChanged:

        Okay, this is my bad then.

        I see what you mean. if a view only reimplemented paintEvent it might break due to this change. fair point probably a note for future releses only then

        To summarise where we are, the current "idea" of QAbstractItemView::dataChanged is:

        void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
        {
            Q_UNUSED(roles)
            // Single item changed
            Q_D(QAbstractItemView);
            if (topLeft == bottomRight && topLeft.isValid()) {
                const QEditorInfo &editorInfo = d->editorForIndex(topLeft);
                //we don't update the edit data if it is static
                if (!editorInfo.isStatic && editorInfo.widget) {
                    QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft);
                    if (delegate) {
                        delegate->setEditorData(editorInfo.widget.data(), topLeft);
                    }
                }
                if (isVisible() && !d->delayedPendingLayout) {
                    // otherwise the items will be update later anyway
                    update(topLeft);
                }
            } else {
                d->updateEditorData(topLeft, bottomRight);
                if (isVisible() && !d->delayedPendingLayout)  {
                    const QRect viewportRect = d->viewport->rect(); 
                    const QModelIndex parent = topLeft.parent();
                    const QRect cornerRect = visualRect(topLeft) | visualRect(bottomRight);
                    // If dirty is 1/4th or more of the viewport rect, just trigger a full update
                    const QRect cornerRectViewPort = cornerRect & viewportRect;
                    if(cornerRectViewPort.width() * cornerRectViewPort.height() * 4 > viewportRect.width() * viewportRect.height())  {
                        d->viewport->update();
                    }
                    else  { // Just fall back to iterating over the model indices
                            QRect dirty(cornerRect);
                            const int maxRow = bottomRight.row();
                            for (int i = topLeft.row(); i < maxRow; ++i)  {
                                const int maxColumn = bottomRight.column() - ((i==maxRow-1) ? 1:0);
                                for (int j = topLeft.column()+ ((i==topLeft.row()) ? 1:0); j < maxColumn; ++j)  {
                                    dirty |= visualRect(d->model->index(i, j, parent));
                                }
                            }
                            dirty &= viewportRect;
                            if (!dirty.isEmpty())
                                d->viewport->update(dirty);
                    }
                }
            }
        #ifndef QT_NO_ACCESSIBILITY
            if (QAccessible::isActive()) {
                QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::DataChanged);
                accessibleEvent.setFirstRow(topLeft.row());
                accessibleEvent.setFirstColumn(topLeft.column());
                accessibleEvent.setLastRow(bottomRight.row());
                accessibleEvent.setLastColumn(bottomRight.column());
                QAccessible::updateAccessibility(&accessibleEvent);
            }
        #endif
            d->updateGeometry();
        }
        

        EDIT:

        Avoid calculating visualRect for the corners twice in the iteration case

        kshegunovK Offline
        kshegunovK Offline
        kshegunov
        Moderators
        wrote on last edited by
        #23

        After some code-mining the above loop should be equivalent to:

        for (int i = topLeft.row(); i < maxRow; ++i)  {
            const int maxColumn = bottomRight.column() - ((i==maxRow-1) ? 1:0);
            for (int j = topLeft.column()+ ((i==topLeft.row()) ? 1:0); j < maxColumn; ++j)  {
                d->viewport->update(visualRect(d->model->index(i, j, parent)));
            }
        }
        

        The updates are compressed trough QWidgetBackingStore::markDirty. I know the docs mention this but I couldn't find it in QCoreApplication::compressEvent so I went spelunking, albeit rather short lived.

        Read and abide by the Qt Code of Conduct

        1 Reply Last reply
        1

        • Login

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt Extensions
        • Unsolved