Kivy 日期选择器小部件

Posted

技术标签:

【中文标题】Kivy 日期选择器小部件【英文标题】:Kivy Date Picker Widget 【发布时间】:2012-11-22 17:43:05 【问题描述】:

[已解决] 请参阅下面的应用程序接受的答案和运行 kivy DatePicker 小部件的源代码。

我一直在学习 Kivy,并决定制作一个日期选择器小部件作为学习练习。

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 设置为具有该日期部分的日期对象。

我试过添加

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()

【问题讨论】:

另外,作为评论,因为这不是您的问题,但如果您以后不使用该值,则不需要存储在您的实例中,因此在 populate_body 中,self.date_label 可以替换只需date_label 无处不在。作为旁注的旁注,请尊重 pep8 :P,在函数调用中 = 周围没有空格,每个类定义之间只有两行。祝你有美好的一天:) 编辑:哦,也感谢您为 kivy 制作日期选择器,我相信它对其他人有用:) 是的,我经历了 date_label 和 self.date_label 的几次迭代,只是碰巧以这种形式发布。顺便说一句,每当我查找资料时,我到处都能看到你的头像。我开始认为这是某种默认/模因头像! 呵呵,好吧,我尽量回答所有kivy问题^^ 我希望你不介意我借这个,@Horba。 您是否将其实现为弹出窗口?如果是这样,您是否会制作一个弹出窗口并将其放入内容区域? 【参考方案1】:

你在绑定中犯了一个小错误,你调用方法而不是传递它(因此,你传递self.set_date(date_cursor.day)的结果,但调用@987654322 @ 也调用了self.populate_body,所以你进入了无限递归,只被python递归限制停止。

您要做的是绑定方法,但将date_cursor.day 作为第一个参数,为此partial 函数来自functools 是完美的。

self.date_label.bind(on_touch_down=partial(self.set_date, date_cursor.day))

创建一个新函数,与 self.set_date 类似,但将 date_cursor.day 作为第一个参数预加载。

编辑:另外,当事件绑定调用您的部分函数时,它将接收其他参数,因此在您使用 ass 回调的函数/方法中的参数末尾添加 **args 是一个好习惯(这里set_date)。

【讨论】:

谢谢,我知道它会是这样的,但是如果不调用函数就无法传递所需的参数。 functools 是!今晚我将再次处理此问题并报告。

以上是关于Kivy 日期选择器小部件的主要内容,如果未能解决你的问题,请参考以下文章

ElementUI之DatePicker日期选择器小细节

获取 kivy 微调器选择的索引

Qt 颜色选择器小部件?

AppMaker 和选择器小部件 - 上传到特定文件夹

在 Handsontable 中强制使用日期格式?

Android HoneyComb DatePicker 文本颜色