Reputation: 33
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
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!
Upvotes: 2
Views: 875
Reputation: 33
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.
Upvotes: 1