Crash with ctkRangeWidget

xriobe
Thu, 31 Dec 2020 14:26:21 +0000
Development

Hi,

I have an issue with a wrong behavior of ctkRangeWidget that ends with the crash of Slicer 4.11.20200930 in Debug build (Ubuntu 20).

I think it is probably the same as this bug that has been closed without knowing how to reproduce:

mantisarchive.slicer.org

0002467: crash when scrolling through multivolumes - MantisBT

In my case it occurs in qMRMLVolumeThresholdWidget when using the sequence player if the frames have data with different scalar range values. At the change of frame, the threshold widget updates and call setRange(double min, double max), ending up in ctkRangeWidget::setRange where the slider and spinbox values are compared in a Q_ASSERT (ctkRangeWidget.cpp line 380):

Q_ASSERT(d->equal(d->Slider->maximumValue(), d->MaximumSpinBox->value()));

What happens is that a few lines above the call to
d->Slider->setRange(d->MaximumSpinBox->minimum(), d->MinimumSpinBox->maximum());
doesn’t always update the slider’s values.

As a side question about this line, shoudn’t it rather be d->Slider->setRange(d->MinimumSpinBox->minimum(), d->MaximumSpinBox->maximum()); ?

The reason why the values are not updated is because there are some rounding calculations with conversions to int during the process. It would be long to describe all here, but in ctkDoubleRangeSlider::setRange, the call to d->Slider->setRange(d->toInt(newMin), d->toInt(newMax)); converts the ranges to number of steps.

In an example with previous max range being 1272 and new one 1267, with a step of 10, it results in the same value rounded_int(1272/10) == rounded_int(1267/10) == 127, and in the implementation of the previous call to setRange, the “emit rangeChanged” is not done:

void QAbstractSlider::setRange(int min, int max)
{
Q_D(QAbstractSlider);
int oldMin = d->minimum;
int oldMax = d->maximum;
d->minimum = min;
d->maximum = qMax(min, max);
if (oldMin != d->minimum || oldMax != d->maximum) {
sliderChange(SliderRangeChange);
emit rangeChanged(d->minimum, d->maximum);
setValue(d->value); // re-bound
}
}

At the end, the slider (d->Slider->maximumValue()) and the spin box (d->MinimumSpinBox->maximum()) values are not synchronized and we are in the assert situation where 1267 != 1272.

I don’t really know what would be the correct fix for that.

should the method QAbstractSlider::setRange be overridden in ctkRangeSlider and use more info for the condition check?

should ctkDoubleRangeSlider or ctkRangeWidget force call ctkRangeSlider::onRangeChanged or ctkRangeSlider::setValues in this situation?

something else ?

9 posts - 3 participants