Skip to main content

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 name pyglass was taken on PyPI), but you still import 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's grabWindow, returns the full screen with every window) and excludes itself from capture via NSWindowSharingNone. 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 grabWindow can't be told to exclude the window, so a periodic re-grab would flicker. PyGlass therefore captures once and stays paused (press R to 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 / ScreenBackdropwhat 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyglass_qt-0.2.0.tar.gz (32.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyglass_qt-0.2.0-py3-none-any.whl (33.7 kB view details)

Uploaded Python 3

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

Hashes for pyglass_qt-0.2.0.tar.gz
Algorithm Hash digest
SHA256 98e5fed9fdae0b89152b6b2895d99f1525ccff4903ae8520bc2ccdbeb987ed6d
MD5 514055b76a1def7f1f2496c10d6623e0
BLAKE2b-256 da1ff058cd4107cb3713387eb4cd89a6ddf1fccac694587cf601a08a901217ec

See more details on using hashes here.

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

Hashes for pyglass_qt-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9cebf13238964cff70cae892c4285cba81db9bdcf81abc4f26a4c40e4f537800
MD5 cd189e67fc0614f3e74fb2784d0d425e
BLAKE2b-256 88ad9b88508711b6f126f477ae98821748eef397a6449f308e9494fd25e88ac9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page