Difficult question (catching object manager events from c4d tools with python?)

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

THREAD CLOSED
 
Thread Tools Search this Thread Display Modes
Old 01 January 2013   #1
Difficult question (catching object manager events from c4d tools with python?)

I have a more difficult question this time =P Maybe someone here knows.

I'm trying to get the console to print "Hello World!" every time a spline object has been created with the Freehand spline tool. Is this only possible by making a plugin, or can it be accomplished through a Python script?

I'm thinking the script (if possible) would be something really complicated, like: start the script with making Freehand spline the active tool (doc.SetAction(14055)???). Then check when a spline object becomes available in the Object Manager while using that tool. And then repeat for everytime the mouse is released (EditorWindow.MouseDragEnd()???). But this would require the script to somehow check all the time if a spline object becomes available in the Object Manager. And also check for when we stop using the Freehand spline tool, and if so stop the script.

Maybe someone can shed some light on this, cause I'm really lost right now. If only to point me in the right direction. I'd really appreciate any help. I've lost two nights sleep over this ha ha =D. Scripting, while very difficult is also very addictive I must say.
 
Old 01 January 2013   #2
i do not think that this is possible with a script, but you might get it to work with some
dirty threading tricks, but scripts are officially not meant to use threads. you cannot run
the script and the freehand tool at the same time. the common way would be message or
tool plugin and overwriting the message method.

this plugin prints hello world every time a new object has been inserted into the document
while the freehand tool is active. the plugin assumes that new objects are being inserted
at the top of the object list/document.

import c4d
from c4d import documents, plugins

PID_MESSAGE = 1000000
TOOLID = 14055

class fhFreehandMessageData(plugins.MessageData):
	def __init__ (self):
		self.Cache = None

	def CoreMessage(self, id, bc):
		doc = documents.GetActiveDocument()
		if not self.Cache:
			self.Cache = doc.GetFirstObject()
		if id == c4d.EVMSG_CHANGE and doc.GetAction() == TOOLID:
			if self.Cache and self.Cache is not doc.GetFirstObject():
				self.Cache = doc.GetFirstObject()
				print "Hello World."
		return True
		
if __name__ == "__main__":
    plugins.RegisterMessagePlugin(id  = PID_MESSAGE, 
                                 str  = "fhFreeHandMessage",
                                 info = c4d.PLUGINFLAG_SMALLNODE,
                                 dat  = fhFreehandMessageData())

Last edited by littledevil : 01 January 2013 at 09:47 AM.
 
Old 01 January 2013   #3
Wow, I didn't expect anyone to be so generous as to write a complete plugin =O. Thank you for taking the time to do this. It works great. Only I wish I had said it was supposed to run as a seperate tool that references to a built-in tool in c4d.

Is it possible to convert it into a ToolData plugin? So that it doesn't affect using the standard built-in Freehand spline tool, but instead acts as a tool of it's own (even though it references to the Freehand spline tool).
 
Old 01 January 2013   #4
well,

it would be basically the same. you'll just have to add some on/off filtering. the easiest
way would be to write a second plugin, a commanddata plugin which does nothing more
than providing a button and a toggle feature. this plugin then would have to send a core
message to the messagedata plugin telling the messagedata plugin if it is meant to be on
or off. try to write the commanddata plugin with the functinality of toggling the on off
state and i'll help you with sending the core message if needed.

here a little snippet on how you write the visual toggle state for commanddata or tooldata
plugin button. you will have to make sure that your commandData class has a variable to
store the state, like self.isActive in the example. then you will have to overwrite Execute
for your command data plugin to toggle this variable on user clicks.

# toggle the plugin button state, pressed / active / disabled (unsaved document)
# --------------------------------------------------------------------------------------
    def GetState(self, doc):
        if (doc.GetDocumentPath() != ""):
            if (self.isActive):
                return c4d.CMD_VALUE + c4d.CMD_ENABLED
            else:
                return c4d.CMD_ENABLED
        else: 
            return 0


ps :

there is also still a 'bug' in my message plugin example, when you have the freehandspline
tool active and insert an object into the document, like for example a parametric sphere,
the plugin prints 'Hello World'. this is actually not intended. try to fix this. hint - you will
find the solution in c4d.C4DAtom or you could also use a python keyword to solve this.

Last edited by littledevil : 01 January 2013 at 12:11 PM.
 
Old 01 January 2013   #5
Thanks for all the help! I really appreciate it. I don't know how many days or weeks/months/months I'd spend trying to figure out certain things without some help.

I'll look into commanddata plugins in the sdk+google. But I should mention I haven't even scratched the surface on that. I've dreaded coding plugins thinking it would be like a completely different language than regular python scripting. Which I've also dreaded . Just recently decided to start learning. It helps a lot to have a code to look at and dissect, so thank you for taking time to write them! I'll post when I've figured it out, could take a while though

About the 'bug', c4datom helped, thanks. Don't know if I've used it as optimised as possible but it seems to be working:
import c4d
from c4d import documents, plugins

PID_MESSAGE = 1000000
TOOLID = 14055

class fhFreehandMessageData(plugins.MessageData):
	def __init__ (self):
		self.Cache = None

	def CoreMessage(self, id, bc):
		doc = documents.GetActiveDocument()
		if not self.Cache:
			self.Cache = doc.GetFirstObject()
		if id == c4d.EVMSG_CHANGE and doc.GetAction() == TOOLID:
			if self.Cache and self.Cache is not doc.GetFirstObject():
				self.Cache = doc.GetFirstObject()
				if self.Cache.GetType() == 5101:
					print "Hello World."
		return True
		
if __name__ == "__main__":
    plugins.RegisterMessagePlugin(id  = PID_MESSAGE, 
                                 str  = "fhFreeHandMessage",
                                 info = c4d.PLUGINFLAG_SMALLNODE,
                                 dat  = fhFreehandMessageData())
 
Old 01 January 2013   #6
your fix is correct i haven't checked the ID, but i guess it is the splineobject id.
as a sidenote. it is better to use the string version of the ids, because it does make
your code more readable. to get hold of the string version of an ID there are two
ways. 1. use a textfile search software on your cinema4d folder. 2. the basic IDs are
also listed in python sdk with the init method of the respective object.

http://www.thirdpartyplugins.com/py...Object.__init__
http://www.thirdpartyplugins.com/py...es/objects.html

if self.Cache.GetType() == c4d.Ospline :


as editable splines are an actual class you could also use the python type keyword.
this is important when you are not sure if the test instance is derived of c4d.C4dAtom.
or in other words if the testobject provides a GetType() method. this will be never
the case for selecteable objects, but nevertheless:

if type(self.Cache) == c4d.SplineObject:


happy coding

Last edited by littledevil : 01 January 2013 at 04:12 PM.
 
Old 01 January 2013   #7
Instead of if self.Cache.GetType() == c4d.Ospline, you could also write if self.Cache.CheckType(c4d.Ospline). And for the type-checking, the isinstance() function is usually the better choice.

Best Regards,
Niklas
__________________
Cinema 4D Plugin Developer
niklasrosenstein.com/ | Freebies | Plugins
 
Old 01 January 2013   #8
Originally Posted by Little_Devil: your fix is correct i haven't checked the ID, but i guess it is the splineobject id.
as a sidenote. it is better to use the string version of the ids, because it does make
your code more readable. to get hold of the string version of an ID there are two
ways. 1. use a textfile search software on your cinema4d folder. 2. the basic IDs are
also listed in python sdk with the init method of the respective object.

http://www.thirdpartyplugins.com/py...Object.__init__
http://www.thirdpartyplugins.com/py...es/objects.html

if self.Cache.GetType() == c4d.Ospline :


as editable splines are an actual class you could also use the python type keyword.
this is important when you are not sure if the test instance is derived of c4d.C4dAtom.
or in other words if the testobject provides a GetType() method. this will be never
the case for selecteable objects, but nevertheless:

if type(self.Cache) == c4d.SplineObject:


happy coding

Cool. Those are very useful, thanks

I've been searching/reading like crazy for a few hours now on the commanddata. I will probably not figure it out today, but I'll post it tomorrow if I do. Good thing is I'm learning more about Python scripting, which is what I'd like to focus on.

I'll also give tooldata a chance as well, cause I would prefer if it would run similar to the Freehand spline tool. Without clicking on a button everytime to initialize. Sadly there's little information about on-off filtering on google The only thing I can find in the sdk is:
http://www.thirdpartyplugins.com/py...ilterPluginList

Which doesn't sound right..

Originally Posted by NiklasR: Instead of if self.Cache.GetType() == c4d.Ospline, you could also write if self.Cache.CheckType(c4d.Ospline). And for the type-checking, the isinstance() function is usually the better choice.

Best Regards,
Niklas

Thank you, more good stuff to look into

Last edited by FantaBurky : 01 January 2013 at 09:10 PM.
 
Old 01 January 2013   #9
on/off filtering isn't an actual term i just used it to describe the functionailty of the plugin.
you are a bit off the track, ill give you a little help, you will have to fill in the rest.

import c4d
from c4d import documents, plugins

# make sure to ge a valid pluin id, these are only testing ids
PLUGINID = 1000001

class myCommandDataPluin(plugins.CommandData):
	# the constructor, this would be a good place to define some variables you 
	# want to use in all methods
	def __init__ (self):
		???

	# the method c4d calls when the command is called / the button has been clicked
	def Execute(self, doc):
		???

	# visually enable or disable the plugin button
	def GetState(self, doc):
		???

if __name__ == "__main__":
    # register the plugin
    plugins.RegisterCommandPlugin(...???
 
Old 01 January 2013   #10


Thank you I'll post it here when I figure out the rest
 
Old 01 January 2013   #11
Man, that went terrible. Got a nasty headache and not feeling much love for Python anymore ha ha. A complete mess sadly , to the point that I'm almost forgetting what I've learnt in regular Python scripting.

I got it (def execute) to stick, but not toggle. I'll give it another go tomorrow

import c4d
from c4d import documents, plugins

# make sure to ge a valid plugin id, these are only testing ids
PLUGIN_ID = 1000009

class FreehandPlusToggle(c4d.plugins.CommandData):
    def __init__ (self):
        self.isActive = 1
        self.isActive = 0

    def Execute(self, doc):
        if (doc.GetDocumentPath() != ""):
            if (self.GetState == c4d.CMD_VALUE + c4d.CMD_ENABLED):
                self.isActive = 0
            else:
                self.isActive = 1
        else:
            return 0

    def GetState(self, doc):
        if (doc.GetDocumentPath() != ""):
            if (self.isActive == 1):
                return c4d.CMD_VALUE + c4d.CMD_ENABLED
            else:
                return c4d.CMD_ENABLED
        else:
            return 0


if __name__ == "__main__":
    plugins.RegisterCommandPlugin(PLUGIN_ID, "FreehandPlusToggle", 0, None, "This is a Tooltip for FreehandPlusToggle.", FreehandPlusToggle())

Last edited by FantaBurky : 01 January 2013 at 10:23 PM.
 
Old 01 January 2013   #12
hi, here is a corrected version, i haven't tested in c4d, so i might have overlooked other
errors.

class FreehandPlusToggle(c4d.plugins.CommandData):
   # this does not make much sense, you are defining the same variable twice
   # it also would be  more convenient to use a BOOL type rather then a INTEGER type
    def __init__ (self):
    	self.isActive = 1
    	self.isActive = 0
        self.isActive = False


   # here is you main error. you are comparing self.isActive against CMD_VALUE +
   # CMD_ENABLED. both are just integer constants which could be translated to 1
   # and 2 (i think, could also be 0 and 1). so you are doing this -
   #
   # if (self.GetState == 3) :
   #
   # which will never be True. i think you wanted to do an logic or here :
   #
   # if self.isActive == c4d.CMD_VALUE or self.isActive == c4d.CMD_ENABLED:
   #
   # however this would still not be correct, as both values are c4d constants to 
   # return the button display state and have no relation to our class isActive 
   # variable. you also check for an unsaved document here, which will
   # cause you button only to work for saved documents. an empty string 
   # document path means that the document has not been saved yet.
    def Execute(self, doc):
        if (doc.GetDocumentPath() != ""):
            if (self.GetState == c4d.CMD_VALUE + c4d.CMD_ENABLED):
                self.isActive = 0
            else:
                self.isActive = 1
        else:
            return 0

            # toggle the boolean state. True becomes False and the other way arround.
            self.isActive = not self.isActive
            # we have alaway return a boolean for execute so that c4d knows if the
            # the code has been executed successfully or not.
            return True
    

    # you do not need to check if the doucment has been saved, your button will
    #  become otheriwse disabled (grey out - unclickable) for unsaved documents.
    def GetState(self, doc):
        if (doc.GetDocumentPath() != ""):
            if (self.isActive):
                return c4d.CMD_VALUE + c4d.CMD_ENABLED
            else:
                return c4d.CMD_ENABLED
        else:
            return 0


if __name__ == "__main__":
    plugins.RegisterCommandPlugin(PLUGIN_ID, "FreehandPlusToggle", 0, None, "This is a Tooltip for FreehandPlusToggle.", FreehandPlusToggle())


edit : lol, after posting this, it looks terrible, but you general direction was the correct one

Last edited by littledevil : 01 January 2013 at 11:12 PM.
 
Old 01 January 2013   #13
Really? I just tried your corrected version, and it works great. And the commented bits are golden for me :
import c4d
from c4d import documents, plugins

PLUGIN_ID = 1000009

class FreehandPlusToggle(c4d.plugins.CommandData):
    def __init__ (self):
        self.isActive = False

    def Execute(self, doc):
        self.isActive = not self.isActive
        return True

    def GetState(self, doc):
            if (self.isActive):
                return c4d.CMD_VALUE + c4d.CMD_ENABLED
            else:
                return c4d.CMD_ENABLED


if __name__ == "__main__":
    plugins.RegisterCommandPlugin(PLUGIN_ID, "FreehandPlusToggle", 0, None, "This is a Tooltip for FreehandPlusToggle.", FreehandPlusToggle())

One thing though, if I add print "activated" and print "deactivated" before each of the GetState returns and toggle the button it prints both messages twice in console. I assume it's not not a problem?

    def GetState(self, doc):
            if (self.isActive):
                print "activated"   #<----here
                return c4d.CMD_VALUE + c4d.CMD_ENABLED
            else:
                print "deactivated"   #<----here
                return c4d.CMD_ENABLED

Console:
activated
activated
deactivated
deactivated

I'll try to get the SendCoreMessage/CoreMessage working, hopefully it's not as difficult as this was.

Thanks again
 
Old 01 January 2013   #14
Originally Posted by FantaBurky: One thing though, if I add print "activated" and print "deactivated" before each of the GetState returns and toggle the button it prints both messages twice in console. I assume it's not not a problem?


nah, for instantiated objects this happens quite often. c4d creates copies of your
objects internally for multiple reasons. these objects also call your methods.
a command data plugin is not instantiated, but the gui of the plugin is. or in
other words - you can have multiple buttons for your plugin the gui. for each
of them getstate seems to be called.

Originally Posted by FantaBurky: I'll try to get the SendCoreMessage/CoreMessage working, hopefully it's not as difficult as this was.


SendCoreMessage is the correct method. but for this you will have to register at
plugin cafe and get some proper ids, because i do not know if the tesing ids are
also unused as message ids. you can register a plugin id from the forum top menu
(next to logut). you can register two ids, one for your plugin, one for the message,
but in your case one id for both would be enough (you would be sending your plugin
id as the message id).

in the message plugin you will have to listen in the coremessage method for your plugin
id when you recieve the id you will have to toggle an internal variable like you did in the
command data plugin. based on this variable the message plugin enables/disables its
output.

http://www.plugincafe.com/forum/default.asp

registering there will you also give access to the help of people being by far more experienced than me and the maxon dev support / the maxon devs.
 
Old 01 January 2013   #15
Sounds pretty straightforward. Just as long as I can find a way to enable/disable the message output. I'll probably try disabling the message plugin first without core messages, and then with. - edit: ok, I forgot coremessage is the reason the message plugin does what it does. That makes it slightly more difficult

I wasn't sure if Plugin Cafe was as active as CGTalk's c4d forum, but thanks for the tip I've registered and generated this one. Hope they don't mind generating ID's for test plugins (maybe I can use it in the future when I've learned more and can create something of my own from scratch):
1029729 	FreeHandToggle

*whips out the SDK

Last edited by FantaBurky : 01 January 2013 at 10:18 AM.
 
Thread Closed share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 07:06 PM.


Powered by vBulletin
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.