Qt 5.15 - 带有 QAbstractTableModel 的 QSortFilterProxyModel 在 dataChanged 信号上崩溃

Posted

技术标签:

【中文标题】Qt 5.15 - 带有 QAbstractTableModel 的 QSortFilterProxyModel 在 dataChanged 信号上崩溃【英文标题】:Qt 5.15 - QSortFilterProxyModel with QAbstractTableModel crashing on dataChanged signal 【发布时间】:2021-01-25 10:22:15 【问题描述】:

我已经实现了一个自定义 QAbstractTableModel,并且我已经通过 QAbstractItemModelTester 运行了它,并且我的模型中没有更多问题。但是,我现在正在尝试通过 QSortFilterProxyModel 实现排序,但我似乎无法得到任何工作。

void RDMSensorModels::UpdateDevice(ArtNet::ArtRdmDevice* rdmDev, const RDM::RDMProcessor::RDMDeviceModel& model, int pid) 
    if (s_RequiredPIDs.contains(pid)) 
        for (int i = 0; i < m_RDMDevices.size(); i++) 
            if (m_RDMDevices[i] == rdmDev) 
                emit dataChanged(createIndex(i, 0), createIndex(i, columnCount() - 1));
                return;
            
        
    

这是发出模型 dataChanged 信号的函数,我认为这里没有问题,但是在发出此信号后,程序在 QSortFilterProxyModels 内部 dataChanged 处理程序中崩溃

As I can't embed pictures in my questions yet, here is a link to where the debugger breaks inside QSortFilterProxyModel

最奇怪的是,无论我向 dataChanged 信号传递什么,QSortFilterProxyModel 中的 proxy_columns 始终为空。

Here you can see in the debugger, that the container is empty

如果有帮助,这里是我的 QSortFilterProxyModel 实现,它基本上是空的。

class RDMSensorSortFilterProxyModel final : public QSortFilterProxyModel 
    enum SortValue 
        MANUFACTUER_MODEL,
        UNIVERSE_DMXADDRESS,
    ;

public:
    RDMSensorSortFilterProxyModel(RDMSensorModels *sourceModel, QObject *parent = nullptr) : QSortFilterProxyModel(parent) 
        setSourceModel(sourceModel);
    

    int SortIndex();
    void SetSortIndex(int value);

protected:
    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;

 private:
    SortValue m_SortValue = MANUFACTUER_MODEL;
;
int RDMSensorSortFilterProxyModel::SortIndex()  return m_SortValue; 

void RDMSensorSortFilterProxyModel::SetSortIndex(int value) 
    m_SortValue = static_cast<SortValue>(value);
    invalidate();


bool RDMSensorSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const  return true; 

bool RDMSensorSortFilterProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const 
    auto leftDeviceManufacturer  = sourceModel()->data(left, RDMSensorModels::Roles::DeviceManufacturerRole).toString();
    auto rightDeviceManufacturer = sourceModel()->data(right, RDMSensorModels::Roles::DeviceManufacturerRole).toString();

    auto same = QString::compare(leftDeviceManufacturer, rightDeviceManufacturer, Qt::CaseInsensitive) == 0;

    return same;

这是我重新实现的 QAbstractTableModel 函数

QVariant RDMSensorModels::headerData(int section, Qt::Orientation orientation, int role) const 
        if (section < 1)
            return QString("Device");
        else
            return QString("Sensor %1").arg(section);
    

    int RDMSensorModels::rowCount(const QModelIndex& parent) const 
        if (parent.isValid())
            return 0;
        return m_RDMDevices.count();
    

    int RDMSensorModels::columnCount(const QModelIndex& parent) const 
        if (parent.isValid())
            return 0;
        return m_ColumnCount;
    

    QVariant RDMSensorModels::data(const QModelIndex& index, int role) const 
        if (!index.isValid())
            return ;

        int deviceIndex = index.row();

        switch (role) 
            case SensorGraphReadingsRole: 
                auto& readings  = m_RDMDevices[deviceIndex]->Sensors()[index.column() - 1]->LastReadings();
                auto maxElement = f_SensorMaxReading(index.row(), index.column() - 1);
                auto minElement = f_SensorMinReading(index.row(), index.column() - 1);

                QVariantList values;
                for (int i = 0; i < readings.size(); i++) 
                    values.push_back(Utils::Math::map(readings[i], maxElement, minElement, 0, 1));
                
                return values;
            
            case SensorMinReadingRole: return f_SensorMinReading(deviceIndex, index.column() - 1);
            case SensorMaxReadingRole: return f_SensorMaxReading(deviceIndex, index.column() - 1);

            case DeviceUIDRole: return f_DeviceUIDString(deviceIndex);
            case DeviceUniverseRole: return f_DeviceUniverseString(deviceIndex);
            case DeviceLabelRole: return f_DeviceLabelString(deviceIndex);
            case DeviceManufacturerRole: return f_DeviceManufacturerString(deviceIndex);
            case DeviceModelRole: return f_DeviceModelString(deviceIndex);

            case SensorRangeMaxValueRole: return f_SensorRangeMaxValueString(deviceIndex, index.column() - 1);
            case SensorRangeMinValueRole: return f_SensorRangeMinValueString(deviceIndex, index.column() - 1);
            case SensorCurrentValueRole: return f_SensorCurrentValueString(deviceIndex, index.column() - 1);
            case SensorNameRole: return f_SensorNameString(deviceIndex, index.column() - 1);
            case SensorCurrentValueNormalizedRole: return f_SensorCurrentValueNormalized(deviceIndex, index.column() - 1);
            case SensorMinNormalValueNormalizedRole: return f_SensorMinNormalValueNormalized(deviceIndex, index.column() - 1);
            case SensorMaxNormalValueNormalizedRole: return f_SensorMaxNormalValueNormalized(deviceIndex, index.column() - 1);

            case SensorValidRole: 
                auto sensorCount = f_DeviceSensorCount(deviceIndex);
                return sensorCount && (index.column() <= sensorCount);
            
            default: return ;
        
    

    QHash<int, QByteArray> RDMSensorModels::roleNames() const  return s_RoleNames; 

任何帮助将不胜感激!

【问题讨论】:

可能相关...您的lessThan 实现不遵守严格的弱排序策略。如果QSortFilterProxyModel 使用可能导致未定义行为的 C++ 标准库容器。如果您使用普通的QSortFilterProxyModel 而不是您自己的派生类,是否会发生崩溃? @G.M.我刚刚将我的实现切换到 QSortFilterProxyModel s_RDMSensors = new RDMSensorModels(); s_RDMSensorProxyModel = new QSortFilterProxyModel(); s_RDMSensorProxyModel-&gt;setSourceModel(s_RDMSensors); 像这样初始化。仍然崩溃,同样的地方,同样的问题 只有在连接到代理模型时才会崩溃,还是直接将模型连接到视图时也会崩溃?对于调试,我建议在调用emit dataChanged(...) 之前分别创建开始和结束索引,一次一个。然后,您可以在将索引传递给dataChanged() 之前检查它们是否确实有效。 @Carlton 是的,它只在代理模型上崩溃,我一直将模型直接连接到视图,只是现在我们需要创建排序和过滤,所以我尝试了 QSortProxyModel,它一直这样崩溃 【参考方案1】:

事实证明,试图在更小的范围内复制这个问题让我的大脑神经元足够活跃,我发现了问题所在。我的模型列数可以更改并且确实会更改,但是,我没有编写任何通知列数更改 beginRemoveColumns 和 endRemoveColumns 以及 beginInsertColumns 和 endInsertColumns 的内容。我在我的代码中实现了这些

void RDMSensorModels::UpdateColumnCount() 
        int sensorCount = 1;
        for (auto device : m_RDMDevices) 
            int deviceSensorCount = device->Sensors().size();
            if (deviceSensorCount + 1 > sensorCount)
                sensorCount = deviceSensorCount + 1; // +1 for device column
        

        if (m_ColumnCount != sensorCount) 
            if (m_ColumnCount < sensorCount) 
                beginInsertColumns(QModelIndex(), m_ColumnCount, sensorCount - 1);
                m_ColumnCount = sensorCount;
                endInsertColumns();
             else 
                beginRemoveColumns(QModelIndex(), sensorCount, m_ColumnCount - 1);
                m_ColumnCount = sensorCount;
                endRemoveColumns();
            
        
    

代理模型现在按预期工作。希望这可以帮助其他遇到 QSortFilterProxyModel 问题的人。

有趣的是,QAbstractItemModelTester 没有像我预期的那样发现这个问题,因为我的模型会根据当前找到的设备的最大传感器数量来更改列数。

【讨论】:

以上是关于Qt 5.15 - 带有 QAbstractTableModel 的 QSortFilterProxyModel 在 dataChanged 信号上崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Linux 5.15将修复早期补丁让软盘驱动器设备出错问题

Mac M1 安装qt6.X版本

Qt 6.4.2在Windows上安装过程及简单验证

Qt 6.4.2在Windows上安装过程及简单验证

QT5.15.2 在线安装下载速度慢的解决办法

练习5.15