zoukankan      html  css  js  c++  java
  • 使用 PySide2 开发 Maya 插件系列二:继承 uic 转换出来的 py 文件中的类 Ui_Form

    使用 PySide2 开发 Maya 插件系列二:继承 uic 转换出来的 py 文件中的类 Ui_Form

    开发环境:

    Wing IDE 6.1

    步骤1:

     打开 Wing IDE,创建一个新的 project,保存这个 project 到某个路径下,把之前生产的 py 文件所在的文件夹添加到该 project 中,然后在文件夹下新建一个 py 文件,我这里命名为 PySideTest.py

    图中 PySide2ToPySide.py 是一个 PySide2 兼容 PySide 的一个补丁代码,出处链接:http://www.cnblogs.com/hksac/p/9502236.html,如果要用需要把不必要的测试代码删除,修改后的代码如下:

      1 from __future__ import with_statement
      2 
      3 import os
      4 import functools
      5 import imp
      6 import subprocess
      7 import sys
      8 import webbrowser
      9 
     10 
     11 class PySide2Patcher(object):
     12     _core_to_qtgui = set([
     13         "QAbstractProxyModel",
     14         "QItemSelection",
     15         "QItemSelectionModel",
     16         "QItemSelectionRange",
     17         "QSortFilterProxyModel",
     18         "QStringListModel"
     19     ])
     20 
     21 
     22     @classmethod
     23     def _move_attributes(cls, dst, src, names):
     24         """
     25         Moves a list of attributes from one package to another.
     26 
     27         :param names: Names of the attributes to move.
     28         """
     29         for name in names:
     30             if not hasattr(dst, name):
     31                 setattr(dst, name, getattr(src, name))
     32 
     33     @classmethod
     34     def _patch_QTextCodec(cls, QtCore):
     35         """
     36         Patches in QTextCodec.
     37 
     38         :param QTextCodec: The QTextCodec class.
     39         """
     40         original_QTextCodec = QtCore.QTextCodec
     41 
     42         class QTextCodec(original_QTextCodec):
     43             @staticmethod
     44             def setCodecForCStrings(codec):
     45                 pass
     46 
     47         QtCore.QTextCodec = QTextCodec
     48 
     49     @classmethod
     50     def _fix_QCoreApplication_api(cls, wrapper_class, original_class):
     51 
     52         wrapper_class.CodecForTr = 0
     53         wrapper_class.UnicodeUTF8 = 1
     54         wrapper_class.DefaultCodec = wrapper_class.CodecForTr
     55 
     56         @staticmethod
     57         def translate(context, source_text, disambiguation=None, encoding=None, n=None):
     58 
     59             if n is not None:
     60                 return original_class.translate(context, source_text, disambiguation, n)
     61             else:
     62                 return original_class.translate(context, source_text, disambiguation)
     63 
     64         wrapper_class.translate = translate
     65 
     66     @classmethod
     67     def _patch_QCoreApplication(cls, QtCore):
     68 
     69         original_QCoreApplication = QtCore.QCoreApplication
     70 
     71         class QCoreApplication(original_QCoreApplication):
     72             pass
     73         cls._fix_QCoreApplication_api(QCoreApplication, original_QCoreApplication)
     74         QtCore.QCoreApplication = QCoreApplication
     75 
     76     @classmethod
     77     def _patch_QApplication(cls, QtGui):
     78 
     79         original_QApplication = QtGui.QApplication
     80 
     81         class QApplication(original_QApplication):
     82             def __init__(self, *args):
     83                 original_QApplication.__init__(self, *args)
     84                 QtGui.qApp = self
     85 
     86             @staticmethod
     87             def palette(widget=None):
     88 
     89                 return original_QApplication.palette(widget)
     90 
     91         cls._fix_QCoreApplication_api(QApplication, original_QApplication)
     92 
     93         QtGui.QApplication = QApplication
     94 
     95     @classmethod
     96     def _patch_QAbstractItemView(cls, QtGui):
     97 
     98         original_QAbstractItemView = QtGui.QAbstractItemView
     99 
    100         class QAbstractItemView(original_QAbstractItemView):
    101             def __init__(self, *args):
    102                 original_QAbstractItemView.__init__(self, *args)
    103 
    104                 if hasattr(self, "dataChanged"):
    105                     original_dataChanged = self.dataChanged
    106 
    107                     def dataChanged(tl, br, roles=None):
    108                         original_dataChanged(tl, br)
    109                     self.dataChanged = lambda tl, br, roles: dataChanged(tl, br)
    110 
    111         QtGui.QAbstractItemView = QAbstractItemView
    112 
    113     @classmethod
    114     def _patch_QStandardItemModel(cls, QtGui):
    115 
    116         original_QStandardItemModel = QtGui.QStandardItemModel
    117 
    118         class SignalWrapper(object):
    119             def __init__(self, signal):
    120                 self._signal = signal
    121 
    122             def emit(self, tl, br):
    123                 self._signal.emit(tl, br, [])
    124 
    125             def __getattr__(self, name):
    126                 return getattr(self._signal, name)
    127 
    128         class QStandardItemModel(original_QStandardItemModel):
    129             def __init__(self, *args):
    130                 original_QStandardItemModel.__init__(self, *args)
    131                 self.dataChanged = SignalWrapper(self.dataChanged)
    132 
    133         QtGui.QStandardItemModel = QStandardItemModel
    134 
    135     @classmethod
    136     def _patch_QMessageBox(cls, QtGui):
    137 
    138         button_list = [
    139             QtGui.QMessageBox.Ok,
    140             QtGui.QMessageBox.Open,
    141             QtGui.QMessageBox.Save,
    142             QtGui.QMessageBox.Cancel,
    143             QtGui.QMessageBox.Close,
    144             QtGui.QMessageBox.Discard,
    145             QtGui.QMessageBox.Apply,
    146             QtGui.QMessageBox.Reset,
    147             QtGui.QMessageBox.RestoreDefaults,
    148             QtGui.QMessageBox.Help,
    149             QtGui.QMessageBox.SaveAll,
    150             QtGui.QMessageBox.Yes,
    151             QtGui.QMessageBox.YesAll,
    152             QtGui.QMessageBox.YesToAll,
    153             QtGui.QMessageBox.No,
    154             QtGui.QMessageBox.NoAll,
    155             QtGui.QMessageBox.NoToAll,
    156             QtGui.QMessageBox.Abort,
    157             QtGui.QMessageBox.Retry,
    158             QtGui.QMessageBox.Ignore
    159         ]
    160 
    161 
    162         def _method_factory(icon, original_method):
    163 
    164             def patch(parent, title, text, buttons=QtGui.QMessageBox.Ok, defaultButton=QtGui.QMessageBox.NoButton):
    165 
    166                 msg_box = QtGui.QMessageBox(parent)
    167                 msg_box.setWindowTitle(title)
    168                 msg_box.setText(text)
    169                 msg_box.setIcon(icon)
    170                 for button in button_list:
    171                     if button & buttons:
    172                         msg_box.addButton(button)
    173                 msg_box.setDefaultButton(defaultButton)
    174                 msg_box.exec_()
    175                 return msg_box.standardButton(msg_box.clickedButton())
    176 
    177             functools.update_wrapper(patch, original_method)
    178 
    179             return staticmethod(patch)
    180 
    181         original_QMessageBox = QtGui.QMessageBox
    182 
    183         class QMessageBox(original_QMessageBox):
    184 
    185             critical = _method_factory(QtGui.QMessageBox.Critical, QtGui.QMessageBox.critical)
    186             information = _method_factory(QtGui.QMessageBox.Information, QtGui.QMessageBox.information)
    187             question = _method_factory(QtGui.QMessageBox.Question, QtGui.QMessageBox.question)
    188             warning = _method_factory(QtGui.QMessageBox.Warning, QtGui.QMessageBox.warning)
    189 
    190         QtGui.QMessageBox = QMessageBox
    191 
    192     @classmethod
    193     def _patch_QDesktopServices(cls, QtGui, QtCore):
    194 
    195         if hasattr(QtGui, "QDesktopServices"):
    196             return
    197 
    198         class QDesktopServices(object):
    199 
    200             @classmethod
    201             def openUrl(cls, url):
    202                 if not isinstance(url, QtCore.QUrl):
    203                     url = QtCore.QUrl(url)
    204 
    205                 if url.isLocalFile():
    206                     url = url.toLocalFile().encode("utf-8")
    207 
    208                     if sys.platform == "darwin":
    209                         return subprocess.call(["open", url]) == 0
    210                     elif sys.platform == "win32":
    211                         os.startfile(url)
    212                         return os.path.exists(url)
    213                     elif sys.platform.startswith("linux"):
    214                         return subprocess.call(["xdg-open", url]) == 0
    215                     else:
    216                         raise ValueError("Unknown platform: %s" % sys.platform)
    217                 else:
    218                     try:
    219                         return webbrowser.open_new_tab(url.toString().encode("utf-8"))
    220                     except:
    221                         return False
    222 
    223             @classmethod
    224             def displayName(cls, type):
    225                 cls.__not_implemented_error(cls.displayName)
    226 
    227             @classmethod
    228             def storageLocation(cls, type):
    229                 cls.__not_implemented_error(cls.storageLocation)
    230 
    231             @classmethod
    232             def setUrlHandler(cls, scheme, receiver, method_name=None):
    233                 cls.__not_implemented_error(cls.setUrlHandler)
    234 
    235             @classmethod
    236             def unsetUrlHandler(cls, scheme):
    237                 cls.__not_implemented_error(cls.unsetUrlHandler)
    238 
    239             @classmethod
    240             def __not_implemented_error(cls, method):
    241                 raise NotImplementedError(
    242                     "PySide2 and Toolkit don't support 'QDesktopServices.%s' yet. Please contact %s" %
    243                     (method.__func__, 'asdf@qq.com')
    244                 )
    245 
    246         QtGui.QDesktopServices = QDesktopServices
    247 
    248     @classmethod
    249     def patch(cls, QtCore, QtGui, QtWidgets, PySide2):
    250 
    251         qt_core_shim = imp.new_module("PySide.QtCore")
    252         qt_gui_shim = imp.new_module("PySide.QtGui")
    253 
    254 
    255         cls._move_attributes(qt_gui_shim, QtWidgets, dir(QtWidgets))
    256         cls._move_attributes(qt_gui_shim, QtGui, dir(QtGui))
    257 
    258 
    259         cls._move_attributes(qt_gui_shim, QtCore, cls._core_to_qtgui)
    260         cls._move_attributes(qt_core_shim, QtCore, set(dir(QtCore)) - cls._core_to_qtgui)
    261 
    262         cls._patch_QTextCodec(qt_core_shim)
    263         cls._patch_QCoreApplication(qt_core_shim)
    264         cls._patch_QApplication(qt_gui_shim)
    265         cls._patch_QAbstractItemView(qt_gui_shim)
    266         cls._patch_QStandardItemModel(qt_gui_shim)
    267         cls._patch_QMessageBox(qt_gui_shim)
    268         cls._patch_QDesktopServices(qt_gui_shim, qt_core_shim)
    269 
    270         return qt_core_shim, qt_gui_shim
    271 
    272 
    273 
    274 
    275 import PySide2
    276 from PySide2 import QtCore, QtGui, QtWidgets
    277 
    278 def _import_module_by_name(parent_module_name, module_name):
    279 
    280     module = None
    281     try:
    282         module = __import__(parent_module_name, globals(), locals(), [module_name])
    283         module = getattr(module, module_name)
    284     except Exception as e:
    285         pass
    286     return module
    287 
    288 
    289 QtCore, QtGui = PySide2Patcher.patch(QtCore, QtGui, QtWidgets, PySide2)
    290 QtNetwork = _import_module_by_name("PySide2", "QtNetwork")
    291 QtWebKit = _import_module_by_name("PySide2.QtWebKitWidgets", "QtWebKit")
    PySide2ToPySide.py

    PySideTest.py 代码如下:

     1 # -*- coding: utf-8 -*-
     2 import sys
     3 try:
     4     from PySide import QtCore, QtGui
     5     import test_ui_pyside as ui
     6 except:
     7     from PySide2ToPySide import QtCore, QtGui   #注意:不能确保完全兼容,但常用的基本兼容
     8     import test_ui_pyside2 as ui    #使用 pyside2-uic 生成 test_ui_pyside2
     9     
    10 class MainWindow(QtGui.QWidget, ui.Ui_Form):    # 如果 designer 新建的时候选的是 MainWindow,则要集成 QtGui.QMainWindow,其它的类型要对应好
    11     def __init__(self, parent = None):
    12         super(MainWindow, self).__init__(parent)    # 执行父类的__init__()
    13         self.setupUi(self)  # 调用 ui.Ui_Form 的 setupUi()
    14 
    15 def main():
    16     """ 和maya中的不一样 """
    17     app = QtGui.QApplication(sys.argv)  # window是基于application的,所以在没有application的情况下要创建一个,如果在 maya 中,则不需要,因为maya本身就是一个 application
    18     win = MainWindow()  #实例一个window
    19     win.show()  # 显示window
    20     sys.exit(app.exec_())   # 退出application,同时会释放win
    21         
    22 if __name__ == '__main__':  #对当前文件进行debug则会运行以下代码,import该文件不会运行,请了解模块默认属性 __name__ 的特点和用处
    23     main()

    这时候已经可以点击debug,运行结果:

    步骤2:

    设置 wing IDE 的 project 属性 Project->Project Properties...

    这样的好处是可以让 IDE 有maya python 模块的命令补全。

    新建一个 PySideTest_maya.py,这是提供给 maya 运行的:

     1 # -*- coding: utf-8 -*-
     2 import sys
     3 
     4 import PySideTest
     5 
     6 try:
     7     from PySide import QtCore, QtGui
     8     import shiboken
     9 except:
    10     from PySide2ToPySide import QtCore, QtGui
    11     import shiboken2 as shiboken
    12     
    13 import maya.OpenMayaUI as omui
    14 def maya_main_window():
    15     main_window_ptr = omui.MQtUtil.mainWindow()     #获得maya主窗口的指针,主要是为了让插件界面设置它为父窗口
    16     return shiboken.wrapInstance(long(main_window_ptr), QtGui.QWidget)  #把maya主窗口封装从QtGui对象
    17     
    18 class MainWindow(PySideTest.MainWindow):
    19     def __init__(self, parent = None):
    20         super(MainWindow, self).__init__(parent)
    21         
    22         self.setWindowTitle("TestWindow")       #设置窗口标题
    23         self.setWindowFlags(QtCore.Qt.Window)   #设置窗口标志为window,这样会使得widget成为独立窗口,不然会附着在maya的左上角,如果UI是继承的是QMainWindow,则不需要设置
    24         self.setAttribute(QtCore.Qt.WA_DeleteOnClose)   #设置属性为关闭窗口则释放它的对象,窗口关闭,实例对象还存在,只要再次show即可,如果win再main中不断的新建MainWindow,则需要设置
    25         
    26 def main():
    27     global win
    28     try:
    29         win.close() #为了不让窗口出现多个,因为第一次运行还没初始化,所以要try,在这里尝试先关闭,再重新新建一个窗口
    30     except:
    31         pass
    32     win = MainWindow(maya_main_window()) #如果把win的初始化放在方法外,则不需要self.setAttribute(QtCore.Qt.WA_DeleteOnClose),同时关闭后再显示,还会保持上一次的窗口状态
    33     win.show()
    34 
    35 if __name__ == "__main__":
    36     main()

    分别在 maya2015 和 maya2017 的 Script Editor 的 python tab 里编写如下代码:

    1 import sys
    2 sys.path.append(r'E:WorksMayaScriptsPySideTest') #把代码所在的路径添加到环境变量PATH中,这样可以import它们
    3 
    4 import PySideTest_maya
    5 reload(PySideTest_maya)
    6 PySideTest_maya.main()

      选中需要运行的代码,Ctrl+Shift+Enter 运行:

    运行结果:

    回到总览使用 PySide2 开发 Maya 插件系列 总览 

  • 相关阅读:
    Html5新标签解释及用法
    时间线效果
    NHibernate数据访问层核心代码
    C#开源资源项目
    安卓开发环境搭建之最新版(So Easy!)
    WIN8相关资源汇总
    HTML5 Shiv – 让该死的IE系列支持HTML5吧
    WCF 提高传输数据性能方法之二 (数据分割)
    支付宝集成问题(.net篇)
    WCF 提高传输数据性能方法之一 (使用压缩)
  • 原文地址:https://www.cnblogs.com/ibingshan/p/9885741.html
Copyright © 2011-2022 走看看