正如我从Qt文档中了解到的那样,在Qt模型/视图框架中,模型及其附加视图应该存在于同一个(GUI)线程中。这可能会导致一些不良影响,如下所示。我已经QAbstractTableModel
子类化并实现了所需的虚函数。在内部,该模型对具有大量记录的sqlite数据库进行查询,并通过重新实现的data()
函数向附加的视图提供相应的数据。
现在,在 GUI 中我有一个QTableView
,我将其附加到该模型。我还有一个QLineEdit
输入字段。在此字段中打印文本会发出textChanged()
信号,该信号连接到模型的(自定义)query()
槽。这样,在输入字段中键入新字符应使用与键入的短语匹配的记录更新表。
好吧,由于我的数据库很大,我不希望在键入另一个字母后立即更新表-更新等待查询完成,这可能需要一两秒钟。
但让我困扰的是,由于我必须在同一个 GUI 线程中拥有模型和表格,因此输入字段也会在每个字母之后出现,直到表格更新。我想让它能够在没有冻结效果的情况下键入短语,然后等待表格更新。仅当按 Enter 键入整个短语时才通知模型对我来说不是一个选择 - 我需要textChanged()
信号才能工作。
所以后来我想 - 如果我忽略文档并将模型放入非 GUI 线程中,是否会对 Qt 造成很大的冒犯?令我惊讶的是,它奏效了!现在打字不会冻结,程序也不会崩溃(至少目前是这样)。
所以我的问题是 - 在非 GUI 线程中使用模型是否仍然不安全,我的程序可能会在其他任何一天突然崩溃?我还应该提到,我想以只读方式使用该模型。如果我需要更改模型底层的数据,我不会使用 view/delegates 来执行此操作,我只会向模型的线程发送适当的信号,所有更改都将在该线程内执行。
想象一下删除最后一行的示例:
同步(同线程)
emit beginRemoveRows(int r = last row)
- 视图反应并删除对 R 的引用
- 从模型中删除 R
endRemoveRows()
- 视图知道它可能会重新粉刷
异步(不同的线程)
emit beginRemoveRows(r)
- 从模型中删除 R
endRemoveRows()
这两个信号都在 GUI 线程的事件队列中。
如果 GUI 事件队列在beginRemoveRows()
之前包含重绘事件,则视图将调用model->data(r)
并且程序可能会崩溃*。
(*) 或者至少遇到了data()
实现的安全性,但引擎盖下还有其他事情,比如QPersistentModelIndex
你无法控制......