3

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

我看到了 kivy 的Spinner小部件,但我不认为做类似的事情Spinner(values=itertool.count())会奏效。它也仅限于字符串值。

有什么简单的方法可以获取类似于QSpinBoxof 的东西Qt吗?

4

1 回答 1

2

目前似乎 kivy 没有提供任何类似于 aSpinnerSpinBox您想要调用它的东西。可能会使用的小部件是,Slider但它看起来很糟糕,如果你想允许一个非常大的范围但只有一小步,它就不是那么有用。

因此,我编写了自己的 a 实现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作为唯一的子级Widget,然后我bind编辑每个大小和位置更改以更新大小和位置这个孩子的(见这个问题为什么我必须这样做)。

它是允许长按TimedButton的子类,当长按时,每隔一定的毫秒发出一个事件(因此用户将能够按住按钮进行连续递增)。如果需要,您可以简单地使用法线,删除s 到事件。Buttonon_time_sliceButtonbindon_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

请注意,我必须绑定state属性而不是使用on_touch_downon_touch_up因为它们会产生一些奇怪的行为,即使在“工作”时也会无缘无故地发生一些奇怪的事情(例如,on_increment即使bind在正确的地方单击导致调用的减量按钮)。


编辑:更新了TimedButton修复一个小错误的类(以前的实现在快速单击多次然后按住按钮会产生太多on_time_slice事件:我忘了“取消计划”_long_press_callback状态何时发生'normal'

于 2012-12-29T08:41:39.970 回答