CustomView
From MODO 801, there's a new type of server that can be created called an ILxCustomView, which allows 3rd party developers the ability to create custom UI and have it embed as a viewport into MODO. As with the rest of the SDK, this is also available through python using PySide.
NB: this is only available on Linux in MODO801 but on Linux, OSX and Windows from MODO901
Contents
Python Examples
Below are a few simple Python examples, but you can create plugins using C/C++ in the same manner.
Example: My Render Button
The first example is a simple button with the text “Render” and when pressed will, you guessed it, fire off a render command
import lx import lxifc import PySide from PySide.QtGui import * def onClicked(): lx.eval("render") # MyRenderButton Server class MyRenderButton(lxifc.CustomView): def customview_Init(self, pane): if pane == None: return False custPane = lx.object.CustomPane(pane) if custPane.test() == False: return False # get the parent object parent = custPane.GetParent() # convert to PySide QWidget p = lx.getQWidget(parent) # Check that it suceeds if p != None: layout = PySide.QtGui.QVBoxLayout() renderButton = QPushButton("RENDER!") f = renderButton.font() f.setPointSize(30) renderButton.setFont(f) renderButton.clicked.connect(onClicked) layout.addWidget(renderButton) layout.setContentsMargins(2,2,2,2) p.setLayout(layout) return True return False lx.bless(MyRenderButton, "My Render Button")
This looks scary but is actually pretty straight forward. Firstly we create a class that implements a single function: customview_Init(). This has one argument, which is always a CustomPane. We can then extract the parent QWidget by calling GetParent on this object and using the helper “lx.getQWidget()” function to convert it into a PySide widget. Once we have this, we can simply add our button using PySide. Simple!
The last line is to register the server with modo. Once this is done, create a “Custom View” viewport and select the server from the list shown in the settings popover:
Example: Embedded Web browser
Here’s another example of creating a web browser that shows The Foundry community webpage:
import lx import lxifc import PySide from PySide.QtWebKit import * class FoundryCommunityServer(lxifc.CustomView): def customview_Init(self, pane): if pane == None: return False custPane = lx.object.CustomPane(pane) if custPane.test() == False: return False # get the parent object parent = custPane.GetParent() # convert to PySide QWidget p = lx.getQWidget(parent) # Check that it suceeds if p != None: layout = PySide.QtGui.QVBoxLayout() web = QWebView() web.load("http://community.thefoundry.co.uk/discussion/") layout.addWidget(web) layout.setContentsMargins(2,2,2,2) p.setLayout(layout) return True return False if( not lx.service.Platform().IsHeadless() ): lx.bless(FoundryCommunityServer, "The Foundry Community")
Example: Python Preview Window
This is a slightly more advanced view, but allows you to create your own Preview window and do pixel processing before showing in UI. Probably better suited to a C++ plugin
import lx import lxifc import PySide from PySide.QtGui import * import PySide.QtCore class MyPreview(QWidget): def onTimerCallback(self): previewImage = self.preview.GetBuffer() width, height = previewImage.Size() buf = lx.object.storage('b', width * height * 3) imgsvc = lx.service.Image() imgsvc.ImageGetBuffer(previewImage, lx.symbol.iIMP_RGB24, buf) data = buf.get() image = QImage(width, height, QImage.Format_RGB32) for x in range(0,width): for y in range(0,height): r = data[y*width*3 + x * 3] g = data[y*width*3 + x * 3 + 1] b = data[y*width*3 + x * 3 + 2] image.setPixel(x,y, qRgb(r,g,b)) self.label.setPixmap(QPixmap.fromImage(image)) def __del__(self): self.timer.stop() self.preview.Stop() def resizeEvent(self, event): # resize the preview and label to show the image previewSize = self.geometry() self.label.resize(previewSize.width(), previewSize.height()) self.preview.SetRes(previewSize.width(), previewSize.height()) def __init__(self, parent=None): super(MyPreview, self).__init__(parent) self.timer = PySide.QtCore.QTimer(self) self.label = QLabel(self) self.preview = lx.service.Preview().CreatePreview() self.preview.SetUseAllThreads(False) self.done = False self.resize(400,300) self.preview.Start() self.timer.timeout.connect(self.onTimerCallback) self.timer.start(1000) def onClicked(): lx.eval("render") # MyPreviewCustView Server class MyPreviewCustView(lxifc.CustomView): def customview_Init(self, pane): if pane == None: return False custPane = lx.object.CustomPane(pane) if custPane.test() == False: return False # get the parent object parent = custPane.GetParent() # convert to PySide QWidget p = lx.getQWidget(parent) # Check that it suceeds if p != None: layout = PySide.QtGui.QVBoxLayout() myPreview = MyPreview() layout.addWidget(myPreview) layout.setContentsMargins(2,2,2,2) p.setLayout(layout) return True return False lx.bless(MyPreviewCustView, "My Preview Window")
Example: Modo Graph Viewer
This is a very simple TreeView showing the current state of all the modo graphs in the open scene.
import PySide from PySide.QtGui import * import lxu.select def addChannels(graphItem, item): channelCount = item.ChannelCount() if( channelCount != 0): channels = QTreeWidgetItem(None, [ "CHANNELS" ]) graphItem.addChild( channels ) for i in range( channelCount ): channelType = item.ChannelType(i) childItem = QTreeWidgetItem(None, [ item.ChannelName(i), str(channelType) ]) channels.addChild( childItem ) def addSubItems(graphItem, item): for i in range( item.SubCount() ): child = item.SubByIndex(i) childItem = QTreeWidgetItem(None, [child.Ident()]) graphItem.addChild( childItem ) addSubItems( childItem, child ) addChannels( childItem, child ) def addGraphItems(graphItem, graph): for i in range( graph.RootCount() ): child = graph.RootByIndex(i) childItem = QTreeWidgetItem(None, [child.Ident()]) graphItem.addChild( childItem ) addSubItems( childItem, child ) addChannels( childItem, child ) def makeView(): scene = lxu.select.SceneSelection().current() w = QMainWindow() graphView= QTreeWidget(w) graphView.setColumnCount(2) for i in range( scene.GraphCount() ): graph = scene.GraphByIndex(i) graphItem = QTreeWidgetItem(None, [graph.Name()]) graphView.addTopLevelItem( graphItem ) addGraphItems( graphItem, graph ) w.setCentralWidget(graphView) w.show() return w w = makeView()
Example: C++ OpenGL Viewport
This is a more advanced example written in C++. This creates a simple opengl viewport and renders a rotating cube inside.
The code is pretty self explanatory, to compile you'll need to use the LXSDK and Qt 4.8.5.
Here is the code: MyOpenGLView.zip
Python Script Editor
We ship a python script editor that is written entirely in Python and using PySide. It’s based off the Nuke and Hiero script editor which has the following features:
- Syntax highlighting
- auto complete
- load/save scripts
- error highlighting
- auto-indentation
- line numbering
Default Stylesheet
We've also created a default Stylesheet so that widgets are, by default, styled as similar as possible to modo. This can be overwritten in your own UI, but ideally custom viewports should as much as possible use the default stylesheet. Currently this is shipped as a css file in “<modo>/resrc/stye/style.css”, but is subject to change
Buttons Groups
To make buttons group in the same manner as modo, you should tag a QPushButton with "left", "right" or "center" to ensure correct bevelling.
Here's a simple python example in how to do this:
import PySide from PySide.QtGui import * def onClicked(): print "Hello!" # Create button group buttonLayout = QHBoxLayout() buttonLayout.setSpacing(0) leftButton = QPushButton("Left") leftButton.setProperty("group", "left") leftButton.clicked.connect(onClicked) rightButton = QPushButton("Right") rightButton.setProperty("group", "right") rightButton.clicked.connect(onClicked) centerButton = QPushButton("Center") centerButton.setProperty("group", "center") centerButton.clicked.connect(onClicked) centerButton2 = QPushButton("Center") centerButton2.setProperty("group", "center") centerButton2.clicked.connect(onClicked) buttonLayout.addWidget(leftButton) buttonLayout.addWidget(centerButton) buttonLayout.addWidget(rightButton) w = QWidget() w.setLayout(buttonLayout) w.show()