執筆者:菅原
インタラクティブコンテンツ制作会社を経てモノリスソフトへ入社。 以来、テクニカルアーティストとして主にMayaツール開発の業務を担当。 好きな食べものはごはん。
TECH BLOG
こんにちは。モノリスソフト テクニカルアーティストの菅原です。
今回はMayaでツールを作成するにあたり、自分が使うPySide周りの小ネタをご紹介できればと思います。
ソースファイルを記事内に含めるととても長くなってしまいますので、この記事ではどういった処理かとその使用例をまとめます。
記事内の使用例はサンプルからの切り抜きとなり、他の処理も必要になってくるためそのままでは動作しません。
詳細な処理内容をご覧になりたい方・実際に使用してみたい方は添付されているサンプルファイル(monolithtech_pyside.zip 10.7KB)をダウンロードしてください。
DOCUMENTS\MAYA\2019\SCRIPTS │ └─techblog_pysideフォルダ │ __init__.py │ ├─mixinフォルダ ├─uiフォルダ └─utilsフォルダ
import techblog_pyside # PySideで作成したFrameLayout techblog_pyside.frame_main() # UIの動的追加処理 techblog_pyside.variadic_main() # 仮ツール techblog_pyside.default_main() # 仮ツールにファイルメニュー・ヘルプメニューの機能追加 techblog_pyside.menu_main() # 全部 techblog_pyside.main()
maya.cmdsのUIとして良く使用されるFrameLayoutを真似てPySideで実装したものです。
▼印のバー部分をクリックすると内部Layoutに設定したWidgetの表示・非表示が切り替えられます。
class FrameLayoutWidget(mayaMixin.MayaQWidgetBaseMixin, qt.QtDefaultWidget): def __init__(self, parent=None): super(FrameLayoutWidget, self).__init__(parent) self.setLayout(QtWidgets.QVBoxLayout()) self.setObjectName('FrameLayoutWidget') # PySideで作成したFrameLayout self.__dummy_frame = qt.FrameLayout(self) # FrameLayoutが閉じた状態になった時の処理を登録 self.__dummy_frame.frame_btn.collapsed.add(lambda: print('collapsed')) # FrameLayoutが開いた状態になった時の処理を登録 self.__dummy_frame.frame_btn.expanded.add(lambda: print('expanded')) # FrameLayoutをWidgetに追加 self.layout().addWidget(self.__dummy_frame) for i in range(5): # FrameLayoutに追加するボタンを作成 btn = QtWidgets.QPushButton(str(i)) # ボタンが押されたときの処理を登録 btn.clicked.connect(functools.partial(print, i)) # FrameLayoutにボタンを追加 self.__dummy_frame.frame_layout.addWidget(btn)
動的にUIを追加したい場合に使用する処理です。
追加したUIへのアクセスはQtVariadic.instancesから取得できる作りになっています。
class PathForm(ui_path.Ui_Form): """UIの動的追加処理に設定したいUIの中で完結できる部分の処理を実装 """ def __init__(self): super(PathForm, self).__init__() self.__subject = common.Subject() @property def updated(self): """関数登録部分の外部公開 """ return self.__subject.listen def setupUi(self, Form): """SIGNAL・SLOTの追加設定 """ super(PathForm, self).setupUi(Form) self.lineEdit.textChanged.connect(self.__on_path_changed) self.toolButton.clicked.connect(functools.partial(self.__on_btn_clicked, self.lineEdit)) def __on_path_changed(self): """LineEdit変更時の登録された関数への通知 """ self.__subject.emit(os.path.basename(self.lineEdit.text())) def __on_btn_clicked(self, line_edit): """ボタン押下時のLineEditへのフォルダパス設定 """ p = QtWidgets.QFileDialog.getExistingDirectory() if p: line_edit.setText(p) class QtVariadicWidget(mayaMixin.MayaQWidgetBaseMixin, qt.QtDefaultWidget): def __init__(self, parent=None): super(QtVariadicWidget, self).__init__(parent) self.setLayout(QtWidgets.QVBoxLayout()) self.layout().setAlignment(QtCore.Qt.AlignTop) self.setObjectName('QtVariadicWidget') # 動的なUIの追加処理に使用するボタン btn_layout = QtWidgets.QHBoxLayout() # 追加用ボタン add_btn = QtWidgets.QPushButton('+', self) add_btn.setObjectName('add_btn') # 削除用ボタン sub_btn = QtWidgets.QPushButton('-', self) sub_btn.setObjectName('sub_btn') # Layoutに配置 btn_layout.addWidget(add_btn) btn_layout.addWidget(sub_btn) self.layout().addLayout(btn_layout) # Layoutに対して動的なUI追加を行う処理の作成と追加できる最小・最大の設定 self.__variadic = qt.QtVariadic(PathForm, self.layout(), 2, 10) # 追加用ボタンの登録 self.__variadic.register_add_btn(add_btn) # 削除用ボタンの登録 self.__variadic.register_sub_btn(sub_btn) # 動的に追加されたUIに対して設定するコールバック処理 self.__variadic.register_callback({'updated.add': print}) # 最小の初期化されたUIの作成 self.__variadic.initialize()
以下の処理は画像のような元々のツール(MixinWidgetクラス)があったと仮定し、それに対して多重継承を用いて機能追加を行っています。
この記事で使用しているMayaのツールUIへの多重継承による機能追加の手法について、機能追加用クラスの作成時・使用時にいくつか注意点がございますので、
同じような作りで処理を作成される場合は以下の点について気を付けてください。
class Hoge(MayaQWidgetBaseMixin, QWidget): def __init__(self, parent=None): super(Hoge, self).__init__(parent) . . .
今回は上記の点に注意して作成・使用したものを掲載しています。
もっと詳しい内容についてはpython mroやMixinで検索してみてください。
ファイルメニューはUIの状態保存・復元機能です。
ヘルプメニューはURLを指定してドキュメントへのリンク作成機能となっています。
多重継承している各クラスの役割は以下になります。
ファイルメニュー・ヘルプメニュー作成機能がメニューバー作成機能を前提としているため使用例のような継承順になっています。
class MenuMixinWidget(menu.MinimumFileMenuMixin, menu.MinimumHelpMenuMixin, menu.MenubarMixin, MixinWidget): def __init__(self, parent=None): # MixinWidget→MenubarMixin→MinimumHelpMenuMixin→MinimumFileMenuMixinの順でinitの処理が実行される super(MenuMixinWidget, self).__init__(parent) self.setObjectName('MenuMixinWidget') # メニュー作成 self.create_file_menu() self.create_help_menu('https://www.monolithsoft.co.jp/')
いかがでしたでしょうか。
今回の記事はPySideの小ネタをご紹介いたしました。今後PySideでツールを作る必要があった時の手助けになれましたら幸いです。
執筆者:菅原
インタラクティブコンテンツ制作会社を経てモノリスソフトへ入社。 以来、テクニカルアーティストとして主にMayaツール開発の業務を担当。 好きな食べものはごはん。