|
| 1 | +# pyTermTk Copilot Instructions |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +pyTermTk is a Text-based User Interface (TUI) library for Python inspired by Qt5, GTK, and tkinter APIs. It creates cross-platform terminal applications with rich widgets, layouts, and an event-driven architecture. |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +### Core Structure |
| 10 | +- **`libs/pyTermTk/TermTk/`** - Main library code organized into logical modules: |
| 11 | + - `TTkCore/` - Core functionality (signals, colors, canvas, configuration) |
| 12 | + - `TTkWidgets/` - All UI widgets inherit from `TTkWidget` base class |
| 13 | + - `TTkLayouts/` - Layout managers (GridLayout, HBoxLayout, VBoxLayout) |
| 14 | + - `TTkGui/` - GUI components (drag & drop, application management) |
| 15 | + - `TTkTemplates/` - Mixin classes for event handling (`TKeyEvents`, `TMouseEvents`, `TDragEvents`) |
| 16 | + |
| 17 | +### Widget Inheritance Pattern |
| 18 | +All widgets follow this pattern: |
| 19 | +```python |
| 20 | +class TTkMyWidget(TTkWidget): # Or TTkContainer for composite widgets |
| 21 | + # Class-level styling |
| 22 | + classStyle = { |
| 23 | + 'default': {'color': TTkColor.fg("#dddd88"), 'borderColor': TTkColor.RST}, |
| 24 | + 'hover': {'color': TTkColor.fg("#ffffff"), 'borderColor': TTkColor.BOLD}, |
| 25 | + 'focus': {'borderColor': TTkColor.fg("#ffff00")}, |
| 26 | + 'disabled': {'color': TTkColor.fg('#888888')} |
| 27 | + } |
| 28 | + |
| 29 | + # Signal declarations |
| 30 | + mySignal: pyTTkSignal |
| 31 | + |
| 32 | + __slots__ = ('_private_vars',) # Always use slots for performance |
| 33 | + |
| 34 | + def __init__(self, **kwargs): |
| 35 | + self.mySignal = pyTTkSignal(int) # Define signals in __init__ |
| 36 | + super().__init__(**kwargs|{'size': (w, h)}) # Merge kwargs, set default size |
| 37 | +``` |
| 38 | + |
| 39 | +### Signal-Slot System (Qt-inspired) |
| 40 | +Use type-safe signal-slot patterns: |
| 41 | +```python |
| 42 | +# Define signals with types |
| 43 | +signal = pyTTkSignal(int) |
| 44 | +# Define slots with decorators |
| 45 | +@pyTTkSlot(int) |
| 46 | +def my_slot(value: int): |
| 47 | + pass |
| 48 | +# Connect them |
| 49 | +signal.connect(my_slot) |
| 50 | +``` |
| 51 | + |
| 52 | +### Event Handling |
| 53 | +Widgets handle events by overriding template methods: |
| 54 | +- `keyEvent()`, `mousePressEvent()`, `paintEvent()` - Core events |
| 55 | +- `focusInEvent()`, `focusOutEvent()` - Focus management |
| 56 | +- `dropEvent()`, `dragEnterEvent()` - Drag & drop |
| 57 | +- Always return `True` if event is handled, `False` to propagate |
| 58 | + |
| 59 | +## Development Workflows |
| 60 | + |
| 61 | +### Testing |
| 62 | +- **Unit tests**: `pytest tests/pytest/` (run via Makefile: `make test`) |
| 63 | +- **Performance tests**: `tests/timeit/` - Contains signal/slot benchmarks and optimization tests |
| 64 | +- **Manual tests**: `tests/t.*/` - Interactive UI tests |
| 65 | +- **CI**: Tests run on Python 3.9-3.14 with flake8 linting |
| 66 | + |
| 67 | +### Build & Deploy |
| 68 | +- **Local build**: `pip install -e libs/pyTermTk` (uses `pip`) |
| 69 | +- **Documentation**: `make doc` (Sphinx-based, outputs to `docs/source/_build/html/`) |
| 70 | +- **Apps deployment**: Individual apps in `apps/` have their own `pyproject.toml` |
| 71 | + |
| 72 | +### Running Examples |
| 73 | +- **Demo**: `python demo/demo.py -f` |
| 74 | +- **Designer**: `pip install -e apps/ttkDesigner ; ttkDesigner` |
| 75 | +- **Individual tests**: `python tests/t.ui/test.ui.036.datetime.01.py` |
| 76 | + |
| 77 | +## Project-Specific Patterns |
| 78 | + |
| 79 | +### Widget State Management |
| 80 | +Many widgets use internal state classes (see `datetime_date_form.py`): |
| 81 | +```python |
| 82 | +class _TTkWidgetState: |
| 83 | + __slots__ = ('_data', 'signal_name') |
| 84 | + def __init__(self): |
| 85 | + self.signal_name = pyTTkSignal() |
| 86 | +``` |
| 87 | + |
| 88 | +### File Organization |
| 89 | +- One widget per file in `TTkWidgets/` |
| 90 | +- Use `__all__ = ['ClassName']` exports |
| 91 | +- Import from `TermTk.TTkCore`, `TermTk.TTkWidgets` etc (not relative imports) |
| 92 | +- Apps in `apps/` are self-contained with `pyproject.toml` |
| 93 | + |
| 94 | +### Color & Theming |
| 95 | +Use `TTkColor` constants and theme system: |
| 96 | +```python |
| 97 | +TTkColor.fg("#ffffff") + TTkColor.bg("#000044") + TTkColor.BOLD |
| 98 | +style = self.currentStyle() # Get theme-aware colors |
| 99 | +``` |
| 100 | + |
| 101 | +### Cross-Platform Considerations |
| 102 | +- Platform-specific code in `TTkCore/drivers/` |
| 103 | +- Terminal compatibility testing in `tests/ansi.images.json` |
| 104 | +- HTML5 export capabilities via `tools/webExporter/` |
| 105 | + |
| 106 | +### Documentation & Docstrings |
| 107 | +Use **Sphinx-compatible docstring format** with Epytext-style field lists: |
| 108 | +```python |
| 109 | +def setGeometry(self, x: int, y: int, width: int, height: int): |
| 110 | + ''' Resize and move the widget |
| 111 | +
|
| 112 | + :param x: the horizontal position |
| 113 | + :type x: int |
| 114 | + :param y: the vertical position |
| 115 | + :type y: int |
| 116 | + :param width: the new width |
| 117 | + :type width: int |
| 118 | + :param height: the new height |
| 119 | + :type height: int |
| 120 | + ''' |
| 121 | + |
| 122 | +# For class/module docstrings include ASCII art examples: |
| 123 | +class TTkButton(TTkWidget): |
| 124 | + ''' TTkButton: |
| 125 | +
|
| 126 | + Border = True |
| 127 | + :: |
| 128 | +
|
| 129 | + ┌────────┐ |
| 130 | + │ Text │ |
| 131 | + ╘════════╛ |
| 132 | +
|
| 133 | + Demo: `formwidgets.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/demo/showcase/formwidgets.py>`_ |
| 134 | + ''' |
| 135 | + |
| 136 | +# For signals, document parameters: |
| 137 | +toggled:pyTTkSignal |
| 138 | +''' |
| 139 | +This signal is emitted whenever the button state changes if checkeable, |
| 140 | +i.e., whenever the user checks or unchecks it. |
| 141 | +
|
| 142 | +:param checked: True if checked otherwise False |
| 143 | +:type checked: bool |
| 144 | +''' |
| 145 | +``` |
| 146 | + |
| 147 | +**Key conventions**: |
| 148 | +- Use single quotes `'''` for docstrings |
| 149 | +- Include ASCII art for visual widgets showing borders/layout |
| 150 | +- Link to demo files with full GitHub URLs |
| 151 | +- Use `:py:class:` for cross-references to other classes |
| 152 | +- Document all parameters with `:param name:` and `:type name:` |
| 153 | +- Include `:return:` and `:rtype:` for non-void methods |
| 154 | +- Signal docstrings document emitted parameters, not the signal itself |
| 155 | + |
| 156 | +## Apps Ecosystem |
| 157 | +The project includes several full applications demonstrating patterns: |
| 158 | +- **ttkDesigner** - Visual UI designer (like Qt Designer) |
| 159 | +- **ttkode** - Code editor with syntax highlighting |
| 160 | +- **dumbPaintTool** - ASCII art editor |
| 161 | +- **tlogg** - Log file viewer |
| 162 | + |
| 163 | +### Testing App Integration |
| 164 | +Apps use the main library via path manipulation: |
| 165 | +```python |
| 166 | +sys.path.append(os.path.join(sys.path[0],'../../libs/pyTermTk')) |
| 167 | +import TermTk as ttk |
| 168 | +``` |
| 169 | + |
| 170 | +## Key Integration Points |
| 171 | + |
| 172 | +### Layout System |
| 173 | +Use Qt-like layout managers: |
| 174 | +```python |
| 175 | +layout = TTkGridLayout() |
| 176 | +layout.addWidget(widget, row, col, rowspan, colspan) |
| 177 | +container.setLayout(layout) |
| 178 | +``` |
| 179 | + |
| 180 | +### Focus & Input Handling |
| 181 | +Set focus policy and handle keyboard navigation: |
| 182 | +```python |
| 183 | +self.setFocusPolicy(TTkK.ClickFocus | TTkK.TabFocus) |
| 184 | +``` |
| 185 | + |
| 186 | +When implementing new widgets, study existing patterns in `TTkWidgets/` and ensure signal-slot integration follows the established type-safe patterns shown in `tests/pytest/test_004_signals_slots.py`. |
0 commit comments