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
  01 January 2013
if you are unsure about the sending part you could use c4d.EVMSG_CHANGE first
to implement the enabling and disabling of the Message plugin. but you should be
aware of the fact, that it is fired ALOT (it is the message for EventAdd()).

about the ids, you do not have to care, as you cann see there already have been
generated almost 30000 ids, i do not think that maxon cares if you get one more
or less but you shouldn't post them on the interwebs , they are super secret
 
  01 January 2013
Originally Posted by Little_Devil: if you are unsure about the sending part you could use c4d.EVMSG_CHANGE first
to implement the enabling and disabling of the Message plugin. but you should be
aware of the fact, that it is fired ALOT (it is the message for EventAdd()).

Hmm, I haven't managed to get SendCoreMessage working yet. But I had some success with using c4d.IsCommandChecked(). If the FreeHandToggle is ON while using Freehand spline tool it prints "Hello World.". If it's OFF nothing happens:
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()
        doc.StartUndo()
        CmdEnabled = c4d.IsCommandChecked(1029729)
        if not self.Cache:
            self.Cache = doc.GetFirstObject()
        if CmdEnabled == True:
            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())

Only problem is I'd have to make sure the Freehand spline tool becomes active when the FreeHandToggle plugin is checked. And deactivated when the plugin is not checked. But that's gonna require SendCoreMessage I guess, so I'll start over now

Originally Posted by Little_Devil: you shouldn't post them on the interwebs , they are super secret

ops, heh

Last edited by FantaBurky : 01 January 2013 at 12:27 PM.
 
  01 January 2013
you are making things to complictaed

1. in both plugin classes create a variable which stores the state like self.IsEnabled.
2. make sure both are initalized with the same state, currently it is false, as you
do not want the tool active per default.
3. when your commanddata plugin is called simply send a plugin message with the id
4. in the core message plugin , every time you recive the id simply toggle your internal
variable too. the parameter id passed to your CoreMessage method will be the
id send by your commanddata plugin.
5. your approach with reading the plugin state is also possible, but it does not
make so much sense for message plugin

so your commanddata plugin shouts 1029729 and messagedata thinks id == 1029729,
ok commanddata buddy thinks something important has happened

the chance of your both plugins gettings out of sync this way is REALLY small, but i am
pretty sure some nitpicker will complain about it, so here you can read how to unpack
core message data (you will have to send your boolean state as a coremessage parameter):

http://www.plugincafe.com/forum/for...PID=31266#31266

Last edited by littledevil : 01 January 2013 at 12:53 PM.
 
  01 January 2013
Sorry

I can't seem to get the ID out from the commanddata plugin. It outputs the name of the plugin instead (FreeHandToggle).
import c4d
from c4d import documents, plugins

PLUGIN_ID = 1029729

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

    def Execute(self, doc):
        msg = c4d.BaseContainer(c4d.COREMSG_CINEMA_GETCOMMANDNAM  E)
        msg.SetLong(c4d.COREMSG_CINEMA_GETCOMMANDNAME, 1029729)
        name = c4d.SendCoreMessage(c4d.COREMSG_CINEMA, msg, 0)
        print name # prints out "FreeHandToggle" in Console.
        self.isActive = not self.isActive
        self.isEnabled = not self.isEnabled
        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, "FreeHandToggle", 0, None, "FreeHandToggle Tooltip.", FreeHandToggle())

edit: I realise that making the c4d.SendCoreMessage a variable won't actually send the message. I just did that to test what the output was.

Last edited by FantaBurky : 01 January 2013 at 01:16 AM.
 
  01 January 2013
hm,
this was kind of my fault, CoreMessage is not the correct method for this, but
c4d.SpecialEventAdd() is . i have merged both plugins into one file. here is a fully
working example, i used a 'wrong' plugin ID for the messagedata plugin, as currently
all test ids are in use here, so you might want to change this.

example with a naughty message plugin the cgtalk code formating is just awfull,
so the code as a link:

http://codepad.org/DeVmgrMB

edit: fixed two things which were really bad, there are still some things which could
be done better, but i want to keep the code small, so i think this will be ok.

Last edited by littledevil : 01 January 2013 at 01:31 AM.
 
  01 January 2013
Freaking awesome
That works great. And thanks for the commented bits in there as well

But it's missing the code to make Freehand spline the active tool once the command plugin is turned on/checked. Where would be the ideal place to add it (doc.SetAction(14055))? And is it possible to return to the tool that was active before the button was pressed, when the command plugin becomes unchecked/turned off again?

Thanks again, can't tell you how much this has helped my learning process
 
  01 January 2013
yeah setaction would be the method. to store the last tool, just write the result of
basedoucment.getaction into a variable of your class before you call setaction. when
the plugin is turned off write this variable/id back into the document with setaction.
 
  01 January 2013
It works, yaaay
import c4d, string
from c4d import documents, plugins

PID_MESSAGE = 1029730
PID_CMDDATA = 1029729
TOOLID      = 14055

class fhFreehandMessageData(plugins.MessageData):

    def __init__ (self):
        self.Cache = None
        self.IsActive = False
        self.Counter = 0

    def CoreMessage(self, id, bc):
        doc = documents.GetActiveDocument()

        # set the cache when the plugin instance has been created
        # and there is no cache yet
        if not self.Cache:
            self.Cache = doc.GetFirstObject()

        # ---> here it happens
        # listen for our commanddata id and togle the state
        if id == PID_CMDDATA:
            self.IsActive = not self.IsActive
            if self.IsActive:
                self.PrevMode = doc.GetMode()
                self.PrevTool = doc.GetAction()
                doc.SetMode(c4d.Mpoints)
                doc.SetAction(TOOLID)
                print 'Yay, i am back again.'
            else:
                doc.SetMode(self.PrevMode)
                doc.SetAction(self.PrevTool)
                print 'I should shut up ? You will be doomed without me !'

        # listen if c4d send EventAdd(EVMSG_CHANGE) and check if the
        # currently topmost object is the same to our stored / cached
        # object, if not there has been inserted a new object.
        elif id == c4d.EVMSG_CHANGE and doc.GetAction() == TOOLID:
            # add 'and self.IsActive, which is the short form for and self.IsActive == True'
            if self.Cache and self.Cache is not doc.GetFirstObject() and self.IsActive:
                self.Cache = doc.GetFirstObject()
                if self.Cache.GetType() == c4d.Ospline:
                    print "Spline No.{0}, named {1} - this boring , can we do somethingg else please ?".format(self.Counter, self.Cache.GetName())
                    self.Counter += 1
            # object is inserted but the plugin is not 'active'
            elif self.Cache and self.Cache is not doc.GetFirstObject() and not self.IsActive:
                print 'I am not allowed to say something... Ooops.'
        return True


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

    def Execute(self, doc):
        self.isActive = not self.isActive
        # ---> send the message
        c4d.SpecialEventAdd(PID_CMDDATA, 0,0)
        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.RegisterMessagePlugin(id  = PID_MESSAGE, 
                                 str  = "fhFreeHandMessage",
                                 info = c4d.PLUGINFLAG_SMALLNODE,
                                 dat  = fhFreehandMessageData())
    plugins.RegisterCommandPlugin(id  = PID_CMDDATA, 
                                 str = "FreehandPlusToggle", 
                                 info = 0, 
                                 icon = None, 
                                 help = "This is a Tooltip for FreehandPlusToggle.", 
                                 dat = FreehandPlusToggle())

I also used SetMode to enter Points Mode when Freehand Spline Tool is selected, cause it was showing the bounding box and axis otherwise (which is not what happens with the standard Freehand spline tool). I tried inserting the variables elsewhere, but either the plugin wouldn't load or it wouldn't see the variables in the else statement.

The only issue I've encountered is if you leave the CommandData plugin enabled/toggled ON, and then delete all the objects in the Object Manager: It prints this in console:
Traceback (most recent call last):
  File "'FreeHandToggle.pyp'", line 46, in CoreMessage
AttributeError: 'NoneType' object has no attribute 'GetType'

Not a big problem though I guess. Same thing happens if you leave the plugin enabled/toggled on, and close the document (and c4d creates a new one). It will keep the ON state and the Freehand spline tool, and if you toggle it OFF and then ON it prints the same message again ...

Anyway, I really have to thank you!! I've learned so much from this And I really did not expect anyone to give so much of their time to help teach others. I really, really appreciate it
 
  01 January 2013
well check line 46:

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

it is an error in my code due to sloppy coding. think of what happens when you call
self.Cache = doc.GetFirstObject() for an empty document. in other lines i have done it
properly, but here i got lazzy. i have also described the problem earlier in the thread
here.

rather than coping similar lines you should look up the c4d.documents.basedocument.
GetFirstObject() return value in sdk and try to understand why this raises a runtime
error (the code is trying to call a method which does not exist).

won't spoil all the fun for you

edit : ps, there will be people who tell you otherwise, but i would define self.PrevMode etc.
in the constructor / the __init__ method of your class with a None value, it does make it
easier to read your code later on. it would make it easier to understand that this class
stores a variable called self.PrevMode which is of some importance.

Last edited by littledevil : 01 January 2013 at 10:01 PM.
 
  01 January 2013
Doh, ok. I'm a dumbass I was somewhat aware of what was causing the error, but the problem was, I became really confused when it didn't help when I added the green highlighted stuff :
        elif id == c4d.EVMSG_CHANGE and doc.GetAction() == TOOLID:
            # add 'and self.IsActive, which is the short form for and self.IsActive == True'
            if self.Cache and self.Cache is not doc.GetFirstObject() and self.IsActive:
                self.Cache = doc.GetFirstObject()
                if not self.Cache:
                    self.Cache = doc.GetFirstObject()
                elif self.Cache.GetType() == c4d.Ospline:
                    print "Spline No.{0}, named {1} - this boring , can we do somethingg else please ?".format(self.Counter, self.Cache.GetName())
                    self.Counter += 1
            # object is inserted but the plugin is not 'active'
            elif self.Cache and self.Cache is not doc.GetFirstObject() and not self.IsActive:
                print 'I am not allowed to say something... Ooops.'
        return True
Turns out I was editing the wrong file LOL. I was writing in a backup copy of the .pyp file that I had somewhere else on my system, so nothing was actually being modified .. Anyway, all the error messages are gone now And I defined the PrevTool/PrevMode in the constructor like you said. Thanks for the tip

But one last thing remains. How to make sure the toggle is OFF everytime the BaseDocument changes (closes or a new one is created/opened) OR a tool other than (!=) TOOLID (Freehand spline) is selected, while it's toggled ON.

I thought since the doc.GetAction() changes usually when the BaseDocument changes, it might be good to check if doc.GetAction() != TOOLID. So I tried inserting that into the def GetState(), and it works great for changing the visual toggle to OFF (c4d.CMD_ENABLED).

But it only toggles the visual toggle, not the actual button toggle. Cause if I click on the toggle again after it's been visually toggled OFF, it toggles the button OFF (I assume, cause it prints 'I should shut up ? You will be doomed without me !'), and then I have to click it a second time to get the button to toggle ON:
    def GetState(self, doc):
            if self.isActive and doc.GetAction() != TOOLID:
                return c4d.CMD_ENABLED
            if (self.isActive):
                return c4d.CMD_VALUE + c4d.CMD_ENABLED
            else:
                return c4d.CMD_ENABLED
Any ideas?

Last edited by FantaBurky : 01 January 2013 at 01:59 AM.
 
  01 January 2013
for the BaseDocment.GetFirstObject() thing :

the method returns None when there are no objects in the document. This is is a fact i
do not have thought of when i wrote the quick message plugin example on my first
posting. your code fixes the runtime error, but it would be a better approach to:

1. do not check in MessageData.CoreMessage if self.Cache is none as an own
if statement.

2. do not overwrite the self.Cache if it is none, as the result could also be none
again, which will cost runtime.

3.on each occasion you have to use self.Cache check if the cache is none:

if self.Cache and ... :

for the document thing. i have asked this question not long ago myself in the
pcafe forum, how this is done properly. there is no message being sent when
the active document has changed. the way maxon wants us to do this is either
a SceneHook plugin (which is not possible in python) or with EVMSG_CHANGE
and check if the current document is the same as the stored reference .
EVMSG_CHANGE is the same ID you are already using in the message pluin and
will result in a lot of checking

if currentDoucment is self.myDoucmentReference:

Last edited by littledevil : 01 January 2013 at 02:10 AM.
 
  01 January 2013
I couldn't seem to get the self.Cache thingy to work

So I'm looking into SceneHook now instead. Something that I probably shouldn't be trying, but I found an example posted on plugincafe. Thought I'd try compiling it and see what happens:
http://www.plugincafe.com/forum/for...ts.asp?TID=3286

But is SceneHook really needed to make the Tool change work. Like if you have the plugin toggled ON with TOOLID selected (Freehand spline), and you choose a tool different from the TOOLID, it doesn't toggle OFF automatically.

I found a recently added method to the SDK which looks like it might be able to help with just tool change.
BaseDocument.SendInfo(type, format, fn, bl, hooks_only)

Last edited by FantaBurky : 01 January 2013 at 03:10 PM.
 
  02 February 2013
The code in the posted link is C++, this won't work with python Please also note that SceneHookData is not wrapped for python. Not sure what you mean with SceneHook in the current context. You can get hold of existing SceeneHooks with BaseDocument.FindSceneHook().

You could implement your desired features just with messages
 
  02 February 2013
Oh, I missunderstood

I thought SceneHook was a specific c++ plugin, with the sole purpose of sending messages that couldn't be sent by other plugins (in this case Python). Like when one BaseDocument closes and another opens. And that the Python plugin could then intercept the message and act on it.

Anyway, keeping it all Python is way better for me, so I'll take another look at messages

I have to thank you again, for having patience with me. I know I must be the worst/slowest learner. So I really appreciate it
 
  02 February 2013
Thread automatically closed

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
__________________
CGTalk Policy/Legalities
Note that as CGTalk Members, you agree to the terms and conditions of using this website.
 
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 11:40 AM.


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