如果模型正在异步加载其数据,则不需要对视图进行子类化。这与视图的行为无关。
模型/视图 模式的整个目的是将模型和视图组件解耦,以增加灵活性和重用性。像那样对视图进行子类化,又将它们耦合在一起了。
我特别担心 onResult() 方法,因为我担心 "setModel" 可能不是线程安全的。
你是对的,setModel
不是线程安全的。您不应该从主线程以外的线程触摸任何 QWidget
,请参见 文档。但是,onResult
方法保证在视图所在的线程中调用(应该是主线程)。所以这里没有任何问题...
但是,看起来你正在从线程池调用的函数中创建模型。 如果你不在函数末尾将模型移动到主线程(很可能你没有这样做),你的模型将存在于一个不运行事件循环的线程中。它将无法接收事件,这只会引发问题。通常情况下,应避免在线程之间传递 QObject
(如果可能的话),并仅传递需要的数据结构。
我将从零开始,通过子类化QAbstractTableModel
来实现整个内容,这里是一个完整的最小示例:
![screenshot](https://istack.dev59.com/XnxPv.webp)
#include <QtWidgets>
#include <QtConcurrent>
#include <tuple>
class AsyncTableModel : public QAbstractTableModel{
Q_OBJECT
using RowsList = QList<std::tuple<QString, QString, QString> >;
RowsList m_rows;
QFutureWatcher<RowsList>* m_watcher;
public:
explicit AsyncTableModel(QObject* parent= nullptr):QAbstractTableModel(parent){
m_watcher = new QFutureWatcher<RowsList>(this);
connect(m_watcher, &QFutureWatcher<RowsList>::finished,
this, &AsyncTableModel::updateData);
QFuture<RowsList> future = QtConcurrent::run(&AsyncTableModel::retrieveData);
m_watcher->setFuture(future);
}
~AsyncTableModel() = default;
static RowsList retrieveData(){
QThread::sleep(2);
RowsList list;
for(int i=0; i<10; i++){
list.append(std::make_tuple(QString("A%0").arg(i),
QString("B%0").arg(i),
QString("C%0").arg(i)));
}
return list;
}
Q_SLOT void updateData(){
beginResetModel();
m_rows = m_watcher->future().result();
endResetModel();
}
int rowCount(const QModelIndex &parent) const {
if(parent.isValid()) return 0;
return m_rows.size();
}
int columnCount(const QModelIndex &parent) const {
if(parent.isValid()) return 0;
return 3;
}
QVariant data(const QModelIndex &index, int role) const {
QVariant value= QVariant();
switch(role){
case Qt::DisplayRole: case Qt::EditRole:
switch(index.column()){
case 0:
value= std::get<0>(m_rows[index.row()]);
break;
case 1:
value= std::get<1>(m_rows[index.row()]);
break;
case 2:
value= std::get<2>(m_rows[index.row()]);
}
break;
}
return value;
}
};
int main(int argc, char* argv[]){
QApplication a(argc, argv);
QTableView tv;
AsyncTableModel model;
tv.setModel(&model);
tv.show();
return a.exec();
}
#include "main.moc"
注意:
上面的示例展示了如何异步地将长时间阻塞线程的函数中加载数据到模型中。这适用于执行重型计算的函数。如果您的目标是通过网络加载数据,您应该使用QTcpSocket
/QNetworkAccessManager
提供的异步API,在这些情况下完全不需要使用线程池,但除此之外,一切都应该类似。