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

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

Правило для Cursor задаёт стандарты разработки PyQt-приложений на Python: запрещает менять сгенерированный интерфейс вручную и требует выносить тяжёлые вычисления из потока интерфейса.

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

Что делает pyqt

Правило активируется для файлов с маской **/*.py и направляет ИИ на чистую архитектуру PyQt6. Главное требование к визуальной части — собирать интерфейс в Qt Designer и загружать .ui динамически через loadUi либо наследовать сгенерированный класс. Править код pyuic напрямую запрещено, иначе вёрстка сломается при первой перегенерации.

Для списков и таблиц агент обязан строить Model-View архитектуру через QAbstractListModel, не сохраняя данные внутри виджетов. Фоновые расчёты выносятся в QThreadPool через QRunnable или QThread. Если запустить тяжёлую задачу в основном потоке интерфейса, окно намертво зависнет, поэтому передача статусов и результатов идёт исключительно через сигналы pyqtSignal. Связи между логикой и кнопками оформляются через слоты.

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

  • предотвратить ручные правки в файлах, сгенерированных pyuic
  • разделить данные и интерфейс через архитектуру Model-View
  • вынести блокирующие задачи из основного UI-потока в QThreadPool
  • связать компоненты интерфейса с бизнес-логикой через сигналы и слоты

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

  • Создай окно со списком задач на PyQt6, загрузи разметку из mainwindow.ui и подключи модель на базе QAbstractListModel
  • Напиши воркер для длительной загрузки данных в QThreadPool, чтобы интерфейс окна не зависал

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

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

.cursor/rules/pyqt.mdc

Во frontmatter: description, globs, alwaysApply

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

Вопросы о pyqt

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

Создайте файл .cursor/rules/pyqt.mdc в корне проекта и добавьте в него содержимое правила. Cursor начнёт применять инструкции ко всем файлам по маске **/*.py.

Что требует правило pyqt при создании интерфейса?

Оно предписывает проектировать экраны в Qt Designer и загружать файлы .ui на лету через loadUi либо одиночное наследование. Ручное редактирование файлов, созданных компилятором pyuic, запрещено.

Как правило pyqt организует фоновые вычисления?

Фоновая работа передаётся в пул потоков через QThreadPool и QRunnable. Обновление элементов интерфейса разрешено только через сигналы pyqtSignal, чтобы избежать блокировки главного цикла событий.

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

pyqt.mdc · CC0-1.0
---
description: Enforce modern, maintainable, and performant PyQt application development standards by leveraging Qt Designer, Model-View architecture, and responsive threading.
globs: **/*.py
---
# PyQt Best Practices

This guide outlines essential practices for building robust, scalable, and maintainable PyQt applications. Adhere to these rules for consistent, high-quality code.

## 1. UI Design: Qt Designer & Dynamic Loading

Always design your UI visually with **Qt Designer** (or Qt Design Studio). Keep the generated `.ui` files pristine. Load them dynamically at runtime or integrate the `pyuic`-generated code via inheritance without modification. This separates design from logic and simplifies UI updates.

❌ **BAD: Hand-editing `pyuic`-generated files**
```python
# ui_mainwindow.py (DO NOT EDIT THIS FILE DIRECTLY)
# This file was generated by pyuic6...
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        # ... generated code ...
        self.my_custom_button.clicked.connect(self.my_logic_function) # Added here
```

✅ **GOOD: Dynamic loading with `QUiLoader` (PyQt6)**
```python
from PyQt6.QtWidgets import QApplication, QWidget
from PyQt6.uic import loadUi

class MyMainWindow(QWidget):
    def __init__(self):
        super().__init__()
        loadUi("mainwindow.ui", self) # Load UI dynamically
        self.my_button.clicked.connect(self._on_button_clicked)

    def _on_button_clicked(self):
        print("Button clicked!")

if __name__ == "__main__":
    app = QApplication([])
    window = MyMainWindow()
    window.show()
    app.exec()
```

✅ **GOOD: Single inheritance with `pyuic`-generated code**
```python
# ui_mainwindow.py (Generated by `pyuic6 -o ui_mainwindow.py mainwindow.ui`)
# DO NOT EDIT THIS FILE.
from PyQt6 import QtWidgets # ... other imports ...
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        # ... generated UI setup code ...
        pass # No signal connections here

# main.py (Your application logic)
from PyQt6.QtWidgets import QApplication, QMainWindow
from ui_mainwindow import Ui_MainWindow # Import generated UI

class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self) # Set up the UI on this window
        self.ui.my_button.clicked.connect(self._on_button_clicked)

    def _on_button_clicked(self):
        print("Button clicked from inherited UI!")

if __name__ == "__main__":
    app = QApplication([])
    window = MyMainWindow()
    window.show()
    app.exec()
```

## 2. Component Architecture: Model-View Separation

For any data-driven UI (lists, tables, trees), adopt the **Model-View architecture**. This decouples data management from UI presentation, leading to cleaner, more scalable code.

❌ **BAD: Storing data directly in widgets**
```python
from PyQt6.QtWidgets import QListWidget, QListWidgetItem
# ...
self.my_list_widget = QListWidget()
self.my_list_widget.addItem("Item 1")
self.my_list_widget.addItem("Item 2")
# To update: self.my_list_widget.item(0).setText("New Item 1") # Tedious
```

✅ **GOOD: Custom `QAbstractListModel` for data**
```python
from PyQt6.QtCore import QAbstractListModel, QModelIndex, Qt
from PyQt6.QtWidgets import QListView, QApplication

class MyListModel(QAbstractListModel):
    def __init__(self, data=None):
        super().__init__()
        self._data = data or []

    def data(self, index: QModelIndex, role: int):
        if role == Qt.ItemDataRole.DisplayRole:
            return self._data[index.row()]
        return None

    def rowCount(self, parent: QModelIndex):
        return len(self._data)

    def add_item(self, item: str):
        self.beginInsertRows(QModelIndex(), len(self._data), len(self._data))
        self._data.append(item)
        self.endInsertRows()

# ... in your QMainWindow or QWidget
# self.my_list_view = QListView()
# self.model = MyListModel(["Initial Item 1", "Initial Item 2"])
# self.my_list_view.setModel(self.model)
# self.model.add_item("New Item from Logic") # Model updates view automatically
```

## 3. Asynchronous Operations: Keep UI Responsive

Never block the UI thread with long-running tasks. Use `QThreadPool` with `QRunnable` or `QThread` for background processing. Emit signals from worker threads to update the UI on the main thread.

❌ **BAD: Blocking UI thread**
```python
import time
from PyQt6.QtWidgets import QPushButton, QLabel
# ...
def on_heavy_button_clicked(self):
    self.status_label.setText("Processing...")
    time.sleep(5) # UI freezes for 5 seconds
    self.status_label.setText("Done!")
```

✅ **GOOD: Offload to `QThreadPool`**
```python
from PyQt6.QtCore import QRunnable, QThreadPool, pyqtSignal, QObject
from PyQt6.QtWidgets import QPushButton, QLabel, QApplication, QWidget
import time

class WorkerSignals(QObject):
    finished = pyqtSignal()
    result = pyqtSignal(str)

class Worker(QRunnable):
    def __init__(self, task_id):
        super().__init__()
        self.signals = WorkerSignals()
        self.task_id = task_id

    def run(self):
        time.sleep(2) # Simulate heavy work
        self.signals.result.emit(f"Task {self.task_id} completed.")
        self.signals.finished.emit()

class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.threadpool = QThreadPool()
        self.button = QPushButton("Start Heavy Task", self)
        self.label = QLabel("Ready", self)
        self.button.clicked.connect(self._start_task)
        # ... layout setup ...

    def _start_task(self):
        self.label.setText("Processing...")
        worker = Worker(time.time()) # Unique ID for task
        worker.signals.result.connect(self.label.setText)
        worker.signals.finished.connect(lambda: print("Worker finished"))
        self.threadpool.start(worker)

if __name__ == "__main__":
    app = QApplication([])
    window = MyWindow()
    window.show()
    app.exec()
```

## 4. Communication: Signals & Slots

Leverage Qt's powerful signals and slots mechanism for decoupled communication between UI components and business logic.

❌ **BAD: Direct method calls for UI events**
```python
# In MainWindow
self.login_button.clicked.connect(self.login_manager.authenticate) # login_manager is tightly coupled
```

✅ **GOOD: Decoupled signals and slots**
```python
from PyQt6.QtCore import pyqtSignal, QObject
# ...
class LoginManager(QObject):
    login_successful = pyqtSignal(str)
    login_failed = pyqtSignal(str)

    def authenticate(self, username, password):
        if username == "user" and password == "pass":
            self.login_successful.emit("Welcome!")
        else:
            self.login_failed.emit("Invalid credentials.")

# In MainWindow
# self.login_manager = LoginManager()
# self.login_manager.login_successful.connect(self.show_dashboard)
# self.login_manager.login_failed.connect(self.display_error)
# self.login_button.clicked.connect(lambda: self.login_manager.authenticate(self.user_input.text(), self.pass_input.text()))
```

## 5. Code Organization & Naming

Follow Pythonic naming conventions (`CamelCase` for Qt classes, `snake_case` for methods/variables). Organize code into logical modules (e.g., `ui/`, `models/`, `workers/`). Use Qt resource files (`.qrc`) for icons, images, and stylesheets.

❌ **BAD: Inconsistent naming, monolithic files**
```python
# myapp.py
class my_main_window(QtWidgets.QMainWindow): # Incorrect class name
    def __init__(self):
        self.MyButton = QtWidgets.QPushButton() # Incorrect variable name
        # ... all logic here ...
```

✅ **GOOD: Consistent naming, modular structure**
```python
# main.py
from PyQt6.QtWidgets import QApplication
from my_app.ui.main_window import MainWindow # Separate UI module

if __name__ == "__main__":
    app = QApplication([])
    main_window = MainWindow()
    main_window.show()
    app.exec()

# my_app/ui/main_window.py
from PyQt6.QtWidgets import QMainWindow
# ...
class MainWindow(QMainWindow): # CamelCase for Qt classes
    def __init__(self):
        super().__init__()
        self._setup_ui() # Private helper method
        self._connect_signals()

    def _on_button_clicked(self): # snake_case for methods
        pass

# my_app/resources.qrc (compiled to my_app/resources_rc.py)
# <RCC>
#   <qresource prefix="/icons">
#     <file>icon.png</file>
#   </qresource>
# </RCC>
# Use: self.setWindowIcon(QIcon(":/icons/icon.png"))
```

## 6. Styling & Theming

Use Qt's stylesheet system (CSS-like `.qss` files) for styling. This allows for easy theming (e.g., dark mode) without modifying Python code.

❌ **BAD: Hardcoding styles in Python**
```python
self.my_button.setStyleSheet("background-color: red; color: white;") # Hardcoded
```

✅ **GOOD: External stylesheets**
```python
# styles.qss
QPushButton {
    background-color: #2196F3; /* Blue */
    color: white;
    border: none;
    padding: 8px 16px;
    border-radius: 4px;
}
QPushButton:hover {
    background-color: #1976D2; /* Darker Blue */
}

# In main.py or MainWindow __init__
with open("styles.qss", "r") as f:
    _stylesheet = f.read()
app.setStyleSheet(_stylesheet) # Apply globally
# Or self.my_widget.setStyleSheet(_stylesheet) # Apply locally
```

## 7. Type Hints

Always use Python type hints for improved readability, maintainability, and static analysis.

❌ **BAD: Untyped code**
```python
def process_data(data):
    # ...
```

✅ **GOOD: Type-hinted code**
```python
from typing import List, Tuple
from PyQt6.QtCore import QModelIndex

def process_data(data: List[Tuple[bool, str]]) -> List[str]:
    # ...
    return [item[1] for item in data]

def data(self, index: QModelIndex, role: int) -> str | None:
    # ...
```

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

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

Все правила IDE по теме «Фронтенд и UI» →
pyqt6-ui-development-rules oimiragieo Скилл задаёт правила для PyQt6-интерфейсов: разделение логики и UI, сигналы и слоты, фоновые задачи и компоновка. Пригодится при разработке и рефакторинге desktop-приложений. CX Codex +1 1 100установок pyqt6-eeg-processing-cursorrules-prompt-file PatrickJS Настраивает Cursor на разработку десктопных приложений на PyQt6 для обработки и визуализации сигналов ЭЭГ. Помогает разделять тяжёлые вычисления и интерфейс. ★ 41 028звёзд репо gpui-kit longbridge Скилл обучает ИИ-агентов правилам разработки десктопных приложений на Rust через крейт gpui-kit. Помогает собирать интерфейсы, настраивать фокус, темы и писать тесты. CX Codex +1 1 000установок roblox-gui TabooHarmony Набор правил для вёрстки интерфейсов в Roblox. Помогает адаптировать HUD и магазины под смартфоны и консоли. CX Codex +1 2 200установок native-sdk vercel-labs Скилл помогает ИИ-агенту создавать десктопные приложения через Native SDK на TypeScript и разметке .native. Нужен для настройки сборки, интерфейса и встроенной автоматизации. CX Codex +1 1 500установок roblox-user-interfaces nonlooped Помогает проектировать и чинить интерфейсы в Roblox: от экранных меню со ScreenGui до табличек BillboardGui и 3D-превью в ViewportFrame. CX Codex +1 328установок

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

Весь набор →
pytest Правило задаёт команде единый стиль тестов на pytest: структуру проекта, фикстуры, параметризацию и моки. Пригодится, когда тесты пишут несколько человек. ★ 3 580звёзд репо pyside Задаёт стандарты разработки интерфейсов на PySide6 в Cursor. Помогает разделять логику и графику через Qt Designer или связку QML с бэкендом. ★ 3 580звёзд репо pyright Правило задаёт рекомендации по настройке Pyright и basedpyright для Python-проектов. Пригодится, если хотите сделать проверку типов строже и последовательнее. ★ 3 580звёзд репо pyramid Правило для Cursor с набором практик по Python-фреймворку Pyramid: структура проекта, конфигурация, транзакции и предикаты представлений. ★ 3 580звёзд репо pylint Правило задаёт рекомендации по настройке Pylint и проверке Python-кода: от docstring и имён до обработки исключений и сложности функций. ★ 3 580звёзд репо pygame Правило для Cursor задаёт каркас Pygame-проекта: один UIManager, фиксированный шаг времени, вёрстка на якорях и темы в JSON. ★ 3 580звёзд репо pydantic Направляет Cursor при создании моделей на Pydantic v2. Правило защищает от мутабельных значений по умолчанию и заставляет выносить настройки в pydantic_settings. ★ 3 580звёзд репо puppeteer Правило подсказывает, как писать скрипты Puppeteer для автоматизации браузера, скрейпинга и тестов. Cursor применяет его к файлам JavaScript и JSX. ★ 3 580звёзд репо

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

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