Tkinter уже много лет остается стандартной библиотекой Python для создания графических интерфейсов, а CustomTkinter позволяет сделать их более современными и привлекательными. В этой статье разберемся, что представляют собой эти библиотеки, чем они отличаются и какие приложения можно создать с помощью CustomTkinter.
Что такое Tkinter и CustomTkinter
Tkinter — это стандартная библиотека Python для создания графических пользовательских интерфейсов (GUI), позволяющая разрабатывать кроссплатформенные приложения с визуальными элементами вроде кнопок, полей ввода, меню и окон; она проста в освоении, не требует установки дополнительных компонентов и интегрирована в базовый дистрибутив Python. Вот только приложения созданные при помощи этой библиотеки выглядят довольно устаревшими.
CustomTkinter представляет собой современную альтернативу стандартной библиотеке Tkinter для языка программирования Python. Эта библиотека использует функционал Tkinter, но обогащает его стилизацией и новыми возможностями. С ее помощью можно легко создавать эстетически привлекательные приложения, затрачивая минимум усилий на настройку.
Основное отличие от Tkinter заключается в современном дизайне с плавными углами, анимационными эффектами и поддержкой темных и светлых цветовых схем. CustomTkinter расширяет набор доступных компонентов.
Появились такие компоненты, как переключатели (CTkSwitch), сегментированные кнопки (CTkSegmentedButton), панели с вкладками (CTkTabview) и прочие, отсутствующие в Tkinter. Синтаксис библиотеки остается схожим с Tkinter, что упрощает переход между ними для разработчиков. Некоторые основные компоненты CustomTkinter следующие:
- CTkButton — стильная кнопка с поддержкой иконок, hover‑эффектов и разных состояний.
- CTkCheckBox — флажок (чекбокс) для отметки/снятия отметки.
- CTkRadioButton — переключатель (радиокнопка) для выбора одного варианта из группы.
- CTkSwitch — тумблер (переключатель) в современном стиле.
- CTkEntry — однострочное поле для ввода текста или чисел.
- CTkTextbox — многострочное текстовое поле с прокруткой и поддержкой форматирования.
- CTkOptionMenu — выпадающее меню для выбора одного варианта из списка.
- CTkComboBox — комбинированный список: сочетает поле ввода и выпадающий список.
- CTkProgressBar — индикатор прогресса для отображения хода выполнения задачи.
- CTkSlider — ползунок для плавного изменения значения (например, громкости или яркости).
- CTkSegmentedButton — группа кнопок‑сегментов для выбора одного варианта из нескольких.
- CTkLabel — метка для отображения текста или изображений.
- CTkScrollbar — полоса прокрутки для виджетов с большим объемом контента.
- CTkTabview — виджет с вкладками для организации интерфейса в виде нескольких страниц.
- CTkCanvas — холст для рисования графиков, диаграмм или кастомных элементов.
Подробнее ознакомиться с информацией о библиотеке CustomTkinter можно на официальном сайте.
Разработку можно вести в любой удобной IDE или вообще в обычном текстовом редакторе. Также можно использовать более навороченный редактор Visual Studio Code с соответствующим расширением. В качестве IDE отлично подойдет PyCharm от компании JetBrains или наша отечественная OpenIDE с установленным плагином Python Community Edition. Об этом и о создании проекта можно прочитать в разделе «Подготовка» этой нашей публикации.
Как написать «Привет, мир!» на Tkinter и на CustomTkinter
Вот пример простейшей программы на Tkinter, которая после нажатия на кнопку выводит в текстовую метку сообщение «Привет, мир!»:
import tkinter as tk
# Создаем главное окно
root = tk.Tk()
root.title("Простая программа")
root.geometry("300x150")
# Метка
label = tk.Label(root, text="Нажми на кнопку!")
label.pack(pady=20)
# Функция для обработки нажатия кнопки
def on_button_click():
label.config(text="Привет, Мир!")
# Кнопка
button = tk.Button(root, text="Нажми на меня!", command=on_button_click)
button.pack(pady=10)
# Запускаем главный цикл
root.mainloop()
Если запустить этот код, то увидим такое окно:

Нажимаем на кнопку и получаем:

А вот то же самое приложение, но написанное на CustomTkinter:
import customtkinter as ctk
# Устанавливаем тему и внешний вид (опционально)
ctk.set_appearance_mode("Light") # Режимы: "Light", "Dark"
ctk.set_default_color_theme("blue") # Темы: "blue", "green", "dark-blue"
# Создаем главное окно
root = ctk.CTk()
root.title("Простая программа")
root.geometry("300x150")
# Метка
label = ctk.CTkLabel(root, text="Нажми на кнопку!")
label.pack(pady=20)
# Функция для обработки нажатия кнопки
def on_button_click():
label.configure(text="Привет, Мир!")
# Кнопка
button = ctk.CTkButton(root, text="Нажми на меня!", command=on_button_click)
button.pack(pady=10)
# Запускаем главный цикл
root.mainloop()
Как видим, код очень похож на предыдущий, только в самом начале исходника, после импорта библиотеки, прописаны установки режима и темы для приложения. А дальше все то же самое: создаем главное окно приложения, далее создаем текстовую метку, потом добавляем функцию для обработки нажатия на кнопку, после этого уже создаем саму кнопку и в самом конце запускаем главный цикл приложения. Вот так приложение выглядит:

Жмем на кнопку:

Да, действительно, вид куда более приятный и современный, чем на Tkinter! А теперь сменим режим на темный, а тему изменим на зеленую. Для этого поменяем аргументы в этих строках:
ctk.set_appearance_mode("Light") # Режимы: "Light", "Dark"
ctk.set_default_color_theme("blue") # Темы: "blue", "green", "dark-blue"
Заменим Light на Dark, а blue на green:
ctk.set_appearance_mode("Dark") # Режимы: "Light", "Dark"
ctk.set_default_color_theme("green") # Темы: "blue", "green", "dark-blue"
Запустим и увидим следующее:

Снова жмем на кнопку:

Далее посмотрим, как создать простую игру в угадывание числа на CustomTkinter.
Как написать игру в угадывание числа на CustomTkinter
Программа загадывает число от 1 до 100, а пользователь должен его угадать. Программа дает подсказки и считает количество попыток. Сначала делаем необходимые импорты. Вместе с customtkinter импортируем еще и модуль random. Он нужен для генерации случайного числа. Далее, как обычно, устанавливаем режим и тему для приложения:
import customtkinter as ctk
import random
# Настройка внешнего вида и темы
ctk.set_appearance_mode("Light") # Темный режим
ctk.set_default_color_theme("green") # Цветовая тема
Потом объявляем класс, где будет содержаться весь основной код, а также задаем заголовок и размер окна:
class GuessNumberGame:
def __init__(self):
# Создание главного окна
self.root = ctk.CTk()
self.root.title("Угадай число")
self.root.geometry("400x300")
Далее генерируем случайное число и инициализируем нулем переменную attempts, в которой будет храниться количество попыток, затраченных на угадывание числа. Следующей командой запускаем функцию create_widgets, создающую пользовательский интерфейс:
self.secret_number = random.randint(1, 100) self.attempts = 0 # Создание и размещение виджетов self.create_widgets()
Интерфейс состоит из следующих шести компонентов:
- Текстовая метка с надписью «Угадайте число от 1 до 100!» крупным жирным шрифтом.
- Текстовое поле, куда игрок вводит предполагаемое число.
- Кнопка «Проверить», при нажатии на которую вызывается функция check_guess для проверки введенного числа.
- Метка для подсказок, где отображаются сообщения о том, больше или меньше введенное число загаданного, а также поздравления при победе.
- Метка для количества попыток.
- Кнопка запуска новой игры.
Функция create_widgets:
def create_widgets(self):
# Заголовок
self.title_label = ctk.CTkLabel(
self.root,
text="Угадайте число от 1 до 100!",
font=ctk.CTkFont(size=20, weight="bold")
)
self.title_label.pack(pady=20)
# Поле ввода
self.entry = ctk.CTkEntry(
self.root,
placeholder_text="Введите число...",
width=200
)
self.entry.pack(pady=10)
# Кнопка проверки
self.check_button = ctk.CTkButton(
self.root,
text="Проверить",
command=self.check_guess
)
self.check_button.pack(pady=10)
# Метка для подсказок
self.hint_label = ctk.CTkLabel(
self.root,
text="",
font=ctk.CTkFont(size=14)
)
self.hint_label.pack(pady=10)
# Метка для количества попыток
self.attempts_label = ctk.CTkLabel(
self.root,
text=f"Попыток: {self.attempts}",
font=ctk.CTkFont(size=12)
)
self.attempts_label.pack(pady=5)
# Кнопка для новой игры
self.new_game_button = ctk.CTkButton(
self.root,
text="Новая игра",
command=self.new_game,
fg_color="gray",
hover_color="dark gray"
)
self.new_game_button.pack(pady=10)
Дальше идет функция check_guess:
def check_guess(self):
try:
# Получаем введенное число
guess = int(self.entry.get())
self.attempts += 1
# Обновляем счетчик попыток
self.attempts_label.configure(text=f"Попыток: {self.attempts}")
# Проверяем, угадал ли пользователь
if guess < self.secret_number:
self.hint_label.configure(
text="Слишком мало! Попробуйте больше.",
text_color="red"
)
elif guess > self.secret_number:
self.hint_label.configure(
text="Слишком много! Попробуйте меньше.",
text_color="red"
)
else:
self.hint_label.configure(
text=f"Поздравляем! Вы угадали число {self.secret_number}",
text_color="green"
)
# Блокируем поле ввода и кнопку проверки после победы
self.entry.configure(state="disabled")
self.check_button.configure(state="disabled")
except ValueError:
# Если введено не число, показываем ошибку
self.hint_label.configure(
text="Пожалуйста, введите целое число!",
text_color="red"
)
Сначала в блоке try текст извлекается из поля ввода, преобразуется в целое число с помощью функции int() и сохраняется в переменной guess:
guess = int(self.entry.get())
Затем счетчик попыток attempts увеличивается на 1, а в метке attempts_label отображается актуальное число попыток:
self.attempts += 1
self.attempts_label.configure(text=f"Попыток: {self.attempts}")
Далее программа сравнивает введенное число guess с загаданным secret_number. Если guess меньше загаданного, в метке подсказки hint_label появляется сообщение «Слишком мало! Попробуйте больше», выделенное красным цветом. Если guess больше загаданного, выводится «Слишком много! Попробуйте меньше» — тоже выделенное красным.
Если числа равны, функция выводит поздравление: «Поздравляем! Вы угадали число…» с указанием загаданного числа — текст отображается зеленым. Одновременно поле ввода entry и кнопка «Проверить» check_button переводятся в неактивное состояние:
self.entry.configure(state="disabled") self.check_button.configure(state="disabled")
Если на этапе преобразования ввода в число возникает ошибка ValueError, когда, например, пользователь ввел буквы или оставил поле пустым, срабатывает блок except ValueError. В этом случае в метке подсказки отображается сообщение «Пожалуйста, введите целое число!» красным цветом — так пользователь понимает, что формат ввода некорректен.
Далее следует функция new_game, которая запускает новую игру. В этой функции генерируется новое случайное число, переменная attempts получает нулевое значение, очищаются и приводятся в изначальные состояния метки и поля, а кнопка проверки вновь становится активной:
def new_game(self):
# Начинаем новую игру
self.secret_number = random.randint(1, 100)
self.attempts = 0
self.attempts_label.configure(text=f"Попыток: {self.attempts}")
self.hint_label.configure(text="")
self.entry.delete(0, "end")
self.entry.configure(state="normal")
self.check_button.configure(state="normal")
Следующими следуют функция запуска главного цикла и конструкция для запуска игры напрямую, а не в качестве отдельного модуля:
def run(self):
# Запуск главного цикла приложения
self.root.mainloop()
# Запуск игры
if __name__ == "__main__":
game = GuessNumberGame()
game.run()
Весь код игры будет выглядеть следующим образом:
import customtkinter as ctk
import random
# Настройка внешнего вида и темы
ctk.set_appearance_mode("Light") # Темный режим
ctk.set_default_color_theme("green") # Цветовая тема
class GuessNumberGame:
def __init__(self):
# Создание главного окна
self.root = ctk.CTk()
self.root.title("Угадай число")
self.root.geometry("400x300")
# Генерация случайного числа от 1 до 100
self.secret_number = random.randint(1, 100)
self.attempts = 0
# Создание и размещение виджетов
self.create_widgets()
def create_widgets(self):
# Заголовок
self.title_label = ctk.CTkLabel(
self.root,
text="Угадайте число от 1 до 100!",
font=ctk.CTkFont(size=20, weight="bold")
)
self.title_label.pack(pady=20)
# Поле ввода
self.entry = ctk.CTkEntry(
self.root,
placeholder_text="Введите число...",
width=200
)
self.entry.pack(pady=10)
# Кнопка проверки
self.check_button = ctk.CTkButton(
self.root,
text="Проверить",
command=self.check_guess
)
self.check_button.pack(pady=10)
# Метка для подсказок
self.hint_label = ctk.CTkLabel(
self.root,
text="",
font=ctk.CTkFont(size=14)
)
self.hint_label.pack(pady=10)
# Метка для количества попыток
self.attempts_label = ctk.CTkLabel(
self.root,
text=f"Попыток: {self.attempts}",
font=ctk.CTkFont(size=12)
)
self.attempts_label.pack(pady=5)
# Кнопка для новой игры
self.new_game_button = ctk.CTkButton(
self.root,
text="Новая игра",
command=self.new_game,
fg_color="gray",
hover_color="dark gray"
)
self.new_game_button.pack(pady=10)
def check_guess(self):
try:
# Получаем введенное число
guess = int(self.entry.get())
self.attempts += 1
# Обновляем счетчик попыток
self.attempts_label.configure(text=f"Попыток: {self.attempts}")
# Проверяем, угадал ли пользователь
if guess < self.secret_number:
self.hint_label.configure(
text="Слишком мало! Попробуйте больше.",
text_color="red"
)
elif guess > self.secret_number:
self.hint_label.configure(
text="Слишком много! Попробуйте меньше.",
text_color="red"
)
else:
self.hint_label.configure(
text=f"Поздравляем! Вы угадали число {self.secret_number}",
text_color="green"
)
# Блокируем поле ввода и кнопку проверки после победы
self.entry.configure(state="disabled")
self.check_button.configure(state="disabled")
except ValueError:
# Если введено не число, показываем ошибку
self.hint_label.configure(
text="Пожалуйста, введите целое число!",
text_color="red"
)
def new_game(self):
# Начинаем новую игру
self.secret_number = random.randint(1, 100)
self.attempts = 0
self.attempts_label.configure(text=f"Попыток: {self.attempts}")
self.hint_label.configure(text="")
self.entry.delete(0, "end")
self.entry.configure(state="normal")
self.check_button.configure(state="normal")
def run(self):
# Запуск главного цикла приложения
self.root.mainloop()
# Запуск игры
if __name__ == "__main__":
game = GuessNumberGame()
game.run()
Запустим игру:

Начинаем угадывать:

Продолжаем попытки угадать и, наконец, у нас это получается:

Как написать кнопочный калькулятор на CustomTkinter
Выше мы уже приводили вот эту ссылку на одну из наших публикаций. Помимо прочего, в ней также рассказывается, как создать кнопочный калькулятор на Tkinter. Прочитать про это можно, перейдя в раздел «Калькуляторы с графическим интерфейсом пользователя» и далее в подраздел «Кнопочный калькулятор на Tkinter». Ниже приводится код такого же калькулятора, но написанного на CustomTkinter:
import customtkinter as ctk
# Настройка внешнего вида
ctk.set_appearance_mode("Light") # Режимы: "Light", "Dark"
def click_on_button(number):
current = calc_entry.get()
calc_entry.delete(0, ctk.END)
calc_entry.insert(0, str(current) + str(number))
def click_on_button_clear():
calc_entry.delete(0, ctk.END)
def click_on_button_equal():
try:
result = eval(calc_entry.get())
calc_entry.delete(0, ctk.END)
calc_entry.insert(0, result)
except Exception:
calc_entry.delete(0, ctk.END)
calc_entry.insert(0, "Ошибка")
# Создаем главное окно приложения
root = ctk.CTk()
root.title("Калькулятор")
root.geometry("400x400")
# Создаем поле ввода
calc_entry = ctk.CTkEntry(
root,
width=300,
height=40,
font=ctk.CTkFont(size=16),
justify="right"
)
calc_entry.grid(row=0, column=0, columnspan=4, padx=20, pady=20, sticky="ew")
# Определяем кнопки и их цвета
buttons_config = [
('7', 1, 0), ('8', 1, 1), ('9', 1, 2), ('/', 1, 3, '#FFA500'),
('4', 2, 0), ('5', 2, 1), ('6', 2, 2), ('*', 2, 3, '#FFA500'),
('1', 3, 0), ('2', 3, 1), ('3', 3, 2), ('-', 3, 3, '#FFA500'),
('0', 4, 0), ('.', 4, 1), ('C', 4, 2), ('+', 4, 3, '#FFA500')
]
# Создаем кнопки
for (text, row, col, *color) in buttons_config:
bg_color = color[0] if color else '#3B8ED0' # Синий по умолчанию, оранжевый для операторов
if text == 'C':
command=click_on_button_clear
else:
command=lambda x=text: click_on_button(x)
btn = ctk.CTkButton(
root,
text=text,
font=ctk.CTkFont(size=14, weight="bold"),
fg_color=bg_color,
hover_color="#4B9ED9",
command=command,
height=40
)
btn.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
# Кнопка равно
equal_btn = ctk.CTkButton(
root,
text="=",
font=ctk.CTkFont(size=14, weight="bold"),
fg_color="#E74C3C",
hover_color="#C0392B",
command=click_on_button_equal,
height=40
)
equal_btn.grid(row=5, column=0, columnspan=4, padx=20, pady=10, sticky="ew")
# Настраиваем веса строк и столбцов для адаптивности
for i in range(5):
root.grid_rowconfigure(i, weight=1)
for j in range(4):
root.grid_columnconfigure(j, weight=1)
# Запускаем главный цикл приложения
root.mainloop()
Сравните этот код с кодом калькулятора на Tkinter. Структура и логика примерно такая же, но, как говорится, есть нюансы. Касаются они в основном кнопок и настройки адаптивности. Кнопки здесь также определяются при помощи списка (buttons_config), в котором для каждой кнопки, помимо отображаемого текста, устанавливается еще и ее положение в сетке.
А для кнопок операций дополнительно устанавливается еще и цвет. Сами кнопки, как и в случае с Tkinter, создаются циклом for. Этот цикл пробегается по списку buttons_config и расставляет все кнопочки по своим ячейкам в сетке. Кроме одной. Кнопка РАВНО добавляется отдельно.
Что касается настроек адаптивности, которые прописаны сразу перед командой запуска главного цикла приложения, то они нужны для того, чтобы размер сетки адаптировался к изменениям размера окна приложения. Ниже увидим, как это работает.
После запуска калькулятор выглядит следующим образом:

Уменьшим окно приложения:

Теперь увеличим:

Как видим, сетка нормально адаптируется к размеру окна. А теперь закомментируем строки, отвечающие за адаптивность. То есть, сделаем так:
# Настраиваем веса строк и столбцов для адаптивности # for i in range(5): # root.grid_rowconfigure(i, weight=1) # for j in range(4): # root.grid_columnconfigure(j, weight=1)
Получим:

Увеличим размер окна:

Адаптивность поломалась! Лучше вернуть все, как было. Теперь проверим калькулятор в работе:

Нажимаем РАВНО:

Работает! Дополнительно вы можете проверить, например, деление на ноль.
