Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
194 views
in Technique[技术] by (71.8m points)

c++ - Qt 5.15 - QSortFilterProxyModel with QAbstractTableModel crashing on dataChanged signal

I have implemented a custom QAbstractTableModel and I have run it through the QAbstractItemModelTester and there are no more issues in my model. However, I am now trying to implement sorting through a QSortFilterProxyModel and I can't seem to get anything working at all.

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;
            }
        }
    }
}

This is the function, which emits the models dataChanged signal and I dont think there is a problem here, but after this signal is emitted the program crashes inside QSortFilterProxyModels internal dataChanged handler

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

The weirdest thing about this is, that no matter what I pass to the dataChanged signal, the proxy_columns inside QSortFilterProxyModel is always empty.

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

If it's any help, here is my QSortFilterProxyModel implementation, its completely empty basically.

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;
}

Here are my QAbstractTableModel reimplemented functions

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; }

Any help would be greatly appreciated!

question from:https://stackoverflow.com/questions/65882845/qt-5-15-qsortfilterproxymodel-with-qabstracttablemodel-crashing-on-datachanged

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Well it turns out, trying to replicate the issue on a smaller scale made my brain neurons fire enough, that i figured out the problem. My model column count can change and it does change, however, I had not written anything that notifies about column count changing beginRemoveColumns and endRemoveColumns and beginInsertColumns and endInsertColumns. I implemented those in my code like so

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();
            }
        }
    }

And the proxy model now works as expected. Hopefully this helps anyone else having issues with QSortFilterProxyModel.

It's interesting to note that the QAbstractItemModelTester did not catch this problem as I would have expected it to as my model changes column count depending on the largest sensor count for devices currently found.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...