Difference between revisions of "IC Python API:Table View"

From Reallusion Wiki!
Jump to: navigation, search
m (Refresh Function)
m (Refresh Function)
Line 81: Line 81:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Notice that we call refresh command in the initialization phase.  Unlike normal python declaration order, methods and call to methods inside classes don't have to follow a specific order due to the use of '''self'''.  The class-specific '''refresh''' method can be called from outside of the class after its declaration; Which would look like the following:
+
Notice that we call refresh command in the initialization phase.  Unlike normal python declaration order, methods and call to methods inside classes don't have to follow a specific order due to the use of '''self'''.  The class-specific '''refresh''' method can also be called from outside of the class after its declaration; Which would look like the following:
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
Line 87: Line 87:
 
table_widget.refresh()
 
table_widget.refresh()
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
Whether you make the call from inside or outside the class initialization, you should only initialize the refresh method once.
  
 
== Dialog Window Boilerplate ==
 
== Dialog Window Boilerplate ==

Revision as of 01:42, 16 December 2019

Main article: RL Python Samples.
Ic python api lights table 01.png
The QT table widget is a useful UI widget suitable for listing distinct non-hierarchical entities that share common set of attributes. Imagine if you were to list the different brands of luxury hand-bags. You would need a column to list the names of each bag such as "Jackie", "Leather Serpenti Forever", "Monogram Canvas", etc. You would likely also have a column to list the brand name that the bags belong to such as "Gucci", "Bulgari", "Coach", "Louis Vuitton", etc. You might even add other columns for pricing, color, type, etc. Likewise, many digital assets can be categorized in this fashion as well.
If your assets are part of a hierarchical structure, then a QT tree widget would be more suitable.

This article will go over the creation of a table widget for listing all the standard lights in the scene; their name, active state, multiplier, and color attributes. To top it off, we will add a refresh button to the window to update the light list to the current state of the scene.

Necessary Modules

Besides the fundamental Reallusion Python module, we'll also need Pyside2 to build the user interface. But we don't need the entire Pyside2 module for this simple example, so I'll just import QtWidgets for building the user interface and wrapInstance to bind the iClone dialog window to the Pyside2 interface.

import RLPy
from PySide2 import QtWidgets
from PySide2.shiboken2 import wrapInstance

Light Table Widget

Ic python api lights table 02.png

In order to create a table widget we must first create the widget as a class that inherits from the QtWidgets.QtableWidget class. This will give us a shell of a table widget to work with, i.e. to populate.

class LightTableWidget(QtWidgets.QTableWidget):
    def __init__(self):
        super().__init__()
        labels = ["Name", "Active", "Multiplier", "Color (RGB)"]
        self.setColumnCount(len(labels))
        self.setHorizontalHeaderLabels(labels)
        # Adjust row resize policy
        header = self.horizontalHeader()
        header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch)

Notice how the resize policy is set by adjusting the header attribute.

Refresh Function

Ic python api lights table 03.png

In order to populate the scene lights table, we have to add a refresh method to search the scene for all of the lights and add them one by one to the table widget. This method belongs in the same class we used to inherit from the QtWidgets.QTableWidget because of this we can rely on the handy self keyword to refer to the instance of the class (after its creation).

    self.refresh()

    def refresh(self):
        # Clear the table widget
        self.setRowCount(0)
        # Grab all props in the scene
        all_lights = RLPy.RScene.FindObjects(RLPy.EObjectType_Light)
        # Add an entry into the combo-box for every prop found
        for i in range(len(all_lights)):
            row_count = self.rowCount()
            self.setRowCount(row_count + 1)
            # Create a object name item
            item_object = QtWidgets.QTableWidgetItem()
            item_object.setText(all_lights[i].GetName())
            self.setItem(row_count, 0, item_object)
            # Create a active toggle item
            active_object = QtWidgets.QTableWidgetItem()
            active_object.setText(str(all_lights[i].GetActive()))
            self.setItem(row_count, 1, active_object)
            # Create multiplier item
            multiplier_object = QtWidgets.QTableWidgetItem()
            multiplier_object.setText(str(round(all_lights[i].GetMultiplier(), 4)))
            self.setItem(row_count, 2, multiplier_object)
            # Create a color item
            color_object = QtWidgets.QTableWidgetItem()
            color = all_lights[i].GetColor()
            color_value = "{} - {} - {}".format(color.Red(), color.Green(), color.Blue())
            color_object.setText(color_value)
            self.setItem(row_count, 3, color_object)

Notice that we call refresh command in the initialization phase. Unlike normal python declaration order, methods and call to methods inside classes don't have to follow a specific order due to the use of self. The class-specific refresh method can also be called from outside of the class after its declaration; Which would look like the following:

table_widget = LightTableWidget()
table_widget.refresh()

Whether you make the call from inside or outside the class initialization, you should only initialize the refresh method once.

Dialog Window Boilerplate

Ic python api lights table 04.png

In order for us to show the scene lights table list we must first create a window, declare an instance of our custom table class, and attach the said table to the window layout.

# Create a dialog window
dialog = RLPy.RUi.CreateRDialog()
dialog.SetWindowTitle("Scene Lights")

# Create Pyside layout for RDialog
qt_dialog = wrapInstance(int(dialog.GetWindow()), QtWidgets.QDialog)
main_widget = QtWidgets.QWidget()
qt_dialog.setFixedWidth(350)

# Create a Light Table widget
table_widget = LightTableWidget()
qt_dialog.layout().addWidget(table_widget)

We can also add a handy refresh button that simply calls the method declared in the custom table class. Finally, we will need to show the window.

# Add a refresh button
refresh_button = QtWidgets.QPushButton()
refresh_button.setFixedHeight(24)
refresh_button.setText("Refresh List")
refresh_button.clicked.connect(lambda: table_widget.refresh())
qt_dialog.layout().addWidget(refresh_button)

dialog.Show()

Notice the use of lambda which is a handy way to declare an anonymous function.

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 QtWidgets
from PySide2.shiboken2 import wrapInstance


class LightTableWidget(QtWidgets.QTableWidget):
    def __init__(self):
        super().__init__()
        labels = ["Name", "Active", "Multiplier", "Color (RGB)"]
        self.setColumnCount(len(labels))
        self.setHorizontalHeaderLabels(labels)
        # Adjust row resize policy
        header = self.horizontalHeader()
        header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch)
        self.refresh()

    def refresh(self):
        # Clear the table widget
        self.setRowCount(0)
        # Grab all props in the scene
        all_lights = RLPy.RScene.FindObjects(RLPy.EObjectType_Light)
        # Add an entry into the combo-box for every prop found
        for i in range(len(all_lights)):
            row_count = self.rowCount()
            self.setRowCount(row_count + 1)
            # Create a object name item
            item_object = QtWidgets.QTableWidgetItem()
            item_object.setText(all_lights[i].GetName())
            self.setItem(row_count, 0, item_object)
            # Create a active toggle item
            active_object = QtWidgets.QTableWidgetItem()
            active_object.setText(str(all_lights[i].GetActive()))
            self.setItem(row_count, 1, active_object)
            # Create multiplier item
            multiplier_object = QtWidgets.QTableWidgetItem()
            multiplier_object.setText(str(round(all_lights[i].GetMultiplier(), 4)))
            self.setItem(row_count, 2, multiplier_object)
            # Create a color item
            color_object = QtWidgets.QTableWidgetItem()
            color = all_lights[i].GetColor()
            color_value = "{} - {} - {}".format(color.Red(), color.Green(), color.Blue())
            color_object.setText(color_value)
            self.setItem(row_count, 3, color_object)


# Create a dialog window
dialog = RLPy.RUi.CreateRDialog()
dialog.SetWindowTitle("Scene Lights")

# Create Pyside layout for RDialog
qt_dialog = wrapInstance(int(dialog.GetWindow()), QtWidgets.QDialog)
main_widget = QtWidgets.QWidget()
qt_dialog.setFixedWidth(350)

# Create a Light Table widget
table_widget = LightTableWidget()
qt_dialog.layout().addWidget(table_widget)

# Add a refresh button
refresh_button = QtWidgets.QPushButton()
refresh_button.setFixedHeight(24)
refresh_button.setText("Refresh List")
refresh_button.clicked.connect(lambda: table_widget.refresh())
qt_dialog.layout().addWidget(refresh_button)

dialog.Show()

APIs Used

You can research the following references for the APIs deployed in this code.