From 594f1014ce1d2abf4410564b3f18e9281bfe83d2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 30 May 2023 16:45:55 +0200 Subject: [PATCH] General: Qt scale enhancement (#5059) * set 'QT_SCALE_FACTOR_ROUNDING_POLICY' to 'PassThrough' * implemented 'get_openpype_qt_app' which set all openpype related attributes * implemented get app functions in igniter and ayon common * removed env varaibles 'QT_SCALE_FACTOR_ROUNDING_POLICY' * formatting fixes * fix line length * fix args --- common/ayon_common/connection/ui/lib.py | 25 +++++++++ igniter/__init__.py | 51 ++++++++++--------- .../hosts/aftereffects/api/launch_logic.py | 6 +-- openpype/hosts/aftereffects/api/pipeline.py | 6 +-- openpype/hosts/photoshop/api/pipeline.py | 6 +-- openpype/tools/context_dialog/window.py | 6 +-- openpype/tools/push_to_project/app.py | 17 +------ openpype/tools/settings/__init__.py | 7 ++- openpype/tools/tray/pype_tray.py | 35 ++----------- openpype/tools/traypublisher/window.py | 6 +-- openpype/tools/utils/__init__.py | 2 + openpype/tools/utils/lib.py | 31 +++++++++++ 12 files changed, 104 insertions(+), 94 deletions(-) diff --git a/common/ayon_common/connection/ui/lib.py b/common/ayon_common/connection/ui/lib.py index e0f0a3d6c2..a3894d0d9c 100644 --- a/common/ayon_common/connection/ui/lib.py +++ b/common/ayon_common/connection/ui/lib.py @@ -1,3 +1,7 @@ +import sys +from qtpy import QtWidgets, QtCore + + def set_style_property(widget, property_name, property_value): """Set widget's property that may affect style. @@ -9,3 +13,24 @@ def set_style_property(widget, property_name, property_value): return widget.setProperty(property_name, property_value) widget.style().polish(widget) + + +def get_qt_app(): + app = QtWidgets.QApplication.instance() + if app is not None: + return app + + for attr_name in ( + "AA_EnableHighDpiScaling", + "AA_UseHighDpiPixmaps", + ): + attr = getattr(QtCore.Qt, attr_name, None) + if attr is not None: + QtWidgets.QApplication.setAttribute(attr) + + if hasattr(QtWidgets.QApplication, "setHighDpiScaleFactorRoundingPolicy"): + QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy( + QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough + ) + + return QtWidgets.QApplication(sys.argv) diff --git a/igniter/__init__.py b/igniter/__init__.py index aa1b1d209e..04026d5a37 100644 --- a/igniter/__init__.py +++ b/igniter/__init__.py @@ -19,21 +19,36 @@ if "OpenPypeVersion" not in sys.modules: sys.modules["OpenPypeVersion"] = OpenPypeVersion +def _get_qt_app(): + from qtpy import QtWidgets, QtCore + + app = QtWidgets.QApplication.instance() + if app is not None: + return app + + for attr_name in ( + "AA_EnableHighDpiScaling", + "AA_UseHighDpiPixmaps", + ): + attr = getattr(QtCore.Qt, attr_name, None) + if attr is not None: + QtWidgets.QApplication.setAttribute(attr) + + if hasattr(QtWidgets.QApplication, "setHighDpiScaleFactorRoundingPolicy"): + QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy( + QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough + ) + + return app + def open_dialog(): """Show Igniter dialog.""" if os.getenv("OPENPYPE_HEADLESS_MODE"): print("!!! Can't open dialog in headless mode. Exiting.") sys.exit(1) - from qtpy import QtWidgets, QtCore from .install_dialog import InstallDialog - scale_attr = getattr(QtCore.Qt, "AA_EnableHighDpiScaling", None) - if scale_attr is not None: - QtWidgets.QApplication.setAttribute(scale_attr) - - app = QtWidgets.QApplication.instance() - if not app: - app = QtWidgets.QApplication(sys.argv) + app = _get_qt_app() d = InstallDialog() d.open() @@ -47,16 +62,10 @@ def open_update_window(openpype_version): if os.getenv("OPENPYPE_HEADLESS_MODE"): print("!!! Can't open dialog in headless mode. Exiting.") sys.exit(1) - from qtpy import QtWidgets, QtCore + from .update_window import UpdateWindow - scale_attr = getattr(QtCore.Qt, "AA_EnableHighDpiScaling", None) - if scale_attr is not None: - QtWidgets.QApplication.setAttribute(scale_attr) - - app = QtWidgets.QApplication.instance() - if not app: - app = QtWidgets.QApplication(sys.argv) + app = _get_qt_app() d = UpdateWindow(version=openpype_version) d.open() @@ -71,16 +80,10 @@ def show_message_dialog(title, message): if os.getenv("OPENPYPE_HEADLESS_MODE"): print("!!! Can't open dialog in headless mode. Exiting.") sys.exit(1) - from qtpy import QtWidgets, QtCore + from .message_dialog import MessageDialog - scale_attr = getattr(QtCore.Qt, "AA_EnableHighDpiScaling", None) - if scale_attr is not None: - QtWidgets.QApplication.setAttribute(scale_attr) - - app = QtWidgets.QApplication.instance() - if not app: - app = QtWidgets.QApplication(sys.argv) + app = _get_qt_app() dialog = MessageDialog(title, message) dialog.open() diff --git a/openpype/hosts/aftereffects/api/launch_logic.py b/openpype/hosts/aftereffects/api/launch_logic.py index ea71122042..e90c3dc5b8 100644 --- a/openpype/hosts/aftereffects/api/launch_logic.py +++ b/openpype/hosts/aftereffects/api/launch_logic.py @@ -13,13 +13,13 @@ from wsrpc_aiohttp import ( WebSocketAsync ) -from qtpy import QtCore, QtWidgets +from qtpy import QtCore from openpype.lib import Logger -from openpype.tools.utils import host_tools from openpype.tests.lib import is_in_tests from openpype.pipeline import install_host, legacy_io from openpype.modules import ModulesManager +from openpype.tools.utils import host_tools, get_openpype_qt_app from openpype.tools.adobe_webserver.app import WebServerTool from .ws_stub import get_stub @@ -43,7 +43,7 @@ def main(*subprocess_args): install_host(host) os.environ["OPENPYPE_LOG_NO_COLORS"] = "False" - app = QtWidgets.QApplication([]) + app = get_openpype_qt_app() app.setQuitOnLastWindowClosed(False) launcher = ProcessLauncher(subprocess_args) diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py index 5566ca9e5b..8fc7a70dd8 100644 --- a/openpype/hosts/aftereffects/api/pipeline.py +++ b/openpype/hosts/aftereffects/api/pipeline.py @@ -23,6 +23,7 @@ from openpype.host import ( ILoadHost, IPublishHost ) +from openpype.tools.utils import get_openpype_qt_app from .launch_logic import get_stub from .ws_stub import ConnectionNotEstablishedYet @@ -236,10 +237,7 @@ def check_inventory(): return # Warn about outdated containers. - _app = QtWidgets.QApplication.instance() - if not _app: - print("Starting new QApplication..") - _app = QtWidgets.QApplication([]) + _app = get_openpype_qt_app() message_box = QtWidgets.QMessageBox() message_box.setIcon(QtWidgets.QMessageBox.Warning) diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index 88f5d63a72..56ae2a4c25 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -20,6 +20,7 @@ from openpype.host import ( from openpype.pipeline.load import any_outdated_containers from openpype.hosts.photoshop import PHOTOSHOP_HOST_DIR +from openpype.tools.utils import get_openpype_qt_app from . import lib @@ -163,10 +164,7 @@ def check_inventory(): return # Warn about outdated containers. - _app = QtWidgets.QApplication.instance() - if not _app: - print("Starting new QApplication..") - _app = QtWidgets.QApplication([]) + _app = get_openpype_qt_app() message_box = QtWidgets.QMessageBox() message_box.setIcon(QtWidgets.QMessageBox.Warning) diff --git a/openpype/tools/context_dialog/window.py b/openpype/tools/context_dialog/window.py index 86c53b55c5..4fe41c9949 100644 --- a/openpype/tools/context_dialog/window.py +++ b/openpype/tools/context_dialog/window.py @@ -5,7 +5,7 @@ from qtpy import QtWidgets, QtCore, QtGui from openpype import style from openpype.pipeline import AvalonMongoDB -from openpype.tools.utils.lib import center_window +from openpype.tools.utils.lib import center_window, get_openpype_qt_app from openpype.tools.utils.assets_widget import SingleSelectAssetsWidget from openpype.tools.utils.constants import ( PROJECT_NAME_ROLE @@ -376,9 +376,7 @@ def main( strict=True ): # Run Qt application - app = QtWidgets.QApplication.instance() - if app is None: - app = QtWidgets.QApplication([]) + app = get_openpype_qt_app() window = ContextDialog() window.set_strict(strict) window.set_context(project_name, asset_name) diff --git a/openpype/tools/push_to_project/app.py b/openpype/tools/push_to_project/app.py index 9ca5fd83e9..b3ec33f353 100644 --- a/openpype/tools/push_to_project/app.py +++ b/openpype/tools/push_to_project/app.py @@ -1,6 +1,6 @@ import click -from qtpy import QtWidgets, QtCore +from openpype.tools.utils import get_openpype_qt_app from openpype.tools.push_to_project.window import PushToContextSelectWindow @@ -15,20 +15,7 @@ def main(project, version): version (str): Version id. """ - app = QtWidgets.QApplication.instance() - if not app: - # 'AA_EnableHighDpiScaling' must be set before app instance creation - high_dpi_scale_attr = getattr( - QtCore.Qt, "AA_EnableHighDpiScaling", None - ) - if high_dpi_scale_attr is not None: - QtWidgets.QApplication.setAttribute(high_dpi_scale_attr) - - app = QtWidgets.QApplication([]) - - attr = getattr(QtCore.Qt, "AA_UseHighDpiPixmaps", None) - if attr is not None: - app.setAttribute(attr) + app = get_openpype_qt_app() window = PushToContextSelectWindow() window.show() diff --git a/openpype/tools/settings/__init__.py b/openpype/tools/settings/__init__.py index a5b1ea51a5..04f64e13f1 100644 --- a/openpype/tools/settings/__init__.py +++ b/openpype/tools/settings/__init__.py @@ -1,7 +1,8 @@ import sys -from qtpy import QtWidgets, QtGui +from qtpy import QtGui from openpype import style +from openpype.tools.utils import get_openpype_qt_app from .lib import ( BTN_FIXED_SIZE, CHILD_OFFSET @@ -24,9 +25,7 @@ def main(user_role=None): user_role, ", ".join(allowed_roles) )) - app = QtWidgets.QApplication.instance() - if not app: - app = QtWidgets.QApplication(sys.argv) + app = get_openpype_qt_app() app.setWindowIcon(QtGui.QIcon(style.app_icon_path())) widget = MainWidget(user_role) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 1cf128e59d..a5876ca721 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -36,7 +36,8 @@ from openpype.settings import ( from openpype.tools.utils import ( WrappedCallbackItem, paint_image_with_color, - get_warning_pixmap + get_warning_pixmap, + get_openpype_qt_app, ) from .pype_info_widget import PypeInfoWidget @@ -858,37 +859,7 @@ class PypeTrayStarter(QtCore.QObject): def main(): - log = Logger.get_logger(__name__) - app = QtWidgets.QApplication.instance() - - high_dpi_scale_attr = None - if not app: - # 'AA_EnableHighDpiScaling' must be set before app instance creation - high_dpi_scale_attr = getattr( - QtCore.Qt, "AA_EnableHighDpiScaling", None - ) - if high_dpi_scale_attr is not None: - QtWidgets.QApplication.setAttribute(high_dpi_scale_attr) - - app = QtWidgets.QApplication([]) - - if high_dpi_scale_attr is None: - log.debug(( - "Attribute 'AA_EnableHighDpiScaling' was not set." - " UI quality may be affected." - )) - - for attr_name in ( - "AA_UseHighDpiPixmaps", - ): - attr = getattr(QtCore.Qt, attr_name, None) - if attr is None: - log.debug(( - "Missing QtCore.Qt attribute \"{}\"." - " UI quality may be affected." - ).format(attr_name)) - else: - app.setAttribute(attr) + app = get_openpype_qt_app() starter = PypeTrayStarter(app) diff --git a/openpype/tools/traypublisher/window.py b/openpype/tools/traypublisher/window.py index 3ac1b4c4ad..a1ed38dcc0 100644 --- a/openpype/tools/traypublisher/window.py +++ b/openpype/tools/traypublisher/window.py @@ -17,7 +17,7 @@ from openpype.pipeline import install_host from openpype.hosts.traypublisher.api import TrayPublisherHost from openpype.tools.publisher.control_qt import QtPublisherController from openpype.tools.publisher.window import PublisherWindow -from openpype.tools.utils import PlaceholderLineEdit +from openpype.tools.utils import PlaceholderLineEdit, get_openpype_qt_app from openpype.tools.utils.constants import PROJECT_NAME_ROLE from openpype.tools.utils.models import ( ProjectModel, @@ -263,9 +263,7 @@ def main(): host = TrayPublisherHost() install_host(host) - app_instance = QtWidgets.QApplication.instance() - if app_instance is None: - app_instance = QtWidgets.QApplication([]) + app_instance = get_openpype_qt_app() if platform.system().lower() == "windows": import ctypes diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index 10bd527692..f35bfaee70 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -25,6 +25,7 @@ from .lib import ( set_style_property, DynamicQThread, qt_app_context, + get_openpype_qt_app, get_asset_icon, get_asset_icon_by_name, get_asset_icon_name_from_doc, @@ -68,6 +69,7 @@ __all__ = ( "set_style_property", "DynamicQThread", "qt_app_context", + "get_openpype_qt_app", "get_asset_icon", "get_asset_icon_by_name", "get_asset_icon_name_from_doc", diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 885df15da9..82ca23c848 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -14,6 +14,7 @@ from openpype.client import ( from openpype.style import ( get_default_entity_icon_color, get_objected_colors, + get_app_icon_path, ) from openpype.resources import get_image_path from openpype.lib import filter_profiles, Logger @@ -152,6 +153,36 @@ def qt_app_context(): yield app +def get_openpype_qt_app(): + """Main Qt application initialized for OpenPype processed. + + This function should be used only inside OpenPype process and never inside + other processes. + """ + + app = QtWidgets.QApplication.instance() + if app is None: + for attr_name in ( + "AA_EnableHighDpiScaling", + "AA_UseHighDpiPixmaps", + ): + attr = getattr(QtCore.Qt, attr_name, None) + if attr is not None: + QtWidgets.QApplication.setAttribute(attr) + + if hasattr( + QtWidgets.QApplication, "setHighDpiScaleFactorRoundingPolicy" + ): + QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy( + QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough + ) + + app = QtWidgets.QApplication(sys.argv) + + app.setWindowIcon(QtGui.QIcon(get_app_icon_path())) + return app + + class SharedObjects: jobs = {} icons = {}