Creating a new Item3D widget for use in EMScene3D
To create a new Item3D widget use must inherit from EMItem3D (you will need to import this class from emitem3d). EMItem3D is a new style class so you don't need to multiply inherit from object. To make a valid object you must re-implement several method and redefine two attributes
class(static) attribute: name, this gives a name for the new Item3D
class(static) attribute: nodetype, this classifies to Item3D. This name can be the same as other Itme3Ds. For example if I were making a torus Item3D, I would name this as "Shapenode" because that is what Cubes, Spheres, etc are named.
method: getEvalString(), this returns a string, which when evaled, creates an instance of this class. Generally you want this to create an instance of this class which is exactly the smae as the current instance because the purpose is for saving state.
method: getItemInspector(), this needs to return an inspector for this widget.
method: renderShape(), this implements the openGL code to actually render your object
For exmaple:
1 class EMNewItem(EMItem3D):
2 name = "myname"
3 nodetype = "mynodetype"
4 def __init__(self, parent=None, children=None, transform=None):
5 if not Transform: transform=transform()
6 EMItem3D.__init__(self,parent=parent,children=children,transform=transform)
7 self.item_inspector = None
8
9 def getEvalString(self):
10 return "EMNewItem()"
11
12 def getItemInspector(self):
13 if not self.item_inspector: self.iteminspector = EMNewItemInspector("New", self)
14 return self.item_inspector
15
16 def renderShape(self):
17 #Do open GL stuff
Enabling your new Item3D to be added to the 'add node' dialog
To allow your Item3D widget to be added to a scene graph using the 'add node' pushbutton(launches a dialog for adding nodes) in the SG inspector, you need to add two static functions to your Item3D class and add a few lines of code to the scene graph.
First add a static function to generate a dialog box to aid the user in adding an instance of this widget to the scene graph:
1 class EMNewItem(EMItem3D):
2 @staticmethod
3 def getNodeDialogWidget(attribdict):
4 mydialogwidget = QtGui.QWidget()
5 grid = QtGui.QGridLayout()
6 mylabel = QtGui.QLabel("My Label")
7 attribdict["myparameter1"] = QtGui.QLineEdit("param1")
8 grid.addWidget(mylabel,0 ,0)
9 grid.addWidget(attribdict["myparameter1"])
10 mydialogwidget.setLayout(grid)
11 return mydialogwidget
Then you need to add another static function to actually generate an instance of this widget. In this example the fist argument to EMNewItem constructor is for this example, but the last argument is mandatory.
Next you need to add a few lines of code to the NodeDialog class in emscene3d.py, in two functions:
1 class NodeDialog(QtGui.QDialog):
2 def __init__(self, inspector, item):
3 ...
4 ...
5 #populate node types
6 self.node_type_combo.addItem("MyItem3D")
7 self.myitem3d_dict = {}
8 self.node_stacked_widget.addWidget(EMNewItem3D.getNodeDialogWidget(self.myitem3d_dict)
9
10 def _on_add_node(self):
11 ...
12 ...
13 #my item3d, purpose it to get a instance of EMNewItem3D
14 if self.node_type_combo.currentText() == "MyItem3D":
15 insertionnode = EMNewItem3D.getNodeForDialog(self.myitem3d_dict)
16 #This line of code is the same for most items
17 node_name = str(self.myitem3d_dict["node_name"].text())
Adding an inspector for your EMNewItem3D widget
To create an inspector for your EMNewItem3D widget you need to inheit from EMInspectorControlShape if your widget is something that gets rendered, otherwise inherit from EMItem3DInspector. The difference is that EMInspectorControlShape adds color controls. All inspector widgets ARE A QtGUi.QTabwidget. It must be pointed out that this inspector is not a stand alone inspector. It is a part of the EMScene3D inspector and occupies the right hand side the EMscene inspector. When Item3D is selected its inspector is loaded(or made visible if it already exists) in the EMscene inspector. After inheriting from say, EMInspectorControlShape, several functions need to be re-implemented:
method: updateItemControls(), this is called every time the scenegraph is rendered AND this inspector is visible in the main scene graph inspector. Here you should put functionality that need to be updated every time the SG is re-rendered
method: updateMetaData(), this is usually called by the EMNewItem3D itself, and is used to update the inspector when the item changes but does not trigger a re-rendering. This may not occur depending on your implementation of EMNewItem3D
method: addTabs(), often you inspector can be quite complex so you'll need multiple tabs to conserve screen realestate. You can have muliple tabs for your inspector if you like.
method: addControls(), this implements the actual layout of the inspector using QT. You can actually call this function whatever you like provided you use the correct name in addTabs(), which calls this function. BTW: addTabs shoudl be the ONLY place that calls this function.
For Example:
1 class EMNewItemInspector(EMInspectorControlShape):
2 def __init__(self, name, item3d):
3 EMInspectorControlShape.__init__(self, name, item3d)
4
5 def updateItemControls(self):
6 super(EMNewItemInspector).updateItemControls() # Call down to base class
7 #Implementation specific stuff goes here
8
9 def updateMetaData(self):
10 super(EMNewItemInspector).updateMetaData() # Call down to base class
11 #Implementation specific stuff goes here
12
13 def addTabs(self):
14 super(EMNewItemInspector).addTabs() # Cal down to base class
15 #adding a tab to the inspector, remember the inspector IS A QtGui.QTabwidget
16 tabwidget = QtGui.QWidget()
17 gridbox = QtGui.QGridLayout()
18 self.addControls(gridbox)
19 tabwidget.setLayout(gridbox)
20 self.addTab(tabwidget)
21 #repeat the above lines of code to add more tabs
22
23 def addControls(self, gridbox):
24 #QT stuff to actual build the GUI goes here.....
BTW: Don't forget to make sure an instance of this widget is returned by EMNewItem3D's getItemInspector()