|
6 | 6 |
|
7 | 7 | from PySide6 import __version__ as PySideVersion |
8 | 8 | from PySide6 import QtCore |
9 | | -from PySide6.QtCore import QUrl, QJsonValue, qInstallMessageHandler, QtMsgType, QSettings |
| 9 | +from PySide6.QtCore import QUrl, QJsonValue, qInstallMessageHandler, QtMsgType, QSettings, Qt |
10 | 10 | from PySide6.QtGui import QIcon |
11 | 11 | from PySide6.QtQml import QQmlDebuggingEnabler |
12 | 12 | from PySide6.QtQuickControls2 import QQuickStyle |
@@ -211,14 +211,26 @@ def __init__(self, inputArgs): |
211 | 211 |
|
212 | 212 | logging.getLogger().setLevel(meshroom.logStringToPython[args.verbose]) |
213 | 213 |
|
| 214 | + # Enable high-DPI scaling before creating QApplication |
| 215 | + QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) |
| 216 | + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) |
| 217 | + QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) |
| 218 | + |
214 | 219 | super().__init__(inputArgs[:1] + qtArgs) |
215 | 220 |
|
| 221 | + # Get DPI information and calculate scaling factors |
| 222 | + self._dpiInfo = self._getDpiInfo() |
| 223 | + self._scalingSettings = self._loadScalingSettings() |
| 224 | + |
216 | 225 | self.setOrganizationName('AliceVision') |
217 | 226 | self.setApplicationName('Meshroom') |
218 | 227 | self.setApplicationVersion(meshroom.__version_label__) |
219 | 228 |
|
| 229 | + # Apply font scaling |
220 | 230 | font = self.font() |
221 | | - font.setPointSize(9) |
| 231 | + basePointSize = 9 |
| 232 | + scaledPointSize = int(basePointSize * self._scalingSettings["fontScale"]) |
| 233 | + font.setPointSize(scaledPointSize) |
222 | 234 | self.setFont(font) |
223 | 235 |
|
224 | 236 | # Use Fusion style by default. |
@@ -331,6 +343,120 @@ def __init__(self, inputArgs): |
331 | 343 |
|
332 | 344 | self.engine.load(os.path.normpath(url)) |
333 | 345 |
|
| 346 | + def _getDpiInfo(self): |
| 347 | + """Get DPI information from the primary screen.""" |
| 348 | + screen = self.primaryScreen() |
| 349 | + if screen: |
| 350 | + dpi = screen.logicalDotsPerInch() |
| 351 | + physicalDpi = screen.physicalDotsPerInch() |
| 352 | + devicePixelRatio = screen.devicePixelRatio() |
| 353 | + return { |
| 354 | + "logicalDpi": dpi, |
| 355 | + "physicalDpi": physicalDpi, |
| 356 | + "devicePixelRatio": devicePixelRatio, |
| 357 | + "isHighDpi": dpi > 96 or devicePixelRatio > 1.0 |
| 358 | + } |
| 359 | + return {"logicalDpi": 96, "physicalDpi": 96, "devicePixelRatio": 1.0, "isHighDpi": False} |
| 360 | + |
| 361 | + def _calculateAutoScaleFactor(self): |
| 362 | + """Calculate automatic UI scale factor based on DPI.""" |
| 363 | + dpi = self._dpiInfo["logicalDpi"] |
| 364 | + deviceRatio = self._dpiInfo["devicePixelRatio"] |
| 365 | + |
| 366 | + # Base DPI is 96 (typical for 1x scaling) |
| 367 | + baseDpi = 96 |
| 368 | + |
| 369 | + # Calculate scale factor from DPI |
| 370 | + dpiScale = dpi / baseDpi |
| 371 | + |
| 372 | + # Use the maximum of DPI scale and device pixel ratio |
| 373 | + autoScale = max(dpiScale, deviceRatio) |
| 374 | + |
| 375 | + # Clamp to reasonable values (0.5x to 4.0x) |
| 376 | + return max(0.5, min(4.0, autoScale)) |
| 377 | + |
| 378 | + def _loadScalingSettings(self): |
| 379 | + """Load scaling settings from QSettings with automatic defaults.""" |
| 380 | + settings = QSettings() |
| 381 | + settings.beginGroup("Display") |
| 382 | + |
| 383 | + autoScale = self._calculateAutoScaleFactor() |
| 384 | + |
| 385 | + scalingSettings = { |
| 386 | + "uiScale": settings.value("uiScale", autoScale, type=float), |
| 387 | + "fontScale": settings.value("fontScale", autoScale, type=float), |
| 388 | + "autoDetect": settings.value("autoDetect", True, type=bool) |
| 389 | + } |
| 390 | + |
| 391 | + settings.endGroup() |
| 392 | + return scalingSettings |
| 393 | + |
| 394 | + def _saveScalingSettings(self): |
| 395 | + """Save current scaling settings to QSettings.""" |
| 396 | + settings = QSettings() |
| 397 | + settings.beginGroup("Display") |
| 398 | + |
| 399 | + settings.setValue("uiScale", self._scalingSettings["uiScale"]) |
| 400 | + settings.setValue("fontScale", self._scalingSettings["fontScale"]) |
| 401 | + settings.setValue("autoDetect", self._scalingSettings["autoDetect"]) |
| 402 | + |
| 403 | + settings.endGroup() |
| 404 | + settings.sync() |
| 405 | + |
| 406 | + @Slot(float) |
| 407 | + def setUiScale(self, scale): |
| 408 | + """Set UI scale factor.""" |
| 409 | + self._scalingSettings["uiScale"] = max(0.5, min(4.0, scale)) |
| 410 | + self._saveScalingSettings() |
| 411 | + self.scalingSettingsChanged.emit() |
| 412 | + |
| 413 | + @Slot(float) |
| 414 | + def setFontScale(self, scale): |
| 415 | + """Set font scale factor.""" |
| 416 | + self._scalingSettings["fontScale"] = max(0.5, min(4.0, scale)) |
| 417 | + |
| 418 | + # Apply font scaling immediately |
| 419 | + font = self.font() |
| 420 | + basePointSize = 9 |
| 421 | + scaledPointSize = int(basePointSize * scale) |
| 422 | + font.setPointSize(scaledPointSize) |
| 423 | + self.setFont(font) |
| 424 | + |
| 425 | + self._saveScalingSettings() |
| 426 | + self.scalingSettingsChanged.emit() |
| 427 | + |
| 428 | + @Slot(bool) |
| 429 | + def setAutoDetect(self, autoDetect): |
| 430 | + """Enable/disable automatic DPI detection.""" |
| 431 | + self._scalingSettings["autoDetect"] = autoDetect |
| 432 | + |
| 433 | + if autoDetect: |
| 434 | + autoScale = self._calculateAutoScaleFactor() |
| 435 | + self.setUiScale(autoScale) |
| 436 | + self.setFontScale(autoScale) |
| 437 | + |
| 438 | + self._saveScalingSettings() |
| 439 | + |
| 440 | + @Slot() |
| 441 | + def resetScalingToDefaults(self): |
| 442 | + """Reset scaling settings to automatic defaults.""" |
| 443 | + autoScale = self._calculateAutoScaleFactor() |
| 444 | + self._scalingSettings = { |
| 445 | + "uiScale": autoScale, |
| 446 | + "fontScale": autoScale, |
| 447 | + "autoDetect": True |
| 448 | + } |
| 449 | + |
| 450 | + # Apply font scaling immediately |
| 451 | + font = self.font() |
| 452 | + basePointSize = 9 |
| 453 | + scaledPointSize = int(basePointSize * autoScale) |
| 454 | + font.setPointSize(scaledPointSize) |
| 455 | + self.setFont(font) |
| 456 | + |
| 457 | + self._saveScalingSettings() |
| 458 | + self.scalingSettingsChanged.emit() |
| 459 | + |
334 | 460 | def terminateManual(self): |
335 | 461 | self.engine.clearComponentCache() |
336 | 462 | self.engine.collectGarbage() |
@@ -702,6 +828,12 @@ def _getEnvironmentVariableValue(self, key: str, defaultValue: bool) -> bool: |
702 | 828 | activeProjectChanged = Signal() |
703 | 829 | activeProject = Property(Variant, lambda self: self._activeProject, notify=activeProjectChanged) |
704 | 830 |
|
| 831 | + scalingSettingsChanged = Signal() |
| 832 | + dpiInfo = Property("QVariantMap", lambda self: self._dpiInfo, constant=True) |
| 833 | + uiScale = Property(float, lambda self: self._scalingSettings["uiScale"], notify=scalingSettingsChanged) |
| 834 | + fontScale = Property(float, lambda self: self._scalingSettings["fontScale"], notify=scalingSettingsChanged) |
| 835 | + autoDetectDpi = Property(bool, lambda self: self._scalingSettings["autoDetect"], notify=scalingSettingsChanged) |
| 836 | + |
705 | 837 | changelogModel = Property("QVariantList", _changelogModel, constant=True) |
706 | 838 | licensesModel = Property("QVariantList", _licensesModel, constant=True) |
707 | 839 | pipelineTemplateFilesChanged = Signal() |
|
0 commit comments