3

ユーザーが特定のステップで、下限または上限なしで整数Spinner値を選択できるウィジェットが必要です(つまり、少なくとも10億の範囲内である必要があるため、シーケンス全体を記憶する機会はありません)。

kivyのウィジェットを見ましSpinnerたが、そのようなことをしてもうまくいくとは思いませんSpinner(values=itertool.count())。また、文字列値に制限されています。

に似たものを取得する簡単な方法はありQSpinBoxますQtか?

4

1 に答える 1

2

Spinner現時点では、kivyはまたはに似たものを提供していないようSpinBoxです。代わりに使用される可能性のあるウィジェットですが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水平をサブクラス化して追加し、すべてのサイズと位置を変更してサイズと位置を更新しました。この子の(なぜ私がそれをしなければならなかったのかについてはこの質問を参照してください)。BoxLayoutWidgetbind

はのTimedButtonサブクラスでありButton、長押しを許可し、長押しすると、on_time_slice特定のミリ秒ごとにイベントを発行します(したがって、ユーザーはボタンを押したままにして連続的に増分することができます)。必要に応じて、 s toイベントを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

state使用する代わりにプロパティをバインドする必要がon_touch_downあり、奇妙な動作on_touch_upをするため、「動作中」でも理由もなく奇妙なことが起こったことに注意してください(たとえば、正しい場所にあるにもかかわらず、デクリメントボタンをクリックすると呼び出されます) )。on_incrementbind


編集:小さなバグを修正するクラスを更新しましたTimedButton(以前の実装では、すばやく複数回クリックしてからボタンを押し続けると、イベントが多すぎます:状態が発生しon_time_sliceたときに「スケジュールを解除」するのを忘れていました_long_press_callback'normal'

于 2012-12-29T08:41:39.970 に答える