问题描述
在我目前正在使用的专有代码库中,有一个自定义列表视图(派生自QListView
形式)。
当前,当许多项目(> 10000)使主线程冻结时,它会出现问题。 视图中的每个项目都是在QtDesigner中设计的自定义窗口小部件。
要渲染每一行,我们使用setIndexWidget
,它在qabstractitemmodel::rowsInserted
信号上被调用。对于每个插入的行,为每个索引设置从first
到last
的自定义窗口小部件。
我试图移植此代码以使用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示例太简单了,我不明白如何使用委托绘制自定义窗口小部件。
解决方法
我尝试使用的最佳解决方法是,当鼠标移动或委托接收事件时,在静态渲染的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
方法并实现虚拟化(仅绘制屏幕上看到的内容)来呈现列表。