├── .gitignore ├── CalendarDialog ├── CalendarDialog.py ├── tkSimpleDialog.py └── ttkcalendar.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /CalendarDialog/CalendarDialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Written in response to: "How do I create a date picker in tkinter?" 4 | # from https://stackoverflow.com/questions/4443786/how-do-i-create-a-date-picker-in-tkinter/27809821 5 | import Tkinter 6 | 7 | import ttkcalendar 8 | import tkSimpleDialog 9 | 10 | 11 | class CalendarDialog(tkSimpleDialog.Dialog): 12 | """Dialog box that displays a calendar and returns the selected date""" 13 | def body(self, master): 14 | self.calendar = ttkcalendar.Calendar(master) 15 | self.calendar.pack() 16 | 17 | def apply(self): 18 | self.result = self.calendar.selection 19 | 20 | # Demo code: 21 | 22 | 23 | class CalendarFrame(Tkinter.LabelFrame): 24 | def __init__(self, master): 25 | Tkinter.LabelFrame.__init__(self, master, text="CalendarDialog Demo") 26 | 27 | def getdate(): 28 | cd = CalendarDialog(self) 29 | result = cd.result 30 | self.selected_date.set(result.strftime("%m/%d/%Y")) 31 | 32 | self.selected_date = Tkinter.StringVar() 33 | 34 | Tkinter.Entry(self, textvariable=self.selected_date).pack(side=Tkinter.LEFT) 35 | Tkinter.Button(self, text="Choose a date", command=getdate).pack(side=Tkinter.LEFT) 36 | 37 | 38 | def main(): 39 | root = Tkinter.Tk() 40 | root.wm_title("CalendarDialog Demo") 41 | CalendarFrame(root).pack() 42 | root.mainloop() 43 | 44 | if __name__ == "__main__": 45 | main() 46 | -------------------------------------------------------------------------------- /CalendarDialog/tkSimpleDialog.py: -------------------------------------------------------------------------------- 1 | from Tkinter import * 2 | from ttk import * 3 | 4 | class Dialog(Toplevel): 5 | """Sourced from http://effbot.org/tkinterbook/tkinter-dialog-windows.htm""" 6 | def __init__(self, parent, title = None): 7 | 8 | Toplevel.__init__(self, parent) 9 | self.transient(parent) 10 | 11 | if title: 12 | self.title(title) 13 | 14 | self.parent = parent 15 | 16 | self.result = None 17 | 18 | body = Frame(self) 19 | self.initial_focus = self.body(body) 20 | body.pack(padx=5, pady=5) 21 | 22 | self.buttonbox() 23 | 24 | self.grab_set() 25 | 26 | if not self.initial_focus: 27 | self.initial_focus = self 28 | 29 | self.protocol("WM_DELETE_WINDOW", self.cancel) 30 | 31 | self.geometry("+%d+%d" % (parent.winfo_rootx()+50, 32 | parent.winfo_rooty()+50)) 33 | 34 | self.initial_focus.focus_set() 35 | 36 | self.wait_window(self) 37 | 38 | # 39 | # construction hooks 40 | 41 | def body(self, master): 42 | # create dialog body. return widget that should have 43 | # initial focus. this method should be overridden 44 | 45 | pass 46 | 47 | def buttonbox(self): 48 | # add standard button box. override if you don't want the 49 | # standard buttons 50 | 51 | box = Frame(self) 52 | 53 | w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE) 54 | w.pack(side=LEFT, padx=5, pady=5) 55 | w = Button(box, text="Cancel", width=10, command=self.cancel) 56 | w.pack(side=LEFT, padx=5, pady=5) 57 | 58 | self.bind("", self.ok) 59 | self.bind("", self.cancel) 60 | 61 | box.pack() 62 | 63 | # 64 | # standard button semantics 65 | 66 | def ok(self, event=None): 67 | 68 | if not self.validate(): 69 | self.initial_focus.focus_set() # put focus back 70 | return 71 | 72 | self.withdraw() 73 | self.update_idletasks() 74 | 75 | self.apply() 76 | 77 | self.cancel() 78 | 79 | def cancel(self, event=None): 80 | 81 | # put focus back to the parent window 82 | self.parent.focus_set() 83 | self.destroy() 84 | 85 | # 86 | # command hooks 87 | 88 | def validate(self): 89 | 90 | return 1 # override 91 | 92 | def apply(self): 93 | 94 | pass # override 95 | -------------------------------------------------------------------------------- /CalendarDialog/ttkcalendar.py: -------------------------------------------------------------------------------- 1 | # Source: http://svn.python.org/projects/sandbox/trunk/ttk-gsoc/samples/ttkcalendar.py 2 | 3 | """ 4 | Simple calendar using ttk Treeview together with calendar and datetime 5 | classes. 6 | """ 7 | import calendar 8 | 9 | try: 10 | import Tkinter 11 | import tkFont 12 | except ImportError: # py3k 13 | import tkinter as Tkinter 14 | import tkinter.font as tkFont 15 | 16 | import ttk 17 | 18 | def get_calendar(locale, fwday): 19 | # instantiate proper calendar class 20 | if locale is None: 21 | return calendar.TextCalendar(fwday) 22 | else: 23 | return calendar.LocaleTextCalendar(fwday, locale) 24 | 25 | class Calendar(ttk.Frame): 26 | # XXX ToDo: cget and configure 27 | 28 | datetime = calendar.datetime.datetime 29 | timedelta = calendar.datetime.timedelta 30 | 31 | def __init__(self, master=None, **kw): 32 | """ 33 | WIDGET-SPECIFIC OPTIONS 34 | 35 | locale, firstweekday, year, month, selectbackground, 36 | selectforeground 37 | """ 38 | # remove custom options from kw before initializating ttk.Frame 39 | fwday = kw.pop('firstweekday', calendar.MONDAY) 40 | year = kw.pop('year', self.datetime.now().year) 41 | month = kw.pop('month', self.datetime.now().month) 42 | locale = kw.pop('locale', None) 43 | sel_bg = kw.pop('selectbackground', '#ecffc4') 44 | sel_fg = kw.pop('selectforeground', '#05640e') 45 | 46 | self._date = self.datetime(year, month, 1) 47 | self._selection = None # no date selected 48 | 49 | ttk.Frame.__init__(self, master, **kw) 50 | 51 | self._cal = get_calendar(locale, fwday) 52 | 53 | self.__setup_styles() # creates custom styles 54 | self.__place_widgets() # pack/grid used widgets 55 | self.__config_calendar() # adjust calendar columns and setup tags 56 | # configure a canvas, and proper bindings, for selecting dates 57 | self.__setup_selection(sel_bg, sel_fg) 58 | 59 | # store items ids, used for insertion later 60 | self._items = [self._calendar.insert('', 'end', values='') 61 | for _ in range(6)] 62 | # insert dates in the currently empty calendar 63 | self._build_calendar() 64 | 65 | def __setitem__(self, item, value): 66 | if item in ('year', 'month'): 67 | raise AttributeError("attribute '%s' is not writeable" % item) 68 | elif item == 'selectbackground': 69 | self._canvas['background'] = value 70 | elif item == 'selectforeground': 71 | self._canvas.itemconfigure(self._canvas.text, item=value) 72 | else: 73 | ttk.Frame.__setitem__(self, item, value) 74 | 75 | def __getitem__(self, item): 76 | if item in ('year', 'month'): 77 | return getattr(self._date, item) 78 | elif item == 'selectbackground': 79 | return self._canvas['background'] 80 | elif item == 'selectforeground': 81 | return self._canvas.itemcget(self._canvas.text, 'fill') 82 | else: 83 | r = ttk.tclobjs_to_py({item: ttk.Frame.__getitem__(self, item)}) 84 | return r[item] 85 | 86 | def __setup_styles(self): 87 | # custom ttk styles 88 | style = ttk.Style(self.master) 89 | arrow_layout = lambda dir: ( 90 | [('Button.focus', {'children': [('Button.%sarrow' % dir, None)]})] 91 | ) 92 | style.layout('L.TButton', arrow_layout('left')) 93 | style.layout('R.TButton', arrow_layout('right')) 94 | 95 | def __place_widgets(self): 96 | # header frame and its widgets 97 | hframe = ttk.Frame(self) 98 | lbtn = ttk.Button(hframe, style='L.TButton', command=self._prev_month) 99 | rbtn = ttk.Button(hframe, style='R.TButton', command=self._next_month) 100 | self._header = ttk.Label(hframe, width=15, anchor='center') 101 | # the calendar 102 | self._calendar = ttk.Treeview(self, show='', selectmode='none', height=7) 103 | 104 | # pack the widgets 105 | hframe.pack(in_=self, side='top', pady=4, anchor='center') 106 | lbtn.grid(in_=hframe) 107 | self._header.grid(in_=hframe, column=1, row=0, padx=12) 108 | rbtn.grid(in_=hframe, column=2, row=0) 109 | self._calendar.pack(in_=self, expand=1, fill='both', side='bottom') 110 | 111 | def __config_calendar(self): 112 | cols = self._cal.formatweekheader(3).split() 113 | self._calendar['columns'] = cols 114 | self._calendar.tag_configure('header', background='grey90') 115 | self._calendar.insert('', 'end', values=cols, tag='header') 116 | # adjust its columns width 117 | font = tkFont.Font() 118 | maxwidth = max(font.measure(col) for col in cols) 119 | for col in cols: 120 | self._calendar.column(col, width=maxwidth, minwidth=maxwidth, 121 | anchor='e') 122 | 123 | def __setup_selection(self, sel_bg, sel_fg): 124 | self._font = tkFont.Font() 125 | self._canvas = canvas = Tkinter.Canvas(self._calendar, 126 | background=sel_bg, borderwidth=0, highlightthickness=0) 127 | canvas.text = canvas.create_text(0, 0, fill=sel_fg, anchor='w') 128 | 129 | canvas.bind('', lambda evt: canvas.place_forget()) 130 | self._calendar.bind('', lambda evt: canvas.place_forget()) 131 | self._calendar.bind('', self._pressed) 132 | 133 | def __minsize(self, evt): 134 | width, height = self._calendar.master.geometry().split('x') 135 | height = height[:height.index('+')] 136 | self._calendar.master.minsize(width, height) 137 | 138 | def _build_calendar(self): 139 | year, month = self._date.year, self._date.month 140 | 141 | # update header text (Month, YEAR) 142 | header = self._cal.formatmonthname(year, month, 0) 143 | self._header['text'] = header.title() 144 | 145 | # update calendar shown dates 146 | cal = self._cal.monthdayscalendar(year, month) 147 | for indx, item in enumerate(self._items): 148 | week = cal[indx] if indx < len(cal) else [] 149 | fmt_week = [('%02d' % day) if day else '' for day in week] 150 | self._calendar.item(item, values=fmt_week) 151 | 152 | def _show_selection(self, text, bbox): 153 | """Configure canvas for a new selection.""" 154 | x, y, width, height = bbox 155 | 156 | textw = self._font.measure(text) 157 | 158 | canvas = self._canvas 159 | canvas.configure(width=width, height=height) 160 | canvas.coords(canvas.text, width - textw, height / 2 - 1) 161 | canvas.itemconfigure(canvas.text, text=text) 162 | canvas.place(in_=self._calendar, x=x, y=y) 163 | 164 | # Callbacks 165 | 166 | def _pressed(self, evt): 167 | """Clicked somewhere in the calendar.""" 168 | x, y, widget = evt.x, evt.y, evt.widget 169 | item = widget.identify_row(y) 170 | column = widget.identify_column(x) 171 | 172 | if not column or not item in self._items: 173 | # clicked in the weekdays row or just outside the columns 174 | return 175 | 176 | item_values = widget.item(item)['values'] 177 | if not len(item_values): # row is empty for this month 178 | return 179 | 180 | text = item_values[int(column[1]) - 1] 181 | if not text: # date is empty 182 | return 183 | 184 | bbox = widget.bbox(item, column) 185 | if not bbox: # calendar not visible yet 186 | return 187 | 188 | # update and then show selection 189 | text = '%02d' % text 190 | self._selection = (text, item, column) 191 | self._show_selection(text, bbox) 192 | 193 | def _prev_month(self): 194 | """Updated calendar to show the previous month.""" 195 | self._canvas.place_forget() 196 | 197 | self._date = self._date - self.timedelta(days=1) 198 | self._date = self.datetime(self._date.year, self._date.month, 1) 199 | self._build_calendar() # reconstuct calendar 200 | 201 | def _next_month(self): 202 | """Update calendar to show the next month.""" 203 | self._canvas.place_forget() 204 | 205 | year, month = self._date.year, self._date.month 206 | self._date = self._date + self.timedelta( 207 | days=calendar.monthrange(year, month)[1] + 1) 208 | self._date = self.datetime(self._date.year, self._date.month, 1) 209 | self._build_calendar() # reconstruct calendar 210 | 211 | # Properties 212 | 213 | @property 214 | def selection(self): 215 | """Return a datetime representing the current selected date.""" 216 | if not self._selection: 217 | return None 218 | 219 | year, month = self._date.year, self._date.month 220 | return self.datetime(year, month, int(self._selection[0])) 221 | 222 | def test(): 223 | import sys 224 | root = Tkinter.Tk() 225 | root.title('Ttk Calendar') 226 | ttkcal = Calendar(firstweekday=calendar.SUNDAY) 227 | ttkcal.pack(expand=1, fill='both') 228 | 229 | if 'win' not in sys.platform: 230 | style = ttk.Style() 231 | style.theme_use('clam') 232 | 233 | root.mainloop() 234 | 235 | if __name__ == '__main__': 236 | test() 237 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tkinter_components 2 | ================== 3 | 4 | Random Tkinter objects. 5 | 6 | Designed to be independent of any third-party libraries. 7 | --------------------------------------------------------------------------------