1 /*=========================================================================
2 
3   Library:   CTK
4 
5   Copyright (c) Kitware Inc.
6 
7   Licensed under the Apache License, Version 2.0 (the "License");
8   you may not use this file except in compliance with the License.
9   You may obtain a copy of the License at
10 
11       http://www.apache.org/licenses/LICENSE-2.0.txt
12 
13   Unless required by applicable law or agreed to in writing, software
14   distributed under the License is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18 
19 =========================================================================*/
20 
21 // Qt includes
22 #include <QList>
23 #include <QString>
24 #include <QComboBox>
25 #include <QUiLoader>
26 #include <QApplication>
27 
28 // CTK includes
29 #include "ctkCmdLineModuleManager.h"
30 #include "ctkCmdLineModuleFrontendQtGui.h"
31 #include "ctkCmdLineModuleBackendFunctionPointer.h"
32 #include "ctkCmdLineModuleParameter.h"
33 #include "ctkCmdLineModuleParameterGroup.h"
34 #include "ctkCmdLineModuleDescription.h"
35 #include "ctkCmdLineModuleXslTransform.h"
36 #include "ctkCmdLineModuleFuture.h"
37 
38 #include "ctkTest.h"
39 
40 // ----------------------------------------------------------------------------
41 struct MyImageData : public ctk::CmdLineModuleBackendFunctionPointer::ImageType
42 {
MyImageDataMyImageData43   MyImageData(const QString& path = QString())
44     : Path(path)
45   {}
46 
47   QString Label;
48   QString Path;
49 };
50 
51 Q_DECLARE_METATYPE(MyImageData)
52 Q_DECLARE_METATYPE(const MyImageData*)
53 
54 // ----------------------------------------------------------------------------
55 namespace ctk {
56 namespace CmdLineModuleBackendFunctionPointer {
57 
58 template<>
GetParameterTypeName()59 QString GetParameterTypeName<MyImageData>()
60 {
61   return "image";
62 }
63 
64 }}
65 
66 // ----------------------------------------------------------------------------
67 class MyImageComboBox : public QComboBox
68 {
69   Q_OBJECT
70 
71 public:
72 
73   Q_PROPERTY(QString currentLabel READ currentLabel WRITE setCurrentLabel)
74   Q_PROPERTY(QString currentPath READ currentPath WRITE setCurrentPath)
75   Q_PROPERTY(const MyImageData* currentImage READ currentImage)
76 
77   MyImageComboBox(QWidget* parent = 0)
78     : QComboBox(parent)
79   {
80     imageData << MyImageData("/path/to/image1")
81               << MyImageData("/path/to/image2")
82               << MyImageData("/path/to/image3");
83 
84     this->addItem("Image 1");
85     this->addItem("Image 2");
86     this->addItem("Image 3");
87   }
88 
currentLabel() const89   QString currentLabel() const
90   {
91     return this->imageData.at(this->currentIndex()).Label;
92   }
93 
setCurrentLabel(const QString & label)94   void setCurrentLabel(const QString& label)
95   {
96     for(int i = 0; i < imageData.size(); ++i)
97     {
98       if (imageData[i].Label == label)
99       {
100         this->setCurrentIndex(i);
101         break;
102       }
103     }
104   }
105 
currentPath() const106   QString currentPath() const
107   {
108     return this->imageData.at(this->currentIndex()).Path;
109   }
110 
setCurrentPath(const QString & path)111   void setCurrentPath(const QString& path)
112   {
113     this->imageData[this->currentIndex()].Path = path;
114   }
115 
currentImage() const116   const MyImageData* currentImage() const
117   {
118     return &this->imageData.at(this->currentIndex());
119   }
120 
121 private:
122 
123   QList<MyImageData> imageData;
124 };
125 
126 
127 // ----------------------------------------------------------------------------
128 class MyQtGuiFrontend : public ctkCmdLineModuleFrontendQtGui
129 {
130 public:
MyQtGuiFrontend(const ctkCmdLineModuleReference & moduleRef)131   MyQtGuiFrontend(const ctkCmdLineModuleReference& moduleRef)
132     : ctkCmdLineModuleFrontendQtGui(moduleRef)
133   {}
134 
uiLoader() const135   QUiLoader* uiLoader() const
136   {
137     struct MyUiLoader : public QUiLoader {
138       QStringList availableWidgets() const
139       {
140         return QUiLoader::availableWidgets() << "MyImageComboBox";
141       }
142       QWidget* createWidget(const QString& className, QWidget* parent, const QString& name)
143       {
144         if (className == "MyImageComboBox")
145         {
146           MyImageComboBox* comboBox = new MyImageComboBox(parent);
147           comboBox->setObjectName(name);
148           comboBox->setCurrentIndex(1);
149           return comboBox;
150         }
151         return QUiLoader::createWidget(className, parent, name);
152       }
153     };
154     static MyUiLoader myUiLoader;
155     return &myUiLoader;
156   }
157 
xslTransform() const158   ctkCmdLineModuleXslTransform* xslTransform() const
159   {
160     static bool initialized = false;
161     ctkCmdLineModuleXslTransform* transform = ctkCmdLineModuleFrontendQtGui::xslTransform();
162     if (!initialized)
163     {
164       transform->bindVariable("imageInputWidget", "MyImageComboBox");
165       transform->bindVariable("imageValueProperty", "currentLabel");
166       static QFile extraXsl(":/MyImageComboBoxTest.xsl");
167       transform->setXslExtraTransformation(&extraXsl);
168       initialized = true;
169     }
170     return transform;
171   }
172 
value(const QString & parameter,int role=LocalResourceRole) const173   QVariant value(const QString &parameter, int role = LocalResourceRole) const
174   {
175     if (role == UserRole)
176     {
177       ctkCmdLineModuleParameter param = this->moduleReference().description().parameter(parameter);
178       if (param.channel() == "input" && param.tag() == "image")
179       {
180         return this->customValue(parameter, "currentImage");
181       }
182       return QVariant();
183     }
184     else if (role == LocalResourceRole)
185     {
186       return this->customValue(parameter, "currentPath");
187     }
188     else
189     {
190       return ctkCmdLineModuleFrontendQtGui::value(parameter, role);
191     }
192   }
193 
setValue(const QString & parameter,const QVariant & value,int role=DisplayRole)194   void setValue(const QString &parameter, const QVariant &value, int role = DisplayRole)
195   {
196     if (role == LocalResourceRole)
197     {
198       this->setCustomValue(parameter, value, "currentPath");
199     }
200     else
201     {
202       ctkCmdLineModuleFrontendQtGui::setValue(parameter, value, role);
203     }
204   }
205 };
206 
207 // ----------------------------------------------------------------------------
208 class MyFpBackend : public ctkCmdLineModuleBackendFunctionPointer
209 {
210 
211 protected:
212 
arguments(ctkCmdLineModuleFrontend * frontend) const213   QList<QVariant> arguments(ctkCmdLineModuleFrontend *frontend) const
214   {
215     QList<QVariant> args;
216     foreach(ctkCmdLineModuleParameter param, frontend->parameters())
217     {
218       QVariant arg = frontend->value(param.name(), ctkCmdLineModuleFrontend::UserRole);
219       if (!arg.isValid())
220       {
221         arg = frontend->value(param.name());
222       }
223       args << arg;
224     }
225 
226     return args;
227   }
228 };
229 
230 // ----------------------------------------------------------------------------
231 QString CustomImageDataPath;
CustomImageTypeModule(const MyImageData * imageData)232 void CustomImageTypeModule(const MyImageData* imageData)
233 {
234   CustomImageDataPath = imageData->Path;
235 }
236 
237 // ----------------------------------------------------------------------------
238 class ctkCmdLineModuleQtCustomizationTester: public QObject
239 {
240   Q_OBJECT
241 
242 private Q_SLOTS:
243 
244   void testCustomization();
245 
246 };
247 
248 // ----------------------------------------------------------------------------
testCustomization()249 void ctkCmdLineModuleQtCustomizationTester::testCustomization()
250 {
251   qRegisterMetaType<const MyImageData*>("const MyImageData*");
252 
253   ctkCmdLineModuleManager moduleManager;
254 
255   MyFpBackend fpBackend;
256   fpBackend.registerFunctionPointer("Image Type Customization", CustomImageTypeModule);
257 
258   moduleManager.registerBackend(&fpBackend);
259   QUrl url = fpBackend.registeredFunctionPointers().front();
260   moduleManager.registerModule(url);
261 
262   ctkCmdLineModuleReference moduleRef = moduleManager.moduleReference(url);
263 
264   ctkCmdLineModuleFrontend* fpFrontend = new MyQtGuiFrontend(moduleRef);
265   // force the creation of the frontend gui
266   fpFrontend->guiHandle();
267 
268   QString expectedImageValue = "/path/to/image2";
269   QString actualImageValue = fpFrontend->value("param0").toString();
270   QCOMPARE(actualImageValue, expectedImageValue);
271 
272   // get a custom QVariant value holding the custom widget
273   QCOMPARE(fpFrontend->value("param0", ctkCmdLineModuleFrontend::UserRole).value<const MyImageData*>()->Path,
274            expectedImageValue);
275 
276   // now set the property for the "LocalResourceRole" (the default property) to something else
277   expectedImageValue = "/tmp/path/to/image2";
278   fpFrontend->setValue("param0", expectedImageValue, ctkCmdLineModuleFrontend::LocalResourceRole);
279   QCOMPARE(fpFrontend->value("param0").toString(), expectedImageValue);
280 
281   QVERIFY(CustomImageDataPath.isEmpty());
282 
283   // run the module (function pointer) and check that is gets the tmp path
284   ctkCmdLineModuleFuture future = moduleManager.run(fpFrontend);
285   QTest::qSleep(500);
286   QApplication::processEvents();
287   future.waitForFinished();
288 
289   QCOMPARE(CustomImageDataPath, expectedImageValue);
290 }
291 
292 
293 // ----------------------------------------------------------------------------
294 CTK_TEST_MAIN(ctkCmdLineModuleQtCustomizationTest)
295 #include "moc_ctkCmdLineModuleQtCustomizationTest.cpp"
296