trying to get date from a customer date picker in pything and tkinter

Question:

I want to minimize the number of clicks and typing a user has to go through to select a date. This code pops up a date picker with the option of either clicking on a date or the cancel button, or hitting the ESC key. My problem is that when I click on a date, the option_changed function is called as expected, but self.calendar.get_date() always returns today’s date

import datetime
from tkinter import Frame, StringVar, Button, Label, Entry, OptionMenu, Tk
from tkinter import simpledialog
from tkcalendar import Calendar


class DateDialog(simpledialog.Dialog):
    def __init__(self, parent, title, selected: datetime.date = datetime.date.today()):
        self.calendar = None
        self.selected_date = selected
        self.parent = parent
        self.type = title
        self.selected_date = selected
        super().__init__(parent, title)

    def body(self, frame):
        horizontal_frame = Frame(self)
        self.calendar = Calendar(frame, selectmode='day',
                                 year=self.selected_date.year, month=self.selected_date.month,
                                 day=self.selected_date.day)
        cancel_button = Button(horizontal_frame, text='Cancel', width=5, command=self.cancel_pressed)
        self.bind('<Escape>', lambda event: self.cancel_pressed())
        for row in self.calendar._calendar:
            for lbl in row:
                lbl.bind('<1>', self.option_changed)
        self.calendar.pack(side='left')
        cancel_button.pack(side='right')
        horizontal_frame.pack()
        frame.pack()
        return horizontal_frame

    # noinspection PyUnusedLocal
    def option_changed(self, *args):
        self.selected_date = self.calendar.get_date()
        self.destroy()

    def cancel_pressed(self):
        self.selected_date = None
        self.destroy()

    def buttonbox(self):
        pass


def main():
    root = Tk()
    test = DateDialog(root, 'testing').selected_date
    root.mainloop()


if __name__ == '__main__':
    main()
Asked By: jordanthompson

||

Answers:

You can bind to the <<CalendarSelected>> event instead and call selection_get method in order to return to the date selected by the user.

See inline comments to indicate where I made changes.

For example:

import datetime
from tkinter import Frame, StringVar, Button, Label, Entry, OptionMenu, Tk
from tkinter import simpledialog
from tkcalendar import Calendar


class DateDialog(simpledialog.Dialog):
    def __init__(self, parent, title, selected: datetime.date = datetime.date.today()):
        self.calendar = None
        self.selected_date = selected
        self.parent = parent
        self.type = title
        self.selected_date = selected
        super().__init__(parent, title)

    def body(self, frame):
        horizontal_frame = Frame(self)
        self.calendar = Calendar(frame, selectmode='day',
                                 year=self.selected_date.year, month=self.selected_date.month,
                                 day=self.selected_date.day)
        cancel_button = Button(horizontal_frame, text='Cancel', width=5, command=self.cancel_pressed)
        self.bind('<Escape>', lambda event: self.cancel_pressed())

        self.bind("<<CalendarSelected>>", self.option_changed)  # added this

        self.calendar.pack(side='left')
        cancel_button.pack(side='right')
        horizontal_frame.pack()
        frame.pack()
        return horizontal_frame

    # noinspection PyUnusedLocal
    def option_changed(self, *args):
        self.selected_date = self.calendar.selection_get()  # changed this
        print(self.selected_date)
        self.destroy()

    def cancel_pressed(self):
        self.selected_date = None
        self.destroy()

    def buttonbox(self):
        pass


def main():
    root = Tk()
    test = DateDialog(root, 'testing').selected_date
    root.mainloop()


if __name__ == '__main__':
    main()
Answered By: Alexander
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.