0

There is a self contained example of the problem:

Rectangle {
    id: rect
    width: 200
    height: 200
    property real v : 50
    onVChanged: console.log(v)
    Button {
        onClicked: scomp.createObject(rect)
    }
    Component {
        id: scomp
        Rectangle {
            id: sli
            anchors.fill: parent
            Column {
                Slider {
                    width: 200
                    minimumValue: 10
                    maximumValue: 100
                    value: rect.v
                    onValueChanged: rect.v = value
                }
                Button {
                    onClicked: sli.destroy()
                }
            }
        }
    }
}

Basically, every time the slider component is created to modify v it sets it to the slider's minimum value. Note that the slider will still work correctly to modify that value, and v will retain its proper value after the slider is closed, but the moment it is opened again, the value corrupts again.

Why is this happening, how to prevent it? It would seem that for some explainable reason, the slider's value property temporarily assumes its minimumValue value, but that doesn't look like adequate behaviour. Maybe a bug? The slider never really assumes the correct initial value, even if value: rect.v is moved before setting the minimum value.

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
IvanB
  • 115
  • 8

2 Answers2

2

It's not a bug in Slider, it's your usage of it:

onValueChanged: rect.v = value

If you add some more debug statements:

qml: maximumValue = 100 value = 0
qml: minimumValue = 10 value = 10
qml: value = 10
qml: v = 10
void __cdecl QQuickRangeModel::setValue(double) 10
qml: in Component.onCompleted of Slider: value = 10 minimumValue = 10 maximumValue = 100

Before it even gets a chance to complete loading, you've already assigned its value to v. The correct solution depends on what your requirements are, which you haven't mentioned. For example, one solution would be to specify the default value in the Slider instead, and bind v to value:

v: slider ? slider.value : 0

it seems that the implementation order has been poorly designed

How would you design it?


To update this answer, with the Slider from Qt Quick Controls 2, you should use the moved() signal to respond to the slider being dragged. Using onValueChanged for this purpose will usually result in issues.

Mitch
  • 23,716
  • 9
  • 83
  • 122
  • This is not a useful usage format. It may work in the tiny isolated example, but it is not possible to do this in production. minimumValue modifying value sounds like a bad design decision. It implies the user has used the slider to specify a value, but occurs without any user interaction. – IvanB Jan 23 '16 at 19:36
  • 1
    A better solution would be to set value first, value itself can push min and max values if it is out of bounds, that would be more appropriate - the slider accommodating to the usage scenario rather than generating bogus value changes. – IvanB Jan 23 '16 at 19:40
  • Yeah, binding data to UI elements surely ain't the greatest idea. And QtQuick Controls are far from perfect, they are but a faint shadow of QtWidgets, still immature and lagging far behind in terms of features. Controls are only good for rudimentary applications, I've tried using them in production and ended up implementing my components for practically every element I attempted to use. Styling issues across different platforms, behavior issues, IMO those are half-baked bait to lure QtWidgets users into QML adoption. Not worth the headache, just do your own slider. – dtech Jan 23 '16 at 23:02
  • Also Mitch - are you sure your solution works? Because it doesn't look like it does. Even if it was possible to address the `Slider` nested in the component by id, which it is not, the moment the slider is destroyed, any adjustment to the value would be lost and it would revert back to 0, which is not even in the correct bounds... Your answer is wrong in a bunch of ways. – dtech Jan 23 '16 at 23:13
  • My solution is untested. It was intended to demonstrate that there other ways of approaching the problem. However, the OP's requirements would have to be known first. Also, as I said, it's open source code. If you think the design is poor and you can do better (as is clearly the case from your comments), go ahead. Personally, I would be very interested to see both of your ideas in the form of patches. – Mitch Jan 24 '16 at 09:09
  • The example I provided is very specific and clearly illustrates the intended usage - the slider is only created to modify the value and afterwards deleted. Anyway, as advised, I implemented my own slider, it took 15 minutes and 50 lines of code, and it doesn't introduce the parasitic value change, so it can be used in the desired format. – IvanB Jan 24 '16 at 11:58
  • 1
    If your improved slider is as good as you say it is, why not contribute the improvements back into the supposedly poorly designed product that you use? – Mitch Jan 24 '16 at 12:09
  • @Mitch - I never said it is "good" - I said it doesn't suffer from that issue. Looking at how easy it was to avoid it, I'd say if the Qt people wanted to, they could do it even easier, since they have a lot more experience than me. "Contributing" to Qt is a tedious process, which I'd rather avoid and spare myself the wasted effort on rejected contribution, since according to Qt evangelists such as you, there is nothing wrong with the stock Slider. Also, it doesn't support Qt's platform specific styling, since my styling requirements are different. – IvanB Jan 24 '16 at 16:20
  • 1
    It's working as intended (that is, documented) from what I can see, yes. Gerrit is the place to go if you think it should be done differently. But it seems that most people would rather complain and criticise without putting forward actual solutions, claiming that the process is too tedious (which is odd, because anyone who can use Git and create an account on the internet can get started quite quickly). – Mitch Jan 24 '16 at 16:42
  • 1
    @Mitch - most people would complain, I sat down and solved the problem. The documentation says nothing about parasitic value changes which limit the usage scenarios. If you are such a dedicated community member, go ahead and fix the stock `Slider` - here is my code, you can borrow the parasitic value change workaround, I do not seek credit or anything in exchange: http://pastebin.com/i4wzvELR – IvanB Jan 24 '16 at 16:49
  • 1
    Thanks, but I don't have a problem with the slider, and have my own stuff to do. – Mitch Jan 24 '16 at 20:23
  • 1
    Yes, you're completely right. I'm not going to fix all of your problems for you. That is correct. What a terrible person I must be. Enjoy living life with that attitude! :) – Mitch Jan 25 '16 at 16:06
-1

Without digging in the implementation of the Slider element, it seems that the implementation order has been poorly designed, the initial value is 0.0, so setting the minimum value to anything higher pushes up the value as well. And it doesn't matter if value is bound to something higher than the minimum value, due to the order of evaluation, the value is always 0.0 when minimum value is set, so in this format the target value will always be corrupted to the slider's minimum value.

The solution I came up with avoids this behavior by delaying the target value binding, it works, but it is not as pretty, so I am still open to other solutions, meanwhile Qt guys - if you are seeing this you might wanna fix the Slider:

Slider {
    width: 200
    minimumValue: 10
    maximumValue: 100
    value: rect.v
    Component.onCompleted: valueChanged.connect(function(){ rect.v = value})
}

This format still initially pushes the slider value to the minimum value, but at this time the binding to set the target value does not exist yet, it is only created after the slider value assumes the correct value.

IvanB
  • 115
  • 8
  • You should not rely on `valueChanged` at all, since like all other handlers is called on each value change, even initial assignments. Bad design choice? You can argue on that but that's how it works currently. Instead bind the value to the slider value: the value is updated with the slider . Something like [this](http://pastebin.com/raw/62cXrMqz). – BaCaRoZzo Jan 23 '16 at 21:31
  • @BaCaRoZzo Your code is absurd, obviously the slider is destroyed after the adjustment is made, binding the target value to the slider value is the worst possible "solution" to the problem and in fact not even a solution. If the slider was properly implemented, setting the slider value to the target value would not loop back to the target value, since the value is the same. There is a parasitic value change in the slider, and yes, parasitic unintended behavior IS bad design. And it is that bad design that prevents the component from being used in the cleanest and most logical way. – IvanB Jan 23 '16 at 22:50
  • Not being functional aside, your solution actually suggests binding data to UI, which itself is very bad design. UI binds to data, data should not depend on UI. UI comes as goes as needed to visualize or interact with data. – IvanB Jan 23 '16 at 22:53
  • Binding data and UI is not good, in general, you are obviously right about that. – BaCaRoZzo Jan 24 '16 at 08:18