Advanced Usage

This section describes how to create custom user-defined menus as explained by EnSight Extension Mechanism in the Ansys EnSight Interface Manual. This section assumes familiarity with the Python language, as well as object-oriented programming. Both custom and built-in menus have the same capabilities because they are built on the same interface. Also, customized menus allow you to display options, including cascading menus. By default, your custom menus will appear below EnSight's default list of options that appear when you right-click. The user may also define a key (using the macro facility) to invoke the operation as noted above.

EnSight includes two well-documented, example Python files to demonstrate the process of creating custom, user-defined pop-up menus. As an exercise to show how to customize EnSight, these two files can be moved from their existing location to a directory where they will automatically load into EnSight at startup and change EnSight's menus.

How Menus Get Loaded

By default EnSight scans the Python files found in subdirectories of the site-specific $CEI/ensight242/site_preferences/extensions/user_defined directory and in the user specific extensions/user_defined subdirectory, under the EnSight Defaults Directory. The EnSight Defaults Directory varies per platform:

Linux: ~/.ensight242/extensions/user_defined/Menus

Mac: ~/Library/Application Support/EnSight242/extensions/user_defined/Menus

Windows 10: C:\Users\username\.ensight242\extensions\user_defined\Menus

Specific details of this process are covered in EnSight Extension Mechanism. When EnSight finds a Python file, it calls a factory function specified in the file. The factory function returns a list of objects that are registered with EnSight. The objects can be user-defined menus, tools and widgets, based on the extension class they subclass from. Whenever a pop-up menu is invoked by the user (normally though the right-mouse button), EnSight will filter the list of registered menu objects, display them as a pop-up menu and will invoke the run method when selected by you.

In the next two sections, we walk through two simple examples of writing custom EnSight user-defined menus.

"Hello World" Menu

Shown below is a simple example menu extension. It can serve as a template for more useful menus. This example Python file is included with your EnSight installation in the following folder $CEI/ensight242/src/user_defined_ext/. Copy the entire directory to the extensions/user_defined/examples subdirectory under your EnSight Defaults Directory (see above) and restart EnSight. The figure that follows shows the new menu item generated from this Python code, as well as the console output which is found in the Python window (FileCommand, and click on the Python tab in the resulting window). It also illustrates the directory structure for placing the files into the user specific EnSight start-up path.

Shown below is the commented Python code. Notice in particular the block of comment lines starting with #ENSIGHT_USER_DEFINED_BEGIN and ending with #ENSIGHT_USER_DEFINED_END. These specially formatted Python comments are what EnSight looks for at startup. They specify the name of the function to call when this file is loaded. That factory function is responsible for creating instances of the menu object and returning them to EnSight.

#
# This comment block is required to identify the file for loading at
# startup by EnSight.  The function named by FACTORY= (in this case
# 'ctor' is called by EnSight on startup.  Note: the file is loaded
# as a module in EnSight and the path to this file is added to sys.path.
#
#ENSIGHT_USER_DEFINED_BEGIN
#FACTORY=ctor
#ENSIGHT_USER_DEFINED_END
#
# Import the parent class for all user-defined menus and the ensight module
#
import ensight
from ensight.core.menu_extension import menu_extension
#
# Define a menu class, a subclass of the menu_extension class
#
class hello_menu(menu_extension):        
        #
        # Construct the menu with reasonable defaults
        #        
        def   init  (self,name,parent,text="",tooltip="",desc="",icon=None): 
                menu_extension.  init  (self,name,  file  ,1.0)
                if (parent): parent.addChild(self) 
                if (icon): self.setIcon(icon) 
                self.setText(text) 
                self.setDesc(text) 
                self.setTooltip(text)                
        #
        # Method that is called when the menu is selected
        #
        def run(self):
                #
                # Do whatever operation this menu should do, replace with your own code
                #
                print("Hello, these parts are selected:")
                for part in ensight.query_parts(client=1):
                        print part[3]
#
# Construct a list of menu objects to be added to the global list of
# user-defined menus
#
def ctor(parent):
        list = []
        #
        # Create the actual menu object, giving it a unique name an
        # onscreen name and a text description
        #
        p = hello_menu("hello_menu",parent,"Hello menu","Menu description")
        list.append(p)
        return list

return list

Context-Sensitive, Hierarchial Menus

Shown below is a more complex menu example. Again, it can be installed in the same manner as described above and the resulting menus are illustrated in the following image.

This example builds on the previous one. First, it demonstrates that a single Python file can generate as many menus as it wants. It also demonstrates the ability to define a hierarchy out of those menus. In this example, the menus themselves have expressed the desire to only be displayed in specific contexts, for example, when a specific type of part has been selected. Finally, several other menu options are set, the name of the vendor for the menu and an icon are specified. More options are documented in the Interface Manual.

The basic file structure is the same as before. There is a header block followed by the various class definitions followed by the factory function.

#
# This comment block is required to identify the file for loading at
# startup by EnSight. The function named by FACTORY= (in this case
# 'ctor' is called by EnSight on startup. Note: the file is loaded
# as a module in EnSight and the path to this file is added to sys.path.
#
#ENSIGHT_USER_DEFINED_BEGIN
#FACTORY=ctor
#ENSIGHT_USER_DEFINED_END
#
# Import the parent class for all user-defined menus and the ensight module
#
import ensight
from ensight.core.menu_extension import menu_extension
#
# Define a "separator" menu class used to put a dividing line between menus
#
class sep_menu(menu_extension):
        def __init__(self,name,parent):
                menu_extension.__init__(self,name,__file__,1.0)
                self.setSeparator(True)
                if (parent): parent.addChild(self)
#
# Define a menu class, a subclass of the menu_extension class
#
class part_menu(menu_extension):
        #
        # Construct the menu with reasonable defaults
        #
        def __init__(self,name,parent,text="",tooltip="",desc="",icon=None):        
                menu_extension.__init__(self,name,__file__,1.0)
                if (parent): parent.addChild(self)
                if (icon): self.setIcon(icon)
                self.setText(text)
                self.setDesc(text)
                self.setTooltip(text)
                self.setVendor("John Q. Public")
        #
        # Method that is called when the menu is selected
        #
        def run(self):
                #
                # We are constructing two instances of this class. They differ by
                # name. We use the name to perform different operations for each
                # of the instances. This could also be done with any other class
                # data, including user defined data members.
                #
                if (self._name == "allpart_menu"):
                        print("All parts hello!")
                else:
                        print("Model part specific hello!")
#
# Construct a hierarchy of menus. A parent "roll-over" menu and two
# children with a separator between them. These menus use context
# sensitive filtering. The parent is only displayed in EnSight "Part" mode.
# One child is always display and the other is only displayed when the
# currently selected part is a "Model" part. Note: this filtering could
# also be performed by overriding the "validFilter()" method on the menu
# object to perform any custom filtering operation.
#
 def ctor(parent):
        #
        # The factory method always returns a list of object to be added.
        # In this example, we only return menu_extension subclass instances,
        # but the list can include any combination of object subclassed from
        # the core_extension class.
        #
        list = []
        #
        # Create the parent roll-over menu
        #
        p = menu_extension("placeholder",None)
        #
        # Create an icon for the menu (this is optional). In this case, we
        # access one of the icons embedded in EnSight itself. See the Qt
        # resource management documentation for details.
        #
        p.setIcon(":/ensight/ens_icn_small")
        #
        # Set the EnSight mode filter to only display in "Part" mode.
        #
        p.setMode("Part")
        #
        p.setText("Simple part tools")
        p.setTooltip("Example part tools")
        if (parent): parent.addChild(p)
        list.append(p)
        #
        # Create the instance of the part_menu class that should be displayed
        # for any part that is selected.
        #
        m = part_menu("allpart_menu",p,"All part menu","Menu description")
        list.append(m)
        #
        # Add a horizontal separator line between the child menus.
        #
        m = sep_menu("sep1",p)
        list.append(m)
        #
        # Create an instance of the part_menu class that is only displayed
        # when a "Model" part is selected.
        #
        m = part_menu("modelpart_menu",p,"Model part menu","Menu description")
        #
        # Set the EnSight part type filter to "Model" parts.
        #
        m.setPartType("Model")
        list.append(m)
        return list

Other Examples

The example source code shown here is included in the $CEI/ensight242/src/user_defined_ext/examples directory, but many other examples exist. The source code to all of the built-in menus is included in the $CEI/ensight242/site_preferences/extensions/user_defined directory. Some other example objects are included in $CEI/ensight242/site_preferences/extensions/user_defined/Tools/QuickTools/Examples. While these are user-defined tools instead of menus, the extension mechanisms for both share a large number of common features and techniques that work in both. There are a pair of tools examples located in the user-defined tool Advanced Usage that demonstrate the ability of all user-defined extensions to generate and execute custom command language and make use of external icon files.

To get the most out of this mechanism, menu developers are encouraged to utilize Python scripts and convert command language scripts into Python using the provided tools. The Python interface is largely documented in the Interface manual Python EnSight module interface.