|
| 1 | +# PythonQwt AI Coding Agent Instructions |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +**PythonQwt** is a pure Python implementation of the Qwt C++ plotting library. It provides low-level Qt plotting widgets that form the foundation for higher-level libraries like PlotPy. |
| 6 | + |
| 7 | +### Technology Stack |
| 8 | + |
| 9 | +- **Python**: 3.9+ |
| 10 | +- **Core**: NumPy (≥1.19), QtPy (≥1.9) |
| 11 | +- **GUI**: Qt via QtPy (PyQt5/PyQt6/PySide6) |
| 12 | +- **Testing**: pytest |
| 13 | +- **Linting**: Ruff, Pylint |
| 14 | + |
| 15 | +### Architecture |
| 16 | + |
| 17 | +``` |
| 18 | +qwt/ |
| 19 | +├── plot.py # QwtPlot main widget |
| 20 | +├── plot_canvas.py # Plot canvas |
| 21 | +├── plot_curve.py # QwtPlotCurve |
| 22 | +├── plot_marker.py # QwtPlotMarker |
| 23 | +├── plot_grid.py # QwtPlotGrid |
| 24 | +├── scale_*.py # Scale engine, map, division, drawing |
| 25 | +├── symbol.py # QwtSymbol for markers |
| 26 | +├── legend.py # QwtLegend |
| 27 | +├── text.py # QwtText, QwtTextLabel |
| 28 | +├── graphic.py # QwtGraphic |
| 29 | +└── tests/ # pytest suite |
| 30 | +``` |
| 31 | + |
| 32 | +## Development Workflows |
| 33 | + |
| 34 | +### Running Commands |
| 35 | + |
| 36 | +Use batch scripts in `scripts/`: |
| 37 | + |
| 38 | +```powershell |
| 39 | +scripts\run_pytest.bat # Run tests |
| 40 | +scripts\run_ruff.bat # Format and lint |
| 41 | +scripts\run_pylint.bat # Pylint checks |
| 42 | +scripts\run_coverage.bat # Coverage report |
| 43 | +scripts\take_screenshots.bat # Generate doc images |
| 44 | +``` |
| 45 | + |
| 46 | +Or directly: |
| 47 | + |
| 48 | +```powershell |
| 49 | +python -m pytest qwt --ff |
| 50 | +python -m ruff format |
| 51 | +python -m ruff check --fix |
| 52 | +``` |
| 53 | + |
| 54 | +### Running Test Launcher |
| 55 | + |
| 56 | +```powershell |
| 57 | +PythonQwt # GUI test launcher |
| 58 | +PythonQwt-tests --mode unattended # Headless tests |
| 59 | +``` |
| 60 | + |
| 61 | +Or from Python: |
| 62 | + |
| 63 | +```python |
| 64 | +from qwt import tests |
| 65 | +tests.run() |
| 66 | +``` |
| 67 | + |
| 68 | +## Core Patterns |
| 69 | + |
| 70 | +### Basic Plot Creation |
| 71 | + |
| 72 | +```python |
| 73 | +import numpy as np |
| 74 | +from qtpy import QtWidgets as QW |
| 75 | +import qwt |
| 76 | + |
| 77 | +app = QW.QApplication([]) |
| 78 | + |
| 79 | +# Create plot widget |
| 80 | +plot = qwt.QwtPlot("My Plot Title") |
| 81 | +plot.insertLegend(qwt.QwtLegend(), qwt.QwtPlot.BottomLegend) |
| 82 | + |
| 83 | +# Add curves |
| 84 | +x = np.linspace(0, 10, 100) |
| 85 | +qwt.QwtPlotCurve.make(x, np.sin(x), "Sine", plot, |
| 86 | + linecolor="blue", antialiased=True) |
| 87 | + |
| 88 | +# Add grid |
| 89 | +grid = qwt.QwtPlotGrid() |
| 90 | +grid.attach(plot) |
| 91 | + |
| 92 | +plot.resize(600, 400) |
| 93 | +plot.show() |
| 94 | +app.exec_() |
| 95 | +``` |
| 96 | + |
| 97 | +### QwtPlotCurve Factory Method |
| 98 | + |
| 99 | +The `make` class method simplifies curve creation: |
| 100 | + |
| 101 | +```python |
| 102 | +curve = qwt.QwtPlotCurve.make( |
| 103 | + x, y, # Data arrays |
| 104 | + title="My Curve", # Legend title |
| 105 | + plot=plot, # Parent plot (auto-attaches) |
| 106 | + linecolor="red", # Line color |
| 107 | + linewidth=2, # Line width |
| 108 | + linestyle=Qt.DashLine, # Qt line style |
| 109 | + antialiased=True, # Anti-aliasing |
| 110 | + marker=qwt.QwtSymbol.Ellipse, # Marker symbol |
| 111 | + markersize=8, # Marker size |
| 112 | +) |
| 113 | +``` |
| 114 | + |
| 115 | +### Key Classes |
| 116 | + |
| 117 | +| Class | Purpose | |
| 118 | +|-------|---------| |
| 119 | +| `QwtPlot` | Main plot widget | |
| 120 | +| `QwtPlotCurve` | 2D curve item | |
| 121 | +| `QwtPlotMarker` | Point/line markers | |
| 122 | +| `QwtPlotGrid` | Grid lines | |
| 123 | +| `QwtLegend` | Legend widget | |
| 124 | +| `QwtSymbol` | Marker symbols | |
| 125 | +| `QwtScaleEngine` | Scale calculations | |
| 126 | +| `QwtScaleMap` | Scale transformations | |
| 127 | +| `QwtText` | Rich text labels | |
| 128 | + |
| 129 | +### Scale Configuration |
| 130 | + |
| 131 | +```python |
| 132 | +# Set axis titles |
| 133 | +plot.setAxisTitle(qwt.QwtPlot.xBottom, "Time (s)") |
| 134 | +plot.setAxisTitle(qwt.QwtPlot.yLeft, "Amplitude") |
| 135 | + |
| 136 | +# Set axis scale |
| 137 | +plot.setAxisScale(qwt.QwtPlot.xBottom, 0, 100) |
| 138 | + |
| 139 | +# Logarithmic scale |
| 140 | +plot.setAxisScaleEngine(qwt.QwtPlot.yLeft, qwt.QwtLogScaleEngine()) |
| 141 | +``` |
| 142 | + |
| 143 | +### Symbols and Markers |
| 144 | + |
| 145 | +```python |
| 146 | +# Create marker |
| 147 | +marker = qwt.QwtPlotMarker() |
| 148 | +marker.setSymbol(qwt.QwtSymbol( |
| 149 | + qwt.QwtSymbol.Diamond, |
| 150 | + QBrush(Qt.yellow), |
| 151 | + QPen(Qt.red, 2), |
| 152 | + QSize(10, 10) |
| 153 | +)) |
| 154 | +marker.setValue(x_pos, y_pos) |
| 155 | +marker.attach(plot) |
| 156 | +``` |
| 157 | + |
| 158 | +## Coding Conventions |
| 159 | + |
| 160 | +### Qt Imports |
| 161 | + |
| 162 | +Use QtPy for Qt binding abstraction: |
| 163 | + |
| 164 | +```python |
| 165 | +from qtpy.QtCore import Qt, QSize |
| 166 | +from qtpy.QtGui import QPen, QBrush, QColor |
| 167 | +from qtpy.QtWidgets import QWidget |
| 168 | +``` |
| 169 | + |
| 170 | +### Docstrings |
| 171 | + |
| 172 | +Standard Python docstrings: |
| 173 | + |
| 174 | +```python |
| 175 | +def setData(self, x, y): |
| 176 | + """Set curve data. |
| 177 | +
|
| 178 | + :param x: X coordinates (array-like) |
| 179 | + :param y: Y coordinates (array-like) |
| 180 | + """ |
| 181 | +``` |
| 182 | + |
| 183 | +## Key Files Reference |
| 184 | + |
| 185 | +| File | Purpose | |
| 186 | +|------|---------| |
| 187 | +| `qwt/plot.py` | QwtPlot implementation | |
| 188 | +| `qwt/plot_curve.py` | QwtPlotCurve with `make()` factory | |
| 189 | +| `qwt/scale_engine.py` | Linear/log scale engines | |
| 190 | +| `qwt/scale_map.py` | Scale transformations | |
| 191 | +| `qwt/symbol.py` | QwtSymbol definitions | |
| 192 | +| `qwt/tests/__init__.py` | Test launcher | |
| 193 | + |
| 194 | +## Limitations vs C++ Qwt |
| 195 | + |
| 196 | +The following are **not implemented** (PlotPy provides these): |
| 197 | + |
| 198 | +- `QwtPlotZoomer` - Use PlotPy's zoom tools |
| 199 | +- `QwtPicker` - Use PlotPy's interactive tools |
| 200 | +- `QwtPlotPicker` - Use PlotPy's selection tools |
| 201 | + |
| 202 | +Only essential plot items are implemented: |
| 203 | +- `QwtPlotItem` (base) |
| 204 | +- `QwtPlotCurve` |
| 205 | +- `QwtPlotMarker` |
| 206 | +- `QwtPlotGrid` |
| 207 | +- `QwtPlotSeriesItem` |
| 208 | + |
| 209 | +## Related Projects |
| 210 | + |
| 211 | +- **guidata**: Dataset/parameter framework (sibling) |
| 212 | +- **PlotPy**: High-level plotting using PythonQwt (downstream) |
0 commit comments