Моя текущая реализация использует гибридный подход QProxyStyle + QApplication.setStyleSheet(...). Эта текущая реализация очень хорошо работает для небольших/простых приложений примерно с 15 или около того различными виджетами (таблицы, списки, деревья, поля ввода, метки, флажки, ползунки), но для более крупных/сложных приложений пользовательский интерфейс замораживается на долгое время, прежде чем вы увидите масштабированное содержимое.
Есть ли более быстрый и надежный способ сделать это?
Среда:
- ОС: Windows 11
- Python: 3.11
- PySide6: 6.10.0
- Унаследованный стиль прокси
Код: Выделить всё
from PySide6.QtWidgets import QStyle, QProxyStyle class ScaledStyle(QProxyStyle): def __init__(self, baseStyle: QStyle, scale: int = 1): super().__init__(baseStyle) self._scale: float = scale self._cachedMetrics: dict[QProxyStyle.PixelMetric, int] = {} def setScale(self, scale: float): self._scale: float = scale self._cachedMetrics.clear() def pixelMetric(self, metric, option=None, widget=None): value = super().pixelMetric(metric, option, widget) # limit the scaling to toolbars/buttons # is it wise to scale everything? if metric in ( QStyle.PixelMetric.PM_SmallIconSize, QStyle.PixelMetric.PM_LargeIconSize, QStyle.PixelMetric.PM_ToolBarIconSize, QStyle.PixelMetric.PM_ButtonIconSize, ): # clamp the scaling to be at least 1 return max(1, int(value * self._scale)) return value - Менеджер масштабирования
Код: Выделить всё
import PySide6.QtWidgets as qtw import PySide6.QtCore as qtc from scaled_style import ScaledStyle class GUIScalingManager(qtc.QObject): scaleChanged = qtc.Signal(float) def __init__(self, app: qtw.QApplication, parent=None) -> None: super().__init__(parent=parent) self.__app: qtw.QApplication = app self.__baseScale = 1.0 self.__scale = 1.0 self.__scaleLB = 0.8 self.__scaleUB = 2.0 self.__scaleStep = 0.2 self.__defaultFontSize = 12.0 self.__scaledStyle: ScaledStyle = ScaledStyle(self.__app.style()) self.__app.setStyle(self.__scaledStyle) # throttle the scaler to approx 30 frames self.__scaleTimer = qtc.QTimer() self.__scaleTimer.setSingleShot(True) self.__scaleTimer.timeout.connect(self.__applyScale) self.__scaleTimer.setInterval(32) def setScale(self, scale: float) -> None: self.__scale = self.__clampToScaleRange(scale) self.applyScale() self.scaleChanged.emit(self.__scale) def __applyScale(self) -> None: if not isinstance(self.__app, qtw.QApplication): return self.__scaledStyle.setScale(self.__scale) for widget in self.__app.topLevelWidgets(): widget.setUpdatesEnabled(False) self.__app.setStyleSheet( f""" QWidget{{ font-size: {self.__defaultFontSize * self.__scale}px; }} """ ) for widget in self.__app.topLevelWidgets(): widget.setUpdatesEnabled(True) widget.updateGeometry() widget.update() def applyScale(self) -> None: self.__scaleTimer.start() def __clampToScaleRange(self, s: float) -> float: return max(self.__scaleLB, min(s, self.__scaleUB)) def scaleUp(self): s = self.__clampToScaleRange(self.__scale + self.__scaleStep) self.setScale(s) def scaleDown(self): s = self.__clampToScaleRange(self.__scale - self.__scaleStep) self.setScale(s)