few_a_fx Posted April 11, 2011 Share Posted April 11, 2011 Create a render button for my fx tool. When you click it mplay pops up and it starts rendering, but nothing shows up. However if I go into the output tab and select the mantra node and click render on it, the scene renders out. Can anyone tell me what I'm doing wrong, or have any suggestions to fix this. Here is the code I have. def OnRender(self, event): renderIt = hou.node('/out/RENDER_IT') renderIt.render() Quote Link to comment Share on other sites More sharing options...
Macha Posted April 12, 2011 Share Posted April 12, 2011 (edited) Works for me. Edit: you need the self there? I'm sure you made a class and and object and everything, just to make sure! renderme.hip Edited April 12, 2011 by Macha Quote Link to comment Share on other sites More sharing options...
few_a_fx Posted April 12, 2011 Author Share Posted April 12, 2011 Your set up works, but for some reason it doesn't work for me. I create a tool in wxPython/Python, and I've added a render button to the UI, now I'm trying to get it to work. If I run the script in the python sheel it works just fine, works just like your example. Here's the set up for the tool: def _add_flex_grid(self): self._add_static_text("Render Effect") self._render_button= self._render_button("Render") def _render_button(self, label_text): button= wx.Button(self, label= label_text) button.Bind(wx.EVT_BUTTON, self.OnRender) self.flex_grid_sizer.Add(button, flag= wx.ALIGN_CENTER_VERTICAL) return button def OnRender(self, event): renderIt = hou.node('/out/RENDER_IT') renderIt.render() Hope this helps with understanding my proble,. I'll continue to work on it and let you know if I figure it out. Still if anyone has anymore suggestions or tips that would be awesome. Quote Link to comment Share on other sites More sharing options...
few_a_fx Posted April 12, 2011 Author Share Posted April 12, 2011 Would this fall under 3rd party rendering? Even though I'm using a mantra, I'm using python to tell mantra to render the scene. If so then that explains it. Using the apprentice version, no 3rd party aloud. Quote Link to comment Share on other sites More sharing options...
graham Posted April 12, 2011 Share Posted April 12, 2011 It wouldn't fall under 3rd party rendering since that only refers to using other render engines such as RenderMan or Mental Ray. As for what the problem is, it is probably not possible to diagnose without a scene file and the full scripting setup. Quote Link to comment Share on other sites More sharing options...
few_a_fx Posted April 12, 2011 Author Share Posted April 12, 2011 As for what the problem is, it is probably not possible to diagnose without a scene file and the full scripting setup. Here is the python script. ############################################################################### ########################### RAIN SYSTEM TOOL | RST ############################ ########################### Created by: Jason Fugh ############################ ############################################################################### # This tool was created to be used in Side Effects Houdini. # It was written in python & wxPython. # This fx tool allows the user to create and control a basic rain/particle system import sys, threading, wx if __name__ != "__main__": import hou import toolutils def exc_log(): print "Exception: '%s' on line %d" % (sys.exc_value, sys.exc_traceback.tb_lineno) class MyApp(wx.App): def OnInit(self): frame = MyFxTool(None, -1, 'Rain System Tool') frame.Show(True) self.SetTopWindow(frame) return True # Creates the Window class MyFxTool(wx.Frame): def __init__(self, parent, id, title): wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size (405, 505), style = wx.CAPTION | wx.SYSTEM_MENU | wx.CLOSE_BOX | wx.STAY_ON_TOP) self.sizer= wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) self.panel= MyFxToolPanel(self) self.sizer.Add(self.panel, 1, flag= wx.EXPAND) self.Layout() # Creates the Layout class MyFxToolPanel(wx.Panel): def __init__(self, parent): super(MyFxToolPanel, self).__init__(parent) self.sizer= wx.BoxSizer(wx.VERTICAL) self._add_flex_grid() self.SetSizer(self.sizer) def _add_flex_grid(self): self.flex_grid_sizer= wx.FlexGridSizer(cols= 2, vgap= 10, hgap= 70) self.sizer.Add(self.flex_grid_sizer, 1, border= 20, flag= wx.ALL|wx.EXPAND) self._add_static_text("Rain System") self.start_button= self._add_button("Make It Rain") self._add_static_text("Render Effect") self._render_button= self._render_button("Render") ########################################################################### ####################### PARAMETER CONTROLS ################################ ########################################################################### def _add_static_text(self, label_text): static_text= wx.StaticText(self, label= label_text, style= wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL) self.flex_grid_sizer.Add(static_text, flag= wx.ALIGN_CENTER_VERTICAL) def _add_button(self, label_text): button= wx.Button(self, label= label_text) button.Bind(wx.EVT_BUTTON, self.OnClickAdd) self.flex_grid_sizer.Add(button, flag= wx.ALIGN_CENTER_VERTICAL) return button def _render_button(self, label_text): button= wx.Button(self, label= label_text) button.Bind(wx.EVT_BUTTON, self.OnRender) self.flex_grid_sizer.Add(button, flag= wx.ALIGN_CENTER_VERTICAL) return button ########################################################################### ####################### PARAMETER EVENTS ################################## ########################################################################### #Allow you to create the acual rain system. #create both SOP and PO nodes. def OnClickAdd(self, event): geo = hou.node("/obj").createNode("geo", "Rain_System", run_init_scripts = False) grid = geo.createNode("grid", "EMIT_FROM") grid.setParms({"ty":10}) popnet = grid.createOutputNode("popnet", "RAIN_EMITTER") popnet.setDisplayFlag(1) popnet.setRenderFlag(1) source = popnet.createNode("source", "RAIN_ORIGIN") defaultGeo = source.parm("usecontextgeo") defaultGeo.set("first") birthPlace = source.parm("emittype") birthPlace.set("surfacerandom") gravity = source.createOutputNode("force", "RAIN_DirCtrl") defaultRainDir = gravity.parm("forcey") defaultRainDir.set(-1) rSpeed = gravity.createOutputNode("drag", "RAIN_Spd_Ctrl") defaultRainSpd = rSpeed.parm("scale") defaultRainSpd.set(2) render = rSpeed.createOutputNode("render", "RENDER_ME") renderAs = render.parm("prtype") renderAs.set("line") renderSize = render.parm("prsize") renderSize.set(.0025) render.setRenderFlag(1) collide = render.createOutputNode("collision", "COLLIDE_WITH") out = hou.node("/out") renderIt = out.createNode("ifd", "RENDER_IT") def OnRender(self, event): try: hou.node('/out/RENDER_IT').render() except: exc_log class AppThread(threading.Thread): def run(self): app = MyApp(0) app.MainLoop() if __name__ == "__main__": AppThread().start() Save it as a .py file and place it in the houdini python folder. Then create a new tool and add this script to it. import sys try: del sys.modules["fx_rst"] except: pass import fx_rst fx_rst.AppThread().start() Once thats done you can click on the tool and a window will pop up, and you can start using the tool. Quote Link to comment Share on other sites More sharing options...
graham Posted April 12, 2011 Share Posted April 12, 2011 Thanks for posting. For whatever reason that problem seems to be due to the threading. This is a bit weird because if works fine from the shell it should be fine in another thread. The shelf runs in the application thread, while the shell runs in a separate one. Upon first glance I couldn't see anything wrong and wasn't sure what to do about it since threading issues with Houdini, if it was actually the issue, are beyond what I can deal with. Since there wasn't much I could do I figured I'd tweak it a bit in to use Houdini's event loop callback since that doesn't involve threading. After making a few adjustments I was able to get it running and rendering without issue. To get it running and working good I basically got rid of all the threading stuff as well as the MyApp class. I copied/pasted the code from the cookbook example, added in a window.Show(True) call and then added the code to a function called rain(). From the shelf tool I just imported the module and called rain(). def runWxApp(app, window): def processWxEvents(): """Houdini runs this callback to from its event loop to process wx events.""" while event_loop.Pending(): event_loop.Dispatch() app.ProcessPendingEvents() def onClose(event): """We bind this callback to the window's close event to destroy the window and stop the wx event loop.""" window.Show(False) window.Destroy() processWxEvents() hou.ui.removeEventLoopCallback(processWxEvents) window.Bind(wx.EVT_CLOSE, onClose) window.Show(True) event_loop = wx.EventLoop() wx.EventLoop.SetActive(event_loop) hou.ui.addEventLoopCallback(processWxEvents) def rain(): app = wx.PySimpleApp() frame = MyFxTool(None, -1, 'Rain System Tool') runWxApp(app, frame) Quote Link to comment Share on other sites More sharing options...
few_a_fx Posted April 13, 2011 Author Share Posted April 13, 2011 Thanks graham. One thing though, I didn't know if you need the entire script or just the render section. I also have several other controls (wxSliders, checkboxes and combo boxes). Is there a way to bring this into my current file with threading, but not have the threading effect what you have posted? (hope that makes since). I guess I should have posted the entire code. ############################################################################### ########################### RAIN SYSTEM TOOL | RST ############################ ########################### Created by: Jason Fugh ############################ ############################################################################### # This tool was created to be used in Side Effects Houdini. # It was written in python & wxPython. # This fx tool allows the user to create and control a basic rain/particle system import sys, threading, wx if __name__ != "__main__": import hou import toolutils def exc_log(): print "Exception: '%s' on line %d" % (sys.exc_value, sys.exc_traceback.tb_lineno) class MyApp(wx.App): def OnInit(self): frame = MyFxTool(None, -1, 'Rain System Tool') frame.Show(True) self.SetTopWindow(frame) return True # Creates the Window class MyFxTool(wx.Frame): def __init__(self, parent, id, title): wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size (405, 505), style = wx.CAPTION | wx.SYSTEM_MENU | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.STAY_ON_TOP) self.sizer= wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) self.panel= MyFxToolPanel(self) self.sizer.Add(self.panel, 1, flag= wx.EXPAND) self.Layout() # Creates the Layout class MyFxToolPanel(wx.Panel): def __init__(self, parent): super(MyFxToolPanel, self).__init__(parent) self.sizer= wx.BoxSizer(wx.VERTICAL) self._add_flex_grid() self.SetSizer(self.sizer) def _add_flex_grid(self): self.flex_grid_sizer= wx.FlexGridSizer(cols= 2, vgap= 10, hgap= 70) self.sizer.Add(self.flex_grid_sizer, 1, border= 20, flag= wx.ALL|wx.EXPAND) self._add_static_text("Rain System") self.start_button= self._add_button("Make It Rain") self._add_static_text("Amount of Rain") self.amount_slider= self._addAmount_slider() self._add_static_text("Rain Speed") self.r_speed_slider= self._addSpeed_slider() self._add_static_text("Wind Speed") self.w_speed_slider= self._addScale_slider() self._add_static_text("Life") self.life_slider= self._addLife_slider() self._add_static_text("Collisions") self._add_colisions() self._add_static_text("Bounce") self.bounce_slider= self._addBounce_slider() self._add_static_text("Render Effect") self._render_button= self._render_button("Render") ########################################################################### ####################### PARAMETER CONTROLS ################################ ########################################################################### def _add_static_text(self, label_text): static_text= wx.StaticText(self, label= label_text, style= wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL) self.flex_grid_sizer.Add(static_text, flag= wx.ALIGN_CENTER_VERTICAL) def _add_button(self, label_text): button= wx.Button(self, label= label_text) button.Bind(wx.EVT_BUTTON, self.OnClickAdd) self.flex_grid_sizer.Add(button, flag= wx.ALIGN_CENTER_VERTICAL) return button def _addAmount_slider(self): slider_sizer= wx.BoxSizer(wx.HORIZONTAL) self.flex_grid_sizer.Add(slider_sizer, flag= wx.ALIGN_CENTER_VERTICAL) self.slider = wx.Slider(self, value= 100, minValue= 1, maxValue= 500, style= wx.SL_LABELS) self.slider.Bind(wx.EVT_SLIDER, self.sliderUpdate1) slider_sizer.Add(self.slider, flag= wx.ALIGN_CENTER_VERTICAL) return self.slider def _addSpeed_slider(self): slider_sizer= wx.BoxSizer(wx.HORIZONTAL) self.flex_grid_sizer.Add(slider_sizer, flag= wx.ALIGN_CENTER_VERTICAL) self.slider1= wx.Slider(self, value= 2, minValue= 0, maxValue= 5, style= wx.SL_LABELS) self.slider1.Bind(wx.EVT_SLIDER, self.sliderUpdate1) slider_sizer.Add(self.slider1, flag= wx.ALIGN_CENTER_VERTICAL) return self.slider1 def _addScale_slider(self): slider_sizer= wx.BoxSizer(wx.HORIZONTAL) self.flex_grid_sizer.Add(slider_sizer, flag= wx.ALIGN_CENTER_VERTICAL) self.slider2= wx.Slider(self, value= 1, minValue=0, maxValue= 10, style= wx.SL_LABELS) self.slider2.Bind(wx.EVT_SLIDER, self.sliderUpdate1) slider_sizer.Add(self.slider2, flag= wx.ALIGN_CENTER_VERTICAL) return self.slider2 def _addLife_slider(self): slider_sizer= wx.BoxSizer(wx.HORIZONTAL) self.flex_grid_sizer.Add(slider_sizer, flag= wx.ALIGN_CENTER_VERTICAL) self.slider3= wx.Slider(self, value= 100, minValue= 0, maxValue= 100, style= wx.SL_LABELS) self.slider3.Bind(wx.EVT_SLIDER, self.sliderUpdate1) slider_sizer.Add(self.slider3, flag= wx.ALIGN_CENTER_VERTICAL) return self.slider3 def _add_colisions(self): colisions_sizer= wx.BoxSizer(wx.HORIZONTAL) self.flex_grid_sizer.Add(colisions_sizer, flag= wx.ALIGN_CENTER_VERTICAL) self.collide_on= wx.CheckBox(self, label= "On/Off") colisions_sizer.Add(self.collide_on, 0, flag= wx.ALIGN_CENTER_VERTICAL) self.collide_on.Bind(wx.EVT_CHECKBOX, self.CheckOn) self.collide_button= wx.Button(self, label= "Collide With") colisions_sizer.Add(self.collide_button, 0, border= 10, flag= wx.LEFT|wx.ALIGN_CENTER_VERTICAL) self.collide_button.Bind(wx.EVT_BUTTON, self.AddCollision) options = ['Die', 'Bounce', 'Stop', 'Stick', 'Slide','Continue'] self.collide_op = wx.ComboBox(self, value= "", choices = options, id=5) self.collide_op.Bind(wx.EVT_COMBOBOX, self.SelectBehavior) colisions_sizer.Add(self.collide_op, 0, border= 10, flag= wx.LEFT|wx.ALIGN_CENTER_VERTICAL) def _addBounce_slider(self): slider_sizer= wx.BoxSizer(wx.HORIZONTAL) self.flex_grid_sizer.Add(slider_sizer, flag= wx.ALIGN_CENTER_VERTICAL,) self.slider4= wx.Slider(self, value= 10, minValue= 0, maxValue= 10, style= wx.SL_LABELS) self.slider4.Bind(wx.EVT_SLIDER, self.sliderUpdate1) slider_sizer.Add(self.slider4, flag= wx.ALIGN_CENTER_VERTICAL) return self.slider4 def _render_button(self, label_text): button= wx.Button(self, label= label_text) button.Bind(wx.EVT_BUTTON, self.OnRender) self.flex_grid_sizer.Add(button, flag= wx.ALIGN_CENTER_VERTICAL) return button ########################################################################### ####################### PARAMETER EVENTS ################################## ########################################################################### #Allow you to create the acual rain system. #create both SOP and PO nodes. def OnClickAdd(self, event): geo = hou.node("/obj").createNode("geo", "Rain_System", run_init_scripts = False) grid = geo.createNode("grid", "EMIT_FROM") grid.setParms({"ty":10}) popnet = grid.createOutputNode("popnet", "RAIN_EMITTER") popnet.setDisplayFlag(1) popnet.setRenderFlag(1) source = popnet.createNode("source", "RAIN_ORIGIN") defaultGeo = source.parm("usecontextgeo") defaultGeo.set("first") birthPlace = source.parm("emittype") birthPlace.set("surfacerandom") gravity = source.createOutputNode("force", "RAIN_DirCtrl") defaultRainDir = gravity.parm("forcey") defaultRainDir.set(-1) rSpeed = gravity.createOutputNode("drag", "RAIN_Spd_Ctrl") defaultRainSpd = rSpeed.parm("scale") defaultRainSpd.set(2) render = rSpeed.createOutputNode("render", "RENDER_ME") renderAs = render.parm("prtype") renderAs.set("line") renderSize = render.parm("prsize") renderSize.set(.0025) render.setRenderFlag(1) collide = render.createOutputNode("collision", "COLLIDE_WITH") out = hou.node("/out") renderIt = out.createNode("ifd", "RENDER_IT") # Next 3 functions are to create a collision. # Once checked render flag(display) switches from drag to collision node. def CheckOn(self, event): collide = hou.node('/obj/Rain_System/RAIN_EMITTER/COLLIDE_WITH') render = hou.node('/obj/Rain_System/RAIN_EMITTER/RENDER_ME') if self.collide_on.GetValue(): collide.setRenderFlag(1) else: render.setRenderFlag(1) def OnRender(self, event): try: hou.node('/out/RENDER_IT').render() except: exc_log Or is it possible to have it when you click the render button it runs a separate script without without threading? Quote Link to comment Share on other sites More sharing options...
graham Posted April 13, 2011 Share Posted April 13, 2011 GUIs and threading are tricky. Like I said, we thought it should have worked fine but apparently not. Back in the day (9.0 alpha) when Luke and I first experimented with using Python GUI toolkits in Houdini, putting the apps in another thread was the only way to go unless you wanted the interface to be locked up when the dialog was open. Once he added the ability to integrate things into Houdini's UI event loop using threading with GUIs became totally unnecessary and still somewhat problematic. Using the even loop approach I posted above is the best and what I'd consider the 'right' way to building a Python GUI in Houdini. Trying to run a script on the button press in another thread, while possible but not straightforward is not guaranteed to work properly. Is there some reason why you need your interface in a different thread that can't be accomplished by building it into the event loop? Quote Link to comment Share on other sites More sharing options...
few_a_fx Posted April 13, 2011 Author Share Posted April 13, 2011 Still a little new to python, so I'm sorry for not following you. In your previous post you said you got rid of the threading and MyApp stuff. What threads do I have to delete and do I just deleted the MyApp class? Also do I have to rebuild the UI once I do this, or can my current UI work with this set up? It would be nice to get this render button working, so the user can use the fx tool and not have to dive into houdini all the time; however if its to complicated for a python newbie, I'll tackle this problem later. Thanks for your tips graham, you've been very helpful. Quote Link to comment Share on other sites More sharing options...
graham Posted April 13, 2011 Share Posted April 13, 2011 You don't have to rebuild anything or make any changes. Here is it working using the first example (just create and render buttons). To add you other stuff you just add it in. I've removed the MyApp class from near the top and gotten rid of all the AppThread stuff at the bottom. To run the dialog you just import the module and call rain(). import sys import wx import hou import toolutils def exc_log(): print "Exception: '%s' on line %d" % (sys.exc_value, sys.exc_traceback.tb_lineno) # Creates the Window class MyFxTool(wx.Frame): def __init__(self, parent, id, title): wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size (405, 505), style = wx.CAPTION | wx.SYSTEM_MENU | wx.CLOSE_BOX | wx.STAY_ON_TOP) self.sizer= wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) self.panel= MyFxToolPanel(self) self.sizer.Add(self.panel, 1, flag= wx.EXPAND) self.Layout() # Creates the Layout class MyFxToolPanel(wx.Panel): def __init__(self, parent): super(MyFxToolPanel, self).__init__(parent) self.sizer= wx.BoxSizer(wx.VERTICAL) self._add_flex_grid() self.SetSizer(self.sizer) def _add_flex_grid(self): self.flex_grid_sizer= wx.FlexGridSizer(cols= 2, vgap= 10, hgap= 70) self.sizer.Add(self.flex_grid_sizer, 1, border= 20, flag= wx.ALL|wx.EXPAND) self._add_static_text("Rain System") self.start_button= self._add_button("Make It Rain") self._add_static_text("Render Effect") self._render_button= self._render_button("Render") ########################################################################### ####################### PARAMETER CONTROLS ################################ ########################################################################### def _add_static_text(self, label_text): static_text= wx.StaticText(self, label= label_text, style= wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL) self.flex_grid_sizer.Add(static_text, flag= wx.ALIGN_CENTER_VERTICAL) def _add_button(self, label_text): button= wx.Button(self, label= label_text) button.Bind(wx.EVT_BUTTON, self.onclickAdd) self.flex_grid_sizer.Add(button, flag= wx.ALIGN_CENTER_VERTICAL) return button def _render_button(self, label_text): button= wx.Button(self, label= label_text) button.Bind(wx.EVT_BUTTON, self.OnRender) self.flex_grid_sizer.Add(button, flag= wx.ALIGN_CENTER_VERTICAL) return button ########################################################################### ####################### PARAMETER EVENTS ################################## ########################################################################### #Allow you to create the acual rain system. #create both SOP and PO nodes. def onclickAdd(self, event): geo = hou.node("/obj").createNode("geo", "Rain_System", run_init_scripts = False) grid = geo.createNode("grid", "EMIT_FROM") grid.setParms({"ty":10}) popnet = grid.createOutputNode("popnet", "RAIN_EMITTER") popnet.setDisplayFlag(1) popnet.setRenderFlag(1) source = popnet.createNode("source", "RAIN_ORIGIN") defaultGeo = source.parm("usecontextgeo") defaultGeo.set("first") birthPlace = source.parm("emittype") birthPlace.set("surfacerandom") gravity = source.createOutputNode("force", "RAIN_DirCtrl") defaultRainDir = gravity.parm("forcey") defaultRainDir.set(-1) rSpeed = gravity.createOutputNode("drag", "RAIN_Spd_Ctrl") defaultRainSpd = rSpeed.parm("scale") defaultRainSpd.set(2) render = rSpeed.createOutputNode("render", "RENDER_ME") renderAs = render.parm("prtype") renderAs.set("line") renderSize = render.parm("prsize") renderSize.set(.0025) render.setRenderFlag(1) collide = render.createOutputNode("collision", "COLLIDE_WITH") out = hou.node("/out") renderIt = out.createNode("ifd", "RENDER_IT") def OnRender(self, event): try: hou.node('/out/RENDER_IT').render() except: exc_log def runWxApp(app, window): def processWxEvents(): """Houdini runs this callback to from its event loop to process wx events.""" while event_loop.Pending(): event_loop.Dispatch() app.ProcessPendingEvents() def onClose(event): """We bind this callback to the window's close event to destroy the window and stop the wx event loop.""" window.Show(False) window.Destroy() processWxEvents() hou.ui.removeEventLoopCallback(processWxEvents) window.Bind(wx.EVT_CLOSE, onClose) window.Show(True) event_loop = wx.EventLoop() wx.EventLoop.SetActive(event_loop) hou.ui.addEventLoopCallback(processWxEvents) def rain(): app = wx.PySimpleApp() frame = MyFxTool(None, -1, 'Rain System Tool') runWxApp(app, frame) Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.