A number of features of EnSight are written as extensions in Python or command language. In EnSight, this system has been written to more fully integrate graphical user interface objects and command language extensions into EnSight. The extension framework itself is written in Python and resides in $CEI/ensight251/site_preferences/extensions. This directory contains two directories: core and user_defined. The core directory contains the Python implementation of all of the base classes described here. The user_defined directory contains the various EnSight extensions that are loaded on startup. At least two replacement Graphical User Interface extensions, ten Tools and a large number of Menus are included by Ansys in the distribution. The source code to these are an extremely valuable reference to the extension developer.
At startup, EnSight builds a default
sys.path
which includes
$CEI/ensight251/site_preferences/extensions
as well as EnSightDefaultDirectory/extensions (if it exists).
EnSightDefaultsDirectory is
%HOMEDRIVE%%HOMEPATH%\(username)\.ensight251
commonly located at
C:\Users\username\.ensight251 on
Vista and newer Windows Operating Systems,
C:\Documents and
Settings\yourusername\.ensight251 on older
Windows, and
~/.ensight251 on
Linux, and ~/Library/Application
Support/ensight251 on the
Mac. It will also add any directories specified by
the CEI_PYTHONPATH environmental variable. This variable may contain
multiple directory names, separated by a : under
UNIX operating systems and a ;
under Windows.
EnSight scans in sys.path
, looking for
entries that end in the name 'extension'. If any subdirectory
of an extension directory contains an __init__.py file, the
subdir is 'import
'ed into the module
ensight.ext (note:
ensight.ext.__path__ is modified to include the
extensions dirnames). If a user has common
Python utilities or a startup script, it can be added
as a module to the internal Python interpreter via this
mechanism.
EnSight supports the concept of a formal extension. An extension
to EnSight is a subclass of one of the base-classes of the extension framework.
Common extensions are for user-defined menus or tools and to
replace the EnSight graphical user interface itself. All extensions include a
mechanism to extend EnSight command language to include custom batch scripting and
action logging. EnSight looks for extensions by scanning all of the
.DEFINE, .py and
.enc files located in subdirectories rooted by a
subdirectory of a sys.path
extension directory (as described above) named
user_defined for specially formatted comment blocks. There
are two comment block forms for Legacy and Direct load extensions.
Legacy Extensions
Legacy extensions can include operations written in command language or Python. The comment block in each of these extensions has the general form:
#ENSIGHT_USER_DEFINED_BEGIN
#TEXT=name used for GUIs
#TOOLTIP=text displayed in tooltips
#ICON=name_of_an_iconfile.png
#NAME=simplename
#DESC=text description of the item
#TYPE=TOOL,MENU,TOOLDIR,MENUDIR
#MODE=All,Part,Annot,Plot,VPort,Frame
#PTYPE=All,None,Mixed,Model,ClipPlane,Contour,DiscreteParticle,Frame,IsoSurface,ParticleTrace,Profile,VectorArrow,ElevatedSurface,DevelopedSurface,ModelExtract,ModelCut,ModelBoundary,IsoVolume,BuiltUp,TensorGlyph,FxVortexCore,FxShock,FxSepAtt,MaterialInterface,Point,AxiSymmetric,ModelMerge,Mult
#ENSIGHT_USER_DEFINED_END
Most of these fields are optional. If they are included in a
.py or .enc file, they inform EnSight
that these are TOOL or MENU extensions (depending on the TYPE= value). EnSight will
build an extension object (a subclass of the tool_extension
or menu_extension
classes) for the file and if the
extension is executed, the contents of the file will be executed. When it is run,
the directory containing the file will be added to sys.path
temporarily.
MODE= and PTYPE= fields are used for filtering the display of the item depending on the current state of EnSight (what mode and what type of parts are selected).
A file named menu.define or
tool.define can contain just this comment block, but uses
the type TOOLDIR or MENUDIR. If this file exists, the
other commented files are included as GUI children of that object. The loader will
instantiate a class of the proper type: menu_extension
or
tool_extension
and registers it with EnSight. The
registration operation has two parts. First, the class is stored under the proper
key in the dictionary ensight.core.extensions so that object
can query and use the various extensions. Second, the register method is called on
the base class (core_extension
). This methods registers a
method on the object as a command language extension. Therefore, an object with
the name foo that is a child of an object with the name bar
will be able to generate command language of the form ext: bar.foo
string
. It is also able to handle the playback of such strings by
overriding the base class.
Direct Loaded (Python) Extensions
Direct load extensions can only be written in Python and have a number of advantages over Legacy extensions, including private namespaces in both Python and command language. Developers are encouraged to use this form of extension whenever possible as it avoid potential namespace collisions both in Python and EnSight. To use this mechanism, the developer writes their own subclass(es) of the appropriate extension base class(es). They then add a comment block to the top of the Python source code file that looks like this:
#ENSIGHT_USER_DEFINED_BEGIN
#FACTORY=function_name
#ENSIGHT_USER_DEFINED_END
In this case, the file will be imported and the function
function_name()
will be invoked with a single
parent parameter. The function should create all of the
instances of the extension class objects needed and return them as a
Python list.
Note: The function_name()
should also do any parent
adding for the objects it wants before returning them. The calling system
will register the objects in the returned list and insert them into the
ensight.core.extension
dictionary. When this
file is imported, the directory it is in will have been added to the
sys.path
variable.
The extension mechanism is based on Python subclasses. All extensions subclass from a code base class. This class provides two important features. First, each extension is in a private namespace in Python, allowing each one to have its own global variables. Second, each extension is given part of the command language namespace complete with methods to generate custom command language and replay it. The simplest extension just extends command language with a new command (note: in command language, all extensions appear as: ext: namespace string). The base classes are core, guibase, gui, tool and menu.
core_extension class
Basic extension of the command language (type = "core"). Override
the cmdExec()
and cmdRecord()
methods to generate your own command language extensions.
Data Members
_version
: version number
_path
: path to the file defining this extension
_name
: name for purposes of
Python/cmdlang namespaces (no spaces, odd chars,
etc)
_text
: Display name of the extension (spaces
allowed)
_desc
: ASCII description of the extension (spaces
allowed)
_namespace
: actual cmdlang
namespace (for example, ext: namespace foo
)
_children
: list of children of this extension
Member Functions
addChild(child)
: add a
child
setDesc(text)
setSort(text)
: set the menu sorting value for this
extension
cmdExec(string)
: called back when
cmdlang
sees ext: namespace
foo String will be foo
cmdRecord(string)
: call to record command language
ext: _namespace string
guibase_extension class (subclass of core_extension)
This class adds common GUI elements such as itcons and tooltips (type = "guibase"). In general, user defined extensions should not subclass this class directly.
Member Functions
setInMenus(v)
: Set the state of dynamic menu
visiblity
getInMenus()
: True if this extension should show up in any
dynamic menus
setTooltip(text)
: the text for the tooltip
setIcon("pathname")
: filename for the icon, can be a
resource reference (for example, ":/ensight/...")
getWidget(w,parent)
: return the widget representation of
the object. This method is overridden by subclasses
setWaitCursor(onoff)
: display/clear the hourglass cursor
for this extension
gui_extension class (subclass of guibase_extension)
A full user-defined GUI (type = "gui"). Subclass this class to completely replace the EnSight GUI with your own GUI.
Note: The cmdExec
method has been overridden to include
activate_gui
.
Member Functions
setSplash(pathname)
: pathname to an SVG file to use as the splash
screen
doActivate(onoff)
: called to enable/disable this graphical
user interface must be overridden, returns False on failure.
doErrorMessage(message,style)
: called when EnSight throws an error or
needs a Yes/No question, must be overridden.
doGuiEvent(message,targetlist)
: called when there is a graphical user
interface event, must be overridden.
Useful Module Functions
These functions can be used directly by applications that want to interact with the core graphical user interface and its events.
ensight.core.gui_extension.gui_activate(name)
gui=ensight.core.gui_extension.current_active_gui()
ensight.core.gui_extension.error_message(msg,what)
ensight.core.gui_extension.gui_event(msg,targets)
tool_extension class (subclass of guibase_extension)
Baseclass for an EnSight user-defined tool (type = "tool"). All tool extensions must subclass from this class.
Member functions
run()
: This function is called when the tool is invoked. The function
must be overridden.
menu_extension class (subclass of guibase_extension)
Baseclass for an EnSight popup menu (type = "menu"). All menu extensions must subclass from this class.
Data Members
_info
: the dictionary of values resulting from the
mousedown that triggered this menu
Member Functions
setSeparator(boolean)
: set to true if this menu is a separator
setMode(mode)
: a string containing the mode names for this menu to be
displayed in. Concatenated from:
Part
,Annot
,Plot
,VPort
,Frame
,All
setPartType(type)
: a string containing the part types for which this
menu is to be displayed. Concatenated from:
All
,Model
,ClipPlane
,Contour
,DiscreteParticle
,Frame
,IsoSurface
,ParticleTrace
,Profile
,VectorArrow
,ElevatedSurface
,DevelopedSurface
,ModelExtract
,ModelCut
,ModelBoundary
,IsoVolume
,BuiltUp
,TensorGlyph
,FxVortexCore
,FxShock
,FxSepAtt
,MaterialInterface
,Point
,AxiSymmetric
,
ModelMerge
,Mult
validFilter(self,emode,ptype)
: return 1 if this menu should be shown.
By default, it filters using the mode and parttype values as noted above, however
for general filtering, one ma override this method.
run()
: override this method do what this menu should do when
selected.
the _info
attribute is set to the
context
of the operation that started the menu operation. It is a
dictionary. Some fields are pre-defined, depending on the EnSight RMB
context.
The pre-defined _info
fields are:
Common to All Modes
pick
: the click target
pos
: the (x,y,z) tuple of the click in data
space
target
: list of ensight object references that are
the action target (list can be empty)
mousepos
: (x,y) position tuple in the graphics
window of the mouse down (normalized coords). -1,-1 if there is no position or one
is not in the window.
pick: CVF_CURSOR_TOOL
pick: CVF_LINE_TOOL
'item': The part of the line clicked: CVF_EVENT_LOCAT_MID, CVF_EVENT_LOCAT_END2, CVF_EVENT_LOCAT_END1, CVF_EVENT_LOCAT_XAXIS, CVF_EVENT_LOCAT_YAXIS, CVF_EVENT_LOCAT_ZAXIS
pick: CVF_PLANE_TOOL
'item': The part of the line clicked: CVF_EVENT_LOCAT_MID, CVF_EVENT_LOCAT_UPPER_RIGHT, CVF_EVENT_LOCAT_LOWER_LEFT, CVF_EVENT_LOCAT_LOWER_RIGHT, CVF_EVENT_LOCAT_UPPER_LEFT, CVF_EVENT_LOCAT_XAXIS, CVF_EVENT_LOCAT_YAXIS, CVF_EVENT_LOCAT_ZAXIS
pick: CVF_CYLINDER_TOOL
'item': The part of the line clicked: CVF_EVENT_LOCAT_MID, CVF_EVENT_LOCAT_END2, CVF_EVENT_LOCAT_END1, CVF_EVENT_LOCAT_XAXIS, CVF_EVENT_LOCAT_YAXIS, CVF_EVENT_LOCAT_ZAXIS, CVF_EVENT_LOCAT_RADIUS
pick: CVF_CONE_TOOL
'item': The part of the line clicked: CVF_EVENT_LOCAT_MID, CVF_EVENT_LOCAT_END2, CVF_EVENT_LOCAT_END1, CVF_EVENT_LOCAT_XAXIS, CVF_EVENT_LOCAT_YAXIS, CVF_EVENT_LOCAT_ZAXIS, CVF_EVENT_LOCAT_RADIUS
pick: CVF_SPHERE_TOOL
'item': The part of the line clicked: CVF_EVENT_LOCAT_MID, CVF_EVENT_LOCAT_END2, CVF_EVENT_LOCAT_END1, CVF_EVENT_LOCAT_XAXIS, CVF_EVENT_LOCAT_YAXIS, CVF_EVENT_LOCAT_ZAXIS
pick: CVF_REVO_TOOL
'item': The part of the line clicked: CVF_EVENT_LOCAT_MID, CVF_EVENT_LOCAT_END2, CVF_EVENT_LOCAT_END1, CVF_EVENT_LOCAT_XAXIS, CVF_EVENT_LOCAT_YAXIS, CVF_EVENT_LOCAT_ZAXIS
pick: CVF_POLYLINE
'item': The spline number clicked on
'point': The index into the spline knot points
pick: CVF_LEGEND
'item': The palette id
'component': The palette id component (for vectors)
pick: CVF_ANNOT
'item': The annotation id (the target object has the type implicitly)
pick: CVF_PART
'item': The part id
pick: CVF_VIEWPORT
'item': The viewport id
pick: CVF_PLOTTER
'curve': The curve number clicked on
'value': The (f,x) value at the clicked point
'_prepick_
': This key is optional. If set to the
name of a user-defined menu, the doPopup() function will not display any menus.
Instead, it will call the 'run()' method on the first menu with that name. This
mechanism makes it easy to execute a menu in a given spatial context.
Useful Module Functions
core.userdefinedmenus.doPopup(infodict)
: popup the current RMB menu at
the mouse. You must pass a proper info dictionary (see: _info
above)
With this function, the caller needs to create the _info
dictionary. One special key has been defined: 'mode'. If this key is present, it
will override the current EnSight mode value for validFilter()
filtering. An example using this function would be:
dict = {'pick': ensight.CVF_PART, 'item': partid, 'target':
partobj
,
'pos': (0,0,0), 'mousepos': (-1,-1) }
ensight.core.userdefinedmenus.doPopup(dict)
Internationalization and EnSight Extensions
The EnSight core includes an instance of the CEItranslate
object. It can be accessed as: ensight.core.tr
. When the
ensight.core
module loads and scans the directories for extensions,
it also scans for any *.qm files and automatically registers
them with ensight.core.tr
. Thus, if you have internationalized an
extension GUI (or any other Qt/PyQt GUI), simply place the translation files in the
extensions directory and they will be loaded. When the
QApplication instance is created,
ensight.core.tr.changelang()
is called. If the user has set
CEI_LANG
(or the host system has a language set), the
translation files for that language are automatically loaded.
CEI_LANG
should be set to the two character ISO 639 code for
the target language. See http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for more
information.
In Python code, you can trap and handle dynamic language changes by including the following method to your top level widget:
# i18n bits: handle dynamic language changes def changeEvent(self, event): if (event.type() == QtCore.QEvent.LanguageChange): self.retranslateUi(self) QtGui.QMainWindow.changeEvent(self, event)
Note: You may need to change QMainWindow
to the appropriate classname. The
'self.retranslateUi()
' call should be replaced with
whatever function regenerates the graphical user interface text contents on your
system (it may be more than one call to auto-generated
retranslateUi()
methods.