如何在微调器中允许无穷整数值? [英] How to allow infinite integer values in a spinner?

查看:81
本文介绍了如何在微调器中允许无穷整数值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need a Spinner widget in which the user can select integer values with a certain step and without lower or upper limits (I mean, they should be at least in the billion range, so no chance of memorizing the whole sequence).

我看到了kivy的 Spinner 小部件,但我没有认为像Spinner(values=itertool.count())这样的事情会起作用. 此外,它仅限于字符串值.

I saw kivy's Spinner widget but I don't think doing something like Spinner(values=itertool.count()) would work. Also it is limited to string values.

是否有任何简单的方法来获取类似于以下内容的 QSpinBox Qt?

Is there any simple way of obtaining something similar to QSpinBox of the Qt?

推荐答案

目前看来,kivy没有提供类似于SpinnerSpinBox的任何东西,或者您想调用它.可以代替使用的小部件是 Slider ,但它看起来很糟糕,如果要允许很大的范围但步幅很小,它就没那么有用了.

It seems like kivy at the moment does not provide anything similar to a Spinner or SpinBox or however you want to call it. A widget that might be used instead is the Slider but it looks awful and it's not so useful if you want to allow a very big range but with a small step.

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

Therefore I wrote my own implementation of 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更改每个大小和位置以更新此孩子的大小和位置(请参见

Actually the code I use is slightly different because I think it is ugly to subclass a layout to implement a widget and thus I subclassed Widget and added a horizontal BoxLayout as only children of the Widget, then I binded every size and position change to update the size and position of this child(see this question for why I had to do that).

TimedButtonButton的子类,它允许长按,并在长按时每隔一定的毫秒数发出一次on_time_slice事件(因此,用户可以按住按钮做一个连续的增量).您可以根据需要简单地使用普通的Button,将bind s更改为on_time_slice事件.

The TimedButton is a subclass of Button that allows long-presses and, when long-pressed, emits a on_time_slice event every a certain amount of millisecond(thus the user will be able to hold the button to do a continuous increment). You can simply use a normal Button if you want, removing the binds to on_time_slice event.

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 ings正确).

Note that I had to bind the state property instead of using on_touch_down and on_touch_up because they give some strange behaviour, and even when "working" there were some strange things happening by no reason(e.g. clicking the decrement button caused on_increment to be called even though the bindings where correct).

更新了TimedButton类,修复了一个小错误(快速单击多次并按住按钮时,以前的实现会产生太多的on_time_slice事件:我忘记了状态变为'normal'

Updated the TimedButton class fixing a little bug(the previous implementation when clicked rapidly multiple times and then holding down the button would yield too many on_time_slice events: I'd forgot to "unschedule" the _long_press_callback when the state goes 'normal'

这篇关于如何在微调器中允许无穷整数值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆