Guest mantragora Posted October 29, 2014 Share Posted October 29, 2014 (edited) I want to have possibility to list point or primitive attributes in my menu according to type user will pick. Lets say that my operator default state for attribute type is Point attrbute which I got covered by PRM_Template(PRM_STRING, 1, &attributeParm_Name, PRMzeroDefaults, &SOP_Mantragora::pointAttribMenu); and user switched type to Primitive. Should I update the menu with PRM_Template::setChoiceListPtr(&SOP_Mantragora::primAttribMenu) ? If so, where I should do this? Putting it in cook() method didn't worked. EDIT: The same case is for groups. There is SOP_Mantragora::groupMenu But it lists all group types at once. And I want to have listed only those specified by user and switch between SOP_Mantragora::pointGroupMenu and SOP_Mantragora::primGroupMenu Thanks Edited October 31, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
edward Posted October 29, 2014 Share Posted October 29, 2014 Make your own callback to decide which one (primGroupMenu or pointGroupMenu) to call. Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted October 29, 2014 Share Posted October 29, 2014 (edited) You mean something similar to example for dynamically generated items that we can find -> http://www.sidefx.com/docs/hdk13.0/_h_d_k__op_basics__overview__parameters.html#HDK_OpBasics_Overview_Parameters_Types ? From Help: static void sopBuildRestGroup(void *data, PRM_Name *choicenames, int listsize, const PRM_SpareData *spare, PRM_Parm *parm) { SOP_Node *sop = CAST_SOPNODE((OP_Node *)data); if( sop ) { sop->buildInputGroups( SOP_REST_INPUT, choicenames, listsize, PRIM_GROUP, 0, true, parm ); } else { choicenames[0].setToken(0); choicenames[0].setLabel(0); } } static PRM_ChoiceList sopRestGroupMenu(PRM_CHOICELIST_TOGGLE, sopBuildRestGroup); Is this the way to go?WTF is this "void *data" or "const PRM_SpareData *spare"? EDIT: I don't get how I should build attributes list. When Parameters are created they don't know on which operator they will land. So I think I need to update them after they are added to list of parameters of the operator so I can evaluate the Type the user picked. Are there any other examples somewhere in HDK examples? Edited October 29, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted October 29, 2014 Share Posted October 29, 2014 (edited) This is what I got: static auto Update_GroupMenu(void* data, PRM_Name* choicenames, int listsize, const PRM_SpareData* spare, PRM_Parm* parm) -> void { } static auto Update_AttributeMenu(void* data, PRM_Name* choicenames, int listsize, const PRM_SpareData* spare, PRM_Parm* parm) -> void { } static auto templateGroupParm_ChoiceList = PRM_ChoiceList(PRM_CHOICELIST_TOGGLE, Update_GroupMenu); static auto attributeParm_ChoiceList = PRM_ChoiceList(PRM_CHOICELIST_TOGGLE, Update_AttributeMenu); auto templateGroup_Parameter = PRM_Template(PRM_STRING, 1, &templateGroupParm_Name, PRMzeroDefaults, &templateGroupParm_ChoiceList); auto attribute_Parameter = PRM_Template(PRM_STRING, 1, &attributeParm_Name, PRMzeroDefaults, &attributeParm_ChoiceList); I'm getting error "No instance of constructor PRM_ChoiceList... maches the argument list". There are two constructors I think i can target:1. PRM_ChoiceList (PRM_ChoiceListType thetype, PRM_ChoiceItemGenFunc thefunc) 2. PRM_ChoiceList (PRM_ChoiceListType thetype, PRM_ChoiceGenFunc thefunc) I looked at typedefs of PRM_ChoiceItemGenFunc()/PRM_ChoiceGenFunc() and it looks that my functions matched argument list correctly and the return type is also good. So what I'm doing wrong? Edited October 30, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
edward Posted October 29, 2014 Share Posted October 29, 2014 Your signature doesn't look like it matches to me. I think you're misunderstanding the nature of the callback. It doesn't "update" a menu, it "generates" the items for the menu when the user opens it. For node PRM_ChoiceList's, the void *data will always be the owning node pointer. Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted October 30, 2014 Share Posted October 30, 2014 (edited) Oh, come on. Not my fault that there is an error in HDK example. It's YOURS signature that doesn't match Missed const keyword in last *PRM_Parm argument of both functions. Now it compiles. Will see what happens next. Thanks! Edited October 30, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
edward Posted October 30, 2014 Share Posted October 30, 2014 Which HDK example are you talking about? We have regression tests for the HDK samples so I'd be very surprised that there's one that doesn't compile. Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted October 30, 2014 Share Posted October 30, 2014 Which HDK example are you talking about? We have regression tests for the HDK samples so I'd be very surprised that there's one that doesn't compile. The one below. Code example is straight from there. No const in last argument. You mean something similar to example for dynamically generated items that we can find -> http://www.sidefx.com/docs/hdk13.0/_h_d_k__op_basics__overview__parameters.html#HDK_OpBasics_Overview_Parameters_Types ? From Help: static void sopBuildRestGroup(void *data, PRM_Name *choicenames, int listsize, const PRM_SpareData *spare, PRM_Parm *parm) { } static PRM_ChoiceList sopRestGroupMenu(PRM_CHOICELIST_TOGGLE, sopBuildRestGroup); Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted October 30, 2014 Share Posted October 30, 2014 (edited) Now I got this: auto SOP_Mantragora::Update_GroupMenu(void* data, PRM_Name* choicenames, int listsize, const PRM_SpareData* spare, const PRM_Parm* parm) -> void { auto sop = CAST_SOPNODE((OP_Node*) data); if (sop) { const PRM_Name* names; cout << parm->getToken() << endl; SOP_Mantragora::pointGroupMenu.getChoiceNames(names, data, spare, parm); SOP_Mantragora::primGroupMenu.getChoiceNames(names, data, spare, parm); choicenames[0].setToken(names->getToken()); choicenames[0].setLabel(names->getLabel()); } else { choicenames[0].setToken(0); choicenames[0].setLabel(0); } } auto SOP_Mantragora::Update_AttributeMenu(void* data, PRM_Name* choicenames, int listsize, const PRM_SpareData* spare, const PRM_Parm* parm) -> void { auto sop = CAST_SOPNODE((OP_Node*) data); if (sop) { const PRM_Name* names; cout << parm->getToken() << endl; SOP_Mantragora::pointAttribMenu.getChoiceNames(names, data, spare, parm); SOP_Mantragora::primAttribMenu.getChoiceNames(names, data, spare, parm); choicenames[0].setToken(names->getToken()); choicenames[0].setLabel(names->getLabel()); } else { choicenames[0].setToken(0); choicenames[0].setLabel(0); } } SOP_Mantragora is my abstract base class from which I derive all my operators and this is the class in which I place all methods that I can reuse between other operators. At this point I know for sure that I will use the same functions in one other operator.Now, I can get the name of the parameter that calls those callback functions, but how do I know that I have to switch between SOP_Mantragora::pointAttribMenu and SOP_Mantragora::primAttribMenu if I can't evaluate my other parameter on which users sets the class of attribute/group? My SOP_Mantragora doesn't know about parameters that are on its child classes. Any tips? Edited October 30, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
edward Posted October 30, 2014 Share Posted October 30, 2014 Oh, well, that's just a docs bug then. The source code files all compile. Your base class should have a simplified method that takes the parameter name to evaluate. Put the actual callbacks themselves in the subclass. Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted October 31, 2014 Share Posted October 31, 2014 (edited) Your base class should have a simplified method that takes the parameter name to evaluate. Put the actual callbacks themselves in the subclass. So, since I do have this layer above SOP_Node thanks to my SOP_Mantragora, and because void* data is always the owning node I could safely make something like this: auto sop = CAST_SOPNODE((OP_Node*) data); if (sop) { auto sop_mantragora = (SOP_Mantragora*) sop; } to access all methods from my base class that are not static.So instead of moving Update_GroupMenu() and Update_AttributeMenu() to subclass I left them in SOP_Mantragora and just made additional helper methods which I can override in my subclass, that will just give correct data to those callback methods by evaluating correct parameter on this subclassed node. virtual auto Eval_IntParmForUpdateGroupMenu(exint& val, fpreal time = 0) -> void; auto SOP_Mantragora::Get_IntParmForUpdateGroupMenu(exint& val, fpreal time /* = 0 */) -> void { // I don't wont it to be pure virtual method so lets add some error to remind myself that it needs to be implemented. cout << "Get_IntParmForUpdateGroupMenu() method need to be overriden in childclass before it can be called!" << endl; val = -1; } So finished version of Callback Update_GroupMenu() method: auto SOP_Mantragora::Update_GroupMenu(void* data, PRM_Name* choicenames, int listsize, const PRM_SpareData* spare, const PRM_Parm* parm) -> void { // We need to get our SOP node auto sop = CAST_SOPNODE((OP_Node*) data); if (sop) { // Since we know that we have SOP, we can safely cast it to my Base node class auto sop_mantragora = (SOP_Mantragora*) sop; // Extract data based on group class type fpreal currentTime = CHgetEvalTime(); const PRM_Name* names; exint groupType; sop_mantragora->Get_IntParmForUpdateGroupMenu(groupType, currentTime); switch (groupType) { case 0: SOP_Mantragora::pointGroupMenu.getChoiceNames(names, data, spare, parm); break; case 1: SOP_Mantragora::primGroupMenu.getChoiceNames(names, data, spare, parm); break; default: cout << "WTF? Maybe Get_IntParmForUpdateGroupMenu() is not implemented in child class?" << endl; break; } // Set data choicenames[0].setToken(names->getToken()); choicenames[0].setLabel(names->getLabel()); } else { choicenames[0].setToken(0); choicenames[0].setLabel(0); } } Edited October 31, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted October 31, 2014 Share Posted October 31, 2014 (edited) Hcuston, we got a problem. I got small method for finding point/primitive attributes to which I can pass PRM_Template which is used to extract string from UI, then there are some string checks performed and attribute test performed. I have something similar for groups too. First on my operator I'm checking group, and if there is no text in group field (empty) operator turns red, but the Update_GroupMenu() method still correctly fills dropdown menu so I can pick group. Second check is for attributes, and if there is no text in attribute text field operator also turn red, but the dropdown menu is not filled. If I instead change it from addError() to addWarning() it fills dropdown correctly. But I don't want to have warning there because attribute is kinda very, very needed for rest of the code in the operator. So I want to have RED error but I also would like to have dropdown working, just like with group field. And yes. String checking method is the same for both, group and attribute text field. Any ideas why it works for groups and not for attributes in error state? Edited October 31, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted October 31, 2014 Share Posted October 31, 2014 I looked at other operators and it looks that standard behavior for those that requires some group or attribute is to just set warning state, not error. Error seems to be for things like missing input. So I changed my code and I'm reporting warnings everywhere and allowing execution of code if it's anything below UT_ERROR_WARNING. Quote Link to comment Share on other sites More sharing options...
edward Posted November 1, 2014 Share Posted November 1, 2014 First, you shouldn't take the list of attributes from your own geometry. You should call getCookedGeoHandle() on your input node. Secondly, empty group parameters are usually taken to mean "everything" and so they wouldn't trigger errors nor warnings. Depending on your use case, you may or may not want to have the "empty semantic" for your attributes parameter as well. 1 Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted November 2, 2014 Share Posted November 2, 2014 (edited) First, you shouldn't take the list of attributes from your own geometry. You should call getCookedGeoHandle() on your input node. I'm lost. Where I should call it? In my Update_AttributeMenu() method? Then I have to pass context there, or is there some other way it shoudl be done? And, if I have to pass something to Update_AttributeMenu(), wouldn't it be easier to just pass there GA_AttributeDict instead of playing with Handle and locking unlocking geometry there? Last thing, I'm not modyfing geometry (so only duplicatePointSource()) in this operator so the attributes in inputGeo and in this operator geo are exactly the same. So do I have to always get attributes from input geo even in this example? And if I shouldn't take attributes from my geometry than in what situations SOP_Node::pointAttribMenu and SOP_Node::primAttribMenu should be used? Edited November 2, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
edward Posted November 2, 2014 Share Posted November 2, 2014 If you're just calling pointAttribMenu(), then it already cooks from the first input. So offhand, I don't see why adding an error on your node will prevent the menu contents from being filled out. Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted November 2, 2014 Share Posted November 2, 2014 (edited) Ok, I stripped the code as much as I could. This is my cook(): auto SOP_Test_Operator::cookMySop(OP_Context& context) -> OP_ERROR { UT_AutoInterrupt progress("Performing Operation"); fpreal currentTime = context.getTime(); // to access input we need to lock it if (lockInputs(context) >= UT_ERROR_ABORT) return error(); // to modify geometry we need to make copy of it duplicatePointSource(0, context); // action! UT_String groupName; evalString(groupName, SOP_Test_PARAMETERS::group_Parameter.getToken(), 0, currentTime); UT_String attributeName; evalString(attributeName, SOP_Test_PARAMETERS::attribute_Parameter.getToken(), 0, currentTime); cout << "Group name: " << groupName << endl; cout << "Attribute name: " << attributeName << endl; cout << "Error() before string check: " << error() << endl; exint reportType = evalInt(SOP_Test_PARAMETERS::reportTypeMenu_Parameter.getToken(), 0, currentTime); if (!groupName.isstring()) { switch (reportType) { case 0: addWarning(SOP_ERR_BADGROUP, "FAIL"); break; default: addError(SOP_ERR_BADGROUP, "FAIL"); break; } } if (!attributeName.isstring()) { switch (reportType) { case 0: addWarning(SOP_ERR_INVALID_ATTRIBUTE_NAME, "FAIL"); break; default: addError(SOP_ERR_INVALID_ATTRIBUTE_NAME, "FAIL"); break; } } cout << "Error() after string check: " << error() << endl; if (error() < UT_ERROR_WARNING) cout << "Now we can test more!" << endl; unlockInputs(); return error(); } My parameters: namespace SOP_Test_PARAMETERS { static auto reportTypeMenuParm_Name = PRM_Name("reporttype", "Report Type"); static auto groupParm_Name = PRM_Name("group", "Group"); static auto attributeParm_Name = PRM_Name("attribute", "Attribute"); static PRM_Name reportTypeMenuParm_Choices[] = { PRM_Name("addwarning", "AddWarning"), PRM_Name("adderror", "AddError"), PRM_Name(0) }; static auto reportTypeMenuParm_ChoiceList = PRM_ChoiceList(PRM_CHOICELIST_SINGLE, reportTypeMenuParm_Choices); // -------------------------- Parameters // auto reportTypeMenu_Parameter = PRM_Template(PRM_ORD, 1, &reportTypeMenuParm_Name, 0, &reportTypeMenuParm_ChoiceList); auto group_Parameter = PRM_Template(PRM_STRING, 1, &groupParm_Name, PRMzeroDefaults, &SOP_Node::pointGroupMenu); auto attribute_Parameter = PRM_Template(PRM_STRING, 1, &attributeParm_Name, PRMzeroDefaults, &SOP_Node::pointAttribMenu); } If I change error report type to AddWarning it fills menu in both, group and attribute menu, with AddError it fill it only with group menu while attribute menu is blank. Edited November 4, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted November 4, 2014 Share Posted November 4, 2014 (edited) Could anyone confirm that he gets the same wrong behavior when using SOP_Node::pointAttribMenu and correct when using SOP_Node::pointGroupMenu? So I could log error to SESI. Stalkerx777, R U THERE? Edited November 4, 2014 by mantragora Quote Link to comment Share on other sites More sharing options...
Guest mantragora Posted November 5, 2014 Share Posted November 5, 2014 Well, it looks that we got a bug. Ticket #22613. This should be fixed in Houdini 13.0.586 All the best, Support. 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.