Как масштабировать компоненты пользовательского интерфейса, такие как размер шрифта и размер значков, для приложения PySPython

Программы на Python
Anonymous
Как масштабировать компоненты пользовательского интерфейса, такие как размер шрифта и размер значков, для приложения PyS

Сообщение Anonymous »

У меня есть большое приложение, которое использует множество компонентов графического интерфейса (таблицы, списки, деревья, поля ввода, метки, флажки, ползунки и т. д.). В целях доступности я хотел бы реализовать функцию масштабирования, аналогичную той, которую использует VS Code или JetBrains, при которой компоненты/содержимое пользовательского интерфейса масштабируются вверх/вниз, но при этом сохраняют свой общий стиль.
Моя текущая реализация использует гибридный подход 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)
    

Вернуться в «Python»