• 4247阅读
  • 6回复

[提问]Qt4升级到Qt5时碰到的一个问题 [复制链接]

上一主题 下一主题
离线stlcours
 

只看楼主 倒序阅读 楼主  发表于: 2015-05-04
我做的一个上传文件的软件,从4.86升级到5.32,突然出现了一个问题
以前每次上传文件的一部分,就发信号刷新界面,这样可以在进度条里显示上传进度。但是自从升级以后,这个进度条就很少更新了(几乎不更新)。我在网上搜到一个线索:
Qt 5中,少量virtual函数有了变化。这不会在迁移时构成编译错误(除非纯虚函数的改变),但是会在运行时引发异常(因为重写的函数不能被执行)。其中一个是,QAbstractItemView::dataChanged 信号有了一个参数
https://blog.qt.io/cn/2012/07/09/porting-from-qt-4-to-qt-5/

但是又仔细看了看官方文档:
dataChanged函数是多了第三个参数,可是文档也说的很清楚,如果第三个参数为空,代表着所有的角色都将被更新,那么应该不影响界面的更新。
http://doc.qt.io/qt-5/qabstractitemview.html#dataChanged

那么问题到底出在哪里呢?还有其它的可能吗?请各位指教一下吧,谢谢!


离线彩阳

只看该作者 1楼 发表于: 2015-05-04
运行异常的话,调试运行一下,然后错误的时候看看调用堆栈。
上海Qt开发联盟,热忱地欢迎你的加入!
离线stlcours

只看该作者 2楼 发表于: 2015-05-06
我在QT4里用的好好的程序,QT5里却无法显示进度条了,但也不出错
在网上搜索了很久,没发现QT5这方面有什么变化,所以就搞不懂了。看现象是emit dataChanged以后,view并没有真正更新。这是为什么呢

其实这个问题就出现在QT的官方博客里:
https://blog.qt.io/cn/2012/07/09/porting-from-qt-4-to-qt-5/
但是我觉得这篇文章好像也是答非所问。QAbstractItemView::dataChanged 信号多了一个参数,但如果这个参数默认为空,就会更新所有的roles,此时相当于对QT4的代码没有任何变化,文章里的两个方案好像根本不是方案啊,默认参数不写也成了一种错吗。

默认参数写不写,不是关键的问题。关键是emit这个信号以后,QT5中不起作用!这是为什么呢?百思不得其解。
离线stlcours

只看该作者 3楼 发表于: 2015-05-06
我的代码很简单,就是在主线程中直接调用这个函数,原先的代码是这样的,在QT4里工作的很好:
void TableModel::updateData(int i)
{
    if (i<0) return;
    // 根据指定行列,得到index
    QModelIndex t1 = index(i, 0);
    QModelIndex t2 = index(i, 5);
    qDebug() << "111_____TableModel::updateData " << i; // 后台打印进度条数据正确
    emit dataChanged(t1, t2); // view 系统自带信号,会导致刷新数据
}
但是以上代码在QT5里不工作,但是官方文档说dataChanged的新增第三个参数等于0就所有角色都刷新,我怀疑它说反了,所以就改成了这样:

void TableModel::updateData(int i)
{
    if (i<0) return;
    // 根据指定行列,得到index
    QModelIndex t1 = index(i, 0);
    QModelIndex t2 = index(i, 5);
    qDebug() << "111_____TableModel::updateData " << i;

    QVector<int> qq;
    qq.append(Qt::DisplayRole);
    qq.append(Qt::DecorationRole);
    qq.append(Qt::EditRole);
    qq.append(Qt::ToolTipRole);
    qq.append(Qt::StatusTipRole);
    qq.append(Qt::WhatsThisRole);
    qq.append(Qt::SizeHintRole);
    qq.append(Qt::AccessibleTextRole);
    qq.append(Qt::AccessibleDescriptionRole);
    qq.append(Qt::UserRole);
    emit dataChanged(t1, t2, qq);
}
结果还是不行。

于是没办法,又在主线程里尝试调用另一个函数:
void TableModel::refrushModel()
{
    beginResetModel(); // view 内置函数,这句是最关键的,可导致view刷新
    endResetModel();
    emit updateCount(this->rowCount(QModelIndex())); // 入口,在model里发信号广播。
}
这下进度条终于刷新了,因为调用beginResetModel和endResetModel后,整个table的数据都被刷新了。这样做有3个坏处:
1. 效率很低,我不需要刷新整个table
2. 整体刷新导致鼠标选中某一行信息丢失
3. 进度条走到最后99%的时候。。。居然还是程序会不响应。。。(虽然是死机,但我觉得不是我的问题,QT4里在找到emit datachanged方法之前也曾这样做过,但没有死机的现象)
所以整体刷新的方案不可取。

正常的逻辑是emit dataChanged(t1, t2, qq);后,QT框架应该会自动调用我重写的void QStyledItemDelegate::paint()函数,现在初步怀疑是QT5里没有把datachanged信号与paint函数联系起来,或者文档说明哪里有遗漏?

另外有一个疑点是,我其实是间接在多线程里(但上了锁的,应该不会有问题,只是我对UI线程的理解还不够深刻,第二个线程直接更新UI界面这样对吗)调用TableModel::updateData(int i),也可能导致潜在的UI问题。但是毕竟这个问题在QT4下从来没有出现过,而QT5下每次都出错,我想应该不是QT5的多线程改变后的结果吧(也没听说QT5对多线程有什么改动)。


离线toby520

只看该作者 4楼 发表于: 2015-05-07
你最好能发个小例子出来,大家可以帮忙调试下,这样看 实在没有办法解决问题
QtQML多多指教开发社区 http://qtclub.heilqt.com
将QtCoding进行到底
关注移动互联网,关注金融
开发跨平台客户端,服务于金融行业
专业定制界面
群号:312125701   373955953(qml控件定做)
离线stlcours

只看该作者 5楼 发表于: 2015-06-02
初步的原因算是明白了。model数据改变以后,调用view->update的确可以刷新,之前崩溃是因为我改写了这个函数,现在只要简单屏蔽就可以了。
离线stlcours

只看该作者 6楼 发表于: 2015-06-02
但是进一步的深层原因,我搜遍整个网络,初步怀疑是model里emit datachanged以后,没有被发送到view的datachanged槽函数(所以不会调用delegate的paint函数)。我仍然怀疑这是一个qt的bug。因为QT4里确实没有这个问题。而且在没有覆盖view的datachanged槽函数情况下(我也不知道该怎么改写),手动connect model的信号和view的槽函数以后,仍不能刷新界面。想要真正解决这个问题,基本上没别的办法了,只有对比两个不同QT版本的源代码。另外就是要做一个demo,才能让大家帮我确定这个问题。其实我已经提交了这个bug,他们也要求我做一个demo。
快速回复
限100 字节
 
上一个 下一个