Skip to content

Commit 55244dc

Browse files
authored
Merge pull request #2 from healkeiser/dev
v11.1.0: Overall bug fixes & improvements
2 parents ee5c72e + 0f210eb commit 55244dc

File tree

95 files changed

+5902
-3525
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+5902
-3525
lines changed

.claude/settings.local.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(python -m py_compile:*)",
5+
"Bash(python -c:*)",
6+
"Bash(echo:*)",
7+
"Bash(pip install:*)",
8+
"Bash(tree:*)",
9+
"Bash(wc:*)",
10+
"Bash(DEVELOPER_MODE=1 python:*)"
11+
]
12+
}
13+
}

.github/workflows/docs.yml

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,40 @@ on:
44
push:
55
branches:
66
- main
7+
tags:
8+
- "v*"
79

810
permissions:
9-
contents: read
10-
pages: write
11-
id-token: write
11+
contents: write
1212

1313
jobs:
1414
deploy:
15-
environment:
16-
name: github-pages
17-
url: ${{ steps.deployment.outputs.page_url }}
1815
runs-on: ubuntu-latest
1916
steps:
20-
- uses: actions/configure-pages@v5
2117
- uses: actions/checkout@v5
18+
with:
19+
fetch-depth: 0
20+
2221
- uses: actions/setup-python@v5
2322
with:
2423
python-version: 3.x
25-
- run: pip install -r requirements.txt
26-
- run: zensical build --clean
27-
- uses: actions/upload-pages-artifact@v4
28-
with:
29-
path: site
30-
- uses: actions/deploy-pages@v4
31-
id: deployment
24+
25+
- name: Install dependencies
26+
run: pip install -r requirements.mkdocs.txt
27+
28+
- name: Configure Git
29+
run: |
30+
git config user.name github-actions
31+
git config user.email github-actions@github.com
32+
33+
- name: Deploy docs (versioned)
34+
env:
35+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
36+
run: |
37+
# Get version from tag or use 'dev' for main branch
38+
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
39+
VERSION="${GITHUB_REF#refs/tags/v}"
40+
mike deploy --push --update-aliases "$VERSION" latest
41+
else
42+
mike deploy --push dev
43+
fi

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ _dev/
1212
*.so
1313

1414
# Distribution / packaging
15+
_version.py
1516
.Python
1617
build/
1718
develop-eggs/

.scripts/capture_widget_screenshots.py renamed to .scripts/docs/capture_widget_screenshots.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33
This script runs each widget module as a subprocess and captures a screenshot
44
before the app exits by patching the example() function.
55
6-
Usage:
7-
python .scripts/capture_widget_screenshots.py
8-
python .scripts/capture_widget_screenshots.py --widget accordion
9-
python .scripts/capture_widget_screenshots.py --list
6+
Examples:
7+
>>> python .scripts/capture_widget_screenshots.py
8+
>>> python .scripts/capture_widget_screenshots.py --widget accordion
9+
>>> python .scripts/capture_widget_screenshots.py --list
1010
"""
1111

12+
# Built-in
1213
import sys
1314
import subprocess
1415
from pathlib import Path
1516

17+
1618
# Project root
1719
PROJECT_ROOT = Path(__file__).parent.parent
1820
OUTPUT_DIR = PROJECT_ROOT / "docs" / "images" / "widgets"
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ if (-not $latestTag) {
2222
$newVersion = "0.1.0"
2323
git tag -a "v$newVersion" -m "Version $newVersion"
2424
git push origin "v$newVersion"
25-
Write-Host "Tag v$newVersion pushed."
25+
Write-Host "Tag v$newVersion pushed. CI will update package.py, create the release, and send Slack notification."
2626
Write-Output $newVersion
2727
exit 0
2828
}
@@ -70,7 +70,7 @@ $newVersion = "$($versionParts[0]).$($versionParts[1]).$($versionParts[2])"
7070
git tag -a "v$newVersion" -m "Version $newVersion"
7171
git push origin "v$newVersion"
7272

73-
Write-Host "Tag v$newVersion pushed."
73+
Write-Host "Tag v$newVersion pushed. CI will update package.py, create the release, and send Slack notification."
7474

7575
# Output the new version
7676
Write-Output $newVersion

.vscode/tasks.json

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,9 @@
22
"version": "2.0.0",
33
"tasks": [
44
{
5-
"label": "Create Initial Version",
5+
"label": "Get Current Git Tag",
66
"type": "shell",
7-
"command": "powershell -ExecutionPolicy Bypass -File ./.scripts/git/CreateFirstVersion.ps1",
8-
"problemMatcher": [],
9-
"group": {
10-
"kind": "none",
11-
"isDefault": true
12-
},
13-
"presentation": {
14-
"reveal": "always",
15-
"panel": "shared"
16-
}
17-
},
18-
{
19-
"label": "Show Current Version",
20-
"type": "shell",
21-
"command": "powershell -ExecutionPolicy Bypass -File ./.scripts/git/ShowCurrentVersion.ps1",
7+
"command": "powershell -ExecutionPolicy Bypass -File ./.scripts/git/Get-CurrentGitTag.ps1",
228
"problemMatcher": [],
239
"group": {
2410
"kind": "none",
@@ -32,7 +18,7 @@
3218
{
3319
"label": "Create and Push Git Tag",
3420
"type": "shell",
35-
"command": "powershell -ExecutionPolicy Bypass -File ./.scripts/git/IncrementVersion.ps1 ${input:incrementType}",
21+
"command": "powershell -ExecutionPolicy Bypass -File ./.scripts/git/Update-GitTag.ps1 ${input:incrementType}",
3622
"problemMatcher": [],
3723
"group": {
3824
"kind": "none",
@@ -44,9 +30,9 @@
4430
}
4531
},
4632
{
47-
"label": "Deploy Documentation",
33+
"label": "Deploy Documentation (MkDocs)",
4834
"type": "shell",
49-
"command": "zensical gh-deploy",
35+
"command": "python -m mkdocs gh-deploy",
5036
"problemMatcher": [],
5137
"group": {
5238
"kind": "none",
@@ -58,9 +44,9 @@
5844
}
5945
},
6046
{
61-
"label": "Serve Documentation",
47+
"label": "Deploy Documentation (MkDocs, Local Preview)",
6248
"type": "shell",
63-
"command": "zensical serve",
49+
"command": "python -m mkdocs serve",
6450
"problemMatcher": [],
6551
"group": {
6652
"kind": "none",

CLAUDE.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
fxgui is a Python library providing custom Qt-based widgets and utilities for VFX Digital Content Creation (DCC) applications. It uses QtPy for compatibility across PySide2/PySide6/PyQt5/PyQt6.
8+
9+
## Development Commands
10+
11+
### Setup
12+
```bash
13+
git clone --recurse-submodules https://github.com/healkeiser/fxgui
14+
pip install -e .
15+
```
16+
17+
### Running Examples
18+
```bash
19+
python -m fxgui.examples # Full showcase application
20+
DEVELOPER_MODE=1 python -m fxgui.fxwidgets._accordion # Individual widget example
21+
```
22+
23+
### Documentation
24+
```bash
25+
pip install -r requirements.mkdocs.txt
26+
mkdocs serve # Local preview at http://127.0.0.1:8000
27+
mkdocs build # Build static docs
28+
```
29+
30+
## Architecture
31+
32+
### Core Modules
33+
34+
- **fxstyle.py** - Theming engine with `FXThemeManager` singleton and `FXThemeAware` mixin. Uses YAML-based theme definitions (`style.yaml`) with semantic color roles
35+
- **fxconfig.py** - QSettings-based persistent configuration (INI format)
36+
- **fxcore.py** - `FXSortFilterProxyModel` with fuzzy matching
37+
- **fxdcc.py** - DCC integration (Houdini, Maya, Nuke) with auto-detection
38+
- **fxicons.py** - Multi-library icon management (beacon, fontawesome, dcc-specific) with LRU caching and theme-aware updates
39+
- **fxutils.py** - UI utilities (load_ui, create_action, shadows, tooltips)
40+
41+
### Widget Patterns
42+
43+
The `fxwidgets/` directory contains 30+ custom widgets. Key patterns:
44+
45+
**Theme Awareness** - Inherit from `FXThemeAware` mixin for automatic theme updates:
46+
```python
47+
class MyWidget(FXThemeAware, QWidget):
48+
def _on_theme_changed(self, _theme_name: str = None):
49+
colors = fxstyle.get_theme_colors()
50+
self.setStyleSheet(f"background: {colors['surface']};")
51+
```
52+
53+
**Icon Usage** - Use `set_icon()` for theme-aware icons:
54+
```python
55+
from fxgui.fxicons import set_icon, get_icon
56+
set_icon(button, "check") # Auto-updates on theme change
57+
```
58+
59+
**Widget Structure** - Each widget module follows this pattern:
60+
- Private module name (e.g., `_accordion.py`)
61+
- Standalone `example()` function for testing (runs with `DEVELOPER_MODE=1`)
62+
- Signals/slots with `@Slot` decorator
63+
- Methods: `_initialize()`, `_connect_signals()` for setup
64+
65+
## Conventions
66+
67+
### Code Style
68+
- Google-style docstrings with Args/Returns sections
69+
- Comprehensive type annotations
70+
- Public API exported via `__all__`
71+
- Module metadata: `__author__`, `__email__`
72+
73+
### Git Commit Messages
74+
Use conventional commit format with type prefix:
75+
```
76+
[FEAT] Add new widget
77+
[FIX] Fix theme not updating
78+
[CHORE] Update dependencies
79+
[DOC] Improve docstrings
80+
[STYLE] Format code
81+
[BUILD] Update release workflow
82+
```
83+
84+
### Branches
85+
- `main` - stable releases
86+
- `dev` - active development
87+
88+
## Release Pipeline
89+
90+
GitHub Actions automates:
91+
1. Changelog generation via auto-changelog
92+
2. GitHub Release creation
93+
3. PyPI publishing
94+
4. Documentation deployment to GitHub Pages
95+
96+
Versioning uses setuptools_scm (derived from git tags).

README.md

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,51 @@ Custom Python classes and utilities tailored for Qt built UI, in VFX-oriented DC
6060
<!-- INSTALLATION -->
6161
## Installation
6262

63-
The package is available on [PyPI](https://pypi.org/project/fxgui) and can be installed via `pip`:
63+
### From PyPI
6464

65+
The package is available on [PyPI](https://pypi.org/project/fxgui):
6566

6667
``` shell
67-
python -m pip install fxgui
68+
pip install fxgui
6869
```
6970

70-
The repository contains submodules, so make sure to clone the repository with the `--recurse-submodules` flag:
71+
### From Source
72+
73+
Clone the repository with submodules:
7174

7275
``` shell
7376
git clone --recurse-submodules https://github.com/healkeiser/fxgui
77+
cd fxgui
78+
pip install -e .
79+
```
80+
81+
Or using the requirements file:
82+
83+
``` shell
84+
pip install -r requirements.txt
7485
```
7586

76-
Or, if you already cloned the repository, you can initialize the submodules with:
87+
### Optional Dependencies
88+
89+
For building documentation with MkDocs:
7790

7891
``` shell
79-
git submodule update --init --recursive
92+
pip install -e ".[mkdocs]"
93+
# or
94+
pip install -r requirements.mkdocs.txt
8095
```
8196

97+
For building documentation with Zensical:
98+
99+
``` shell
100+
pip install -e ".[zensical]"
101+
# or
102+
pip install -r requirements.zensical.txt
103+
```
104+
105+
> [!NOTE]
106+
> Zensical is still in early development and does not yet support all MkDocs plugins.
107+
82108
> [!IMPORTANT]
83109
> In order to have access to the module inside your application, make sure to add `fxgui` to the `$PYTHONPATH` of the DCCs. For Houdini, you can find the [`houdini_package.json` example file](./houdini_package.json).
84110
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""Generate the technical documentation pages."""
2+
3+
# Built-in
4+
import logging
5+
from pathlib import Path
6+
7+
# Third-party
8+
import mkdocs_gen_files
9+
10+
11+
# Set up logging to match MkDocs style
12+
log = logging.getLogger("mkdocs.plugins.gen-files")
13+
14+
nav = mkdocs_gen_files.Nav()
15+
root = Path(__file__).parent.parent.parent
16+
src = root / "fxgui"
17+
18+
log.info("gen-files: Generating technical documentation...")
19+
20+
file_count = 0
21+
skipped_count = 0
22+
23+
for path in sorted(src.rglob("*.py")):
24+
# Skip non-package directories (no __init__.py)
25+
if "ui" in path.parts:
26+
skipped_count += 1
27+
continue
28+
29+
module_path = path.relative_to(root)
30+
doc_path = path.relative_to(src).with_suffix(".md")
31+
full_doc_path = Path("technical", doc_path)
32+
33+
parts = tuple(module_path.with_suffix("").parts)
34+
35+
# Skip __init__ files in navigation but still generate docs
36+
if parts[-1] == "__init__":
37+
parts = parts[:-1]
38+
if not parts:
39+
continue
40+
doc_path = doc_path.with_name("index.md")
41+
full_doc_path = full_doc_path.with_name("index.md")
42+
43+
# Skip private modules (starting with _) in navigation display
44+
# but use cleaned name for nav
45+
nav_parts = tuple(
46+
part.lstrip("_") if part.startswith("_") else part for part in parts
47+
)
48+
49+
nav[nav_parts] = doc_path.as_posix()
50+
51+
with mkdocs_gen_files.open(full_doc_path, "w") as fd:
52+
ident = ".".join(parts)
53+
fd.write(f"::: {ident}")
54+
55+
mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))
56+
log.info(f"gen-files: generated {full_doc_path.as_posix()}")
57+
file_count += 1
58+
59+
with mkdocs_gen_files.open("technical/SUMMARY.md", "w") as nav_file:
60+
nav_file.writelines(nav.build_literate_nav())
61+
62+
log.info(f"gen-files: Generated {file_count} documentation pages")
63+
if skipped_count:
64+
log.debug(
65+
f"gen-files: Skipped {skipped_count} files (non-package directories)"
66+
)

0 commit comments

Comments
 (0)