Files
ETPlot/README.md
T
lars 5109b6505c
CI / lint:ruff (push) Successful in 25s
CI / format:ruff (push) Successful in 14s
CI / typecheck:ty (push) Successful in 15s
CI / vulnerabilities:pip-audit (push) Successful in 17s
CI / test:pytest (push) Successful in 24s
Move build system from setuptools to uv, wire uv into CI/CD
Convert both pyproject.toml files to the uv_build backend and turn the
repo into a proper uv workspace (plotstyle moved to src layout, since
uv_build requires the module in its own subdirectory). Dev tooling
moves from an optional-dependencies extra to a dependency-group, and
plotstyle's matplotlib dependency is now its own rather than a
gallery "plotting" extra.

Gitea Actions workflows and the Dockerfile now use astral-sh/setup-uv,
uv sync, uv run, uv build, and uv publish throughout instead of pip,
build, and twine. Also fixes .dockerignore, which was excluding
uv.lock, deploy/, and README.md and would have broken even the
previous Dockerfile's COPY of deploy/entrypoint.sh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 10:30:20 +02:00

306 lines
9.4 KiB
Markdown

# Gallery: Scientific Plot Organizer
[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
**Create responsive HTML galleries for scientific plot collections. Convert PDFs to PNG, organize plots hierarchically, and generate beautiful static websites.**
## Features
### Core Functionality
- PDF to PNG Conversion using ImageMagick
- Incremental updates only when plot is newer than cached
- Responsive design using Jinja2 (also on mobile)
- Support for nested folder structures
- Search bar
- Breadcrump navigation
### Advanced Features
- Compare two plots from the same folder
- YAML/JSON metadata with inheritance and display to properly label each folder
- Recent plots
- Dark and light mode
- Keyboard shortcuts
### Developer-Friendly
- Full python API: `import gallery`
- CLI: `gallery`
- TUI: `gallery tui`
- Config file stored under user `$HOME/.config/gallery`
### Producing Plots (`plotstyle`)
- Companion matplotlib styling toolkit (optional `plotting` extra) for producing figures that look consistent across a thesis and slide deck, ready to drop straight into a gallery source directory — see [Producing Plots with `plotstyle`](#producing-plots-with-plotstyle)
## Installation
### From Gitea
Pip install:
```bash
pip install git+https://git.larsbogner.de/lars/ETPlot
```
Or git clone and `pip install .`. After installation the `gallery` command is available in your shell. Verify with:
```bash
gallery --help
```
### Dependencies
- Python 3.8+
- Python packages are installed automatically by pip (Jinja2, PyYAML, PyMuPDF, Textual, argcomplete, platformdirs)
- [ImageMagick](https://imagemagick.org/) is **optional** — used as a fallback if `PyMuPDF` is not available
### Optional: `plotstyle` (for producing plots)
`plotstyle` is a separate package (its own `plotstyle/pyproject.toml`, a member of this repo's uv workspace) — installing `gallery` alone never pulls in `matplotlib`.
```bash
# Working in this repo (installs gallery + plotstyle + matplotlib together):
uv sync --all-packages
# Just the plotstyle library, standalone:
pip install "plotstyle @ git+https://git.larsbogner.de/lars/ETPlot#subdirectory=plotstyle"
```
`plotstyle` also requires a local LaTeX toolchain (`latex` + `dvipng`) to be installed separately; see [Producing Plots with `plotstyle`](#producing-plots-with-plotstyle).
### Shell Completion (optional)
Install tab-completion for bash/zsh/fish:
```bash
gallery install-completion
```
Then restart your shell or follow the printed instructions to activate it.
## CLI Usage
The `gallery` command has four subcommands: `generate`, `config`, `tui`, and `install-completion`.
### Quick start
```bash
# 1. Set your web output directory
gallery config set paths.web_folder /path/to/your/public_html
# 2. Add one or more plot source directories
gallery config add-source --path /path/to/plots --name my_analysis
# 3. Generate the gallery
gallery generate
```
### `gallery generate`
```bash
# Full regeneration
gallery generate
# Verbose output
gallery generate --verbose
# Clean rebuild (delete output before generating)
gallery generate --clean
# Regenerate a single source only (name will be the basename of the path)
gallery generate --source /path/to/plots
# Use a non-default config file
gallery --config /path/to/config.yaml generate
```
### `gallery config`
```bash
# Show all configuration values
gallery config list
# Show the resolved config file path
gallery config path
# List configured sources
gallery config sources
# Get a single value
gallery config get gallery.png_dpi
# Set a value (all standard YAML types accepted)
gallery config set gallery.png_dpi 300
gallery config set metadata.cache_enabled true
# Add a source directory
gallery config add-source --path /path/to/plots --name my_plots
# Remove a source
gallery config remove-source my_plots
```
### Serving locally
```bash
python -m http.server 8000 -d /path/to/public_html
```
Then open `http://localhost:8000/gallery/` in your browser. You can of course use any port you want to.
## Screenshots
### Main Gallery View
![gallery_view](docs/images/main_gallery_view.png)
### Metadata Display
![metadata](docs/images/metadata_view.png)
### Plot Comparison Tool
![plot_comparison](docs/images/plot_comparison.png)
### Search Functionality
<img src="docs/images/search.png" width="500px">
## TUI Usage
The TUI is a terminal user interface built with [Textual](https://textual.textualize.io/). It lets you edit the configuration and trigger generation without leaving your terminal.
```bash
gallery tui
```
## Configuration
The config file lives at `$HOME/.config/gallery/config.yaml` by default. You can point to a different file with `gallery --config /path/to/config.yaml <subcommand>`.
### Config structure
```yaml
paths:
web_folder: "/web/user/public_html" # required
gallery:
plot_root: "gallery" # subdirectory inside web_folder
png_dpi: 400 # thumbnail resolution
sources:
- name: "analysis_results"
path: "/path/to/plots/directory"
- name: "specific_plot"
path: "/path/to/another/directory"
metadata:
cache_enabled: true
inherit_from_parent: true
```
### Metadata Files
Create `metadata.yaml` files in your source directories. Fields are arbitrary and rendered via YAML object interpretation (dict, list, …). Lower-level files override parent values, which is useful for labelling specific experiments.
```yaml
# metadata.yaml
title: "Analysis Results"
description: "Results for my analysis"
experiment: "CMS"
dataset: "Run2_2016_nano_v9"
parameters:
luminosity: "35.9 fb^{-1}"
center_of_mass_energy: "13 TeV"
selection: "baseline"
tags:
- "physics"
- "analysis"
- "cms"
authors:
- "Researcher A"
- "Researcher B"
```
LaTeX formulas are supported in metadata values and rendered with MathJax:
```yaml
formula: "$$E = mc^2$$"
```
## Producing Plots with `plotstyle`
`plotstyle` is a companion matplotlib styling toolkit shipped in this repo (the `plotstyle/` package) for producing the plots you'll point a gallery source at — a KIT (Karlsruhe Institute of Technology) corporate-design color palette, consistent spines/ticks/gridlines, LaTeX text in a modern sans font, and a few building-block functions (figure titles with a parameters subtitle, a same-size colorbar helper, an outside-axes legend, panel labels). It has no code dependency on `gallery` — the two only meet on disk, through the PDF files (and optional `metadata.yaml`) a `plotstyle` script writes into a directory that `gallery` then scans.
Install it with the `plotting` extra (see [Installation](#installation)) and make sure a LaTeX toolchain (`latex` + `dvipng`) is available locally — `plotstyle` always renders text through real LaTeX, there's no fallback.
```python
import numpy as np
import plotstyle as ps
ps.use() # once, before creating any figure
fig, ax = ps.new_figure(
"thesis-single",
title="Measured signal",
params={"N": 512, "seed": 42},
)
x = np.linspace(0, 10, 200)
ax.plot(x, np.sin(x), label="signal")
ax.set_xlabel("Time (s)")
ax.set_ylabel(r"Amplitude $A(t)$")
ps.style_legend(ax, title="Series")
# Save straight into a gallery source directory:
ps.savefig(fig, "/path/to/plots/measured_signal", formats=("pdf",))
```
That PDF (plus an optional `metadata.yaml` next to it, as described above) is exactly what `gallery generate --source /path/to/plots` picks up — `gallery` converts the PDF to a thumbnail PNG itself, so `plotstyle` scripts should stick to `formats=("pdf",)` rather than also producing a PNG.
See `examples/plotstyle_showcase.ipynb` for a fully rendered tour of every function, and `plotstyle/CLAUDE.md` for the full API reference and best practices (aimed at coding agents, but equally useful for humans).
## Shortcuts
| Icon | Button | Function | Shortcut |
|------|---------|--------------------------|----------|
| 🔍 | Search | Real-time plot search | `Ctrl+K` |
| 📋 | Recent | Recently viewed plots | `Ctrl+R` |
| ⚖️ | Compare | Side-by-side comparison | `Ctrl+C` |
| ☀️ | Theme | Toggle dark/light theme | `Ctrl+T` |
## Troubleshooting
### Common Issues
| Issue | (Potential) Solution |
|------------------------------------|----------|
| `gallery` command not found | Run `pip install -e .` from the repo root |
| PDF conversion fails — no renderer | Install PyMuPDF: `pip install pymupdf` (or `apt-get install imagemagick` as fallback) |
| PDF conversion fails — corrupt file | Verify the PDF opens in a viewer; try `pip install --upgrade pymupdf` |
| Permission denied on web dir | Check file permissions and web directory access |
| Metadata not showing | Check YAML syntax with `python -c "import yaml; yaml.safe_load(open('metadata.yaml'))"` |
### ImageMagick memory limits (fallback backend only)
```bash
export MAGICK_MEMORY_LIMIT=2GB
export MAGICK_MAP_LIMIT=2GB
gallery generate --verbose
```
## License
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
## Disclaimer on the use of Artificial Intelligence
This project was made *entirely* using Claude Sonnet 4.0 and GPT 4.1. 100% of the code was AI generated and no human hand was involved other than prompting the Agent.
## Links
- [Example Gallery](https://etpwww.etp.kit.edu/~kschmidt/gallery/index.html)