#!/usr/bin/env python # -*- coding: utf-8 -*- """ Tool - Qt Compatibility Module Provides cross-version Qt compatibility support for Maya """ import os import sys import importlib import maya.cmds as cmds import maya.mel as mel # 全局变量 QT_VERSION = None QtCore = None QtGui = None QtWidgets = None wrapInstance = None unwrapInstance = None def init_qt(): """Initialize Qt module, automatically selecting appropriate Qt binding based on Maya version""" global QT_VERSION, QtCore, QtGui, QtWidgets, wrapInstance, unwrapInstance # Get Maya version maya_version = int(cmds.about(version=True).split()[0]) # Maya 2025+ use PySide6 if maya_version >= 2025: try: from PySide6 import QtCore, QtGui, QtWidgets from shiboken6 import wrapInstance QT_VERSION = "PySide6" print(f"QtCompat: Using PySide6 (Maya {maya_version})") # Define unwrapInstance function try: from shiboken6 import wrapInstance as _wrapInstance from shiboken6 import getCppPointer def unwrapInstance(object): """Convert Qt object to C++ pointer""" return getCppPointer(object)[0] except ImportError: def unwrapInstance(object): """Unable to get C++ pointer, return None""" print("QtCompat: shiboken6.getCppPointer not available") return None except ImportError as e: print(f"QtCompat: PySide6 import error: {str(e)}") try: from PySide2 import QtCore, QtGui, QtWidgets from shiboken2 import wrapInstance QT_VERSION = "PySide2" print(f"QtCompat: Using PySide2 (fallback mode, Maya {maya_version})") # Define unwrapInstance function try: from shiboken2 import wrapInstance as _wrapInstance from shiboken2 import getCppPointer def unwrapInstance(object): """Convert Qt object to C++ pointer""" return getCppPointer(object)[0] except ImportError: def unwrapInstance(object): """Unable to get C++ pointer, return None""" print("QtCompat: shiboken2.getCppPointer not available") return None except ImportError: cmds.error("QtCompat: Unable to import PySide6 or PySide2, please ensure they are installed") # Maya 2022-2024 use PySide2 elif maya_version >= 2022: try: from PySide2 import QtCore, QtGui, QtWidgets from shiboken2 import wrapInstance QT_VERSION = "PySide2" print(f"QtCompat: Using PySide2 (Maya {maya_version})") # Define unwrapInstance function try: from shiboken2 import wrapInstance as _wrapInstance from shiboken2 import getCppPointer def unwrapInstance(object): """Convert Qt object to C++ pointer""" return getCppPointer(object)[0] except ImportError: def unwrapInstance(object): """Unable to get C++ pointer, return None""" print("QtCompat: shiboken2.getCppPointer not available") return None except ImportError as e: print(f"QtCompat: PySide2 import error: {str(e)}") try: from PySide6 import QtCore, QtGui, QtWidgets from shiboken6 import wrapInstance QT_VERSION = "PySide6" print(f"QtCompat: Using PySide6 (non-standard configuration, Maya {maya_version})") # Define unwrapInstance function try: from shiboken6 import wrapInstance as _wrapInstance from shiboken6 import getCppPointer def unwrapInstance(object): """Convert Qt object to C++ pointer""" return getCppPointer(object)[0] except ImportError: def unwrapInstance(object): """Unable to get C++ pointer, return None""" print("QtCompat: shiboken6.getCppPointer not available") return None except ImportError: cmds.error("QtCompat: Unable to import PySide2 or PySide6, please ensure they are installed") # Maya 2020-2021 use PySide2 or PySide else: try: from PySide2 import QtCore, QtGui, QtWidgets from shiboken2 import wrapInstance QT_VERSION = "PySide2" print(f"QtCompat: Using PySide2 (Maya {maya_version})") # Define unwrapInstance function try: from shiboken2 import wrapInstance as _wrapInstance from shiboken2 import getCppPointer def unwrapInstance(object): """Convert Qt object to C++ pointer""" return getCppPointer(object)[0] except ImportError: def unwrapInstance(object): """Unable to get C++ pointer, return None""" print("QtCompat: shiboken2.getCppPointer not available") return None except ImportError: try: from PySide import QtCore, QtGui from PySide import QtGui as QtWidgets # PySide中没有QtWidgets from shiboken import wrapInstance QT_VERSION = "PySide" print(f"QtCompat: Using PySide (Maya {maya_version})") # Define unwrapInstance function try: from shiboken import wrapInstance as _wrapInstance from shiboken import getCppPointer def unwrapInstance(object): """Convert Qt object to C++ pointer""" return getCppPointer(object)[0] except ImportError: def unwrapInstance(object): """Unable to get C++ pointer, return None""" print("QtCompat: shiboken.getCppPointer not available") return None except ImportError: cmds.error("QtCompat: Unable to import PySide or PySide2, please ensure they are installed") # Export global variables globals()["QtCore"] = QtCore globals()["QtGui"] = QtGui globals()["QtWidgets"] = QtWidgets globals()["wrapInstance"] = wrapInstance globals()["unwrapInstance"] = unwrapInstance return QT_VERSION def maya_main_window(): """Get Maya main window QWidget""" import maya.OpenMayaUI as omui if QT_VERSION in ["PySide2", "PySide6"]: main_window_ptr = omui.MQtUtil.mainWindow() if main_window_ptr: return wrapInstance(int(main_window_ptr), QtWidgets.QWidget) else: # PySide main_window_ptr = omui.MQtUtil.mainWindow() if main_window_ptr: return wrapInstance(long(main_window_ptr), QtWidgets.QWidget) return None def load_ui_file(ui_file, parent=None): """Load UI file and return created window""" if QT_VERSION == "PySide6": from PySide6.QtUiTools import QUiLoader loader = QUiLoader() ui_file_obj = QtCore.QFile(ui_file) ui_file_obj.open(QtCore.QFile.ReadOnly) ui = loader.load(ui_file_obj, parent) ui_file_obj.close() return ui elif QT_VERSION == "PySide2": from PySide2.QtUiTools import QUiLoader loader = QUiLoader() ui_file_obj = QtCore.QFile(ui_file) ui_file_obj.open(QtCore.QFile.ReadOnly) ui = loader.load(ui_file_obj, parent) ui_file_obj.close() return ui else: # PySide from PySide.QtUiTools import QUiLoader loader = QUiLoader() ui_file_obj = QtCore.QFile(ui_file) ui_file_obj.open(QtCore.QFile.ReadOnly) ui = loader.load(ui_file_obj, parent) ui_file_obj.close() return ui def compile_ui_file(ui_file, py_file=None): """Compile UI file to Python file""" if not py_file: py_file = os.path.splitext(ui_file)[0] + ".py" if QT_VERSION == "PySide6": from PySide6.QtUiTools import pysideuic with open(py_file, "w") as f: pysideuic.compileUi(ui_file, f) elif QT_VERSION == "PySide2": from PySide2.QtUiTools import pysideuic with open(py_file, "w") as f: pysideuic.compileUi(ui_file, f) else: # PySide from PySide.QtUiTools import pysideuic with open(py_file, "w") as f: pysideuic.compileUi(ui_file, f) return py_file def get_save_file_name(parent=None, caption="", dir="", filter=""): """Get save file name, compatible with different Qt versions""" if QT_VERSION in ["PySide2", "PySide6"]: return QtWidgets.QFileDialog.getSaveFileName(parent, caption, dir, filter)[0] else: # PySide return QtWidgets.QFileDialog.getSaveFileName(parent, caption, dir, filter) def get_open_file_name(parent=None, caption="", dir="", filter=""): """Get open file name, compatible with different Qt versions""" if QT_VERSION in ["PySide2", "PySide6"]: return QtWidgets.QFileDialog.getOpenFileName(parent, caption, dir, filter)[0] else: # PySide return QtWidgets.QFileDialog.getOpenFileName(parent, caption, dir, filter) def get_existing_directory(parent=None, caption="", dir=""): """Get existing directory, compatible with different Qt versions""" if QT_VERSION in ["PySide2", "PySide6"]: return QtWidgets.QFileDialog.getExistingDirectory(parent, caption, dir) else: # PySide return QtWidgets.QFileDialog.getExistingDirectory(parent, caption, dir) def get_icon(icon_path): """Get icon, compatible with different Qt versions""" if os.path.exists(icon_path): return QtGui.QIcon(icon_path) return QtGui.QIcon() def get_pixmap(image_path): """Get pixmap, compatible with different Qt versions""" if os.path.exists(image_path): return QtGui.QPixmap(image_path) return QtGui.QPixmap() def get_color(parent=None, title="Choose Color", initial=None): """Get color, compatible with different Qt versions""" if initial is None: initial = QtGui.QColor(255, 255, 255) if QT_VERSION == "PySide6": color = QtWidgets.QColorDialog.getColor(initial, parent, title) else: # PySide2 or PySide color = QtWidgets.QColorDialog.getColor(initial, parent, title) if color.isValid(): return color return None # Initialize Qt module init_qt()