spacr.qt.gui_scale¶
The whole-GUI scale, applied live: one factor for every size spaCR sets (471).
WHAT IT IS. “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.
HOW, WITHOUT TOUCHING 1,420 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 the sizes
slice B persists survive a change of scale.
HOW IT COMPOSES WITH FONT SCALE. Font scale stays where it is: 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 touched and multiplies on the
outside, as Qt always has.
KEEP OR REVERT. 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()) remains the backup.
WHAT DOES NOT FOLLOW LIVE. Sizes the scale cannot see because they do not
pass through a setter: 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:973
- _dialog_classes.KeepOrRevertDialog._show_left(self) None¶
Say how long is left.
spacr/qt/gui_scale.py:1029
- _dialog_classes.KeepOrRevertDialog._tick(self) None¶
One second less; at zero, revert.
spacr/qt/gui_scale.py:1034
- _dialog_classes.KeepOrRevertDialog.done(self, result)¶
Stop the countdown on any answer.
- Parameters:
result – the dialog result.
spacr/qt/gui_scale.py:1043
- _install_application_sheet.setStyleSheet(self, text)¶
Record the sheet at 100 % and apply its sizes at the GUI scale.
spacr/qt/gui_scale.py:623
- _install_application_sheet.styleSheet(self)¶
The sheet as the code set it, at 100 %.
spacr/qt/gui_scale.py:630
- _install_icon_setters.getter(original)¶
The wrapper that answers an icon size at 100 %.
spacr/qt/gui_scale.py:562
- _install_icon_setters.getter.iconSize(self)¶
The icon size in 100 % units.
spacr/qt/gui_scale.py:564
- _install_icon_setters.setter(original)¶
The wrapper that records an icon size and scales it.
spacr/qt/gui_scale.py:543
- _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:545
- _install_layout_setters.addSpacing(self, size)¶
Add a spacer recorded at 100 % and drawn at the GUI scale.
spacr/qt/gui_scale.py:524
- _install_layout_setters.insertSpacing(self, index, size)¶
Insert a spacer recorded at 100 % and drawn at the GUI scale.
spacr/qt/gui_scale.py:528
- _install_layout_setters.margins_getter(original)¶
The wrapper that answers a layout’s margins at 100 %.
spacr/qt/gui_scale.py:446
- _install_layout_setters.margins_getter.contentsMargins(self)¶
The margins in 100 % units.
spacr/qt/gui_scale.py:448
- _install_layout_setters.margins_setter(original)¶
The wrapper that records a layout’s margins and scales them.
spacr/qt/gui_scale.py:432
- _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:434
- _install_layout_setters.spacer(self, index, size)¶
A fixed spacer item along the box’s direction.
spacr/qt/gui_scale.py:499
- _install_layout_setters.spacing_getter(key)¶
The factory for one spacing getter, by its record key.
spacr/qt/gui_scale.py:474
- _install_layout_setters.spacing_getter.make(original)¶
The wrapper that answers a spacing at 100 %.
spacr/qt/gui_scale.py:476
- _install_layout_setters.spacing_getter.make.getter(self)¶
The spacing in 100 % units.
spacr/qt/gui_scale.py:478
- _install_layout_setters.spacing_setter(key)¶
The factory for one spacing setter, by its record key.
spacr/qt/gui_scale.py:460
- _install_layout_setters.spacing_setter.make(original)¶
The wrapper that records a spacing and scales it.
spacr/qt/gui_scale.py:462
- _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:464
- _install_splitter_setters.setSizes(self, sizes)¶
Apply 100 % sizes at the GUI scale, and remember them.
spacr/qt/gui_scale.py:591
- _install_splitter_setters.sizes(self)¶
The sizes in 100 % units.
spacr/qt/gui_scale.py:601
- _install_widget_setters.maximumHeight(self)¶
The maximum height in 100 % units.
spacr/qt/gui_scale.py:341
- _install_widget_setters.maximumSize(self)¶
The maximum size in 100 % units.
spacr/qt/gui_scale.py:349
- _install_widget_setters.maximumWidth(self)¶
The maximum width in 100 % units.
spacr/qt/gui_scale.py:337
- _install_widget_setters.minimumHeight(self)¶
The minimum height in 100 % units.
spacr/qt/gui_scale.py:333
- _install_widget_setters.minimumSize(self)¶
The minimum size in 100 % units.
spacr/qt/gui_scale.py:345
- _install_widget_setters.minimumWidth(self)¶
The minimum width in 100 % units.
spacr/qt/gui_scale.py:329
- _install_widget_setters.resize(self, *args)¶
Resize at the GUI scale; the main window keeps its size.
spacr/qt/gui_scale.py:355
- _install_widget_setters.setFixedHeight(self, h)¶
Record
hat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:320
- _install_widget_setters.setFixedSize(self, *args)¶
Record the size at 100 % and apply it at the GUI scale.
spacr/qt/gui_scale.py:324
- _install_widget_setters.setFixedWidth(self, w)¶
Record
wat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:316
- _install_widget_setters.setMaximumHeight(self, h)¶
Record
hat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:302
- _install_widget_setters.setMaximumSize(self, *args)¶
Record the size at 100 % and apply it at the GUI scale.
spacr/qt/gui_scale.py:311
- _install_widget_setters.setMaximumWidth(self, w)¶
Record
wat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:298
- _install_widget_setters.setMinimumHeight(self, h)¶
Record
hat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:294
- _install_widget_setters.setMinimumSize(self, *args)¶
Record the size at 100 % and apply it at the GUI scale.
spacr/qt/gui_scale.py:306
- _install_widget_setters.setMinimumWidth(self, w)¶
Record
wat 100 % and apply it at the GUI scale.spacr/qt/gui_scale.py:290
- _install_widget_setters.setStyleSheet(self, text)¶
Record the sheet at 100 % and apply its sizes at the GUI scale.
spacr/qt/gui_scale.py:364
- _install_widget_setters.styleSheet(self)¶
The sheet as the code set it, at 100 %.
spacr/qt/gui_scale.py:377
- mend_matplotlib_icons._at_least_one(self)¶
The toolbar’s device ratio, never below 1.
spacr/qt/gui_scale.py:1229