简体   繁体   中英

Paste the calendar into the menu in tkinter

I'm trying to create a menu in tkinter, in which there will be different conditions. One of them will be the date choice. Now you need to enter the date manually. I want to make a calendar. I thought to use the answer to this question and the code to which there is a link. But I do not know how to use it with my code.

Below is the code of my menu:

import numpy as np
items5 = np.arange(1,14)
items6 = np.arange(1, 28)
items7 = np.arange(4, 28)
choose_y_n = ['yes', 'no']
items12 = np.arange(50, 100)
items13 = np.arange(0,15)

from tkinter import*

class MyOptionMenu(OptionMenu):
    def __init__(self, master, status, *options):
        self.var = StringVar(master)
        self.var.set(status)
        OptionMenu.__init__(self, master, self.var, *options)
        self.config(font=('Arial',(10)),bg='white',width=20)
        self['menu'].config(font=('Arial',(10)),bg='white')


root = Tk()

optionList5 = items5
optionList6 = items6
optionList7 = items7
optionList8 = choose_y_n
optionList12 = items12
optionList13 = items13

lab = Label(root, text="Enter the date in the format 'dd.mm.yyyy'", font="Arial 10", anchor='w')
ent1 = Entry(root,width=20,bd=3)
lab5 = Label(root, text="condition №1", font="Arial 10", anchor='w')
mymenu5 = MyOptionMenu(root, '2', *optionList5)
lab6 = Label(root, text="condition №2", font="Arial 10", anchor='w')
mymenu6 = MyOptionMenu(root, '12', *optionList6)
lab7 = Label(root, text="condition №3", font="Arial 10", anchor='w')
mymenu7 = MyOptionMenu(root, '13', *optionList7)
lab8 = Label(root, text="condition №4", font="Arial 10", anchor='w')
mymenu8 = MyOptionMenu(root, 'yes', *optionList8)
lab12 = Label(root, text="condition №5", font="Arial 10", anchor='w')
mymenu12 = MyOptionMenu(root, '80', *optionList12)
lab13 = Label(root, text="condition №6", font="Arial 10", anchor='w')
mymenu13 = MyOptionMenu(root, '9', *optionList13)

labels = [lab, lab5, lab6, lab7, lab8, lab12, lab13]
mymenus = [ent1, mymenu5, mymenu6, mymenu7, mymenu8, mymenu12, mymenu13]

def save_selected_values():
    global values1
    values1 = [ent1.get(), mymenu5.var.get(), mymenu6.var.get(), mymenu7.var.get(), mymenu8.var.get(),
               mymenu12.var.get(), mymenu13.var.get()]
    print(values1)


def close_window(): 
    root.destroy() 

button = Button(root, text="Ok", command=save_selected_values)
button1 = Button(root, text="Quit", command=close_window)

for index, (lab, mymenu) in enumerate(zip(labels, mymenus)):
    lab.grid(row=index, column=0)
    mymenu.grid(row=index, column=1)
    button.grid(row=index, column=2)
    button1.grid(row=index, column=3)

root.mainloop()

For me, it's important to save the date in a variable.

I will be very grateful for any recommendations!

UPD
My new code is below.

It seems that my code doesn't save the date at all.

I just don't understand how to get the date as a variable. What am I doing wrong?

import numpy as np
items5 = np.arange(1,14)
items6 = np.arange(1, 28)
items7 = np.arange(4, 28)
choose_y_n = ['yes', 'no']
items12 = np.arange(50, 100)
items13 = np.arange(0,15)

from tkinter import*
import tkinter.font as tkFont
from tkinter import ttk

def get_calendar(locale, fwday):
    # instantiate proper calendar class
    if locale is None:
        return calendar.TextCalendar(fwday)
    else:
        return calendar.LocaleTextCalendar(fwday, locale)

class Calendar(ttk.Frame):
    # XXX ToDo: cget and configure

    datetime = calendar.datetime.datetime
    timedelta = calendar.datetime.timedelta

    def __init__(self, master=None, **kw):
        """
        WIDGET-SPECIFIC OPTIONS

            locale, firstweekday, year, month, selectbackground,
            selectforeground
        """
        # remove custom options from kw before initializating ttk.Frame
        fwday = kw.pop('firstweekday', calendar.MONDAY)
        year = kw.pop('year', self.datetime.now().year)
        month = kw.pop('month', self.datetime.now().month)
        locale = kw.pop('locale', None)
        sel_bg = kw.pop('selectbackground', '#ecffc4')
        sel_fg = kw.pop('selectforeground', '#05640e')

        self._date = self.datetime(year, month, 1)
        self._selection = None # no date selected

        ttk.Frame.__init__(self, master, **kw)

        self._cal = get_calendar(locale, fwday)

        self.__setup_styles()       # creates custom styles
        self.__place_widgets()      # pack/grid used widgets
        self.__config_calendar()    # adjust calendar columns and setup tags
        # configure a canvas, and proper bindings, for selecting dates
        self.__setup_selection(sel_bg, sel_fg)

        # store items ids, used for insertion later
        self._items = [self._calendar.insert('', 'end', values='')
                            for _ in range(6)]
        # insert dates in the currently empty calendar
        self._build_calendar()

        # set the minimal size for the widget
        self._calendar.bind('<Map>', self.__minsize)

    def __setitem__(self, item, value):
        if item in ('year', 'month'):
            raise AttributeError("attribute '%s' is not writeable" % item)
        elif item == 'selectbackground':
            self._canvas['background'] = value
        elif item == 'selectforeground':
            self._canvas.itemconfigure(self._canvas.text, item=value)
        else:
            ttk.Frame.__setitem__(self, item, value)

    def __getitem__(self, item):
        if item in ('year', 'month'):
            return getattr(self._date, item)
        elif item == 'selectbackground':
            return self._canvas['background']
        elif item == 'selectforeground':
            return self._canvas.itemcget(self._canvas.text, 'fill')
        else:
            r = ttk.tclobjs_to_py({item: ttk.Frame.__getitem__(self, item)})
            return r[item]

    def __setup_styles(self):
        # custom ttk styles
        style = ttk.Style(self.master)
        arrow_layout = lambda dir: (
            [('Button.focus', {'children': [('Button.%sarrow' % dir, None)]})]
        )
        style.layout('L.TButton', arrow_layout('left'))
        style.layout('R.TButton', arrow_layout('right'))

    def __place_widgets(self):
        # header frame and its widgets
        hframe = ttk.Frame(self)
        lbtn = ttk.Button(hframe, style='L.TButton', command=self._prev_month)
        rbtn = ttk.Button(hframe, style='R.TButton', command=self._next_month)
        self._header = ttk.Label(hframe, width=15, anchor='center')
        # the calendar
        self._calendar = ttk.Treeview(show='', selectmode='none', height=7)

        # pack the widgets
        hframe.pack(in_=self, side='top', pady=4, anchor='center')
        lbtn.grid(in_=hframe)
        self._header.grid(in_=hframe, column=1, row=0, padx=12)
        rbtn.grid(in_=hframe, column=2, row=0)
        self._calendar.pack(in_=self, expand=1, fill='both', side='bottom')

    def __config_calendar(self):
        cols = self._cal.formatweekheader(3).split()
        self._calendar['columns'] = cols
        self._calendar.tag_configure('header', background='grey90')
        self._calendar.insert('', 'end', values=cols, tag='header')
        # adjust its columns width
        font = tkFont.Font()
        maxwidth = max(font.measure(col) for col in cols)
        for col in cols:
            self._calendar.column(col, width=maxwidth, minwidth=maxwidth,
                anchor='e')

    def __setup_selection(self, sel_bg, sel_fg):
        self._font = tkFont.Font()
        self._canvas = canvas = tkinter.Canvas(self._calendar,
            background=sel_bg, borderwidth=0, highlightthickness=0)
        canvas.text = canvas.create_text(0, 0, fill=sel_fg, anchor='w')

        canvas.bind('<ButtonPress-1>', lambda evt: canvas.place_forget())
        self._calendar.bind('<Configure>', lambda evt: canvas.place_forget())
        self._calendar.bind('<ButtonPress-1>', self._pressed)

    def __minsize(self, evt):
        width, height = self._calendar.master.geometry().split('x')
        height = height[:height.index('+')]
        self._calendar.master.minsize(width, height)

    def _build_calendar(self):
        year, month = self._date.year, self._date.month

        # update header text (Month, YEAR)
        header = self._cal.formatmonthname(year, month, 0)
        self._header['text'] = header.title()

        # update calendar shown dates
        cal = self._cal.monthdayscalendar(year, month)
        for indx, item in enumerate(self._items):
            week = cal[indx] if indx < len(cal) else []
            fmt_week = [('%02d' % day) if day else '' for day in week]
            self._calendar.item(item, values=fmt_week)

    def _show_selection(self, text, bbox):
        """Configure canvas for a new selection."""
        x, y, width, height = bbox

        textw = self._font.measure(text)

        canvas = self._canvas
        canvas.configure(width=width, height=height)
        canvas.coords(canvas.text, width - textw, height / 2 - 1)
        canvas.itemconfigure(canvas.text, text=text)
        canvas.place(in_=self._calendar, x=x, y=y)

    # Callbacks

    def _pressed(self, evt):
        """Clicked somewhere in the calendar."""
        x, y, widget = evt.x, evt.y, evt.widget
        item = widget.identify_row(y)
        column = widget.identify_column(x)
        self.event_generate("<<CalendarChanged>>")

        if not column or not item in self._items:
            # clicked in the weekdays row or just outside the columns
            return

        item_values = widget.item(item)['values']
        if not len(item_values): # row is empty for this month
            return

        text = item_values[int(column[1]) - 1]
        if not text: # date is empty
            return

        bbox = widget.bbox(item, column)
        if not bbox: # calendar not visible yet
            return

        # update and then show selection
        text = '%02d' % text
        self._selection = (text, item, column)
        self._show_selection(text, bbox)

    def _prev_month(self):
        """Updated calendar to show the previous month."""
        self._canvas.place_forget()

        self._date = self._date - self.timedelta(days=1)
        self._date = self.datetime(self._date.year, self._date.month, 1)
        self._build_calendar() # reconstuct calendar

    def _next_month(self):
        """Update calendar to show the next month."""
        self._canvas.place_forget()

        year, month = self._date.year, self._date.month
        self._date = self._date + self.timedelta(
            days=calendar.monthrange(year, month)[1] + 1)
        self._date = self.datetime(self._date.year, self._date.month, 1)
        self._build_calendar() # reconstruct calendar

    # Properties

    @property
    def selection(self):
        """Return a datetime representing the current selected date."""
        if not self._selection:
            return None

        year, month = self._date.year, self._date.month
        return self.datetime(year, month, int(self._selection[0]))


class MyOptionMenu(OptionMenu):
    def __init__(self, master, status, *options):
        self.var = StringVar(master)
        self.var.set(status)
        OptionMenu.__init__(self, master, self.var, *options)
        self.config(font=('Arial',(10)),bg='white',width=20)
        self['menu'].config(font=('Arial',(10)),bg='white')


root = Tk()

optionList5 = items5
optionList6 = items6
optionList7 = items7
optionList8 = choose_y_n
optionList12 = items12
optionList13 = items13

lab = Label(root, text="Select date", font="Arial 10", anchor='w')
ent1 = Calendar(firstweekday=calendar.SUNDAY)
lab5 = Label(root, text="condition №1", font="Arial 10", anchor='w')
mymenu5 = MyOptionMenu(root, '2', *optionList5)
lab6 = Label(root, text="condition №2", font="Arial 10", anchor='w')
mymenu6 = MyOptionMenu(root, '12', *optionList6)
lab7 = Label(root, text="condition №3", font="Arial 10", anchor='w')
mymenu7 = MyOptionMenu(root, '13', *optionList7)
lab8 = Label(root, text="condition №4", font="Arial 10", anchor='w')
mymenu8 = MyOptionMenu(root, 'yes', *optionList8)
lab12 = Label(root, text="condition №5", font="Arial 10", anchor='w')
mymenu12 = MyOptionMenu(root, '80', *optionList12)
lab13 = Label(root, text="condition №6", font="Arial 10", anchor='w')
mymenu13 = MyOptionMenu(root, '9', *optionList13)

labels = [lab, lab5, lab6, lab7, lab8, lab12, lab13]
mymenus = [ent1, mymenu5, mymenu6, mymenu7, mymenu8, mymenu12, mymenu13]

def save_selected_values():
    global values1
    values1 = [ent1, mymenu5.var.get(), mymenu6.var.get(), mymenu7.var.get(), mymenu8.var.get(),
               mymenu12.var.get(), mymenu13.var.get()]
    print(values1)

def on_calendar_changed(event):
    date = cal.selection
    datestr = "{:%d.%m.%Y}".format(date)
    print(datestr)
    dateVar.set(datestr)

def close_window(): 
    root.destroy() 

button = Button(root, text="Ok", command=save_selected_values)
button1 = Button(root, text="Quit", command=close_window)

for index, (lab, mymenu) in enumerate(zip(labels, mymenus)):
    lab.grid(row=index, column=0)
    mymenu.grid(row=index, column=1)
    button.grid(row=index, column=2)
    button1.grid(row=index, column=3)

root.mainloop()

That ttkcalendar.py needs a little help I reckon. You want to perform an action when the calendar widget has a new date selected and to do that you should bind to an event. However it is not generating any at the moment so at the end of the _pressed method add the following line to generate a virtual event you can bind to:

self.event_generate("<<CalendarChanged>>")

You can then add this widget to your UI and bind a function to this CalendarChanged virtual event as shown:

def on_calendar_changed(event):
    date = cal.selection
    datestr = "{:%d.%m.%Y}".format(date)
    print(datestr)
    dateVar.set(datestr)

cal.bind("<<CalendarChanged>>", on_calendar_changed)

Here I set a StringVar named dateVar as the textvariable of the ent1 widget. Now when the calendar selection changes, it puts the properly formatted string into your entry widget.

You would do well to convert from the OptionMenu widget to ttk.Combobox widgets and generally use ttk widgets in preference to the older Tk widgets.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM