- Python tkCalendar – Creating a Date Picker Calendar
- Calendars with Tkinter tkCalendar
- Customizing Python tkCalendar
- Customizing the Color of the tkCalendar
- Создание собственных виджетов и Notebook / tkinter 23
- Как работает этот виджет
- Применение стилей Ttk
- Как работают стили
- Создание виджета выбора даты
- Как создается виджет
Python tkCalendar – Creating a Date Picker Calendar
In this Tkinter tutorial, we will explore how to use the Python tkCalendar widget to create a Date Picker Calendar. What is a “Date Picker” Calendar though? Well its basically a regular calendar from which you can select a date, and have that value returned to your Tkinter application.
Calendars with Tkinter tkCalendar
The very first thing you need to do is download and install tkcalendar. This is not actually a part of the Tkinter Library, rather it is a separate package that we need to acquire.
Run the above command (or its equivalent depending on your coding environment) on the Command Prompt, and now you are ready to begin making Calendars in Tkinter.
Lets start off with a very basic example.
Here we import the Calendar class as a regular widget, and pass it into our Window. We will focus more on customizing it and interacting with it as we proceed through the tutorial.
import tkinter as tk from tkcalendar import Calendar root = tk.Tk() calendar = Calendar(root) calendar.pack() root.mainloop()
It is important to note, that the default date that shows on the Calendar is the current day/month/year. This will not obvious to you until you run this code for yourself and check the default date.
You can also control the default date by changing the year, month and day parameters as shown below.
import tkinter as tk from tkcalendar import Calendar root = tk.Tk() calendar = Calendar(root, year=2023, month=4, day=4) calendar.pack() root.mainloop()
As you can see, we are now showing a different date as the default date.
Customizing Python tkCalendar
We can also specify a “range” of values from which the user is allowed to select from. This can be done by passing a datetime object (part of the Python standard library) into the mindate and maxdate parameters of the Calendar class.
from datetime import datetime import tkinter as tk from tkcalendar import Calendar root = tk.Tk() calendar = Calendar(root, mindate=datetime(2021, 1, 1), maxdate=datetime(2023, 12, 30), showweeknumbers=False, showothermonthdays=False) calendar.pack() root.mainloop()
Try running this code, and you will see you cannot select a date from before 2021 or after 2023.
We also went for a more “minimalistic” approach by disabling some extra details such as the week numbers and the dates for the next month. (Compare this output to the previous one)
Lets take a look at one last thing in this tutorial. That is, how to interact and get the current date from the Calendar.
What we will do is bind a function called updateLabel() to the > virtual event. This event triggers whenever we select a date on the Calendar.
Inside this function, we will update a label (which we will also create) with the selected date. We can get the current date by using the get_date() function on the Calendar object.
from datetime import datetime import tkinter as tk from tkcalendar import Calendar def updateLabel(event): label.config(text="Selected Date: " + calendar.get_date()) root = tk.Tk() calendar = Calendar(root, mindate=datetime(2021, 1, 1), maxdate=datetime(2023, 12, 30), showweeknumbers=False, showothermonthdays=False) calendar.pack() calendar.bind('>', updateLabel) label = tk.Label(root, text="Selected Date: ") label.pack() root.mainloop()
Instead of using events, we could also have retrieved the current date with a button.
Customizing the Color of the tkCalendar
Having a good looking Calendar that matches the color theme of your application is also quite important. You can customize the color, text, and background of your tkCalendar widget using some of the options provided in the documentation, demonstrated below.
from datetime import datetime import tkinter as tk from tkcalendar import Calendar def updateLabel(event): label.config(text = "Selected Date: " + calendar.get_date()) root = tk.Tk() calendar = Calendar(root, mindate = datetime(2020, 1, 1), maxdate = datetime(2023, 12, 30), showweeknumbers = False, showothermonthdays = False, background = "green", foreground = "white", selectbackground = "red", normalbackground = "lightgreen", weekendbackground = "darkgreen", weekendforeground = "white") calendar.pack() calendar.bind(">", updateLabel) label = tk.Label(root, text = "Selected Date: ") label.pack() root.mainloop()
This marks the end of the Python tkCalendar, Creating a Date Picker Calendar Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content can be asked in the comments section below.
Создание собственных виджетов и Notebook / tkinter 23
Класс ttk.Notebook — еще один новый виджет из модуля ttk . Он позволяет добавлять разные виды отображения приложения в одном окне, предлагая после этого выбрать желаемый с помощью клика по соответствующей вкладке.
Панели с вкладками — это удобный вариант повторного использования графического интерфейса для тех ситуаций, когда содержимое нескольких областей не должно отображаться одновременно.
Следующее приложение показывает список дел, разбитый по категориям. В этом примере данные доступны только для чтения (для упрощения):
Создаем ttk.Notebook с фиксированными размерами, и затем проходимся по словарю с заранее определенными данными. Он выступит источником вкладок и названий для каждой области:
import tkinter as tk
import tkinter.ttk as ttk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("Ttk Notebook")
todos = "Дом": ["Постирать", "Сходить за продуктами"],
"Работа": ["Установить Python", "Учить Tkinter", "Разобрать почту"],
"Отпуск": ["Отдых!"]
>
self.notebook = ttk.Notebook(self, width=250, height=100, padding=10)
for key, value in todos.items():
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text=key, underline=0,
sticky=tk.NE + tk.SW)
for text in value:
ttk.Label(frame, text=text).pack(anchor=tk.W)
self.label = ttk.Label(self)
self.notebook.pack()
self.label.pack(anchor=tk.W)
self.notebook.enable_traversal()
self.notebook.bind(">", self.select_tab)
def select_tab(self, event):
tab_id = self.notebook.select()
tab_name = self.notebook.tab(tab_id, "text")
text = "Ваш текущий выбор: <>".format(tab_name)
self.label.config(text=text)
if __name__ == "__main__":
app = App()
app.mainloop()При клике по вкладке метка в нижней части экрана обновляет содержимое, показывая название текущей вкладки.
Как работает этот виджет
Виджет ttk.Notebook создается с фиксированными шириной, высотой и внешними отступами.
Каждый ключ из словаря todos используется в качестве названия вкладки, а список значений добавляется в виде меток в ttk.Frame , который представляет собой область окна:
self.notebook = ttk.Notebook(self, width=250, height=100, padding=10)
for key, value in todos.items():
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text=key,
underline=0, sticky=tk.NE+tk.SW)
for text in value:
ttk.Label(frame, text=text).pack(anchor=tk.W)После этого у виджета ttk.Notebook вызывается метод enable_traversal() . Это позволяет пользователям переключаться между вкладками с помощью Ctrl + Shift + Tab и Ctrl + Tab соответственно.
Благодаря этому также можно переключиться на определенную вкладку, зажав Alt и подчеркнутый символ: Alt + H для вкладки Home, Alt + W — для Work, а Alt + V — для Vacation.
Виртуальное событие " >" генерируется автоматически при изменении выбора. Оно связывается с методом select_tab() . Стоит отметить, что это событие автоматически срабатывает при добавлении вкладки в ttk.Notebook :
self.notebook.pack()
self.label.pack(anchor=tk.W)
self.notebook.enable_traversal()
self.notebook.bind(">", self.select_tab)При упаковке элементов необязательно размещать дочерние элементы ttk.Notebook , поскольку это делается автоматически с помощью вызова geometry manager:
def select_tab(self, event):
tab_id = self.notebook.select()
tab_name = self.notebook.tab(tab_id, "text")
self.label.config(text=f"Ваш текущий выбор: ")Если нужно получить текущий дочерний элемент ttk.Notebook , то для этого не нужно использовать дополнительные структуры данных для маппинга индекса вкладки и окна виджета.
Метод nametowidget() доступен для всех классов виджетов, так что с его помощью можно легко получить объект виджета, соответствующий определенному имени:
def select_tab(self, event):
tab_id = self.notebook.select()
frame = self.nametowidget(tab_id)
# .Применение стилей Ttk
У тематических виджетов есть отдельный API для изменения внешнего вида. Прямо задавать параметры нельзя, потому что они определены в классе ttk.Style .
В этом разделе разберем, как изменять виджеты и добавлять им стили.
Для добавления дополнительных настроек нужен объект ttk.Style , который предоставляет следующие методы:
- configure(style, opts) — меняет внешний вид opts для style виджета. Именно здесь задаются такие параметры, как фон, отступы и анимации.
- map(style, query) — меняет динамический вид style виджета. Аргумент query — аргумент-ключевое слово, где каждый ключ отвечает за параметр стиля, а значение — список кортежей в виде (state, value) . Это значит, что значение каждого параметра определяется его текущим состоянием.
Например, отметим следующие примеры для двух ситуаций:
import tkinter as tk
import tkinter.ttk as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("Tk themed widgets")
style = ttk.Style(self)
style.configure("TLabel", padding=10)
style.map("TButton",
foreground=[("pressed", "grey"), ("active", "white")],
background=[("pressed", "white"), ("active", "grey")]
)
# .Теперь каждый ttk.Label отображается с внутренним отступом 10, а у ttk.Button динамические стили: серая заливка с белым фоном, когда состояние кнопки — pressed и белая заливка с серым фоном — когда active.
Как работают стили
Создавать ttk.Style довольно просто. Нужно лишь создать экземпляр с родительским виджетом в качестве первого параметра.
После этого можно задать настройки стиля для виджетов с помощью символа T в верхнем регистре и названия виджета: Tbutton для ttk.Button , Tlabel для ttk.Label и так далее. Однако есть и исключения, поэтому рекомендуется сверяться с помощью интерпретатора Python, вызывая winfo_class() для экземпляра виджета.
Также можно добавить префикс, чтобы указать, что этот стиль должен быть не по умолчанию, а явно задаваться для определенных виджетов:
style.configure("My.TLabel", padding=10)
# .
label = ttk.Label(master, text="Какой-то текст", style="My.TLabel")Создание виджета выбора даты
Если нужно позволить пользователям выбирать дату в приложении, то можно попробовать оставить текстовую подсказку, которая бы побудила их написать строку в формате даты. Еще одно решение — добавить несколько числовых полей для ввода дня, месяца и года, но в этом случае понадобятся несколько правил валидации.
В отличие от других фреймворков для создания графических интерфейсов, в Tkinter нет класса для этих целей, но можно воспользоваться знаниями тематических виджетов для создания виджета календаря.
В этом материале пошагово разберем процесс создания виджета выбора даты с помощью виджетов Ttk:
Помимо модулей tkinter также нужны модули calendar и datetime из стандартной библиотеки. Это поможет моделировать данные виджета и взаимодействовать с ними.
Заголовок виджета показывает стрелки для перемещения между месяцами. Их внешний вид зависит от текущих выбранных стилей Tk. Тело виджета состоит из таблицы ttk.Treeview с экземпляром Canvas , который подсвечивает ячейку выбранной даты:
import calendar
import datetime
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
from itertools import zip_longest
class TtkCalendar(ttk.Frame):
def __init__(self, master=None, **kw):
now = datetime.datetime.now()
fwday = kw.pop('firstweekday', calendar.MONDAY)
year = kw.pop('year', now.year)
month = kw.pop('month', now.month)
sel_bg = kw.pop('selectbackground', '#ecffc4')
sel_fg = kw.pop('selectforeground', '#05640e')
super().__init__(master, **kw)
self.selected = None
self.date = datetime.date(year, month, 1)
self.cal = calendar.TextCalendar(fwday)
self.font = tkfont.Font(self)
self.header = self.create_header()
self.table = self.create_table()
self.canvas = self.create_canvas(sel_bg, sel_fg)
self.build_calendar()
# .
def main():
root = tk.Tk()
root.title('Календарь Tkinter')
ttkcal = TtkCalendar(firstweekday=calendar.SUNDAY)
ttkcal.pack(expand=True, fill=tk.BOTH)
root.mainloop()
if __name__ == '__main__':
main()Полный код в файле lesson_23/creating_widget.py на Gitlab.
Как создается виджет
Этот класс TtkCalendar можно кастомизировать, передавая параметры в виде аргументов-ключевых слов. Их можно получать при инициализации, указав кое-какие значения по умолчанию, например, текущие месяц и год: