From what I can see, the behavior any Range
-based widget is defined by an Adjustements
one.
Since there seems to be no official way to restrict the range to a few discrete values, I have tried a lots of ways to actually reset the values.
What works, but is not optimal, is simply to take the current value and determine which valid discrete value is the nearest from that continuous value and use that instead.
But what I would like is to visually freeze the slider to its current position until the user grabs it far enough and then change its value at once to the next valid value.
I want the user to understand and feel that these are discrete values. The problem with my working solution above is that the 3 users who tested the program told me that I should put more figures in the number I displayed, thinking the change was continuous.
NB: by "official", I mean either a "hidden" option or subclass of the Adjustements
class if at all available. If not, any way that is reasonably efficient to achieve such a requirement (i.e. that don't use 50% of CPU for a simple slider!). In particular, I am not asking for the "ultimate best way" to do this.
It seems you can just override the change-value
signal:
class DiscreteScale(Gtk.Scale):
def __init__(self, values, *args, **kwargs):
super().__init__(*args, **kwargs)
values.sort()
self.values = values
adjustment = self.get_adjustment()
adjustment.set_lower(values[0])
adjustment.set_upper(values[-1])
self.__changed_value_id = self.connect('change-value', self.__change_value)
def __change_value(self, scale, scroll_type, value):
# find the closest valid value
value = self.__closest_value(value)
# emit a new signal with the new value
self.handler_block(self.__changed_value_id)
self.emit('change-value', scroll_type, value)
self.handler_unblock(self.__changed_value_id)
return True #prevent the signal from escalating
def __closest_value(self, value):
return min(self.values, key=lambda v:abs(value-v))
A little demo:
w = Gtk.Window()
s = DiscreteScale([0, 1, 5, 20, 22])
w.add(s)
w.set_size_request(500, 50)
w.connect('delete-event', Gtk.main_quit)
w.show_all()
Gtk.main()