Skip to content

Commit f63df6e

Browse files
committed
Implement save/restore for MLClient node in Nuke
The MLClient node now has a custom knob to save and restore the current model selection and its parameters state.
1 parent d2d1ebe commit f63df6e

File tree

4 files changed

+243
-1
lines changed

4 files changed

+243
-1
lines changed

Plugins/Client/MLClient.cpp

+79-1
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,30 @@ bool MLClient::refreshModelsAndKnobsFromServer(std::string& errorMsg)
270270
_chosenModel = 0;
271271
}
272272

273+
// We try to select the model saved in the serial serialiseKnob if any.
274+
bool restoreModel = false;
275+
MLClientModelKnob* modelKnob = nullptr;
276+
DD::Image::Knob* k = knob("serialiseKnob");
277+
if(k != nullptr) {
278+
modelKnob = dynamic_cast<MLClientModelKnob*>(k);
279+
if(modelKnob != nullptr) {
280+
std::string modelLabel = modelKnob->getModel();
281+
if(modelLabel != "") {
282+
const std::vector<std::string>& models = pSelectModelEnum->menu();
283+
int i = 0;
284+
for(auto& m : models) {
285+
if(m == modelLabel) {
286+
_chosenModel = i;
287+
_selectedModelknob->set_value(_chosenModel);
288+
restoreModel = true;
289+
break;
290+
}
291+
i++;
292+
}
293+
}
294+
}
295+
}
296+
273297
// Set member variables to indicate our connections and model set-up succeeded.
274298
_modelSelected = true;
275299
_showDynamic = true;
@@ -280,10 +304,52 @@ bool MLClient::refreshModelsAndKnobsFromServer(std::string& errorMsg)
280304
getModelManager().parseOptions(m);
281305
}
282306
setNumNewKnobs(replace_knobs(knob("models"), getNumNewKnobs(), addDynamicKnobs, this->firstOp()));
307+
308+
// If we have restored a model, we also need to restore its parameters
309+
// now that its knobs have been created,
310+
if(restoreModel && (modelKnob != nullptr)) {
311+
for(const std::pair<std::string, std::string>& keyVal: modelKnob->getParameters()) {
312+
restoreKnobValue(keyVal.first, keyVal.second);
313+
}
314+
}
315+
283316
// Return true if control made it here, success.
284317
return true;
285318
}
286319

320+
void MLClient::restoreKnobValue(const std::string& knobName, const std::string& value)
321+
{
322+
// We look for the corresponding knob
323+
DD::Image::Knob* paramKnob = knob(knobName.c_str());
324+
if(paramKnob != nullptr) {
325+
// Is this an animation curve?
326+
if(value.substr(0, 6) == "{curve") {
327+
// This is a curve, we remove the { }
328+
std::string curveString = value.substr(1, value.find("}") - 1);
329+
paramKnob->set_animation(curveString.c_str(), 0);
330+
}
331+
else if(value.substr(0, 1) == "{") {
332+
// That's an expression
333+
std::string expressionString = value.substr(1, value.find("}") - 1);
334+
// If the expression is within double quote, we need to extract it
335+
if(expressionString.substr(0, 1) == "\"") {
336+
expressionString.erase(0, 1);
337+
expressionString = expressionString.substr(0, expressionString.find("\""));
338+
} else {
339+
// The expression might be followed by keys that we ignore here.
340+
if(expressionString.find(" ") != std::string::npos) {
341+
expressionString = expressionString.substr(0, expressionString.find(" "));
342+
}
343+
}
344+
paramKnob->set_expression(expressionString.c_str(), 0);
345+
}
346+
else {
347+
// That's one value
348+
paramKnob->set_text(value.c_str());
349+
}
350+
}
351+
}
352+
287353
//! Return whether we successfully managed to pull model
288354
//! info from the server at some time in the past, and the selected model is
289355
//! valid.
@@ -537,25 +603,29 @@ void MLClient::addDynamicKnobs(void* p, Knob_Callback f)
537603
std::string name = ((MLClient *)p)->getModelManager().getDynamicIntName(i);
538604
std::string label = ((MLClient *)p)->getModelManager().getDynamicIntName(i);
539605
Int_knob(f, ((MLClient *)p)->getModelManager().getDynamicIntValue(i), name.c_str(), label.c_str());
606+
SetFlags(f, Knob::DO_NOT_WRITE);
540607
Newline(f, " ");
541608
}
542609
for (int i = 0; i < ((MLClient *)p)->getModelManager().getNumOfFloats(); i++) {
543610
std::string name = ((MLClient *)p)->getModelManager().getDynamicFloatName(i);
544611
std::string label = ((MLClient *)p)->getModelManager().getDynamicFloatName(i);
545612
Float_knob(f, ((MLClient *)p)->getModelManager().getDynamicFloatValue(i), name.c_str(), label.c_str());
546613
ClearFlags(f, Knob::SLIDER);
614+
SetFlags(f, Knob::DO_NOT_WRITE);
547615
Newline(f, " ");
548616
}
549617
for (int i = 0; i < ((MLClient *)p)->getModelManager().getNumOfBools(); i++) {
550618
std::string name = ((MLClient *)p)->getModelManager().getDynamicBoolName(i);
551619
std::string label = ((MLClient *)p)->getModelManager().getDynamicBoolName(i);
552620
Bool_knob(f, ((MLClient *)p)->getModelManager().getDynamicBoolValue(i), name.c_str(), label.c_str());
621+
SetFlags(f, Knob::DO_NOT_WRITE);
553622
Newline(f, " ");
554623
}
555624
for (int i = 0; i < ((MLClient *)p)->getModelManager().getNumOfStrings(); i++) {
556625
std::string name = ((MLClient *)p)->getModelManager().getDynamicStringName(i);
557626
std::string label = ((MLClient *)p)->getModelManager().getDynamicStringName(i);
558627
String_knob(f, ((MLClient *)p)->getModelManager().getDynamicStringValue(i), name.c_str(), label.c_str());
628+
SetFlags(f, Knob::DO_NOT_WRITE);
559629
Newline(f, " ");
560630
}
561631
for (int i = 0; i < ((MLClient *)p)->getModelManager().getNumOfButtons(); i++) {
@@ -570,7 +640,11 @@ void MLClient::addDynamicKnobs(void* p, Knob_Callback f)
570640
void MLClient::knobs(Knob_Callback f)
571641
{
572642
String_knob(f, &_host, "host");
643+
SetFlags(f, Knob::ALWAYS_SAVE);
644+
573645
Int_knob(f, &_port, "port");
646+
SetFlags(f, Knob::ALWAYS_SAVE);
647+
574648
Button(f, "connect", "Connect");
575649
Divider(f, " ");
576650
static const char* static_choices[] = {
@@ -581,6 +655,11 @@ void MLClient::knobs(Knob_Callback f)
581655
}
582656
SetFlags(f, Knob::SAVE_MENU);
583657

658+
// We create a knob to save/load the current state of the dynamic knobs.
659+
if(f.makeKnobs()) {
660+
CustomKnob1(MLClientModelKnob, f, this, "serialiseKnob");
661+
}
662+
584663
if (!f.makeKnobs()) {
585664
MLClient::addDynamicKnobs(this->firstOp(), f);
586665
}
@@ -654,7 +733,6 @@ int MLClient::knob_changed(Knob* knobChanged)
654733
mlserver::StringAttrib pythonScript = object.values(0).string_attributes(0);
655734
// Run Python Script in Nuke
656735
if (pythonScript.values_size() != 0) {
657-
std::cout << " cmd=\n" << pythonScript.values(0) << "\n" << std::flush;
658736
script_command(pythonScript.values(0).c_str(), true, false);
659737
script_unlock();
660738
}

Plugins/Client/MLClient.h

+4
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ class MLClient : public DD::Image::PlanarIop
121121
//! Return whether the dynamic knobs should be shown or not.
122122
bool getShowDynamic() const;
123123

124+
//! Look for the knob with the given name. If found, restore its value
125+
//! from the given serialised value.
126+
void restoreKnobValue(const std::string& knobName, const std::string& value);
127+
124128
private:
125129
std::string _host;
126130
bool _hostIsValid;

Plugins/Client/MLClientModelManager.cpp

+118
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,124 @@
1515

1616
#include "MLClientModelManager.h"
1717
#include "DDImage/Knob.h"
18+
#include "MLClient.h"
19+
20+
MLClientModelKnob::MLClientModelKnob(DD::Image::Knob_Closure* kc, DD::Image::Op* op, const char* name)
21+
: DD::Image::Knob(kc, name)
22+
, _op(op)
23+
, _model("")
24+
{ }
25+
26+
const char* MLClientModelKnob::Class() const
27+
{
28+
return "MLClientModelKnob";
29+
}
30+
31+
bool MLClientModelKnob::not_default () const
32+
{
33+
// Always flag as not default, so it's always serialised.
34+
return true;
35+
}
36+
37+
std::string MLClientModelKnob::getModel() const
38+
{
39+
return _model;
40+
}
41+
42+
const std::map<std::string, std::string>& MLClientModelKnob::getParameters() const
43+
{
44+
return _parameters;
45+
}
46+
47+
void MLClientModelKnob::to_script (std::ostream &out, const DD::Image::OutputContext *, bool quote) const
48+
{
49+
std::string saveString;
50+
std::stringstream ss;
51+
if (_op != nullptr) {
52+
DD::Image::Knob* k = _op->knob("models");
53+
if(k != nullptr) {
54+
const int modelIndex = k->get_value();
55+
DD::Image::Enumeration_KnobI* eKnob = k->enumerationKnob();
56+
if(eKnob != nullptr) {
57+
ss << "model:" << eKnob->getItemValueString(modelIndex) << ";";
58+
MLClient* mlClient = dynamic_cast<MLClient*>(_op);
59+
if(mlClient != nullptr) {
60+
MLClientModelManager& mlManager = mlClient->getModelManager();
61+
toScriptT(mlManager, ss, &MLClientModelManager::getNumOfInts, &MLClientModelManager::getDynamicIntName);
62+
toScriptT(mlManager, ss, &MLClientModelManager::getNumOfFloats, &MLClientModelManager::getDynamicFloatName);
63+
toScriptT(mlManager, ss, &MLClientModelManager::getNumOfBools, &MLClientModelManager::getDynamicBoolName);
64+
toScriptStrings(mlManager, ss);
65+
}
66+
}
67+
}
68+
}
69+
saveString = ss.str();
70+
if(quote) {
71+
saveString.insert(saveString.begin(),'{');
72+
saveString+='}';
73+
}
74+
out << saveString;
75+
}
76+
77+
bool MLClientModelKnob::from_script(const char * src)
78+
{
79+
std::string loadString(src);
80+
81+
if ((_op != nullptr) && (loadString!="")) {
82+
bool success = false;
83+
84+
// We parse the serialised string to extract the pairs of key:val;
85+
const std::string delimiter = ";";
86+
const std::string keyValDelimiter = ":";
87+
_parameters.clear();
88+
size_t pos = 0;
89+
std::string token;
90+
while ((pos = loadString.find(delimiter)) != std::string::npos) {
91+
token = loadString.substr(0, pos);
92+
std::cout << token << std::endl;
93+
94+
// We further split the key:value pair
95+
std::string key = token.substr(0, token.find(keyValDelimiter));
96+
std::string val = token.substr(token.find(keyValDelimiter) + keyValDelimiter.length(), token.length() - key.length() - keyValDelimiter.length());
97+
if(key == "model") {
98+
_model = val;
99+
} else {
100+
_parameters.insert(std::make_pair(key, val));
101+
}
102+
103+
loadString.erase(0, pos + delimiter.length());
104+
}
105+
106+
return success;
107+
}
108+
return true;
109+
}
110+
111+
void MLClientModelKnob::toScriptT(MLClientModelManager& mlManager, std::ostream &out,
112+
int (MLClientModelManager::*getNum)() const,
113+
std::string (MLClientModelManager::*getDynamicName)(int)) const
114+
{
115+
const int num = (mlManager.*getNum)();
116+
for(int i = 0; i < num; i++) {
117+
DD::Image::Knob* k = _op->knob((mlManager.*getDynamicName)(i).c_str());
118+
if(k != nullptr) {
119+
std::stringstream ss;
120+
k->to_script(ss, nullptr, false);
121+
out << (mlManager.*getDynamicName)(i) << ":" << ss.str() << ";";
122+
}
123+
}
124+
}
125+
126+
void MLClientModelKnob::toScriptStrings(MLClientModelManager& mlManager, std::ostream &out) const
127+
{
128+
const int numFloats = mlManager.getNumOfStrings();
129+
for(int i = 0; i < numFloats; i++) {
130+
DD::Image::Knob* k = _op->knob(mlManager.getDynamicStringName(i).c_str());
131+
if(k != nullptr) {
132+
out << mlManager.getDynamicStringName(i) << ":" << k->get_text() << ";";
133+
}
134+
}
135+
}
18136

19137
MLClientModelManager::MLClientModelManager(DD::Image::Op* parent)
20138
: _parent(parent)

Plugins/Client/MLClientModelManager.h

+42
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,48 @@
2121
#include "message.pb.h"
2222

2323
#include "DDImage/Op.h"
24+
#include "DDImage/Knobs.h"
25+
26+
class MLClientModelManager;
27+
28+
//! The role of this custom knob is to serialise and to store the selected model and its parameters.
29+
//! As these exist as dynamic knobs, this is to workaround the fact that we would need to know about these knobs
30+
//! in advance to save them the usual way.
31+
class MLClientModelKnob : public DD::Image::Knob
32+
{
33+
public:
34+
MLClientModelKnob(DD::Image::Knob_Closure* kc, DD::Image::Op* op, const char* name);
35+
36+
// Knob overrides.
37+
const char* Class() const override;
38+
bool not_default () const override;
39+
//! Serialises the currently selected model and its parameters as follows:
40+
//! {model:modelName;param1:value1;param2:value2}
41+
void to_script (std::ostream &out, const DD::Image::OutputContext *, bool quote) const override;
42+
//! Deserialises the saved model and its parameters.
43+
//! The model can then be retreived with getModel()
44+
//! and the dictionary of parameters with getParameters().
45+
bool from_script(const char * src) override;
46+
47+
std::string getModel() const;
48+
const std::map<std::string, std::string>& getParameters() const;
49+
50+
private:
51+
//! Serialises the dynamic knobs to the given output stream.
52+
//! This function is generic for the Ints, Floats and Bools knobs
53+
//! provided that the corresponding getNumOfT and getDynamicTName
54+
//! functions are given.
55+
void toScriptT(MLClientModelManager& mlManager, std::ostream &out,
56+
int (MLClientModelManager::*getNum)() const,
57+
std::string (MLClientModelManager::*getDynamicName)(int)) const;
58+
//! Serialises the dynamic knobs containing strings to the given output stream.
59+
void toScriptStrings(MLClientModelManager& mlManager, std::ostream &out) const;
60+
61+
private:
62+
DD::Image::Op* _op;
63+
std::string _model;
64+
std::map<std::string, std::string> _parameters;
65+
};
2466

2567
//! Class to parse and store knobs for a given model.
2668
class MLClientModelManager

0 commit comments

Comments
 (0)