問題描述
[已解決] 請參閱下面的應用程序接受的答案和運行 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))
但只有最大遞歸錯誤.
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
作為第一個參數,對于這個 partial
函數,來自 functools
完美.
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.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.
另外,當你的部分函數被事件綁定調用時,它會接收其他參數,所以在函數的參數末尾添加 **args
是一個好習慣/使用 ass 回調的方法(此處為 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
).
這篇關于Kivy 日期選擇器小部件的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!