Skip to content

Instantly share code, notes, and snippets.

@44hero
Last active May 10, 2024 10:38
Show Gist options
  • Save 44hero/30512a4a353a17962d323caf38e10bfa to your computer and use it in GitHub Desktop.
Save 44hero/30512a4a353a17962d323caf38e10bfa to your computer and use it in GitHub Desktop.
使用したいそれぞれのファイルで、 CustomScriptEditor2 の新しいインスタンスを作成する代わりに、 これらのインスタンスを一度だけ、ここで作成し、 これを、他のすべてのファイルの場所で再利用することをお勧めします。 これは一つの方法であり、 当ファイルのように、別の Pythonファイル(例えば singleton.py )を作成し、 その中で、CustomScript
# -*- coding: utf-8 -*-
u"""
CustomScriptEditor2.py
:Author:
oki yoshihiro
okiyoshihiro.job@gmail.com
:Version: -1.0-
:Date: 2024/05/09
.. note:: 当コード記述時の環境
- Maya2022 python3系
- Python version: 3.7.7
- PySide2 version: 5.15.2
概要(overview):
CustomScriptEditor2.py シングルトン ScriptEditor2
です。
詳細(details):
シングルトンパターンは、ソフトウェア設計パターンの一つで、
特定のクラスのインスタンスが常に一つだけ存在することを保証する方法です。
このパターンは、アプリケーション内で特定のリソースに対する
一意のアクセスポイントを提供する必要がある場合に特に便利です。
シングルトンパターンを実装するには、通常、以下の要素が含まれます:
- プライベートなコンストラクタ:
クラスのインスタンス化を制限し、外部から新しいインスタンスを生成できないようにします。
- 静的なメンバ変数:
唯一のインスタンスを保持するための静的なメンバ変数がクラス内に定義されます。
- 静的なメソッド:
唯一のインスタンスにアクセスするための静的なメソッドが提供されます。
このメソッドは常に同じインスタンスを返します。
Pythonでは、シングルトンを実装する方法がいくつかありますが、
最も一般的な方法はクラス変数やデコレータを使う方法です。
例えば、以下のように実装します。
::
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# 以下はテストコード
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True
使用法(usage):
::
# -*- coding: utf-8 -*-
from YO_utilityTools.TemplateForPySide2.CustomScriptEditor2 import CustomScriptEditor2
# e.g.):
_title = 'test'
_infoDetail = 'test'
custom_script_editor2_instance = CustomScriptEditor2(title = _title,
infoDetail = _infoDetail
)
注意(note):
・ 他に必須な独自モジュール
::
# ローカルで作成したモジュール ######################################################
# shiboken2 独自モジュール
from YO_utilityTools.TemplateForPySide2.qt import getMayaWindow # 利用時は、getMayaWindow()
-リマインダ-
done: 2024/05/10
新規作成
version = '-1.0-'
"""
# 標準ライブラリ #################################################################
# サードパーティライブラリ #########################################################
from PySide2.QtWidgets import (QAction, QApplication, QMainWindow,
QMenu, QTextEdit, QWidget,
QVBoxLayout,
)
from PySide2.QtCore import Qt, Signal
# ローカルで作成したモジュール ######################################################
# shiboken2 独自モジュール
from YO_utilityTools.TemplateForPySide2.qt import getMayaWindow # 利用時は、getMayaWindow()
class CustomScriptEditor2(QMainWindow):
closed = Signal() # ウィンドウが閉じられたときにシグナルを送信する設定
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self
, title = None
, infoDetail = None
, parent = None, flags = Qt.WindowFlags()
):
if parent is None:
parent = getMayaWindow()
# super(CustomScriptEditor2, self).__init__(parent, flags)
super().__init__(parent, flags)
self.size = (100, 900, 800, 200) # 100, 100, 800, 600
self.title = title
self.win = self.title + '_ui'
# 追加と変更と新規1
self.infoDetail = infoDetail
self._windowBasicSettings()
self._setupUI()
self._createContextMenu()
def _duplicateWindowAvoidFunction(self, winName):
widgets = QApplication.allWidgets()
for w in widgets:
if w.objectName() == winName:
w.deleteLater()
def _windowBasicSettings(self):
self.setWindowTitle(self.title)
if not self.isVisible():
self.setGeometry(*self.size)
self._duplicateWindowAvoidFunction(self.win)
self.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
self.setAttribute(Qt.WA_DeleteOnClose)
self.setObjectName(self.win)
def _setupUI(self):
self.text_edit = QTextEdit(self)
self.text_edit.setReadOnly(True) # 読み取り専用に設定
layout = QVBoxLayout()
layout.addWidget(self.text_edit)
central_widget = QWidget()
# 追加と変更と新規1
# central_widget に 装飾 を追加
central_widget.setStatusTip(self.infoDetail)
central_widget.setToolTip(self.infoDetail)
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
# self.show()
def _createContextMenu(self):
self.text_edit.setContextMenuPolicy(Qt.CustomContextMenu)
self.text_edit.customContextMenuRequested.connect(self.showContextMenu)
self.context_menu = QMenu(self)
self.select_all_action = QAction("Select All", self)
self.select_all_action.triggered.connect(self.selectAll)
self.context_menu.addAction(self.select_all_action)
self.copy_action = QAction("Copy", self)
self.copy_action.triggered.connect(self.copyText)
self.context_menu.addAction(self.copy_action)
self.clear_action = QAction("Clear Output", self)
self.clear_action.triggered.connect(self.clearOutput)
self.context_menu.addAction(self.clear_action)
def showContextMenu(self, pos):
self.context_menu.exec_(self.text_edit.mapToGlobal(pos))
def selectAll(self):
self.text_edit.selectAll()
def copyText(self):
cursor = self.text_edit.textCursor()
selected_text = cursor.selectedText()
clipboard = QApplication.clipboard()
clipboard.setText(selected_text)
def clearOutput(self):
self.text_edit.clear()
# メソッド
# テキストを追加する
def append_text(self, text):
self.text_edit.append(text)
def append_warning(self, text):
self.append_with_color(text, 'yellow')
def append_error(self, text):
self.append_with_color(text, 'red')
def append_default(self, text):
self.append_with_color(text, 'lime')
def append_default2(self, text):
self.append_text(text)
def append_with_color(self, text, color):
self.text_edit.append(f"<font color='{color}'>{text}</font>")
# オーバーライド
# closeEvent メソッド 組み込み関数
def closeEvent(self, event):
u""" < (オーバーライド) closeEvent メソッド 組み込み関数 です >
オーバーライド
.. note::
当該 closeEvent メソッド は、基は組み込み関数であり、 イベントハンドラー です
閉じる要求を受信したときにトップレベル ウィンドウに対してのみ呼び出されます
self.close でも発動します
"""
# ウィンドウが閉じられたときにシグナルを送信
self.closed.emit()
# super(CustomScriptEditor2, self).closeEvent(event)
# 追加と変更と新規1
# ウィンドウを非表示にする
self.hide()
# 追加と変更と新規1
# イベントを無視してウィンドウを閉じないようにする
# イベントがまだ完全には処理されていないことを示します。
# これは、例えばウィジェットがクリックイベントを部分的にしか処理しない場合や、イベントを無視して親ウィジェットに伝播させたい場合に使用
event.ignore()
# イベントを無視してウィンドウを閉じないようにする
# イベントが適切に処理され、それ以上の処理は必要ないことを示します。
# これは、例えばウィジェットがクリックイベントを処理し、それ以上の処理(例えば親ウィジェットへのイベントの伝播)は必要ない場合に使用
# event.accept()
if __name__ == '__main__':
print(u'{}.py: loaded as script file'.format(__name__))
# cse2 = CustomScriptEditor2(title = 'test', infoDetail = 'test')
# cse2.show()
else:
print(u'{}.py: loaded as module file'.format(__name__))
print('{}'.format(__file__)) # 実行したモジュールフルパスを表示する
# pprint.pprint(RT4_UI_PyMel.mro()) # メソッドを呼び出す順番が解ります
print(u'モジュール名:{}\n'.format(__name__)) # 実行したモジュール名を表示する
# -*- coding: utf-8 -*-
u"""
sampleA_cmdsUI.py
:Author:
oki yoshihiro
okiyoshihiro.job@gmail.com
:Version: -1.0-
:Date: 2024/05/09
.. note:: 当コード記述時の環境
- Maya2022 python3系
- Python version: 3.7.7
- cmds
概要(overview):
CustomScriptEditor2.py シングルトン ScriptEditor2
用の
テスト sampleA cmdsUI
です
cmds UI を使用したモジュールを想定しています
注意(note):
・ 他に必須な独自モジュール
::
# ローカルで作成したモジュール ######################################################
from YO_utilityTools.lib.message import message
from YO_utilityTools.TemplateForPySide2.singleton import custom_script_editor2_instance
-リマインダ-
done: 2024/05/10
新規作成
version = '-1.0-'
"""
# 標準ライブラリ #################################################################
from functools import partial # partial 利用時は、最後の引数に、*args 要時あり
import pkgutil
from importlib import import_module, reload
# サードパーティライブラリ #########################################################
from maya import cmds
from maya.common.ui import LayoutManager
from PySide2.QtCore import Slot
# ローカルで作成したモジュール ######################################################
# # パッケージ内のモジュールの更新を都度反映するように記述 ####################### start
# import YO_utilityTools.lib # lib パッケージに対して
# for key in YO_utilityTools.lib.__dict__:
# if not key.startswith('__'):
# reload(YO_utilityTools.lib.__dict__[key])
# # パッケージ内のモジュールの更新を都度反映するように記述 ####################### end
#
# # パッケージ内のモジュールの更新を都度反映するように記述 ####################### start
# import YO_utilityTools # YO_utilityTools パッケージに対して
# for key in YO_utilityTools.__dict__:
# if not key.startswith('__'):
# reload(YO_utilityTools.__dict__[key])
# # パッケージ内のモジュールの更新を都度反映するように記述 ####################### end
#
# # パッケージ内の子階層に、sabパッケージフォルダ が存在しているので、特殊なケース ################# start
# # note): sabパッケージフォルダ のモジュールも同時に読み込むので、多少動作が遅くなります
# import YO_utilityTools.TemplateForPySide2 as package
# assert(hasattr(package, "__path__"))
# for _, module_name, _ in pkgutil.walk_packages(path = package.__path__,
# prefix = package.__name__ + '.',
# onerror = lambda x: None
# ):
# if not module_name.endswith('__init__'):
# try:
# module = import_module(module_name)
# reload(module)
# # print(f'Reloaded module: {module_name}')
# except ImportError as e:
# print(f'Error reloading module {module_name}: {e}')
# # パッケージ内の子階層に、sabパッケージフォルダ が存在しているので、特殊なケース ################# end
#
from YO_utilityTools.lib.message import message
from YO_utilityTools.lib.message_warning import message_warning
from YO_utilityTools.TemplateForPySide2.singleton import custom_script_editor2_instance
class SampleA_cmdsUI(object):
def __init__(self):
"""Initialize data attributes. Constructor for class."""
self.title = 'SampleA_cmds'
self.win = self.title + '_ui'
self.pushButtonMessage = 'hello, this sampleA_cmdsUI pushButton.'
# note): custom_script_editor2_instance 本モジュール基で、
# CustomScriptEditor2(title, infoDetail)
# 引数: title, 引数: infoDetail
# を定義しています
self.script_editor2 = custom_script_editor2_instance
self.script_editor2.closed.connect(self.on_script_editor2_closed)
# カスタムで、専用の スクリプトエディタ を初期化
# self.script_editor2 = None
self.statusCurrent_scriptEditor2 = 'open'
def createUI(self):
# UI-0. 重複しないウインドウ
if cmds.window(self.win, ex = True):
cmds.deleteUI(self.win)
cmds.window(self.win, title = self.title
, widthHeight = (145, 60)
, menuBar = True, sizeable = True
, minimizeButton = False
, maximizeButton = False
)
with LayoutManager(cmds.columnLayout(adjustableColumn = True)
):
cmds.button(label = 'testA', c = partial(self.pushButton_exe))
cmds.evalDeferred(lambda *args: cmds.showWindow(self.win)) # ウィンドウを表示
print('--------- ' + f'{self.__class__}' + ' ---------')
print('outPut 専用 script_editor2 ウィジェット も作成します')
self.create_script_editor2_and_show()
print(self.script_editor2)
print('--------- ' + f'{self.__class__}' + ' ---------' * 3 + 'end\n')
def create_script_editor2_and_show(self):
# CustomScriptEditor2 モジュール先では、あえて、show() せず、ここで show() しています。
self.script_editor2.show() # note): これは必須です。
self.statusCurrent_scriptEditor2 = 'open'
@Slot()
def on_script_editor2_closed(self):
message_warning("Script editor was hided. Not closed !!")
self.statusCurrent_scriptEditor2 = 'closed'
# # QTextEdit の内容を保存
# self.script_editor_content = self.script_editor2.text_edit.toPlainText()
# print(self.script_editor_content)
return self.statusCurrent_scriptEditor2
def pushButton_exe(self, *args):
check_string = 'check'
message(check_string)
message(self.pushButtonMessage)
print(self.statusCurrent_scriptEditor2)
if self.statusCurrent_scriptEditor2 == 'closed':
self.create_script_editor2_and_show()
print(self.statusCurrent_scriptEditor2)
self.script_editor2.append_default(self.pushButtonMessage)
if __name__ == '__main__':
print(u'{}.py: loaded as script file'.format(__name__))
sampleAcmdsUI = SampleA_cmdsUI() # ui
sampleAcmdsUI.createUI()
else:
print(u'{}.py: loaded as module file'.format(__name__))
print('{}'.format(__file__)) # 実行したモジュールフルパスを表示する
print(u'モジュール名:{}\n'.format(__name__)) # 実行したモジュール名を表示する
# -*- coding: utf-8 -*-
u"""
sampleB_pymelUI.py
:Author:
oki yoshihiro
okiyoshihiro.job@gmail.com
:Version: -1.0-
:Date: 2024/05/09
.. note:: 当コード記述時の環境
- Maya2022 python3系
- Python version: 3.7.7
- PyMel version: 1.2.0
概要(overview):
CustomScriptEditor2.py シングルトン ScriptEditor2
用の
テスト sampleB pymelUI
です
pymel UI を使用したモジュールを想定しています
注意(note):
・ 他に必須な独自モジュール
::
# ローカルで作成したモジュール ######################################################
from YO_utilityTools.lib.message import message
from YO_utilityTools.TemplateForPySide2.singleton import custom_script_editor2_instance
-リマインダ-
done: 2024/05/10
新規作成
version = '-1.0-'
"""
# 標準ライブラリ #################################################################
from functools import partial # partial 利用時は、最後の引数に、*args 要時あり
import pkgutil
from importlib import import_module, reload
# サードパーティライブラリ #########################################################
from maya import cmds
from pymel import core as pm
from PySide2.QtCore import Slot
# ローカルで作成したモジュール ######################################################
# # パッケージ内のモジュールの更新を都度反映するように記述 ####################### start
# import YO_utilityTools.lib # lib パッケージに対して
# for key in YO_utilityTools.lib.__dict__:
# if not key.startswith('__'):
# reload(YO_utilityTools.lib.__dict__[key])
# # パッケージ内のモジュールの更新を都度反映するように記述 ####################### end
#
# # パッケージ内のモジュールの更新を都度反映するように記述 ####################### start
# import YO_utilityTools # YO_utilityTools パッケージに対して
# for key in YO_utilityTools.__dict__:
# if not key.startswith('__'):
# reload(YO_utilityTools.__dict__[key])
# # パッケージ内のモジュールの更新を都度反映するように記述 ####################### end
#
# # パッケージ内の子階層に、sabパッケージフォルダ が存在しているので、特殊なケース ################# start
# # note): sabパッケージフォルダ のモジュールも同時に読み込むので、多少動作が遅くなります
# import YO_utilityTools.TemplateForPySide2 as package
# assert(hasattr(package, "__path__"))
# for _, module_name, _ in pkgutil.walk_packages(path = package.__path__,
# prefix = package.__name__ + '.',
# onerror = lambda x: None
# ):
# if not module_name.endswith('__init__'):
# try:
# module = import_module(module_name)
# reload(module)
# # print(f'Reloaded module: {module_name}')
# except ImportError as e:
# print(f'Error reloading module {module_name}: {e}')
# # パッケージ内の子階層に、sabパッケージフォルダ が存在しているので、特殊なケース ################# end
#
from YO_utilityTools.lib.message import message
from YO_utilityTools.lib.message_warning import message_warning
from YO_utilityTools.TemplateForPySide2.singleton import custom_script_editor2_instance
class SampleB_pymelUI(object):
def __init__(self):
"""Initialize data attributes. Constructor for class."""
self.title = 'SampleB_pymel'
self.win = self.title + '_ui'
self.pushButtonMessage = 'hello, this sampleB_pymelUI pushButton.'
# note): custom_script_editor2_instance 本モジュール基で、
# CustomScriptEditor2(title, infoDetail)
# 引数: title, 引数: infoDetail
# を定義しています
self.script_editor2 = custom_script_editor2_instance
self.script_editor2.closed.connect(self.on_script_editor2_closed)
# カスタムで、専用の スクリプトエディタ を初期化
# self.script_editor2 = None
self.statusCurrent_scriptEditor2 = 'open'
def createUI(self):
# UI-0. 重複しないウインドウ
try:
pm.deleteUI(self.win, window = True)
except:
pass
with pm.window(self.win, title = self.title
, widthHeight = (145, 60)
, menuBar = True, sizeable = True
, minimizeButton = False
, maximizeButton = False
):
with pm.columnLayout(adj = True
):
pm.button(label = 'testB', c = partial(self.pushButton_exe))
cmds.evalDeferred(lambda *args: pm.showWindow(self.win)) # ウィンドウを表示
print('--------- ' + f'{self.__class__}' + ' ---------')
print('outPut 専用 script_editor2 ウィジェット も作成します')
self.create_script_editor2_and_show()
print(self.script_editor2)
print('--------- ' + f'{self.__class__}' + ' ---------' * 3 + 'end\n')
def create_script_editor2_and_show(self):
# CustomScriptEditor2 モジュール先では、あえて、show() せず、ここで show() しています。
self.script_editor2.show() # note): これは必須です。
self.statusCurrent_scriptEditor2 = 'open'
@Slot()
def on_script_editor2_closed(self):
message_warning("Script editor was hided. Not closed !!")
self.statusCurrent_scriptEditor2 = 'closed'
# # QTextEdit の内容を保存
# self.script_editor_content = self.script_editor2.text_edit.toPlainText()
# print(self.script_editor_content)
return self.statusCurrent_scriptEditor2
def pushButton_exe(self, *args):
check_string = 'check'
message(check_string)
message(self.pushButtonMessage)
print(self.statusCurrent_scriptEditor2)
if self.statusCurrent_scriptEditor2 == 'closed':
self.create_script_editor2_and_show()
print(self.statusCurrent_scriptEditor2)
self.script_editor2.append_default(self.pushButtonMessage)
if __name__ == '__main__':
print(u'{}.py: loaded as script file'.format(__name__))
sampleBpymelUI = SampleB_pymelUI() # ui
sampleBpymelUI.createUI()
else:
print(u'{}.py: loaded as module file'.format(__name__))
print('{}'.format(__file__)) # 実行したモジュールフルパスを表示する
print(u'モジュール名:{}\n'.format(__name__)) # 実行したモジュール名を表示する
# -*- coding: utf-8 -*-
u"""
singleton.py
:Author:
oki yoshihiro
okiyoshihiro.job@gmail.com
:Version: -1.0-
:Date: 2024/05/10
.. note:: 当コード記述時の環境
- Maya2022 python3系
- Python version: 3.7.7
- PySide2 version: 5.15.2
概要(overview):
シングルトンパターンを実装するためのモジュールです
詳細(details):
使用したいそれぞれのファイルで、
CustomScriptEditor2 の新しいインスタンスを作成する代わりに、
これらのインスタンスを一度だけ、ここで作成し、
これを、他のすべてのファイルの場所で再利用することをお勧めします。
これは一つの方法であり、
当ファイルのように、別の Pythonファイル(例えば singleton.py )を作成し、
その中で、CustomScriptEditor2 のインスタンスを作成することです。
これにより、CustomScriptEditor2 のインスタンスは一度だけ作成され、
例えば、
SampleA_cmdsUI.py と SampleB_pymelUI.py の両方で共有されるようになります。
これがシングルトンパターンの一般的な実装方法です。
使用法(usage):
::
# -*- coding: utf-8 -*-
from YO_utilityTools.singleton import custom_script_editor2_instance
self.script_editor2 = custom_script_editor2_instance
# CustomScriptEditor2 モジュール先では、あえて、show() せず、使用先で show() します。
self.script_editor2.show() # note): これは必須です。
注意(note):
・ 他に必須な独自モジュール
::
# ローカルで作成したモジュール ######################################################
# Class CustomScriptEditor2 のインスタンスを作成し、UI要素を作成
from YO_utilityTools.TemplateForPySide2.CustomScriptEditor2 import CustomScriptEditor2
-リマインダ-
done: 2024/05/10
新規作成
version = '-1.0-'
"""
# ローカルで作成したモジュール ######################################################
# Class CustomScriptEditor2 のインスタンスを作成し、UI要素を作成
from YO_utilityTools.TemplateForPySide2.CustomScriptEditor2 import CustomScriptEditor2
_title = 'resultOutputUI_forHeadsUpSpecialization2'
_infoDetail = ('<注意喚起特化用_結果_出力_UI>\n\n'
'各コード実行中に頻繁に出力される結果表示において、\n'
'注意喚起の結果出力だけにフォーカスした、\n'
'注意喚起特化用UI\n'
'です。\n\n'
'note): \n'
'右ボタン押下でエディタ用途として\n簡単な編集も可能です。')
custom_script_editor2_instance = CustomScriptEditor2(title = _title, infoDetail = _infoDetail)
if __name__ == '__main__':
print(u'{}.py: loaded as script file'.format(__name__))
else:
print(u'{}.py: loaded as module file'.format(__name__))
print('{}'.format(__file__)) # 実行したモジュールフルパスを表示する
# pprint.pprint(RT4_UI_PyMel.mro()) # メソッドを呼び出す順番が解ります
print(u'モジュール名:{}\n'.format(__name__)) # 実行したモジュール名を表示する
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment