Qt 5.15 — QSortFilterProxyModel с сбоем QAbstractTableModel при изменении сигнала

#c #qt #view #model #qml

#c #qt #Вид #Модель #qml

Вопрос:

Я внедрил пользовательскую QAbstractTableModel и запустил ее через QAbstractItemModelTester, и в моей модели больше нет проблем. Однако сейчас я пытаюсь реализовать сортировку через QSortFilterProxyModel, и, похоже, у меня вообще ничего не получается.

 void RDMSensorModels::UpdateDevice(ArtNet::ArtRdmDevice* rdmDev, const RDM::RDMProcessor::RDMDeviceModelamp; 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;
            }
        }
    }
}
 

Это функция, которая выдает сигнал изменения данных модели, и я не думаю, что здесь есть проблема, но после того, как этот сигнал выдается, программа завершает работу внутри внутреннего обработчика изменения данных QSortFilterProxyModels

Поскольку я пока не могу вставлять изображения в свои вопросы, вот ссылка на то, где отладчик прерывается внутри QSortFilterProxyModel

Самое странное в этом то, что независимо от того, что я передаю сигналу dataChanged, proxy_columns внутри QSortFilterProxyModel всегда пуст.

Здесь вы можете видеть в отладчике, что контейнер пуст

Если это поможет, вот моя реализация 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 amp;sourceParent) const override;
    bool lessThan(const QModelIndex amp;left, const QModelIndex amp;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 QModelIndexamp; sourceParent) const { return true; }

bool RDMSensorSortFilterProxyModel::lessThan(const QModelIndexamp; left, const QModelIndexamp; 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 QModelIndexamp; parent) const {
        if (parent.isValid())
            return 0;
        return m_RDMDevices.count();
    }

    int RDMSensorModels::columnCount(const QModelIndexamp; parent) const {
        if (parent.isValid())
            return 0;
        return m_ColumnCount;
    }

    QVariant RDMSensorModels::data(const QModelIndexamp; index, int role) const {
        if (!index.isValid())
            return {};

        int deviceIndex = index.row();

        switch (role) {
            case SensorGraphReadingsRole: {
                autoamp; 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 amp;amp; (index.column() <= sensorCount);
            }
            default: return {};
        }
    }

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

Любая помощь будет с благодарностью!

Комментарии:

1. Возможно, связано … ваша lessThan реализация не соответствует строгой политике слабого упорядочения. If QSortFilterProxyModel использует контейнеры стандартной библиотеки C , что может привести к неопределенному поведению. Происходит ли сбой, если вы используете обычный QSortFilterProxyModel , а не свой собственный производный класс?

2. @G.M. Я только что переключил свою реализацию на QSortFilterProxyModel s_RDMSensors = new RDMSensorModels(); s_RDMSensorProxyModel = new QSortFilterProxyModel(); s_RDMSensorProxyModel->setSourceModel(s_RDMSensors); , инициализированную следующим образом. Все еще сбой, то же место, та же проблема

3. Происходит ли сбой только при подключении к прокси-модели или он также завершается сбоем, если вы подключаете модель непосредственно к представлению? Для отладки я бы предложил создавать начальные и конечные индексы отдельно, по одному за раз, прежде чем вы выполните вызов emit dataChanged(...) . Затем вы можете проверить, что индексы действительно действительны, прежде чем передавать их dataChanged() .

4. @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 не обнаружил эту проблему, как я ожидал, поскольку моя модель изменяет количество столбцов в зависимости от наибольшего количества датчиков для найденных в настоящее время устройств.