Physically-grounded refractive glass widgets for PyQt6 — real refraction, chromatic dispersion, Fresnel reflection and frost.
Project description
PyGlass
Physically-grounded refractive glass for PyQt6 — drop it onto any app.
PyGlass renders glass the way glass behaves: refraction through a beveled slab, chromatic dispersion, Fresnel reflectance, an iridescent rim and an optional frosted (rough-surface) blur. It ships as a reusable package with two layers — a one-line widget for the common case, and the raw engine for custom widgets.
Install
pip install pyglass-qt # from PyPI
# or straight from GitHub:
pip install "git+https://github.com/neomosh8/pyglass.git"
The distribution is
pyglass-qt(the namepyglasswas taken on PyPI), but you stillimport pyglass. Only PyQt6 + numpy are pulled in.
To run the demos from a clone instead:
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt # PyQt6 + numpy
.venv/bin/python main.py
Use it in your app
High level — GlassPane. A frameless glass widget. Give it a parent and it
becomes an in-app modal/panel that refracts your app; leave it parentless and it
becomes a top-level window that refracts the live desktop. Draggable, with live
dials built in.
from pyglass import GlassPane, GlassMaterial
from PyQt6.QtWidgets import QVBoxLayout, QLabel
# A glass modal over your existing window — refracts whatever's behind it.
pane = GlassPane(my_window, material=GlassMaterial(thickness=0.6, frost=0.3))
QVBoxLayout(pane.content).addWidget(QLabel("Hello from glass"))
pane.show()
# …or a glass window over the live desktop:
desk = GlassPane(material=GlassMaterial(thickness=0.7, frost=0.15))
desk.show()
Put your widgets in pane.content. The pane captures its parent (with itself
hidden) for the backdrop, so no cooperation from the host is needed — it
works on any widget. See examples/.
Low level — compose it yourself. Build the glass inside your own
paintEvent with the engine pieces: a backdrop provider →
GlassRenderer (backdrop array → refracted pixmap) →
paint_glass (shadow + refraction + tint + rim). See
pyglass/glass.py (GlassPopup) for a full worked example
with a scrim and an open/close animation.
from pyglass import GlassRenderer, paint_glass, WidgetBackdrop, GlassMaterial
backdrop = WidgetBackdrop(host) # or ScreenBackdrop(window)
renderer = GlassRenderer(GlassMaterial(), w, h, radius)
backdrop.changed.connect(lambda: self.update())
# in paintEvent:
pm = renderer.refract(backdrop.array(), origin, backdrop.dpr())
paint_glass(painter, panel_rect, radius, pm)
The two dials
The entire look is driven by GlassMaterial — two
perceptual dials in [0, 1] that re-derive a dozen physical parameters so the
pane always reads as one coherent piece of glass. The neutral pair
(thickness=0.5, frost=0) reproduces the tuned baseline exactly.
| Dial | What it means | What it drives |
|---|---|---|
thickness |
perceived slab depth / mass (optical path length) | displacement (strength), the curved lens-wrap width (bevel), the IOR range / rim bend, chromatic dispersion (chroma), the spectral rim-line width, and the capture margin so the wrap never clamps |
frost |
surface roughness (ground / milk glass) | a transmission blur (scatter), a milky multiple-scatter haze, and a softened dispersion line — transmission-side only, so frost=0 is byte-for-byte the sharp look |
thickness is a single scalar standing in for T: a thicker slab bends light
more, has a bigger rounded edge, disperses colour more (longer optical path) and
casts a thicker rim — all slaved together. frost is microfacet roughness: a
rough face scatters transmitted light into a cone that projects to a blur, plus
a faint milky veil.
GlassStyle separately tunes the non-physical chrome (shadow, tint, sheen, rim).
Run the demos
.venv/bin/python main.py # in-app frosted refractive modal
.venv/bin/python main.py --desktop # glass window over your live desktop
.venv/bin/python examples/in_app_modal.py
.venv/bin/python examples/desktop_window.py
In any of them: drag the panel; [ / ] adjust thickness;
- / = adjust frost; R refreshes the backdrop; on the desktop
pane L toggles live auto-refresh; Esc closes.
Desktop mode — glass over your real screen
A parentless GlassPane (or python main.py --desktop) floats over your live
desktop and refracts whatever is behind it — all your windows, not just the
wallpaper.
-
macOS: it shells out to the system
screencapture(which, unlike Qt'sgrabWindow, returns the full screen with every window) and excludes itself from capture viaNSWindowSharingNone. Because the window is excluded, the backdrop auto-refreshes live with no hide/flicker, and dragging stays smooth (it re-slices the last capture each frame).Needs Screen Recording permission (System Settings → Privacy & Security → Screen Recording) for the terminal/app running Python. If only the wallpaper shows, grant it and relaunch.
-
Windows / Linux: Qt's
grabWindowcan't be told to exclude the window, so a periodic re-grab would flicker. PyGlass therefore captures once and stays paused (pressRto refresh) — no flicker. The dials still work live against the cached frame.
Platform support
Cross-platform — macOS, Windows, Linux. PyQt6 + numpy only. The in-app glass reads the app's own rendered scene (no OS screen-capture permission needed); fonts fall back gracefully (SF Pro → Segoe UI → Arial) and device-pixel-ratio is handled, so it renders correctly on Windows HiDPI and Retina alike.
Render a preview without a display
QT_QPA_PLATFORM=offscreen .venv/bin/python scripts/render_preview.py preview.png
Layout
| File | Purpose |
|---|---|
pyglass/refract.py |
Engine — GlassKernel (refraction + Fresnel over a beveled SDF) and GlassMaterial (the two dials) |
pyglass/effect.py |
GlassRenderer, paint_glass, GlassStyle — the reusable rendering core |
pyglass/backdrop.py |
WidgetBackdrop / ScreenBackdrop — what the glass refracts |
pyglass/pane.py |
GlassPane — the drop-in glass widget (+ ui_font) |
pyglass/glass.py |
GlassPopup — in-app modal demo built on the low-level core |
pyglass/desktop.py |
DesktopGlass — desktop-window demo, a thin GlassPane subclass |
pyglass/demo.py |
DemoBackground — colourful host scene + launch button |
examples/ |
Standalone third-party usage of GlassPane |
main.py |
Entry point (--desktop for desktop mode) |
How the refraction works
The panel is a beveled glass slab over a rounded-rectangle signed distance
field. The flat centre passes light straight through; the rim is a quarter-circle
roundover whose slope grows toward the edge. The vertical incident ray is
refracted there with Snell's law and projected through the glass thickness,
so the 1/(-T_z) term curls the background into a curved lens-wrap (not a flat
shift). Each colour channel uses its own IOR → a chromatic-dispersion fringe.
The Schlick–Fresnel term rises from ~F0 at the centre to ~1 at the grazing
rim, where the surface reflects a virtual environment (horizon ambient + a warm
key and cool fill light). A lightened iridescent spectral line is added along
the border. Frost adds a fast separable blur of the transmitted background.
All geometry-dependent work (normals, per-channel sample coordinates, Fresnel
weight, reflected environment) is precomputed once into a GlassKernel; each
frame only runs the bilinear gather (+ the box blur when frosted), so dragging
stays smooth.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyglass_qt-0.2.0.tar.gz.
File metadata
- Download URL: pyglass_qt-0.2.0.tar.gz
- Upload date:
- Size: 32.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98e5fed9fdae0b89152b6b2895d99f1525ccff4903ae8520bc2ccdbeb987ed6d
|
|
| MD5 |
514055b76a1def7f1f2496c10d6623e0
|
|
| BLAKE2b-256 |
da1ff058cd4107cb3713387eb4cd89a6ddf1fccac694587cf601a08a901217ec
|
File details
Details for the file pyglass_qt-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pyglass_qt-0.2.0-py3-none-any.whl
- Upload date:
- Size: 33.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9cebf13238964cff70cae892c4285cba81db9bdcf81abc4f26a4c40e4f537800
|
|
| MD5 |
cd189e67fc0614f3e74fb2784d0d425e
|
|
| BLAKE2b-256 |
88ad9b88508711b6f126f477ae98821748eef397a6449f308e9494fd25e88ac9
|