Kivy日期选择器小部件
[已解决]请参见下面的信息,以获取可接受的答案的应用以及使kivy DatePicker小部件正常工作的源代码.
[SOLVED] Please see below for application of accepted answer and source code for functioning kivy DatePicker widget.
我一直在学习Kivy,因此决定制作一个日期选择器小部件作为学习练习.
I've been learning Kivy and decided to make a date picker widgets as a learning exercise.
import kivy
kivy.require('1.4.0')
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.app import App
from datetime import date, timedelta
class DatePicker(BoxLayout):
def __init__(self, **kwargs):
super(DatePicker, self).__init__(**kwargs)
self.date = date.today()
self.orientation = "vertical"
self.header = BoxLayout(orientation = 'horizontal',
size_hint = (1, 0.2))
self.body = GridLayout(cols = 7)
self.add_widget(self.header)
self.add_widget(self.body)
self.populate_body()
self.populate_header()
def populate_header(self):
self.header.clear_widgets()
self.previous_month = Button(text = "<")
self.next_month = Button(text = ">")
self.current_month = Label(text = repr(self.date),
size_hint = (2, 1))
self.header.add_widget(self.previous_month)
self.header.add_widget(self.current_month)
self.header.add_widget(self.next_month)
def populate_body(self):
self.body.clear_widgets()
date_cursor = date(self.date.year, self.date.month, 1)
while date_cursor.month == self.date.month:
self.date_label = Label(text = str(date_cursor.day))
self.body.add_widget(self.date_label)
date_cursor += timedelta(days = 1)
# Not yet implimented ###
# def set_date(self, day):
# self.date = date(self.date.year, self.date.month, day)
# self.populate_body()
# self.populate_header()
#
# def move_next_month(self):
# if self.date.month == 12:
# self.date = date(self.date.year + 1, 1, self.date.day)
# else:
# self.date = date(self.date.year, self.date.month + 1, self.date.day)
# def move_previous_month(self):
# if self.date.month == 1:
# self.date = date(self.date.year - 1, 12, self.date.day)
# else:
# self.date = date(self.date.year, self.date.month -1, self.date.day)
# self.populate_header()
# self.populate_body()
class MyApp(App):
def build(self):
return DatePicker()
if __name__ == '__main__':
MyApp().run()
我遇到了障碍,无法解决如何继续.我想添加一种方法,当单击date_labels时,它们会将self.date设置为具有该天部分的日期对象.
I've hit a road block and can't work out how to continue. I want to add a method such that when the date_labels are clicked, they set self.date to a date object with that day part.
我尝试添加
self.date_label.bind(on_touch_down = self.set_date(date_cursor.day))
但只有最大的递归错误.
but only got max recursion errors.
import kivy
kivy.require('1.4.0')
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.app import App
from datetime import date, timedelta
from functools import partial
class DatePicker(BoxLayout):
def __init__(self, *args, **kwargs):
super(DatePicker, self).__init__(**kwargs)
self.date = date.today()
self.orientation = "vertical"
self.month_names = ('January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December')
if kwargs.has_key("month_names"):
self.month_names = kwargs['month_names']
self.header = BoxLayout(orientation = 'horizontal',
size_hint = (1, 0.2))
self.body = GridLayout(cols = 7)
self.add_widget(self.header)
self.add_widget(self.body)
self.populate_body()
self.populate_header()
def populate_header(self, *args, **kwargs):
self.header.clear_widgets()
previous_month = Button(text = "<")
previous_month.bind(on_press=partial(self.move_previous_month))
next_month = Button(text = ">", on_press = self.move_next_month)
next_month.bind(on_press=partial(self.move_next_month))
month_year_text = self.month_names[self.date.month -1] + ' ' + str(self.date.year)
current_month = Label(text=month_year_text, size_hint = (2, 1))
self.header.add_widget(previous_month)
self.header.add_widget(current_month)
self.header.add_widget(next_month)
def populate_body(self, *args, **kwargs):
self.body.clear_widgets()
date_cursor = date(self.date.year, self.date.month, 1)
for filler in range(date_cursor.isoweekday()-1):
self.body.add_widget(Label(text=""))
while date_cursor.month == self.date.month:
date_label = Button(text = str(date_cursor.day))
date_label.bind(on_press=partial(self.set_date,
day=date_cursor.day))
if self.date.day == date_cursor.day:
date_label.background_normal, date_label.background_down = date_label.background_down, date_label.background_normal
self.body.add_widget(date_label)
date_cursor += timedelta(days = 1)
def set_date(self, *args, **kwargs):
self.date = date(self.date.year, self.date.month, kwargs['day'])
self.populate_body()
self.populate_header()
def move_next_month(self, *args, **kwargs):
if self.date.month == 12:
self.date = date(self.date.year + 1, 1, self.date.day)
else:
self.date = date(self.date.year, self.date.month + 1, self.date.day)
self.populate_header()
self.populate_body()
def move_previous_month(self, *args, **kwargs):
if self.date.month == 1:
self.date = date(self.date.year - 1, 12, self.date.day)
else:
self.date = date(self.date.year, self.date.month -1, self.date.day)
self.populate_header()
self.populate_body()
class MyApp(App):
def build(self):
return DatePicker()
if __name__ == '__main__':
MyApp().run()
您在绑定中犯了一个小错误,您调用该方法,而不是传递(因此,您传递了self.set_date(date_cursor.day)
的结果,但是调用self.set_date
也会调用self.populate_body
,因此您获得了无限递归,仅受到python递归限制的限制.
you do a small mistake in your binding, you call the method instead of passing it (thus, you pass the result of self.set_date(date_cursor.day)
, but calling self.set_date
calls self.populate_body
too, so you get in an infinite recursion, only stopped by python recursion limit.
您想要做的是绑定方法,但是将date_cursor.day
作为第一个参数,为此,来自functools
的partial
函数是完美的.
What you want to do is bind the method but with date_cursor.day
as a first parameter, for this the partial
function, from functools
is perfect.
self.date_label.bind(on_touch_down=partial(self.set_date, date_cursor.day))
self.date_label.bind(on_touch_down=partial(self.set_date, date_cursor.day))
创建一个新函数,就像self.set_date一样,但是将date_cursor.day预加载为第一个参数.
Creates a new fonction, that is just like self.set_date, but with date_cursor.day preloaded as a first argument.
edit:同样,当事件绑定调用您的部分函数时,它将接收其他参数,因此,在使用ass回调的函数/方法中,在参数末尾添加**args
是一个好习惯(此处set_date
).
edit: also, when your partial function is called by the event binding, it will recieve other arguments, so it's a good habit to add **args
at the end of your arguments in functions/methods you use ass callbacks (here set_date
).