我需要一个Spinner
窗口小部件,用户可以在其中选择具有特定步骤的整数值且没有下限或上限
(我的意思是,它们应该至少在十亿范围内,所以没有机会记住整个序列。)
我看到了kivy的Spinner
小部件,但我认为做Spinner(values=itertool.count())
这样的事情是行不通的。
它也仅限于字符串值。
是否有任何简单的方法可以获得与Qt
QSpinBox
类似的内容?
答案 0 :(得分:2)
目前kivy似乎没有提供类似于Spinner
或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
子类化,并添加了一个水平BoxLayout
作为{{{1}的子项。 1}},然后我Widget
编辑每个大小和位置更改以更新此孩子的大小和位置(请参阅this问题,了解我为什么必须这样做)。
bind
是TimedButton
的子类,允许长按,并且在长按时,每隔一定毫秒发出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
属性,而不是使用state
和on_touch_down
,因为它们会提供一些strange behaviour,即使在“工作”时也会有一些奇怪的无缘无故发生的事情(例如,点击减量按钮导致on_touch_up
被调用,即使on_increment
正确无误。
编辑更新了修复一个小错误的bind
类(快速点击多次后执行上一个实施,然后按住该按钮会产生太多TimedButton
个事件:当州进入on_time_slice
_long_press_callback