IC Python API:Stopwatch
- Main article: RL Python Samples.
This article will demonstrate the usage of the timer class and handling the timer callback events by creating a stopwatch inside iClone.
Required Modules
Besides the fundamental Reallusion Python module, we'll also need Pyside2 to build the user interface, os to read a qt UI file, and datetime to handle the time format.
import RLPy
import os
import datetime
from PySide2 import *
from PySide2.shiboken2 import wrapInstance
Timer Callback Class
In order to create a timer callback event, we have to inherit from the RLPy.RPyTimerCallback class and modify it -specifically the Timeout function that triggers on each interval.
class TimerCallback(RLPy.RPyTimerCallback):
def __init__(self):
RLPy.RPyTimerCallback.__init__(self)
def Timeout(self):
global stop_watch
if stop_watch["milliseconds"] > 5949999: # Restart the timer at 99:99:99 mark
stop_watch["milliseconds"] = 0
stop_watch["milliseconds"] += 10
time = datetime.datetime.min + datetime.timedelta(milliseconds=stop_watch["milliseconds"])
widget.lcdNumber.display(time.strftime('%M:%S:%f')[:-4])
widget.dial.setValue(stop_watch["milliseconds"])
Dialog Event Callback
A timer is a very powerful feature in iClone scripting but is easily prone to abuse due to its tendency to proliferate the session with background calculations that are invisible to the user once initiated. Therefore, it is highly recommended to also handle the events clean up process with a interface dependent callback, specifically in the form of a dialog event callback. By doing so, we tie the timer events that were fired off with the presence of a user interface, and once that interface ceases to exist (for instance, when closed) then the timer can be recalled and all related global variables can be sanitized.
class DialogEventCallback(RLPy.RDialogCallback):
def __init__(self):
RLPy.RDialogCallback.__init__(self)
def OnDialogHide(self):
global stop_watch
stop_watch["timer"].UnregisterPyTimerCallback()
del stop_watch
Timer Event Globals
All events in iClone must be declared as global variables. This means that event variables lie outside the scope of class and functions and must be so in order for iClone to properly handle callback requests. There is no one size fits all for this scenario, some people prefer to create separate global variables, but for this simple exercise, we'll just create a dictionary that'll house all of the global variables needed for this script and populate it.
# Global variables
stop_watch = {}
stop_watch["activated"] = False
stop_watch["milliseconds"] = 0
stop_watch["timer"] = RLPy.RPyTimer()
stop_watch["timer"].SetInterval(10)
stop_watch["callback"] = TimerCallback()
stop_watch["timer"].RegisterPyTimerCallback(stop_watch["callback"])
Stopwatch Start/Stop Button
The stopwatch start/stop button is simply a toggle function that inverts the state of the watch from running to stopped and vice versa.
def toggle():
global stop_watch
stop_watch["activated"] = not stop_watch["activated"]
if stop_watch["activated"]:
widget.toggle.setText("Stop")
stop_watch["timer"].Start()
else:
widget.toggle.setText("Start")
stop_watch["timer"].Stop()
Stopwatch Reset Button
As a fancy feature, we'll add the function for stopping the watch and resetting the counter back to 00:00:00.
def reset():
global stop_watch
stop_watch["milliseconds"] = 0
widget.dial.setValue(0)
widget.lcdNumber.display("00:00:00")
stop_watch["activated"] = True
toggle()
Creating the Interface
Let's load the configured QT UI file.
# Create the dialog window in iClone
window = RLPy.RUi.CreateRDialog()
window.SetWindowTitle("Stop Watch")
# Empower the window with Python QT functionalities
dialog = wrapInstance(int(window.GetWindow()), QtWidgets.QDialog)
dialog.setFixedHeight(230)
dialog.setFixedWidth(202)
# Read the QT UI file from location and deploy as widget
ui = QtCore.QFile(os.path.dirname(__file__) + "/Stop_Watch.ui")
ui.open(QtCore.QFile.ReadOnly)
widget = QtUiTools.QUiLoader().load(ui)
ui.close()
dialog.layout().addWidget(widget)
# Configure and connect functions to the stopwatch widget
widget.lcdNumber.display("00:00:00")
widget.toggle.clicked.connect(toggle)
widget.reset.clicked.connect(reset)
# Dialog event callback
dialogCallback = DialogEventCallback()
window.RegisterEventCallback(dialogCallback)
window.Show()
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
import os
import datetime
from PySide2 import *
from PySide2.shiboken2 import wrapInstance
class TimerCallback(RLPy.RPyTimerCallback):
def __init__(self):
RLPy.RPyTimerCallback.__init__(self)
def Timeout(self):
global stop_watch
if stop_watch["milliseconds"] > 5949999: # Restart the timer at 99:99:99 mark
stop_watch["milliseconds"] = 0
stop_watch["milliseconds"] += 10
time = datetime.datetime.min + datetime.timedelta(milliseconds=stop_watch["milliseconds"])
widget.lcdNumber.display(time.strftime('%M:%S:%f')[:-4])
widget.dial.setValue(stop_watch["milliseconds"])
class DialogEventCallback(RLPy.RDialogCallback):
def __init__(self):
RLPy.RDialogCallback.__init__(self)
def OnDialogHide(self):
global stop_watch
stop_watch["timer"].UnregisterPyTimerCallback()
del stop_watch
# Global variables
stop_watch = {}
stop_watch["activated"] = False
stop_watch["milliseconds"] = 0
stop_watch["timer"] = RLPy.RPyTimer()
stop_watch["timer"].SetInterval(10)
stop_watch["callback"] = TimerCallback()
stop_watch["timer"].RegisterPyTimerCallback(stop_watch["callback"])
def toggle():
global stop_watch
stop_watch["activated"] = not stop_watch["activated"]
if stop_watch["activated"]:
widget.toggle.setText("Stop")
stop_watch["timer"].Start()
else:
widget.toggle.setText("Start")
stop_watch["timer"].Stop()
def reset():
global stop_watch
stop_watch["milliseconds"] = 0
widget.dial.setValue(0)
widget.lcdNumber.display("00:00:00")
stop_watch["activated"] = True
toggle()
# Create the dialog window in iClone
window = RLPy.RUi.CreateRDialog()
window.SetWindowTitle("Stop Watch")
# Empower the window with Python QT functionalities
dialog = wrapInstance(int(window.GetWindow()), QtWidgets.QDialog)
dialog.setFixedHeight(230)
dialog.setFixedWidth(202)
# Read the QT UI file from location and deploy as widget
ui = QtCore.QFile(os.path.dirname(__file__) + "/Stop_Watch.ui")
ui.open(QtCore.QFile.ReadOnly)
widget = QtUiTools.QUiLoader().load(ui)
ui.close()
dialog.layout().addWidget(widget)
# Configure and connect functions to the stopwatch widget
widget.lcdNumber.display("00:00:00")
widget.toggle.clicked.connect(toggle)
widget.reset.clicked.connect(reset)
# Dialog event callback
dialogCallback = DialogEventCallback()
window.RegisterEventCallback(dialogCallback)
window.Show()
APIs Used
You can research the following references for the APIs deployed in this code.