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

标签 python kivy

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

我看到了基维的Spinner小部件,但我不认为像 Spinner(values=itertool.count()) 这样的事情会起作用。 它也仅限于字符串值。

是否有任何简单的方法来获得类似于 QSpinBox 的东西? Qt?

最佳答案

目前 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 作为Widget 的唯一子项,然后我绑定(bind)每个大小和位置更改以更新该子项的大小和位置(请参阅 this 问题了解我为什么必须这样做那个)。

TimedButtonButton 的子类,它允许长按,当长按时,每隔一定时间发出一个 on_time_slice 事件毫秒数(因此用户将能够按住按钮进行连续增量)。如果需要,您可以简单地使用普通的 Button,删除 bindon_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

请注意,我必须绑定(bind) state 属性而不是使用 on_touch_downon_touch_up 因为它们提供了一些 strange behaviour ,甚至在“工作”时,也会无缘无故地发生一些奇怪的事情(例如,即使 bindings 正确,单击递减按钮也会调用 on_increment )。


编辑: 更新了 TimedButton 类修复了一个小错误(以前的实现在快速多次点击然后按住按钮时会产生太多的 on_time_slice 事件:当状态变为 'normal'

时,我忘记“取消安排”_long_press_callback

关于python - 如何在微调器中允许无限整数值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14073807/

相关文章:

python - Jinja docx 模板,避免嵌套换行

python - Python 的 id() 有多独特?

python - 通过使用多线程/内核提高性能

python - 函数的递归版本在装饰时失败

python - 使用 pyinstaller 生成的基于 kivy 的 Windows exe 黑屏

python-2.7 - Kivy 在 Mac 中找不到 PIL 模块

desktop-application - Kivy 创建桌面程序?

python - 当 NTEventLogHandler 抛出异常时如何使用 Python 2.7 写入 windows 系统日志

python - 动态创建的 kivy 按钮立即运行 on_press 和 on_release

Python-Kivy : AttributeError: 'super' object has no attribute '__getattr__' when trying to get self. ids