Skip to main content

qtflex - responsive width adaptation for Qt

Qt widgets don't adapt their labels to available width. qtflex adds a small, cooperating set: a container that measures and broadcasts a width CLASS, and consumers (action bars, tree headers) that adapt to it - CSS-media-query thinking, but content-driven instead of pixel breakpoints.

Bindings: PySide6 (tested live), PyQt6 (import-smoked), PySide2/PyQt5 (supported by the shim, untested). Zero dependencies beyond your Qt binding.

ResponsiveContainer

A width-broadcasting parent. Emits width_class_changed(str) with 'full' | 'compact' | 'min', classified against the CONTENT (member children's natural width vs available), not magic px:

  • full: everything fits at natural width
  • compact: fits only when children shrink (>= 60% of natural)
  • min: the floor

API: add(w, member=True) (member widths define the breakpoints), add_layout(l), add_stretch(), layout_box(), width_class property.

Implementation note: a Qt container cannot shrink below its children's minimumSizeHint, so the container uses Ignored horizontal size policy and classifies against the resize EVENT width, never self.width().

ResponsiveBar

An action bar that degrades gracefully as width shrinks, in order:

  1. PROMOTE: restore wider label tiers while they fit
  2. compress spacing (max_spacing -> min_spacing)
  3. DEMOTE: the widest button with a shorter tier drops one tier (repeat)
  4. SPILL: at the tier floor, lowest-priority buttons hide into a » overflow menu (menu label = shortest tier, click = original handler). The bar is never fully empty - the highest-priority button always stays visible.

API:

  • add_button(tiers, tooltip=None, on_click=None, style=None, cursor=True, priority=100) - tiers is a list widest->shortest, e.g. ["Re-read all ({n})", "Re-read {n}", "RR"]; {n} templates update via set_count(btn, n). Lower priority spills first. Returns the QPushButton.
  • add_widget(w) - fixed pass-through widget (dropdown, LED), never demoted.
  • add_stretch()
  • subscribe_to(container) - drive layout from a ResponsiveContainer; bars sharing one container demote in concert. Without it the bar self-measures via its own resizeEvent.

Constructor: min_spacing=1, max_spacing=6, margins=(5, 0, 5, 5), spill=True, demote="staged", compact_counts=False.

  • spill=False: buttons stop at their shortest tier, no overflow menu; below the floor they shrink proportionally (elide) rather than paint past the bar's edges.
  • demote="staged" (default): buttons demote one step at a time, ordered by the tier FRACTION the step would reach - unequal tier-list lengths degrade proportionally, no button hits its floor while others sit untouched. Promotion mirrors this: the deepest-demoted (by fraction) recovers first, with a hysteresis band so boundary widths never flap. ("uniform" reserved, not yet implemented.)
  • compact_counts=True: {n} counts render as 1.1k / 2.3M above 999, keeping label width bounded as counts grow. set_count re-runs the layout, so a growing number can trigger demotion without a resize.

Tier state is tracked internally (never re-derived from button text) - count changes cannot desync the tier machine.

Driving the bar from a parent widget instead of a ResponsiveContainer: set bar._driven = True and call bar._apply_layout(bar.width()) from the parent's resizeEvent. Exactly ONE width source per bar - a bar fed by two sources (own resizeEvent + a parent driver) will oscillate.

ResponsiveHeader

Column collapse for QTreeView/QTreeWidget, same priority vocabulary. ResponsiveHeader(tree, {2: 10, 3: 20}) - lower priority hides first; column 0 is never hidden; unlisted columns default to priority 100. apply(width_class): full = all shown; compact = hide 1; min = hide half. subscribe_to(container) joins the same broadcast.

Wiring

from qtflex import ResponsiveBar, ResponsiveContainer, ResponsiveHeader

cont = ResponsiveContainer()
bar = ResponsiveBar()
cont.add(bar)
bar.subscribe_to(cont)
hdr = ResponsiveHeader(tree, {2: 10, 3: 20})
hdr.subscribe_to(cont)

qtcompat

Internal binding shim (PySide6 > PySide2 > PyQt6 > PyQt5, first importable wins); Signal normalized across PySide/PyQt. qtflex.QT_BINDING names the active binding.

License

MIT

FloorCap - stop child widgets from hard-flooring your window

Qt sums every child's minimumSizeHint into the window's hard minimum; a multi-pane panel can silently block window-edge resize at ~window width. setSizePolicy(Ignored) does NOT shrink the hint - only an override does.

from qtflex import FloorCap, floor_cap

class MyPanel(FloorCap, QWidget):   # mixin form
    FLOOR_W = 200

Capped = floor_cap(SomePanel, w=200)  # wrapper form

Metadata

Release files for qtflex 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qtflex 0.1.2
File Size Uploaded Provenance
qtflex-0.1.2.tar.gz 9.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for qtflex 0.1.2
File Interpreter ABI Platform
qtflex-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 20.7 kB

Release files / qtflex-0.1.2.tar.gz

Download URL qtflex-0.1.2.tar.gz
Size 9.3 kB
Tags Source
SHA-256 checksum
How to use checksums
fb768926bd9ef7f0660136bf68123971a901dda6e024f8393a29eda4b582ce6b
BLAKE2b-256 checksum
How to use checksums
6a3c705649267c88ae08dccd09fa90b3b1edb8f8bea3dc8b34d5f8a3656adf4f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 4, 2026.

Transparency log

Release files / qtflex-0.1.2-py3-none-any.whl

Download URL qtflex-0.1.2-py3-none-any.whl
Size 11.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2a2cf571e2def8ff5d323e5eb428eb58d8523ac9dc383664fd830a1481f87535
BLAKE2b-256 checksum
How to use checksums
679a20c2782b3145cdc029a8f03b493abba9bc9d8259b192f264afe3b6261bc6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 4, 2026.

Transparency log

Security for qtflex 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for qtflex-0.1.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 4, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page