К основному содержанию
правило

tkinter — правило для Cursor: что делает и как установить

Направляет разработку GUI на Python: диктует использование ttk и ttkbootstrap, классовую архитектуру экранов и сетку grid.

Установить в Cursor · Вручную: положить файлы Все способы и платформы ↓
.cursor/rules/tkinter.mdc
Открыть на GitHub

Что делает tkinter

Правило применяется ко всем файлам **/*.py и запрещает устаревшие виджеты классического tk. Вместо них агент обязан генерировать интерфейсы на tkinter.ttk или ttkbootstrap. Это сразу даёт поддержку стилей вроде flatly или superhero без ручной перерисовки стандартных кнопок.

В коде закрепляется классовая модульная структура с разделением на представления (BaseView) и контроллер AppController. Для позиционирования предписывается использовать grid либо простой pack. Смешивание обоих менеджеров геометрии внутри одного контейнера запрещено, потому что это вызывает TclError. Также блокируются вызовы цепочкой вроде Button().pack(), возвращающие None.

Привязку динамических данных правило требует делать через переменные StringVar или IntVar. Главный поток нельзя блокировать вызовами time.sleep(). Для задержек используется root.after(), а для фоновых задач — потоки или asyncio.

Какие задачи решает

  • Собрать современный интерфейс на Python с темами ttkbootstrap
  • Разделить экраны десктопного приложения на классовые компоненты
  • Избежать ошибок TclError из-за смешивания pack и grid в одном контейнере
  • Привязать поля ввода к переменным StringVar и IntVar
  • Предотвратить зависание интерфейса из-за блокировок главного потока

Примеры запросов агенту

  • Создай окно авторизации на ttkbootstrap с темой superhero и валидацией полей
  • Разбей монолитный скрипт на Tkinter на классы BaseView и AppController
  • Построй форму настроек через grid с привязкой значений к IntVar

Как установить tkinter

Вручную: положить файлы

.cursor/rules/tkinter.mdc

Во frontmatter: description, globs, alwaysApply

По шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules

Вопросы о tkinter

Как установить правило tkinter в Cursor?

Сохраните файл правила по пути .cursor/rules/tkinter.mdc в корне вашего проекта.

Что делает правило tkinter?

Оно задаёт стандарты архитектуры GUI на Python: требует применять ttk или ttkbootstrap, классовую структуру представлений, аккуратный grid и Tkinter-переменные.

Почему правило запрещает смешивать pack и grid?

Вызов pack() и grid() внутри одного родительского контейнера приводит к конфликту TclError и ломает расчёт геометрии окна.

Исходный текст

tkinter.mdc · CC0-1.0
---
description: Definitive guidelines for building modern, maintainable, and performant Tkinter applications using best practices, ttk, and ttkbootstrap.
globs: **/*.py
---
# tkinter Best Practices

Tkinter is Python's standard GUI toolkit. To build modern, maintainable, and responsive applications in 2025, adhere to these opinionated best practices.

## 1. Modern Theming: `ttk` and `ttkbootstrap`

Always use `tkinter.ttk` widgets for a native look and feel. For a truly modern aesthetic, integrate `ttkbootstrap`.

*   **Default to `ttk`**: `ttk` widgets automatically adopt the OS theme.
*   **Embrace `ttkbootstrap`**: For a flat, dark-mode-ready, and consistent cross-platform design, `ttkbootstrap` is the go-to. It's a drop-in replacement for `ttk` with enhanced styling.

❌ BAD: Using classic `tk` widgets
```python
import tkinter as tk
root = tk.Tk()
tk.Button(root, text="Old Button").pack()
```

✅ GOOD: Using `ttk` widgets
```python
import tkinter as tk
from tkinter import ttk # Always import ttk
root = tk.Tk()
ttk.Button(root, text="Themed Button").pack()
```

✅ BEST: Using `ttkbootstrap` for modern themes
```python
import ttkbootstrap as ttk # Import ttkbootstrap as ttk
from ttkbootstrap.constants import * # For constants like PRIMARY, INFO

root = ttk.Window(themename="superhero") # Apply a modern theme
ttk.Button(root, text="Bootstrap Button", bootstyle="primary").pack(pady=10)
```

## 2. Code Organization: Class-Based Views

Structure your application with clear separation of concerns. Use classes for each logical view or component to encapsulate widgets and their logic.

*   **Main Application Class**: Manages the root window and switches between views.
*   **View Classes**: Each class represents a distinct part of the UI (e.g., `LoginView`, `DashboardView`).
*   **Thin Bootstrap**: A minimal `main.py` to launch the app.

❌ BAD: Monolithic script
```python
import tkinter as tk
# ... hundreds of lines of mixed UI and logic ...
def create_ui():
    root = tk.Tk()
    # ...
    root.mainloop()
create_ui()
```

✅ GOOD: Class-based structure
```python
import ttkbootstrap as ttk
from typing import Type

class BaseView(ttk.Frame):
    def __init__(self, master: ttk.Window, controller: 'AppController') -> None:
        super().__init__(master)
        self.controller = controller

class MainView(BaseView):
    def __init__(self, master: ttk.Window, controller: 'AppController') -> None:
        super().__init__(master, controller)
        ttk.Label(self, text="Welcome to the App!").pack(pady=20)
        ttk.Button(self, text="Go to Settings", command=self.controller.show_settings).pack()

class SettingsView(BaseView):
    def __init__(self, master: ttk.Window, controller: 'AppController') -> None:
        super().__init__(master, controller)
        ttk.Label(self, text="Settings Page").pack(pady=20)
        ttk.Button(self, text="Back to Main", command=self.controller.show_main).pack()

class AppController:
    def __init__(self, root: ttk.Window) -> None:
        self.root = root
        self.current_view: BaseView | None = None
        self._views: dict[str, Type[BaseView]] = {
            "main": MainView,
            "settings": SettingsView
        }
        self.show_main()

    def _switch_view(self, view_name: str) -> None:
        if self.current_view:
            self.current_view.destroy()
        ViewClass = self._views[view_name]
        self.current_view = ViewClass(self.root, self)
        self.current_view.pack(fill=ttk.BOTH, expand=True)

    def show_main(self) -> None:
        self._switch_view("main")

    def show_settings(self) -> None:
        self._switch_view("settings")

if __name__ == "__main__":
    root = ttk.Window(themename="flatly")
    root.title("My Modern Tkinter App")
    root.geometry("400x300")
    app = AppController(root)
    root.mainloop()
```

## 3. Geometry Managers: `grid` is King, `pack` for Simplicity

Choose one geometry manager per parent container and stick to it. `grid` offers the most control and is generally preferred for complex layouts. Use `pack` only for very simple, linear arrangements.

*   **Never mix `pack()` and `grid()` in the same parent widget.** This is a common source of layout bugs.
*   **Separate widget creation from layout calls.** Chaining `widget.pack()` returns `None`, making the widget unusable later.

❌ BAD: Mixing `pack` and `grid` in the same frame
```python
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
tk.Label(frame, text="Label 1").pack()
tk.Button(frame, text="Button 1").grid(row=0, column=1) # ❌ Will cause TclError
```

❌ BAD: Chaining geometry manager calls
```python
import tkinter as tk
root = tk.Tk()
my_button = tk.Button(root, text="Click Me").pack() # my_button is now None
# my_button.config(text="New Text") # ❌ AttributeError: 'NoneType' object has no attribute 'config'
```

✅ GOOD: Using `grid` consistently
```python
import ttkbootstrap as ttk
root = ttk.Window()
root.title("Grid Layout")

# Configure grid to expand with window
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

main_frame = ttk.Frame(root)
main_frame.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)

# Configure frame's grid
main_frame.columnconfigure(1, weight=1) # Make column 1 expandable

ttk.Label(main_frame, text="Username:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
username_entry = ttk.Entry(main_frame)
username_entry.grid(row=0, column=1, sticky="ew", padx=5, pady=5)

ttk.Label(main_frame, text="Password:").grid(row=1, column=0, sticky="w", padx=5, pady=5)
password_entry = ttk.Entry(main_frame, show="*")
password_entry.grid(row=1, column=1, sticky="ew", padx=5, pady=5)

ttk.Button(main_frame, text="Login").grid(row=2, column=0, columnspan=2, pady=10)
```

## 4. Data Binding: Use Tkinter Variables

For dynamic UI elements (labels, entries, checkboxes), use `StringVar`, `IntVar`, `BooleanVar`, or `DoubleVar`. These variables notify linked widgets of changes automatically.

❌ BAD: Updating labels with regular Python variables
```python
import tkinter as tk
count = 0
def increment():
    global count
    count += 1
    label.config(text=f"Count: {count}") # Manual update
root = tk.Tk()
label = tk.Label(root, text=f"Count: {count}")
label.pack()
tk.Button(root, text="Increment", command=increment).pack()
```

✅ GOOD: Using `tk.IntVar`
```python
import tkinter as tk
root = tk.Tk()
count_var = tk.IntVar(value=0) # Initialize with a Tkinter variable
def increment():
    count_var.set(count_var.get() + 1) # Update the Tkinter variable
label = tk.Label(root, textvariable=count_var) # Link label to variable
label.pack()
tk.Button(root, text="Increment", command=increment).pack()
```

## 5. Responsiveness: Avoid Blocking the Event Loop

Never use `time.sleep()` in a GUI application's main thread. It will freeze the entire UI. For delayed actions, use `root.after()`. For long-running tasks, use threading or `asyncio`.

❌ BAD: Freezing the UI with `time.sleep()`
```python
import tkinter as tk
import time
root = tk.Tk()
status_label = tk.Label(root, text="Ready")
status_label.pack()
def long_task():
    status_label.config(text="Working...")
    root.update_idletasks() # Force update, but still blocks
    time.sleep(3) # ❌ GUI FREEZES for 3 seconds
    status_label.config(text="Done!")
tk.Button(root, text="Start Task", command=long_task).pack()
```

✅ GOOD: Non-blocking delays with `root.after()`
```python
import tkinter as tk
root = tk.Tk()
status_label = tk.Label(root, text="Ready")
status_label.pack()
def finish_task():
    status_label.config(text="Done!")
def start_task():
    status_label.config(text="Working...")
    root.after(3000, finish_task) # Schedule finish_task after 3000ms (3 seconds)
tk.Button(root, text="Start Task", command=start_task).pack()
```

## 6. Image Handling: Keep References

Tkinter does not store references to `PhotoImage` objects internally. If your Python variable holding the `PhotoImage` goes out of scope, the image will disappear from the widget.

❌ BAD: Image disappears due to garbage collection
```python
import tkinter as tk
from PIL import Image, ImageTk # Requires Pillow: pip install Pillow
root = tk.Tk()
def create_image_label():
    img = Image.open("icon.png") # Assume icon.png exists
    photo = ImageTk.PhotoImage(img)
    label = tk.Label(root, image=photo) # photo might be garbage collected
    label.pack()
create_image_label()
```

✅ GOOD: Explicitly keep a reference to the image
```python
import tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
img = Image.open("icon.png") # Assume icon.png exists
photo = ImageTk.PhotoImage(img)
label = tk.Label(root, image=photo)
label.image = photo # ✅ Keep a reference directly on the widget
label.pack()
```

## 7. Essential Boilerplate: `root.mainloop()`

Always call `root.mainloop()` as the very last statement in your main application script. Without it, your GUI window will flash and immediately close.

❌ BAD: Window flashes and disappears
```python
import tkinter as tk
root = tk.Tk()
root.title("My App")
tk.Label(root, text="Hello!").pack()
# Forgot root.mainloop()
```

✅ GOOD: Proper application lifecycle
```python
import tkinter as tk
root = tk.Tk()
root.title("My App")
tk.Label(root, text="Hello!").pack()
root.mainloop() # ✅ This is non-negotiable
```

## 8. Robust Callbacks: Handle Exceptions

Wrap potentially failing operations in callbacks with `try...except` blocks. Tkinter often swallows exceptions in event handlers, making debugging difficult. Use `messagebox` to provide user feedback.

❌ BAD: Silent crashes in callbacks
```python
import tkinter as tk
def risky_action():
    # This will raise FileNotFoundError if 'nonexistent.txt' doesn't exist
    with open("nonexistent.txt", "r") as f:
        content = f.read()
    print(content) # This line might never be reached
root = tk.Tk()
tk.Button(root, text="Do Risky Action", command=risky_action).pack()
```

✅ GOOD: Graceful error handling
```python
import tkinter as tk
from tkinter import messagebox
def risky_action_safe():
    try:
        with open("nonexistent.txt", "r") as f:
            content = f.read()
        messagebox.showinfo("Success", "File read successfully!")
    except FileNotFoundError:
        messagebox.showerror("Error", "The file 'nonexistent.txt' was not found.")
    except Exception as e:
        messagebox.showerror("Error", f"An unexpected error occurred: {e}")
root = tk.Tk()
tk.Button(root, text="Do Risky Action Safely", command=risky_action_safe).pack()
```

## 9. Type Hints

Use type hints consistently for improved readability, maintainability, and static analysis. This is standard modern Python practice.

❌ BAD: Untyped functions and methods
```python
class MyWidget(tk.Frame):
    def __init__(self, master, data):
        super().__init__(master)
        self.data = data
    def update_label(self, new_text):
        # ...
```

✅ GOOD: Typed functions and methods
```python
import tkinter as tk
from typing import Any

class MyWidget(tk.Frame):
    def __init__(self, master: tk.Tk | tk.Frame, data: dict[str, Any]) -> None:
        super().__init__(master)
        self.data = data
        self.label = tk.Label(self, text=self.data.get("name", ""))
        self.label.pack()

    def update_label(self, new_text: str) -> None:
        self.label.config(text=new_text)
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Похожие правила IDE

Все правила IDE по теме «Фронтенд и UI» →
winui3 официальный github Правило задаёт рекомендации для WinUI 3 и Windows App SDK: от XAML и потоков до окон и MVVM. Пригодится, чтобы Copilot учитывал особенности настольных приложений Windows. ★ 40 478звёзд репо vue официальный github Правило задаёт Copilot стандарты Vue 3: Composition API, <script setup>, реактивность, Pinia и Vue Router. Пригодится, когда агент пишет компоненты в вашем стиле. ★ 40 478звёзд репо tanstack-start-shadcn-tailwind официальный github Правило задаёт Copilot стек TanStack Start, Shadcn/ui и Tailwind CSS и требует валидировать внешние данные через Zod. ★ 40 478звёзд репо tailwind-v4-vite официальный github Правило помогает подключить Tailwind CSS v4 к проекту на Vite через официальный плагин. Пригодится и для новой установки, и при переходе с v3. ★ 40 478звёзд репо svelte официальный github Направляет GitHub Copilot на стандарты Svelte 5 и SvelteKit 2: руны вместо старых сторов, типизация компонентов и безопасная работа с SSR. ★ 40 478звёзд репо power-bi-custom-visuals-development официальный github Правило для GitHub Copilot, которое подсказывает агенту, как писать кастомные визуалы Power BI на React, D3.js и TypeScript. ★ 40 478звёзд репо

Из того же набора240

Весь набор →
transformers Правило для Cursor задаёт стиль работы с библиотекой 🤗 Transformers: модульные модели, типизация, Trainer вместо ручных циклов обучения. ★ 3 580звёзд репо tqdm Файл правил для Cursor, который задаёт агенту единый стиль работы с tqdm: импорты, контекстные менеджеры, вывод в консоль и метрики в постфиксе. ★ 3 580звёзд репо tortoise-orm Правило задаёт рекомендации по работе с Tortoise ORM в Python-проектах: от управления подключениями до пакетных операций и миграций. ★ 3 580звёзд репо tornado Правило для Cursor задаёт стиль кода в Tornado: только async/await для ввода-вывода, полные аннотации типов и один обработчик ошибок на всё приложение. ★ 3 580звёзд репо tinygrad Правило задаёт практики для tinygrad: ленивые вычисления, сбор параметров, профилирование и работу с графом операций. ★ 3 580звёзд репо three-js Правило для Cursor задаёт стандарты разработки на three.js: обработка ошибок WebGL, ленивая загрузка ассетов, оптимизация цикла рендера и освобождение ресурсов. ★ 3 580звёзд репо terraform Задаёт стандарты написания манифестов Terraform для Cursor: структуру каталогов, именование, блокировки стейта и правила модулей. ★ 3 580звёзд репо tensorflow Правило задаёт ориентиры для Python-кода с TensorFlow: модели на tf.keras, конвейеры данных на tf.data и компиляцию функций через @tf.function. Пригодится для настройки проекта машинного обучения. ★ 3 580звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой