如何在微调器中允许无限整数值?

时间:2012-12-28 18:08:57

标签: python kivy

我需要一个Spinner窗口小部件,用户可以在其中选择具有特定步骤的整数值且没有下限或上限 (我的意思是,它们应该至少在十亿范围内,所以没有机会记住整个序列。)

我看到了kivy的Spinner小部件,但我认为做Spinner(values=itertool.count())这样的事情是行不通的。 它也仅限于字符串值。

是否有任何简单的方法可以获得与Qt QSpinBox类似的内容?

1 个答案:

答案 0 :(得分:2)

目前kivy似乎没有提供类似于SpinnerSpinBox的任何内容,或者您​​想要调用它。可能会使用的窗口小部件是Slider,但它看起来很糟糕,如果你想允许一个非常大的范围但步数很小,它就没用了。

因此我编写了自己的SpinBox实现:

class SpinBox(BoxLayout):
    """A widget to show and take numeric inputs from the user.

    :param min_value: Minimum of the range of values.
    :type min_value: int, float
    :param max_value: Maximum of the range of values.
    :type max_value: int, float
    :param step: Step of the selection
    :type step: int, float
    :param value: Initial value selected
    :type value: int, float
    :param editable: Determine if the SpinBox is editable or not
    :type editable: bool
    """

    min_value = NumericProperty(float('-inf'))
    max_value = NumericProperty(float('+inf'))
    step = NumericProperty(1)
    value = NumericProperty(0)
    range = ReferenceListProperty(min_value, max_value, step)

    def __init__(self, btn_size_hint_x=0.2, **kwargs):
        super(SpinBox, self).__init__(orientation='horizontal', **kwargs)

        self.value_label = Label(text=str(self.value))
        self.inc_button = TimedButton(text='+')
        self.dec_button = TimedButton(text='-')

        self.inc_button.bind(on_press=self.on_increment_value)
        self.inc_button.bind(on_time_slice=self.on_increment_value)
        self.dec_button.bind(on_press=self.on_decrement_value)
        self.dec_button.bind(on_time_slice=self.on_decrement_value)

        self.buttons_vbox = BoxLayout(orientation='vertical',
                                      size_hint_x=btn_size_hint_x)
        self.buttons_vbox.add_widget(self.inc_button)
        self.buttons_vbox.add_widget(self.dec_button)

        self.add_widget(self.value_label)
        self.add_widget(self.buttons_vbox)

    def on_increment_value(self, btn_instance):
        if float(self.value) + float(self.step) <= self.max_value:
            self.value += self.step

    def on_decrement_value(self, btn_instance):
        if float(self.value) - float(self.step) >= self.min_value:
            self.value -= self.step

    def on_value(self, instance, value):
        instance.value_label.text = str(value)

实际上我使用的代码略有不同,因为我认为将一个布局子类化为实现一个小部件很难看,因此我将Widget子类化,并添加了一个水平BoxLayout作为{{{1}的子项。 1}},然后我Widget编辑每个大小和位置更改以更新此孩子的大小和位置(请参阅this问题,了解我为什么必须这样做)。

bindTimedButton的子类,允许长按,并且在长按时,每隔一定毫秒发出Button事件(因此用户将是能够按住按钮进行连续增量)。如果需要,您可以简单地使用普通on_time_slice,将Button移至bind事件。

on_time_slice源代码是:

TimedButton

请注意,我必须绑定class TimedButton(Button): """A simple ``Button`` subclass that produces an event at regular intervals when pressed. This class, when long-pressed, emits an ``on_time_slice`` event every ``time_slice`` milliseconds. :param long_press_interval: Defines the minimum time required to consider the press a long-press. :type long_press_interval: int :param time_slice: The number of milliseconds of each slice. :type time_slice: int """ def __init__(self, long_press_interval=550, time_slice=225, **kwargs): super(TimedButton, self).__init__(**kwargs) self.long_press_interval = long_press_interval self.time_slice = time_slice self._touch_start = None self._long_press_callback = None self._slice_callback = None self.register_event_type('on_time_slice') self.register_event_type('on_long_press') def on_state(self, instance, value): if value == 'down': start_time = time.time() self._touch_start = start_time def callback(dt): self._check_long_press(dt) Clock.schedule_once(callback, self.long_press_interval / 1000.0) self._long_press_callback = callback else: end_time = time.time() delta = (end_time - (self._touch_start or 0)) * 1000 Clock.unschedule(self._slice_callback) # Fixes the bug of multiple presses causing fast increase Clock.unschedule(self._long_press_callback) if (self._long_press_callback is not None and delta > self.long_press_interval): self.dispatch('on_long_press') self._touch_start = None self._long_press_callback = self._slice_callback = None def _check_long_press(self, dt): delta = dt * 1000 if delta > self.long_press_interval and self.state == 'down': self.dispatch('on_long_press') self._long_press_callback = None def slice_callback(dt): self.dispatch('on_time_slice') return self.state == 'down' Clock.schedule_interval(slice_callback, self.time_slice / 1000.0) self._slice_callback = slice_callback def on_long_press(self): pass def on_time_slice(self): pass 属性,而不是使用stateon_touch_down,因为它们会提供一些strange behaviour,即使在“工作”时也会有一些奇怪的无缘无故发生的事情(例如,点击减量按钮导致on_touch_up被调用,即使on_increment正确无误。


编辑更新了修复一个小错误的bind类(快速点击多次后执行上一个实施,然后按住该按钮会产生太多TimedButton个事件:当州进入on_time_slice

时,我忘了“取消预定”_long_press_callback