Allowing plugins to talk to eachother?
-
On 07/01/2013 at 08:54, xxxxxxxx wrote:
What do you mean with "sharing the public class members"? From a kickstart, I'd say you could
just pass the Tag plugin instance to the dialog, or the dialog to the Tag plugin, or both, and
access each others public members. Or you allocate a piece of memory both objects can share:typedef struct { Real member_one; LONG member_two; String member_three; } SharedData; class MyTag : public BaseTag { private: SharedData* data; public: Bool Message(GeListNode* node, LONG msgType, void* msgData) { if (msgType == OPEN_MY_DIALOG) { if (!this->data) { this->data = new SharedData; } MyDialog dlg(this->data); dlg.Open(DLG_TYPE_MODAL); } } // ... }; class MyDialog : public GeDialog { private: SharedData* data; public: MyDialog(SharedData* data) : GeDialog(), data(data) {}; // ... };
-
On 07/01/2013 at 09:22, xxxxxxxx wrote:
@Niklas,
Thanks. You have the right idea and that should work.
I don't need anything as fancy as the tag being able to launch the dialog. But it's interesting to see your way of doing that though.
I just need the Tag plugin and the GeDialog plugin to have access to each others public class member variables so that each plugin can use the other plugin's class member variables when needed.
That way one plugin can control the other plugin.
Using a struct will probably work. I'll give it a try.Anyone else have a different way they do this?
I'm interested in seeing how people handle this problem.-ScottA
-
On 07/01/2013 at 19:28, xxxxxxxx wrote:
You could pass the Tag to the GeDialog and get the underlying nodedata (which is a pointer to your plugin TagData class (instance)) with tag->GetNodeData() typecast to your class. I typically do this to call public methods in one plugin from another plugin or class.
But if they are going to share data on a regular basis, NiklasR's way is the same that I use (an intermediary class to pass between other classes).
-
On 07/01/2013 at 20:20, xxxxxxxx wrote:
^OK thanks.
I've been using both of these methods already. But I was just wondering if there was maybe a better way.I'm still learning the ins and outs of making two plugins talk to each other. And trying to figure out how to store images in my tags so the Dialog can use them for it's bitmap buttons.
-ScottA
-
On 13/01/2013 at 10:15, xxxxxxxx wrote:
I've been working a project that requires a Gedialog plugin to work very tightly with a Tag plugin.
This is something that is not covered in any of the SDK examples.
One problem was to figure out how to get the tag to save images inside of it internally.
That problem has been solved thanks to help from Robert&Steve.This is what's left to solve:
1- How can I make the GeDialog not open at all if there is no object currently selected?2- The way I have the code written in the GeDialog. It always resets the BitmapButton's image to be blank(NULL) when a saved scene is opened. And I need to find a way for the GeDialog to be intelligent enough to load the image that's saved in the tag onto the bitmap button when the dialog launches.
All of my attempts at trying to check for an active object. And also load the tag's saved image data into the BitmapButton while the GeDialog is opening have failed due to the GeDialog not doing what I expected during it's launch phase.
I obviously don't understand enough about what happens when a GeDialog launches. And how to do these tasks while it's launching. Or if it's even possible.Here is a working example of where I'm at currently with it.
Notes are at the top of the source code:https://sites.google.com/site/scottayersmedia/Dialog_StorageTag.zipAny tips or ideas on how to solve these two problems is much appreciated.
-ScottA
-
On 13/01/2013 at 12:08, xxxxxxxx wrote:
not so sure, if i am just misunderstading you again, but i do not really see the problem at least for
the first problem. if haven't searched very hard for all appearences of GeDialog.Open(). so there
might be more:Bool SimpleDialog::Execute(BaseDocument *doc) { StopAllThreads(); return dlg.Open(DLG_TYPE_ASYNC,DIALOG_PLUGIN_ID, -1, -1, 300,150); }
in python :
def Execute(self, doc) : # this is actually just covering objects, so no materials, tags and so on selection = doc.GetActiveObjects(0) if(selection) : result myDialog.Open(Blah, Blah, Blah)
note that there is also the overwriteable GetState Method, which disables the command
button itself when you retun False (this gray disabled thing) :edit :it is not False but 0, here a small example (not sure if False will work also) :
# 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
for the second one. you are talking about a bitmap stored inside the TagData class, not
the BitmapIcon of the tag itself ? And you want to get the bitmap of the currently selected
object / the custom tag of this object ?geting hold of the actual intance of this TagClass seems almost impossible for me without
writing an extra plugin for that but you could try your luck with a CoreMessage/Message.
the chain would be something like that :1. myCommandDataInstance.Execute:
myTag = GetSelectedObject().GetTagClassXYZ
myDialogInstance.PassDataMethod(myTag)2. myDialogInstance.PassDataMethod:
SpecialEventAdd(GetBitmapID, myTag)3. myTagInstance.CoreMessage:
if id == GetBitmapID and data = = self then SpecialEventAdd(SendBitmapID, bitmap)4. myDialogInstance.CoreMessage:
if id == SendBitmapID then myBitmap = data5. myCommandDataInstance.Execute:
myDialogInstance.Open6. myDialogInstance.Somewhere:
use myBitmapthis really does not sound like fun
-
On 13/01/2013 at 13:02, xxxxxxxx wrote:
Thanks ferdinand,
This seems to work ok for stopping the dialog from launching if no object is activeBool SimpleDialog::Execute(BaseDocument *doc) { StopAllThreads(); BaseObject *activeObj = doc->GetActiveObject(); if(activeObj) return dlg.Open(DLG_TYPE_ASYNC,DIALOG_PLUGIN_ID, -1, -1, 300,150); }
That's one problem solved.
As for the second problem of getting the GeDialog to read the saved image data in the tag plugin.
Yes. That's proving to be not much fun at all.-ScottA
-
On 13/01/2013 at 16:49, xxxxxxxx wrote:
Hi Scott,
I hadn't have the time to actually tak a close look at your code. But as for
Problem 2, why don't you just ask the Tag for the Icon? You could either
do this with a message or by calling a method on the tags' NodeData.
Here a small example about the Message approach (IMHO the better, this
would allow other developers to react on the message, too).struct MyMsgData { Bool supported; BaseBitmap* bmp; MyMsgData() : supported(FALSE), bmp(NULL); }; MyMsgData data; tag->Message(MSG_RETRIEVE_MY_BITMAPBUTTON_IMAGE, (void* ) &data); if (data.supported) { if (!data.bmp) data.bmp = defaultBmp; } else { // Tag does not support this operation (i.e. you don't want // your dialog to show up, eh?) }
Best,
NiklasSent from my smartphone, pls don't bug me for typos.
-
On 13/01/2013 at 18:20, xxxxxxxx wrote:
Ok. I won't give you a hard time about typos Niklas.
But I can't find anything even loosely resembling this in any form in the SDK:MSG_RETRIEVE_MY_BITMAPBUTTON_IMAGE
There are hundreds of messages in the SDK.
Maybe I'm just not seeing it due to the sheer volume of them?I would love it if I could simply ask the tag for the image from inside the dialog. But I can't figure out any way to do that.
-ScottA
-
On 13/01/2013 at 18:42, xxxxxxxx wrote:
it is meant to be a message ID you have registerd for you plugin, like the SendBitmapID and
GetBitmapID in my pseudocode example. you cannot find it in the sdk because it isn't thereyou will have to register a pluhinID here at the cafe which you could use then as your
plugins message ID. -
On 13/01/2013 at 21:01, xxxxxxxx wrote:
I tried using it like this: tag->Message(TAG_PLUGIN_ID, (void* ) &imgData);
with the other code Niklas posted. But it's still not working for me.Once I close the dialog. And then open it again. The image is gone from the button.
Even though the image is still saved inside the tag.-ScottA
-
On 14/01/2013 at 06:15, xxxxxxxx wrote:
Originally posted by xxxxxxxx
But I can't find anything even loosely resembling this in any form in the SDK:
MSG_RETRIEVE_MY_BITMAPBUTTON_IMAGE
There are hundreds of messages in the SDK.
Maybe I'm just not seeing it due to the sheer volume of them?Hello Scott,
MSG_RETRIEVE_MY_BITMAPBUTTON_IMAGE is not defined in any of the Cinema 4D headers. It is your own message type. You need to retrieve an id from the plugincafe for it to make sure it does not clash with any other message type and some NodeData recieving that message thinks it should work with it because it thinks the message you've sent is some other message (the message type it clashes with). The name is, btw, really awkward, I'm giving it another name in the code below..
So, here's again the theory I was thinking of, but more expanded (including partial implementation of what the TagData subclass should do).
#include <c4d.h> enum { // Registered at plugincafe.com ! PMSG_GETSAMPLEIMAGE = 1029665, }; class PMSG_GetSampleImageData { public: BaseBitmap* bmp; Bool supported; PMSG_GetSampleImageData() : bmp(NULL), supported(FALSE) {}; }; class MyDialog : public GeDialog { // {static} private: static BaseBitmap* defaultImage; public: static Bool InitStatic() { defaultImage = BaseBitmap::Alloc(); if (!defaultImage) { return FALSE; } // Load the default image, from wherever.. return TRUE; } // {instance} private: BitmapButtonCustomGui* bmpButton; public: // {GeDialog} Overrides Bool CreateLayout() { // Create layout and `bmpButton`. return TRUE; } Bool CoreMessage(LONG msgType, const BaseContainer& msg) { Bool result = GeDialog::Message(msgType, msg); switch (msgType) { case EVMSG_CHANGE: { BaseDocument* doc = GetActiveDocument(); BaseTag* tag = doc->GetActiveTag(); if (!tag) return TRUE; // As we use the message channels of Cinema 4D, we can // preform this action on any tag. PMSG_GetSampleImageData data; tag->Message(PMSG_GETSAMPLEIMAGE, (void* ) &data); Bool isSet = FALSE; if (data.supported) { if (data.bmp) { Bool copyImage = TRUE; bmpButton->setImage(data.bmp, copyImage); isSet = TRUE; } } if (!isSet) { bmpButton->setImage(GeDialog::defaultImage); } break; } default: break; } return result; } }; class MyTag : public TagData { // {instance} private: BaseBitmap* image; public: Bool Message(LONG msgType, void* msgData) { Bool result = TagData::Message(msgType, msgData); switch (msgType) { // Handle our custom message here ! case PMSG_GETSAMPLEIMAGE: { // C++11 Feature! auto data = (PMSG_GetSampleImageData* ) msgData; data->supported = TRUE; data->bmp = image; } default: break; } return result; } }; /* ... */ Bool PluginStart() { Bool success; success = MyDialog::InitStatic(); if (!success) { MessageDialog("MyDialog class could not initialize internals."); return FALSE; } // Register Command and Tag and stuff you need.. }
Note: I hope there are no syntax errors or typos in it, I didn't have the time to actually compile it, that is also why this is just a snippet.
General notes about your code
The following is based on the code you shared in [this post.
- Line 33:
myDialog *dlg;
I wonder why you would want to store a reference to another dialog in the dialog itself? - Line 52:
myDialog::~myDialog(void)
{
GeFree(dlg);
}
You are freeing an uninitialized variable here. I really wonder why Cinema doesn't crash when this destructor is called. - Line 230:
Bool SimpleTag::Write(GeListNode *node, HyperFile *hf)
{
hf->WriteImage(bmp,FILTER_JPG,NULL,SAVEBIT_0);
return TRUE;
}
The SDK does not state anything about passing a nullpointer for bmp. I would not assume it to be safe, therefore you should find another way than just passing the bitmap-pointer to this method. (E.g. first something like this hf->WriteBool(bmp != NULL); ) - Line 255:
void SimpleTag::createImage(void)
{
BaseDocument *doc = GetActiveDocument();
bmp = BaseBitmap::Alloc();
/* ... */
You do not free the bitmap that might have been allocated before (i.e. when createImage() is called twice, are an image has already been loaded from the HyperFile). You should add the following lines:// Check if we already have a bitmap stored on this tag. If not,
// we need to allocate a new bitmap.
if (bmp) {
bmp->FlushAll();
}
else {
bmp = BaseBitmap::Alloc();
}
if (!bmp) {
// Handle memory error here..
}Best,
Niklas - Line 33:
-
On 14/01/2013 at 09:20, xxxxxxxx wrote:
Thanks for the code Niklas.
I'm having a hard time with this: Bool result = GeDialog::Message(msgType, msg);
It's giving me errors because the Message() method that we override in these plugins is a LONG type. Not a Bool.So I have to figure out another way to write that line of code.
-ScottA
-
On 14/01/2013 at 10:08, xxxxxxxx wrote:
Hi Scott,
I'm very sorry, that is a typo. I was originally overriding Message() but then noticed that I need
to use CoreMessage(), changed the type only and forgot to change the parent-call. It should of
course be like the following:Bool result = GeDialog::CoreMessage(msgType, msgData);
I hope everything else is clear to you (and I didn't mess any other things up :P)
Best wishes,
Niklas -
On 14/01/2013 at 13:11, xxxxxxxx wrote:
No worries. Happens to me all the time.
I've got most of your code in place. But I'm afraid it's not working for me.
The Bitmap button is always blank.The only code I couldn't use at all was this:
Bool PluginStart() { Bool success; success = MyDialog::InitStatic(); if (!success) { MessageDialog("MyDialog class could not initialize internals."); return FALSE; } // Register Command and Tag and stuff you need.. }
I'm guessing you want me to put that in the main.cpp file?
I couldn't make it work. But it looks like that just handles a no image found scenario. And shouldn't stop the plugin from working if there's an image in the tag.You also used an Auto type C++ 11 feature that didn't work for me. I don't think the SDK supports this new C++ feature yet.
So I wrote it this way instead:switch (type) { // Handle our custom message here ! case PMSG_GETSAMPLEIMAGE: { // C++11 Feature! //auto data = (PMSG_GetSampleImageData* ) data; //<--Not supported by the SDK? PMSG_GetSampleImageData *data = gNew PMSG_GetSampleImageData; data->supported = TRUE; data->bmp = image; gDelete(data); } break; }
I'm not sure if it's correct. But is doesn't crash.
I see what you're trying to do. But it's not working for me.
I'm still a learner with this C++ stuff so I might be doing something wrong.Thanks anyway for trying to help,
-ScottA -
On 14/01/2013 at 14:44, xxxxxxxx wrote:
Hi Scott!
The PluginStart() function from above should just give you a hint that I intended
MyDialog::InitStatic() to be called so that MyDialog::defaultImage will be initialized (and
that it will be handled when this method returned FALSE). It should not cover your complete
PluginStart() implementation (didn't even take a close look at yours).The auto keyword is a C++11 feature supported by VC2010 and newer. It is just a shortcut
so you don't have to write the type identifier twice. Therefore, the correct non-C++11 version
of that line is as follows:PMSG_GetSampleImageData* data = (PMSG_GetSampleImageData* ) msgData;
while msgData is the second parameter from the MyTag::Message() method (being a void*
pointer that needs to be casted into the expected data-type). In your version of the code,
you do not pass the requested data to the informant (sender of the message), but instead,
you create a new data object temporarily and freeing it afterwards. This is why we pass the
address of our data object to the message so the tag can fill in the data and we can retrieve it.I'll try to set up some working and compiling code as soon as I have the time for it!
Best,
Niklas -
On 14/01/2013 at 15:28, xxxxxxxx wrote:
Ok. Thanks Niklas.
There's so many parts to these C++ plugins it's really hard to cover something like this with code snippets. It's not like sharing python code. There's too many places it can go wrong.
I had hoped that by providing a complete example it would lesson the burden of people trying to help me. And they could just edit my code without having to build it all from scratch.I appreciate the help,
-ScottA -
On 15/01/2013 at 04:40, xxxxxxxx wrote:
Hi Scott,
I've spent my last three hours working on a complete plugin. You definately owe
me a beer, okay? Windows x86 build is included. To build it on your own, you
must add threee new include paths to your VC project (I build from the command
line that is why the VC project is not included). These are ./ ./include and
../../resource/modules/xtensions/res/description/ (to include
drendersettings.h from PComUtils.h).When you open the dialog, you will see an image showing a red exclamation mark
with the text "not supported". This image is shown in the BitmapButton when
no Tag is selected or the Tag does not respond positive to the
MSG_PCOM_CANSETBITMAP message. When you create a PComTag on an object and
select it, the image shown in the dialog will turn into a green one with the
word "Render". This happens due to the fact that the PComTag responds positive
to the MSG_PCOM_CANSETBITMAP message and additionally does not have an
internal bitmap assigned yet. When you click on that "Render" image, it will
turn into an image of the current scene (just like in your example). The
MSG_PCOM_SETBITMAP message is sent and the PComTag will store the rendered
image internally. When selecting another Tag, you will again see the red
exclamation mark. Selecting the PComTag again will display the stored image.
The image is retrieved from a Tag by sending the MSG_PCOM_RETRIEVEBITMAP
message. Clicking on the button when the stored image is displayed will
remove the image from the tag by sending MSG_PCOM_SETBITMAP with a NULL pointer
instead of a valid address to a bitmap (which by definition of the message
type states, that the internal image should be removed).
https://docs.google.com/file/d/0B8i9BbP98pJRWC0tV3Rkb1BtRGc/editBest wishes,
Niklas -
On 15/01/2013 at 09:07, xxxxxxxx wrote:
Wow.That's really great.
Thanks a lot Niklas.Sorry it took up so much of your time.
-ScottA
-
On 25/01/2013 at 08:58, xxxxxxxx wrote:
Has anyone ever used friend classes within these C4D plugins?
In some cases it would be much less complicated if the two plugins simply had access to each others class members. Rather than using a third party interface to make them communicate.I've Googled about friend classes trying to learn how to write them.
But when I try to use them inside of these plugins. I can't get it to work.
I can set up the basic framework so it will compile without errors. But when I try to access the member variables from the other class. I get undefined type errors.Example:
//Forward declare the tag so the Dialog can access it class MyTag; class MyDialog : public GeDialog { public: virtual blah, blah, blah; virtual blah, blah, blah; LONG dialogMemberVariable; //A class member that the tag can also access friend MyTag; }; ... //How do I access use the tagMemberVariable without getting undefined class errors? class MyTag : public TagData { public: virtual blah, blah, blah; virtual blah, blah, blah; LONG tagMemberVariable; //A class member that the dialog can also access friend MyDialog; }; //How do I access use the dialogMemberVariable without getting undefined class errors? ...
-ScottA