MetaWhiz/scripts/QtCompat.py
2025-04-17 13:00:39 +08:00

297 lines
11 KiB
Python

#!/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()