Difference between revisions of "IC Python API:Plugin Window"
Chuck (RL) (Talk | contribs) m (→Showing the Window) |
Chuck (RL) (Talk | contribs) m (→Showing the Window) |
||
Line 100: | Line 100: | ||
== Showing the Window == | == Showing the Window == | ||
+ | |||
+ | [[File:Ic_python_api_plugin_window_03.png|frame]] | ||
Here comes the crucial part of deciding which action to take depending on the state of the plugin window. | Here comes the crucial part of deciding which action to take depending on the state of the plugin window. |
Revision as of 00:49, 17 December 2019
- Main article: RL Python Samples.
If you are interested in creating plugins and tools for the iClone environment, then you'll eventually want to add the access to the scripts to iClone upon every start-up. This way you can execute your scripts from the Plugins menu instead of reading it from file each and every time.
Window Creation Workflow
The standard start-up boilerplate for a plugin is the following:
- iClone automatically calls the run_script command.
- The plugin should initialize the process of adding itself into the plugin menu.
- The plugin menu action should call to the function for showing the window.
- The show window function should decide which action to take depending on the situation:
- If the window instance does not exist, then create a new window.
- If the window is hidden then show it.
- If the window is visible then show a message notifying the user of its existence.
Necessary Modules and Global Variables
Besides the rudimentary Reallusion Python module, we'll also need Pyside2 to build the user interface. And we'll need a global variable to house a reference to our window elements.
import RLPy
from PySide2 import *
from PySide2.QtCore import Qt
from PySide2.shiboken2 import wrapInstance
ui = {}
Dialog Event Callback
Next, we'll need a dialog event callback for the dialog window that will be created. This custom class inherits from RLPy.RDialogCallback.
class DialogEventCallback(RLPy.RDialogCallback):
def __init__(self):
RLPy.RDialogCallback.__init__(self)
def OnDialogClose(self):
global ui
ui.clear() # Clear global variables
return True
Notice how OnDialogClose returns True in order to notify iClone that the window is to be disposed of.
Create the Window
Let's create the actual plugin window interface. We'll also add a hide button to make it convenient to call the hide command on this window.
def create_window():
global ui
ui["window"] = RLPy.RUi.CreateRDialog()
ui["window"].SetWindowTitle("Plugin Window")
# Create Pyside layout for RDialog
qt_dialog = wrapInstance(int(ui["window"].GetWindow()), QtWidgets.QDialog)
qt_dialog.setFixedWidth(400)
qt_dialog.setFixedHeight(280)
# Create a label for previous status
ui["pre-state"] = QtWidgets.QLabel("This window was just created.")
ui["pre-state"].setAlignment(Qt.AlignCenter)
ui["pre-state"].setStyleSheet("color:#ff7c7c; font-weight:bold")
qt_dialog.layout().addWidget(ui["pre-state"])
# Create a description label
label = QtWidgets.QLabel(
'''<p>This is a dialog window created by the {0}Plugin Window{1} Python script.</p>
<p>Window check works by registering the window to a global variable where it can be compared.
This only works if the script is executed from the {0}Plugins{1} menu
because global variable don't share the same scope when loaded via the {0}Script{1} menu.</p>
<p>If the variable does not exist, then a new window is created.
If the variable does exist and the window is hidden then the it will be shown again.
If the variable does exist and the window is visible,
then a message will be issued to notify the user of its existence.</p>'''.format(
'''<span style="color:#a2ec13; font-weight:bold">''', '''</span>'''))
label.setWordWrap(True)
label.setTextFormat(Qt.RichText)
qt_dialog.layout().addWidget(label)
qt_dialog.layout().addStretch()
hide_button = QtWidgets.QPushButton("Hide Window")
hide_button.clicked.connect(lambda: ui["window"].Hide())
hide_button.setFixedHeight(28)
qt_dialog.layout().addWidget(hide_button)
ui["window"].Show()
Showing the Window
Here comes the crucial part of deciding which action to take depending on the state of the plugin window.
def show_window():
global ui
if "window" in ui:
if ui["window"].IsVisible():
RLPy.RUi.ShowMessageBox(
"Plugin Window",
"The current Plugin Window session is still running. You must first close the window to start another session.",
RLPy.EMsgButton_Ok
)
ui["pre-state"].setText("This window is still visible.")
else:
ui["window"].Show()
ui["pre-state"].setText("This window was hidden.")
else:
create_window()
ui["dialog_events"] = DialogEventCallback()
ui["window"].RegisterEventCallback(ui["dialog_events"])
Initializing the Plugin
def initialize_plugin():
# Create Pyside interface with iClone main window
ic_dlg = wrapInstance(int(RLPy.RUi.GetMainWindow()), QtWidgets.QMainWindow)
plugin_menu = ic_dlg.menuBar().findChild(QtWidgets.QMenu, "potm_fundamentals") # Check if the menu item exists
if plugin_menu is None:
# Create Pyside layout for QMenu named "POTM Fundamentals" and attach it to the Plugins menu
plugin_menu = wrapInstance(int(RLPy.RUi.AddMenu("POTM Fundamentals", RLPy.EMenu_Plugins)), QtWidgets.QMenu)
plugin_menu.setObjectName("potm_fundamentals") # Setting an object name for the menu is equivalent to giving it an ID
# Check if the menu action already exists
menu_actions = plugin_menu.actions()
for i in range(len(menu_actions)):
if menu_actions[i].text() == "Plugin Window":
plugin_menu.removeAction(menu_actions[i]) # Remove duplicate actions
# Set up the menu action
menu_action = plugin_menu.addAction("Plugin Window")
menu_action.triggered.connect(show_window)
Running the Script
def run_script():
initialize_plugin()
RLPy.RUi.ShowMessageBox(
"Plugin Window",
"You can run the Plugin Window script from the menu bar under <b>Plugins > POTM Fundamentals</b>.",
RLPy.EMsgButton_Ok
)
Everything Put Together
You can copy and paste the following code into a PY file and load it into iClone via Script > Load Python.
import RLPy
from PySide2 import *
from PySide2.QtCore import Qt
from PySide2.shiboken2 import wrapInstance
ui = {}
class DialogEventCallback(RLPy.RDialogCallback):
def __init__(self):
RLPy.RDialogCallback.__init__(self)
def OnDialogClose(self):
global ui
ui.clear() # Clear global variables
return True
def create_window():
global ui
ui["window"] = RLPy.RUi.CreateRDialog()
ui["window"].SetWindowTitle("Plugin Window")
# Create Pyside layout for RDialog
qt_dialog = wrapInstance(int(ui["window"].GetWindow()), QtWidgets.QDialog)
qt_dialog.setFixedWidth(400)
qt_dialog.setFixedHeight(280)
# Create a label for previous status
ui["pre-state"] = QtWidgets.QLabel("This window was just created.")
ui["pre-state"].setAlignment(Qt.AlignCenter)
ui["pre-state"].setStyleSheet("color:#ff7c7c; font-weight:bold")
qt_dialog.layout().addWidget(ui["pre-state"])
# Create a description label
label = QtWidgets.QLabel(
'''<p>This is a dialog window created by the {0}Plugin Window{1} Python script.</p>
<p>Window check works by registering the window to a global variable where it can be compared.
This only works if the script is executed from the {0}Plugins{1} menu
because global variable don't share the same scope when loaded via the {0}Script{1} menu.</p>
<p>If the variable does not exist, then a new window is created.
If the variable does exist and the window is hidden then the it will be shown again.
If the variable does exist and the window is visible,
then a message will be issued to notify the user of its existence.</p>'''.format(
'''<span style="color:#a2ec13; font-weight:bold">''', '''</span>'''))
label.setWordWrap(True)
label.setTextFormat(Qt.RichText)
qt_dialog.layout().addWidget(label)
qt_dialog.layout().addStretch()
hide_button = QtWidgets.QPushButton("Hide Window")
hide_button.clicked.connect(lambda: ui["window"].Hide())
hide_button.setFixedHeight(28)
qt_dialog.layout().addWidget(hide_button)
ui["window"].Show()
def show_window():
global ui
if "window" in ui:
if ui["window"].IsVisible():
RLPy.RUi.ShowMessageBox(
"Plugin Window",
"The current Plugin Window session is still running. You must first close the window to start another session.",
RLPy.EMsgButton_Ok
)
ui["pre-state"].setText("This window is still visible.")
else:
ui["window"].Show()
ui["pre-state"].setText("This window was hidden.")
else:
create_window()
ui["dialog_events"] = DialogEventCallback()
ui["window"].RegisterEventCallback(ui["dialog_events"])
def initialize_plugin():
# Create Pyside interface with iClone main window
ic_dlg = wrapInstance(int(RLPy.RUi.GetMainWindow()), QtWidgets.QMainWindow)
plugin_menu = ic_dlg.menuBar().findChild(QtWidgets.QMenu, "potm_fundamentals") # Check if the menu item exists
if plugin_menu is None:
# Create Pyside layout for QMenu named "POTM Fundamentals" and attach it to the Plugins menu
plugin_menu = wrapInstance(int(RLPy.RUi.AddMenu("POTM Fundamentals", RLPy.EMenu_Plugins)), QtWidgets.QMenu)
plugin_menu.setObjectName("potm_fundamentals") # Setting an object name for the menu is equivalent to giving it an ID
# Check if the menu action already exists
menu_actions = plugin_menu.actions()
for i in range(len(menu_actions)):
if menu_actions[i].text() == "Plugin Window":
plugin_menu.removeAction(menu_actions[i]) # Remove duplicate actions
# Set up the menu action
menu_action = plugin_menu.addAction("Plugin Window")
menu_action.triggered.connect(show_window)
def run_script():
initialize_plugin()
RLPy.RUi.ShowMessageBox(
"Plugin Window",
"You can run the Plugin Window script from the menu bar under <b>Plugins > POTM Fundamentals</b>.",
RLPy.EMsgButton_Ok
)
APIs Used
You can research the following references for the APIs deployed in this code.