spacr.qt.gui_scale¶
Apply one GUI scale factor to every size spaCR sets, while spaCR runs.
“GUI scale” in Preferences (10 % to 200 %, default 100 %) scales widget sizes, fixed sizes, margins, spacing, style-sheet sizes (fonts and paddings included), icons, splitter sizes and figure dpi – and it does so while spaCR runs, with no restart.
The scale is applied without editing the call sites. Counted on nightly
2026-09-22 across the 328 modules under spacr/qt: about 1,420 hard-coded
geometry calls in 181 files (470 setContentsMargins, 489 setSpacing,
179 setMinimumWidth/Height, 67 setMaximumWidth/Height, 74
setFixed*, 30 setMinimumSize, 56 QSize, 64 resize, 6
setIconSize) and 486 px literals in style-sheet text in 64 files.
Routing each by hand is a change to every screen, and every size added later
would have to remember it. Instead install_scaling_layer() – called
once from spacr.qt.app.launch() before the first widget exists –
replaces the setters themselves on the Qt classes (QWidget’s size setters
and setStyleSheet, the layouts’ margin and spacing setters, every
setIconSize, QSplitter.setSizes). Each replacement
remembers, on the object, the value the code asked for – its size at 100 %;
passes the value times the current scale on to Qt;
and answers the matching getter with the remembered value, so code that reads a size back and sets it again (the theme compares its own sheet by digest, a splitter saves
sizes()) keeps working in 100 % units and never compounds.
A change of scale (set_gui_scale_live()) walks every live widget and
layout, puts every remembered value back at the new scale, and asks the
windows to lay out again. A widget built later is scaled as it is built.
A splitter’s panes keep the room they have on screen – what shrinks is
what is inside them – and sizes() answers in 100 % units, so saved
splitter sizes survive a change of scale.
Font scale is applied separately: it writes the font sizes into the theme’s
style sheet. That sheet goes through the setStyleSheet replacement like
any other, so a font size ends up as base x font scale x GUI scale. 50 % GUI
at 200 % font is half-size widgets with text the usual size on screen.
A user’s own QT_SCALE_FACTOR is not changed and multiplies the result,
as it does in any Qt application.
Every change of GUI scale or font scale applies at once and then asks “Keep
these settings?” (KeepOrRevertDialog), counting down 15 s; the
countdown, Esc and closing the dialog all put the old values back. The dialog
is exempt from the scale and sets its own text size, so it is readable at 10 %
GUI and 10 % font alike. Ctrl+Alt+0 (reset_every_scale()) also
resets every scale to 100 %.
Sizes that do not pass through a setter are not rescaled live: text and shapes
a widget paints itself at fixed pixel coordinates, pixmaps a widget scales to
a number it computed, setFont with an explicit size,
move/setGeometry positions, header section sizes, a Python
sizeHint override that returns a constant, the Fusion style’s own metrics
that the theme sheet does not restate, and pyqtgraph’s axis text. Sizes
computed from font metrics are already scaled once by the font and are scaled
again by the setter – the one way the layer can over-shrink. The main
window’s own size is left alone, so the gained room goes to the content.
Functions¶
|
Call |
|
Draw a matplotlib canvas at base dpi x GUI scale x its preview scale. |
|
Start at the saved GUI scale; called once the application exists. |
|
Apply a new GUI and/or font scale now, then ask whether to keep it. |
|
The GUI scale in force now (1.0 = 100 %). |
|
Draw a new matplotlib canvas at the current GUI scale. |
|
Put the scaling layer on the Qt classes. Idempotent. |
|
Whether |
|
A new |
|
Keep matplotlib's toolbar icons full size below a device ratio of 1. |
|
Rebuild what a style sheet cannot reach: icons, tiles, window chrome. |
|
Put GUI scale, font scale and every preview scale back to 100 %, now. |
|
|
|
Scale the sizes in a style sheet, leaving every other byte as it was. |
|
Draw spaCR at |
Module Contents¶
- spacr.qt.gui_scale.add_listener(callback: Callable[[float], None]) None[source]¶
Call
callback(scale)after every change of GUI scale.Held weakly when it is a bound method, so a listener does not keep its widget alive.
- Parameters:
callback – called with the new scale.
- spacr.qt.gui_scale.apply_canvas_dpi(canvas, factor: float | None = None) bool[source]¶
Draw a matplotlib canvas at base dpi x GUI scale x its preview scale.
A figure draws in points, so its dpi is what makes a 9 pt label take more or fewer pixels; scaling the dpi scales every label, dot and line while the widget keeps the size its layout gives it.
- Parameters:
canvas – a
FigureCanvasQTAgg.factor – the GUI scale; the current one by default.
- Returns:
Trueif the canvas’s dpi was set.
- spacr.qt.gui_scale.apply_saved_gui_scale() float[source]¶
Start at the saved GUI scale; called once the application exists.
- Returns:
the scale in force.
- spacr.qt.gui_scale.change_scales(parent=None, *, gui: float | None = None, font: float | None = None, ask: bool = True, seconds: int = KEEP_SECONDS, previous: tuple | None = None, require_parent: bool = True, on_done: Callable[[bool], None] | None = None)[source]¶
Apply a new GUI and/or font scale now, then ask whether to keep it.
The question is shown without blocking (
open, notexec): the rest of spaCR keeps running under it, and the countdown reverts by itself if nobody answers.- Parameters:
parent – the window the question is centred on.
gui – the new GUI scale; unchanged when
None.font – the new font scale; unchanged when
None.ask –
Falsekeeps without asking.seconds – the countdown before it reverts by itself.
previous –
(gui, font)to revert to when the caller has already applied and saved the new values (the Z + wheel gesture); nothing is applied again then.require_parent – ask only when there is a window to centre the question on.
Falseasks anyway, which is what a test does.on_done – called with
Truewhen kept,Falsewhen reverted.
- Returns:
the question dialog, or
Nonewhen nothing was asked.
- spacr.qt.gui_scale.follow_canvas(canvas) bool[source]¶
Draw a new matplotlib canvas at the current GUI scale.
- Parameters:
canvas – a
FigureCanvasQTAggjust built.- Returns:
Trueif its dpi was set.
- spacr.qt.gui_scale.install_scaling_layer() bool[source]¶
Put the scaling layer on the Qt classes. Idempotent.
Called from
spacr.qt.app.launch()before the first widget, so every size spaCR sets is remembered at 100 %. At 100 % every replacement hands Qt exactly the value it was given, which is why a default session draws the pixels it drew before the layer existed.- Returns:
Truethe first time,Falseif it was already in place.
- spacr.qt.gui_scale.installed() bool[source]¶
Whether
install_scaling_layer()has run in this process.
- spacr.qt.gui_scale.keep_or_revert_dialog(parent=None, seconds: int = KEEP_SECONDS, what: str = '')[source]¶
A new
KeepOrRevertDialog(see_dialog_classes()).- Parameters:
parent – the window it is centred on.
seconds – how long before it reverts by itself.
what – the line naming what changed.
- spacr.qt.gui_scale.mend_matplotlib_icons() bool[source]¶
Keep matplotlib’s toolbar icons full size below a device ratio of 1.
Matplotlib’s toolbar icon engine multiplies the size Qt asks for by the device pixel ratio, and Qt has already done so; above 1 the two cancel, below 1 (a user’s
QT_SCALE_FACTORunder 1) they compound. Measured offscreen at a ratio of 0.5: the icons drew 3 px tall instead of 11. Holding the engine’s ratio at 1 or more draws them 11 px tall and changes nothing at a ratio of 1 or 2. Idempotent.- Returns:
Trueif the mend is in place.
- spacr.qt.gui_scale.refresh_the_windows() int[source]¶
Rebuild what a style sheet cannot reach: icons, tiles, window chrome.
The same step the Z + wheel gesture ends with.
MainWindow.refresh_themerepaints the marks in the window corner and the Home tiles, which paint their own pixmaps at a size they compute – and a pixmap is not a size the scaling layer ever sees.- Returns:
how many windows rebuilt.
- spacr.qt.gui_scale.reset_every_scale(parent=None) bool[source]¶
Put GUI scale, font scale and every preview scale back to 100 %, now.
The backup way out of a scale too small to read:
Ctrl+Alt+0. It needs no reading and no answer.- Parameters:
parent – unused; kept so a shortcut can pass its window.
- Returns:
True.
- spacr.qt.gui_scale.scale_int(value, factor: float | None = None) int[source]¶
valueatfactor, keeping 0, negatives and Qt’s maximum as they are.- Parameters:
value – a size in 100 % pixels.
factor – the scale; the current one by default.
- spacr.qt.gui_scale.scale_qss_text(text: str, factor: float | None = None) str[source]¶
Scale the sizes in a style sheet, leaving every other byte as it was.
Only the values of size properties (font sizes, paddings, margins, min/max sizes, widths, heights, spacing, radii) change; selectors, colours, borders and
url(...)data are untouched.- Parameters:
text – the style sheet.
factor – the scale; the current one by default.
- spacr.qt.gui_scale.set_gui_scale_live(scale: float) float[source]¶
Draw spaCR at
scalenow: every live widget, and every one built later.Does not save the preference; see
spacr.qt.preferences.set_gui_scale().- Parameters:
scale – the factor, 1.0 = 100 %; clamped to 10-200 %.
- Returns:
the scale applied.
Nested helpers¶
- _dialog_classes.KeepOrRevertDialog.__init__(self, parent=None, seconds: int = KEEP_SECONDS, what: str = '')¶
Build the question and start the countdown.
spacr/qt/gui_scale.py:1106
- _dialog_classes.KeepOrRevertDialog._show_left(self) None¶
Say how long is left.
spacr/qt/gui_scale.py:1162
- _dialog_classes.KeepOrRevertDialog._tick(self) None¶
One second less; at zero, revert.
spacr/qt/gui_scale.py:1167
- _dialog_classes.KeepOrRevertDialog.done(self, result)¶
Stop the countdown on any answer.
- Parameters:
result – the dialog result.
spacr/qt/gui_scale.py:1176
- _install_application_sheet.setStyleSheet(self, text)¶
Record the sheet at 100 % and apply its sizes at the GUI scale.
spacr/qt/gui_scale.py:756
- _install_application_sheet.styleSheet(self)¶
The sheet as the code set it, at 100 %.
spacr/qt/gui_scale.py:763
- _install_icon_setters.getter(original)¶
The wrapper that answers an icon size at 100 %.
spacr/qt/gui_scale.py:695
- _install_icon_setters.getter.iconSize(self)¶
The icon size in 100 % units.
spacr/qt/gui_scale.py:697
- _install_icon_setters.setter(original)¶
The wrapper that records an icon size and scales it.
spacr/qt/gui_scale.py:676
- _install_icon_setters.setter.setIconSize(self, size)¶
Record the icon size at 100 % and apply it at the GUI scale.
spacr/qt/gui_scale.py:678
- _install_layout_setters.addSpacing(self, size)¶
Add a spacer recorded at 100 % and drawn at the GUI scale.
spacr/qt/gui_scale.py:657
- _install_layout_setters.insertSpacing(self, index, size)¶
Insert a spacer recorded at 100 % and drawn at the GUI scale.
spacr/qt/gui_scale.py:661
- _install_layout_setters.margins_getter(original)¶
The wrapper that answers a layout’s margins at 100 %.
spacr/qt/gui_scale.py:579
- _install_layout_setters.margins_getter.contentsMargins(self)¶
The margins in 100 % units.
spacr/qt/gui_scale.py:581
- _install_layout_setters.margins_setter(original)¶
The wrapper that records a layout’s margins and scales them.
spacr/qt/gui_scale.py:565
- _install_layout_setters.margins_setter.setContentsMargins(self, *args)¶
Record the margins at 100 % and apply them at the GUI scale.
spacr/qt/gui_scale.py:567
- _install_layout_setters.spacer(self, index, size)¶
A fixed spacer item along the box’s direction.
spacr/qt/gui_scale.py:632
- _install_layout_setters.spacing_getter(key)¶
The factory for one spacing getter, by its record key.
spacr/qt/gui_scale.py:607
- _install_layout_setters.spacing_getter.make(original)¶
The wrapper that answers a spacing at 100 %.
spacr/qt/gui_scale.py:609
- _install_layout_setters.spacing_getter.make.getter(self)¶
The spacing in 100 % units.
spacr/qt/gui_scale.py:611
- _install_layout_setters.spacing_setter(key)¶
The factory for one spacing setter, by its record key.
spacr/qt/gui_scale.py:593
- _install_layout_setters.spacing_setter.make(original)¶
The wrapper that records a spacing and scales it.
spacr/qt/gui_scale.py:595
- _install_layout_setters.spacing_setter.make.setter(self, value)¶
Record the spacing at 100 % and apply it at the GUI scale.
spacr/qt/gui_scale.py:597
- _install_splitter_setters.setSizes(self, sizes)¶
Apply 100 % sizes at the GUI scale, and remember them.
spacr/qt/gui_scale.py:724
- _install_splitter_setters.sizes(self)¶
The sizes in 100 % units.
spacr/qt/gui_scale.py:734
- _install_widget_setters.maximumHeight(self)¶
The maximum height in 100 % units.
spacr/qt/gui_scale.py:474
- _install_widget_setters.maximumSize(self)¶
The maximum size in 100 % units.
spacr/qt/gui_scale.py:482
- _install_widget_setters.maximumWidth(self)¶
The maximum width in 100 % units.
spacr/qt/gui_scale.py:470
- _install_widget_setters.minimumHeight(self)¶
The minimum height in 100 % units.
spacr/qt/gui_scale.py:466
- _install_widget_setters.minimumSize(self)¶
The minimum size in 100 % units.
spacr/qt/gui_scale.py:478
- _install_widget_setters.minimumWidth(self)¶
The minimum width in 100 % units.
spacr/qt/gui_scale.py:462
- _install_widget_setters.resize(self, *args)¶
Resize at the GUI scale; the main window keeps its size.
spacr/qt/gui_scale.py:488
- _install_widget_setters.setFixedHeight(self, h)¶
Record
hat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:453
- _install_widget_setters.setFixedSize(self, *args)¶
Record the size at 100 % and apply it at the GUI scale.
spacr/qt/gui_scale.py:457
- _install_widget_setters.setFixedWidth(self, w)¶
Record
wat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:449
- _install_widget_setters.setMaximumHeight(self, h)¶
Record
hat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:435
- _install_widget_setters.setMaximumSize(self, *args)¶
Record the size at 100 % and apply it at the GUI scale.
spacr/qt/gui_scale.py:444
- _install_widget_setters.setMaximumWidth(self, w)¶
Record
wat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:431
- _install_widget_setters.setMinimumHeight(self, h)¶
Record
hat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:427
- _install_widget_setters.setMinimumSize(self, *args)¶
Record the size at 100 % and apply it at the GUI scale.
spacr/qt/gui_scale.py:439
- _install_widget_setters.setMinimumWidth(self, w)¶
Record
wat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:423
- _install_widget_setters.setStyleSheet(self, text)¶
Record the sheet at 100 % and apply its sizes at the GUI scale.
spacr/qt/gui_scale.py:497
- _install_widget_setters.styleSheet(self)¶
The sheet as the code set it, at 100 %.
spacr/qt/gui_scale.py:510
- mend_matplotlib_icons._at_least_one(self)¶
The toolbar’s device ratio, never below 1.
spacr/qt/gui_scale.py:1362