1

使用qt6.2.3开发列表,发现在删除行的时候,会出现无响应,CPU飙升,如下图所示

image.png

经过排查,最终定位为QAbstractItemModelPrivate::rowsRemoved方法,如下图所示

image.png

当前persistent_moved的大小为table的(row * column)

persistent_moved是在QAbstractItemModelPrivate::rowsAboutToBeRemoved里由persistent.indexes决定的,如下图所示

image.png

通过抓取窗体windows消息,发现是由WM_GETOBJECT导致persistent.indexes飙升,参考https://learn.microsoft.com/zh-cn/windows/win32/winauto/wm-ge...,UI自动化程序获取到了窗体句柄,QT处理该消息,进而持久化index

image.png

原因

有自动化程序调用了如下代码(python):

import uiautomation as ui
MainWin = ui.WindowControl(searchDepth=1, ClassName='TaskManagerWindow')
DataGrid = MainWin.DataGridControl(searchDepth=5, ClassName='TmScrollViewer')
TaskList = DataGrid.GetChildren()

解决方法

  1. 重载QMainWindow的nativeevent方法,拦截WM_GETOBJECT消息即可
  2. 不使用endremoverows,删除也使用beginresetmodel()和endresetmodel()
  3. 在main函数里自定义QAcessible的updateHandler方法,使之不做任何事

    QAccessible::installUpdateHandler([](QAccessibleEvent* event) {
     // do nothing
    });

测试代码


#include "mainwindow.h"

#include <QApplication>
#include <QAccessible>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);


    QAccessible::installUpdateHandler([](QAccessibleEvent* event) {
        // do nothing
    });

    MainWindow w;
    w.show();

    return a.exec();
}
#ifndef MYMODEL_H
#define MYMODEL_H

#include <QAbstractTableModel>

class MyModel : public QAbstractTableModel {
  public:
    explicit MyModel(QObject* parent = nullptr);
    int getPILSize() { return this->persistentIndexList().size(); }

    int rowCount(const QModelIndex& parent = QModelIndex()) const;
    int columnCount(const QModelIndex& parent = QModelIndex()) const;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
};

#endif  // MYMODEL_H
#include "mymodel.h"

MyModel::MyModel(QObject *parent)
    : QAbstractTableModel{parent}
{}

int MyModel::rowCount(const QModelIndex &parent) const
{
    return 100;
}

int MyModel::columnCount(const QModelIndex &parent) const
{
    return 20;
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    return 1;
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MyModel;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result);


private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    MyModel * m_model;
    int m_getobjectcount = 0;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"

#include "mymodel.h"
#include "ui_mainwindow.h"
#ifdef Q_OS_WIN
#    include <Windows.h>
#    include <windowsx.h>
#    pragma comment(lib, "user32.lib")
#endif

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    m_model = new MyModel(this);
    ui->tableView->setModel(m_model);
}

MainWindow::~MainWindow() { delete ui; }

bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) {
    MSG* msg = static_cast<MSG*>(message);
    if (msg->message == WM_GETOBJECT) {
        ++m_getobjectcount;
    }
    return QMainWindow::nativeEvent(eventType, message, result);
}

void MainWindow::on_pushButton_clicked() {
    int v = m_model->getPILSize();
    ui->label->setText(QString::number(v));
    ui->label_2->setText(QString::number(m_getobjectcount));
}
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout_2">
    <item>
     <layout class="QVBoxLayout" name="verticalLayout">
      <item>
       <layout class="QGridLayout" name="gridLayout">
        <item row="3" column="1">
         <widget class="QLabel" name="label">
          <property name="text">
           <string>0</string>
          </property>
         </widget>
        </item>
        <item row="4" column="0">
         <widget class="QLabel" name="label_4">
          <property name="text">
           <string>WM_GETOBJECT_COUNT</string>
          </property>
         </widget>
        </item>
        <item row="4" column="1">
         <widget class="QLabel" name="label_2">
          <property name="text">
           <string>0</string>
          </property>
         </widget>
        </item>
        <item row="3" column="0">
         <widget class="QLabel" name="label_3">
          <property name="text">
           <string>PerIndexCount</string>
          </property>
         </widget>
        </item>
        <item row="2" column="0">
         <widget class="QPushButton" name="pushButton">
          <property name="text">
           <string>Refresh</string>
          </property>
         </widget>
        </item>
       </layout>
      </item>
      <item>
       <widget class="QTableView" name="tableView"/>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

点墨
26 声望3 粉丝

全栈前端开发工程师


引用和评论

0 条评论