Script vs Plugin with a UI [SOLVED]
On 19/11/2014 at 16:49, xxxxxxxx wrote:
I'm currently researching plugins and how to build them. I'll be honest, they don't make it look easy.. especially when compared to just running a script.
I have a script, with a dialog that has 3 buttons on it. Let's say X, Y, and Z.
Here's the dilemma.. I can get the script to execute what I want to happen when a button is pressed. However, I simply cannot get the object that's being manipulated to update in the viewport until I completely close the dialog box and manually update the viewport. Only then will I be able to see the effect of the button press.
Things I've tried to circumvent this issue so far:
Tried an asynchronous dialog - result is buttons no longer work.
Attempted to quickly build a plugin instead - result is ..........umm.. this is extremely complicated looking..
After doing some research, I think the toolData plugin would be the way to go.. right? After each button press, the selected object would update in the viewport. Also, the UI could be dockable. Am I headed in the right direction?
I have a lot of questions about building a plugin.. because the documentation is very cryptic currently.
I want to help answer people's questions on here as well.. but I simply do not know enough yet.
Any help would be appreciated.
On 19/11/2014 at 18:20, xxxxxxxx wrote:
Sometimes it's simple things that will trip you up the most.
Did you remember to update your objects after you changed them?
On 20/11/2014 at 01:09, xxxxxxxx wrote:
if you just want an asynchronous dialog that allows you to edit the scene I suggest to create a CommandData plugin that opens and owns this dialog. You find examples in the SDK like the RSS Reader Example or the Memory Viewer example.
Creating a CommandData plugin is especially simple since no other resource files are necessarily needed. In the documentation you find a description of the basic plugin structure.
On 20/11/2014 at 16:59, xxxxxxxx wrote:
Alright! This is definitely working out to be something. However, I'm still in the similar dilemma.
Current update as to where things are.
Plowed through the examples and extracted the shell of what I needed to build the CommandData plugin.
FINALLY! I got the plugin to give me a working ASYNC dialog. This will be extremely helpful for future projects!
I now have a plugin with my dialog and this is fantastic. I threw in the code from the script version of what I would like those buttons to do.. and alas.. I tried to implement the
obj.Message( c4d.MSG_UPDATE )
and while it didn't give me an error.. it just didn't do anything.
On a test, I removed the dialog completely and just ran the function of the button on its own. I was able to update the object with
Is the EventAdd sloppy for updating the scene? Should I be using something else instead? Something like the MSG_UPDATE?
Thank you so much for your help. This is frustratingly fun! :)
On 20/11/2014 at 17:17, xxxxxxxx wrote:
Okay so I ran into a smaller issue with the whole 'updating objects' thing.
The c4d.EventAdd () will update the position of the tangent on the spline.. but it won't physically update the spline curve. It's like only the physical tangent updated. Would I have to redraw the viewport to get the spline updated to match the new tangent?
On 20/11/2014 at 17:48, xxxxxxxx wrote:
EventAdd() is a CoreMessage that notifies C4D that something has changed.
It's often required at the end of your scripts in the script manager. And also in plugins that execute only once. Like the CommandData and GeDialog type plugins for example.
It's not a hack to use it. It's often required.
The most important thing to know is NEVER use it on code that loops. Unless you really know what you're doing.
Things like generators and tags are examples of code that constantly loops. And using EventAdd() in them will often create an infinite loop and a crash.
MSG_UPDATE is sometimes required to be used after you've changed an object.
That include things like moving it, or editing it's vertices.
There are times when you can get away without using it. But it's a best practice to always use it if you edit an object.
So you should try to get into the habit of using it even if it doesn't appear to do anything.
There are lots of other Events and Messages in C4D that sometimes need to be used.
But those tend to be needed in specialized situations.
For example. When adding or removing points from a spline object. This message needs to be used afterwards: obj.Message(c4d.MSG_POINTS_CHANGED)
Because there are so many different situations, events, and messages. It's hard to give an answer that fits all scenarios. So it's best to post as much of your code as you can. So that we can give you the proper advice.
On 24/11/2014 at 09:51, xxxxxxxx wrote:
Thank you for that info. That has been quite helpful figuring all of this out. I do have one question about refreshing the viewport though.
I have changed the position of vertices ( in which there's another problem I've ran into ) and applied the MSG_UPDATE. Now it did update the object. However, I had to run
c4d.CallCommand ( 12147 ) # Redraw
in order for it to visually update. I've tried this same approach to editing the vertices of a polygonal object and everything updates. But with splines you have to refresh the viewport as well.. which I find a bit interesting.
Is it because when editing the points on a spline, you've only updated the points and not the spline segments?
Along the lines of using CallCommand, I feel like that portion is the hack. Is there a way to update the viewport not using CallCommand?
Finally, when using .GetPoint ( id ), it will retrieve the local coordinates of that particular point. Is there a way to get the world coordinates from it? Is there a function or call that translates the coordinates to world or am I going to have to get the world axis of the object and add/subtract the point to get that in world coordinates?
Again, thank you for your help.
On 24/11/2014 at 10:37, xxxxxxxx wrote:
to force a redraw simply use EventAdd():
padr = op.GetAllPoints() worldSpaceMatrix = op.GetMg() for i, point in enumerate(padr) : worldSpacePoint = point * worldSpaceMatrix print worldSpacePoint
For questions no longer related to this thread's original topic please open a new thread. Thanks.
On 24/11/2014 at 10:59, xxxxxxxx wrote:
Thank you for an insanely fast response!
Also, I apologize for mixing topics. I'll work on that for next time.
So to sum it up, if you want a dialog and you don't need to edit the scene while the dialog is open, you can just use a script. If you need the dialog to remain open while you change stuff the scene, use a commandData plugin to create an asynchronous dialog.
Also, if anyone needs the code for an asynchronous dialog. Here's the framework.
Just save it in a folder in the plugins directory.
PLUGIN_ID = class guiDialog ( gui.GeDialog ) : # IDs LabelID = 999 mainGroup = 1000 btn1 = 1001 def CreateLayout ( self ) : self.SetTitle ( 'Dialog Title' ) self.GroupBegin ( self.mainGroup, 0, 2, 0 ) self.AddButton ( self.btn1, c4d.BFH_SCALEFIT, 120, 15, 'Button 1' ) self.GroupEnd () return True def GetLabelID ( self ) : self.LabelID = self.LabelID + 1 return self.LabelID def StartGroup ( self, label, rows ) : self.GroupBegin ( self.GetLabelID (), 0, rows, 20, label, 0 ) self.GroupBorder ( c4d.BORDER_THIN_IN ) self.GroupBorderSpace ( 8, 8, 8, 8 ) def Command ( self, id, msg ) : if id == self.btn1: function1 ( args ) return True def function1 ( args ) : print "Button 1 pressed" class pluginName_CommandData ( c4d.plugins.CommandData ) : dialog = None def Execute ( self, doc ) : if self.dialog is None: self.dialog = guiDialog () return self.dialog.Open ( dlgtype = c4d.DLG_TYPE_ASYNC, pluginid = PLUGIN_ID ) def RestoreLayout ( self, sec_ref ) : if self.dialog is None: self.dialog = guiDialog () return self.dialog.Restore ( pluginid = PLUGIN_ID, secret = sec_ref ) if __name__ == "__main__": dir, f = os.path.split ( __file__ ) #bmp = bitmaps.BaseBitmap () #fn = os.path.join ( dir, "res", "Plugin_Name.tif" ) #bmp.InitWith ( fn ) c4d.plugins.RegisterCommandPlugin( id = PLUGIN_ID, str = "Plugin Name", help = "Plugin Info", info = 0, dat = pluginName_CommandData (), icon = None )
On 24/11/2014 at 11:40, xxxxxxxx wrote:
Originally posted by xxxxxxxx
TSo to sum it up, if you want a dialog and you don't need to edit the scene while the dialog is open, you can just use a script. If you need the dialog to remain open while you change stuff the scene, use a commandData plugin to create an asynchronous dialog.
That's not quite right.
You can use a dialog to edit the scene in both a script, or a full blown plugin, with Python.
In many cases you won't see a difference.
That's one of the benefits of Python over C++. We can write plugin classes in our scripts(most of the time). So we don't need to write a full blown plugin.
There are some cases when that is not the case though. And you'll need to write a full plugin.
And in some cases a plugin is easier to distribute to other people who don't like dealing with script code. And just want to use a plugin without seeing any code.
That probably sounds a bit vague.
But without going into all of the cases. It's much easier to say that if you can do it from a script. And if you like it being done from a script. Then It's OK to do it in a script as long as it's working properly.