【问题标题】:How to use custom widget with model/view/delegates in Qt5?如何在 Qt5 中使用带有模型/视图/委托的自定义小部件?
【发布时间】:2020-12-08 09:36:05
【问题描述】:

在我目前使用的专有代码库中,有一个自定义列表视图(派生形式 QListView)。

目前当大量项目 (>10000) 使主线程冻结时会出现问题。 视图中的每个项目都是在 QtDesigner 中设计的自定义小部件。

为了渲染每一行,我们使用setIndexWidget,它在QAbstractItemModel::rowsInserted 信号上调用。对于每个插入的行,为每个索引设置从firstlast 的自定义小部件。

我尝试将此代码移植到使用QStyledItemDelegate,因为断开项目小部件与实际模型的连接似乎可以解决渲染缓慢的问题。

在这种情况下,Qt5 可以按需懒惰地渲染视图中的项目。在显示列表之前,我们不需要为视图创建每个小部件。

我使用派生自QStyledItemDelegate 的类取得了初步成果。我在构造函数中创建了一个列表项小部件,然后像这样覆盖绘制事件。

    void paint(QPainter *painter, const QStyleOptionViewItem &option,const QModelIndex &index) const override {
        auto baseWid = getBaseWidget(); // Get's list item widget pointer
        setSubEditorData(baseWid,index); // Set's it's state to display current item
        baseWid->resize(option.rect.size());
        QPixmap pixmap(option.rect.size());
        baseWid->render(&pixmap);
        painter->drawPixmap(option.rect, pixmap);
    }

这对于静态内容来说已经足够了,但是我的小部件有复选框并且可以被选中。

我真的不明白如何使它具有交互性,同时保留代表提供的好处(按需渲染等)。

我的问题是如何让委托处理用户事件?就像鼠标点击一样,选择会发生变化。

Qt5 的例子覆盖委托太简单了,我不明白如何用委托绘制一个自定义的小部件。

【问题讨论】:

  • 为什么它一次插入 10K 小部件而不是按需获取它们?我认为您应该修复模型而不是委托。
  • @vahancho 遗留代码
  • 能否覆盖原始模型并在视图中替换它?
  • @vahancho 是的,有可能。

标签: c++ qt qt5 qtwidgets


【解决方案1】:

我尝试使用的最佳解决方法是在鼠标移动或委托接收事件时在静态呈现的 QPixmap 和真实小部件之间动态切换。

首先我覆盖QAbstractItemDelegate::editorEvent,所以当代理收到任何事件时,它会切换到QModelIndex的真实小部件。

使用QAbstractItemView::openPersistentEditor 切换到真实的小部件。之后调用QAbstractItemDelegate::createEditor 会自动调用获取小部件。

bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
{
    auto view = qobject_cast<NyView*>(parent());
    view->openPersistentEditor(index);
    return Base::editorEvent(event, model, option, index);
}

在我看来,我还启用了所有描述的打开编辑器的方式。

this->setEditTriggers(QAbstractItemView::EditTrigger::AllEditTriggers);
this->setMouseTracking(true);
connect(this, &MyView::entered, this, &MyView::openPersistentEditor);

当鼠标悬停在小部件上时会发出QAbstractItemView::entered 信号。

从用户的角度来看,没有任何变化,在他们可以与列表项交互之前,它已经动态替换为真实的小部件。

一些垃圾收集策略似乎也是必要的,因为如果用户将鼠标悬停在许多小部件上,即使他们很长时间没有与之交互,它们也会保留在内存中。对于已删除的行,编辑器会自动销毁,但这可能还不够。

基于 Qt5 的开源软件 (Telegram Desktop) 无需使用小部件即可呈现大型列表。他们手动渲染列表,覆盖 QWidget::paint 方法并实现虚拟化(仅绘制屏幕上看到的内容)。

【讨论】:

  • 我实际上打算推荐一个与此类似的解决方案。在填充容器小部件时创建自定义小部件可能非常昂贵。我看到您已将MyView::entered() 连接到MyView::openPersistenEditor(),但我认为您还应该调用MyView::closePersistentEditor() 以删除小部件。否则你可能会发生内存泄漏。这是因为createEditor() 正在堆上创建小部件,为了释放内存,必须调用QWidget::close()QWidget::deleteLater()
猜你喜欢
  • 2023-03-11
  • 2011-11-06
  • 2016-01-26
  • 1970-01-01
  • 1970-01-01
  • 2016-08-25
  • 1970-01-01
相关资源
最近更新 更多