| name | qt-testing |
| description | Capture and visually inspect FreeCCR's PySide6/Qt GUI widgets using screenshots. Use when asked to verify GUI rendering, test widget appearance, check layouts, theming/QSS, or visually inspect any FreeCCR widget (image preview, sliders panel, curve editor, thumbnail list, dust panel, export dialog, main window). Enables Claude to "see" the Qt interface by capturing offscreen screenshots and analyzing them with vision. |
Qt GUI Testing (FreeCCR)
Capture screenshots of FreeCCR's Qt widgets for visual inspection without
displaying windows on screen, then read the image back and critique it.
Quick Start
import sys
sys.path.insert(0, ".claude/skills/qt-testing")
from scripts.qt_capture import capture_widget, init_qt, setup_freeccr_path
setup_freeccr_path()
app = init_qt()
path = capture_widget(my_widget, "description_here")
Core Script
Run scripts/qt_capture.py or import helpers from it:
python .claude/skills/qt-testing/scripts/qt_capture.py
Helpers exposed by scripts/qt_capture.py:
setup_freeccr_path() — add the repo root and src/ to sys.path (call before importing FreeCCR modules).
init_qt(offscreen=False) — create/return the QApplication. Pass offscreen=True to force the offscreen Qt platform (matches FreeCCR's headless tests); the default Windows platform also works.
capture_widget(widget, "desc") — render offscreen and save a PNG, returns the path.
capture_and_click(widget, x, y, "desc") — click the child at (x, y), then capture.
Output Location
All screenshots save to: <repo>/scratch/.qt-screenshots/ (git-ignored).
Naming: {YYYY-MM-DD.HH-MM-SS}_{description}.png
Workflow
- Create/obtain the FreeCCR widget to test (see examples below)
- Call
capture_widget(widget, "description")
- Read the saved screenshot with the Read tool
- Analyze with vision to verify correctness (layout, spacing, theming, labels)
FreeCCR Widget Cheat-Sheet
Import after setup_freeccr_path(). None require images loaded; the
ccr_backend singleton is created on import.
from core.ccr_backend import ccr_backend
from ui.main_window import MainWindow
from widgets.sliders_panel import SlidersPanel
from widgets.image_preview import ImagePreview
from widgets.curve_editor import CurveEditor
from widgets.thumbnail_list import ThumbnailList
from widgets.tether_banner import TetherBanner
from widgets.dust_panel import DustRemovalPanel
from widgets.export_dialog import ExportSettingsDialog
Example: Capture the sliders panel
import sys
sys.path.insert(0, ".claude/skills/qt-testing")
from scripts.qt_capture import capture_widget, init_qt, setup_freeccr_path
setup_freeccr_path()
app = init_qt()
from core.ccr_backend import ccr_backend
from widgets.sliders_panel import SlidersPanel
panel = SlidersPanel(None)
panel.resize(360, 700)
path = capture_widget(panel, "sliders_panel_default")
print(f"Inspect: {path}")
Example: Capture the full main window
import sys
sys.path.insert(0, ".claude/skills/qt-testing")
from scripts.qt_capture import capture_widget, init_qt, setup_freeccr_path
setup_freeccr_path()
app = init_qt()
from ui.main_window import MainWindow
win = MainWindow()
win.resize(1280, 800)
path = capture_widget(win, "main_window_empty")
win.close()
print(f"Inspect: {path}")
Interaction Pattern
To interact with widgets (click buttons, etc.):
target = widget.childAt(x, y)
if hasattr(target, 'click'):
target.click()
QApplication.processEvents()
capture_widget(widget, "after_click")
Key Points
- Uses
Qt.WA_DontShowOnScreen - no window popup.
- Renders identically to on-screen display (verified upstream).
- Call
setup_freeccr_path() before importing FreeCCR modules; import ccr_backend first so the singleton exists.
- Give widgets a size (
resize/setFixedSize) before grab() — an unsized widget may render at its minimum.
- Call
processEvents() after interactions before capture.
- Use
childAt(x, y) to map vision coordinates to widgets.
- Direct method calls (
.click()) work; simulated mouse events don't.
DustRemovalPanel requires a main_window and image_preview; pass real instances or minimal stubs exposing the attributes it reads.