WMTemplate.cpp 55.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------

25 26 27 28 29 30 31
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// How to run this module in OpenWalnut:
//   * Download a sample Dataset (http://www.informatik.uni-leipzig.de/~wiebel/public_data/walnut/walnut_masked.nii.gz)
//   * Load the dataset by dragging it into OpenWalnut
//   * Click on it and add the template module
//     * This can be done via right-click menu or the module toolbar below the menu
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
32 33 34

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// How to create your own module in OpenWalnut? Here are the steps to take:
35 36
//   * Setup your module building framework
//     * See http://www.openwalnut.org/projects/openwalnut/wiki/Module_ExternalDevelopment for details
Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
37 38 39 40
//   * copy the template module directory
//   * think about a name for your module
//   * rename the files from WMTemplate.cpp and WMTemplate.h to WMYourModuleName.cpp and WMYourModuleName.h
//   * rename the class inside these files to WMYourModuleName
41
//   * rename the class inside "W_LOADABLE_MODULE" to WMYourModuleName
Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
42 43
//   * change WMYourModuleName::getName() to a unique name, like "Your Module Name"
//   * read the documentation in this module and modify it to your needs
44
//   * compile
Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
45 46 47 48 49 50 51 52 53 54
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Some rules to the inclusion of headers:
//  * Ordering:
//    * C Headers
//    * C++ Standard headers
//    * External Lib headers (like OSG or Boost headers)
//    * headers of other classes inside OpenWalnut
//    * your own header file

55 56 57 58 59 60 61 62
#include <string>

#include <osg/ShapeDrawable>
#include <osg/Group>
#include <osg/Geode>
#include <osg/Material>
#include <osg/StateAttribute>

63
#include "core/kernel/WKernel.h"
64
#include "core/common/exceptions/WFileNotFound.h"
65 66 67
#include "core/common/WColor.h"
#include "core/common/WPathHelper.h"
#include "core/common/WPropertyHelper.h"
68 69 70 71
#include "core/common/WItemSelection.h"
#include "core/common/WItemSelectionItem.h"
#include "core/common/WItemSelectionItemTyped.h"
#include "core/common/WItemSelector.h"
72 73
#include "core/graphicsEngine/WGEUtils.h"
#include "core/graphicsEngine/WGERequirement.h"
74

75 76 77
#include "icons/bier.xpm"
#include "icons/wurst.xpm"
#include "icons/steak.xpm"
Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
78
#include "WMTemplate.h"
79

80 81
// This line is needed by the module loader to actually find your module. You need to add this to your module too. Do NOT add a ";" here.
W_LOADABLE_MODULE( WMTemplate )
82

83 84 85
WMTemplate::WMTemplate():
    WModule()
{
86 87 88 89
    // In the constructor, you can initialize your members and all this stuff. You must not initialize connectors or properties here! You also
    // should avoid doing computationally expensive stuff, since every module has its own thread which is intended to be used for such calculations.
    // Please keep in mind, that every member initialized here is also initialized in the prototype, which may be a problem if the member is large,
    // and therefore, wasting a lot of memory in your module's prototype instance.
90 91 92 93
}

WMTemplate::~WMTemplate()
{
94
    // Cleanup!
95 96 97 98
}

boost::shared_ptr< WModule > WMTemplate::factory() const
{
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
    // To properly understand what this is, we need to have a look at how module instances get created. At first, if you are not familiar with the
    // design patterns "Prototype", "Abstract Factory" and "Factory Method" you should probably read about them first. For short: while the kernel
    // is starting up, it also creates an instance of WModuleFactory, which creates a prototype instance of every module that can be loaded.
    // These prototypes are then used to create new instances of modules, check compatibility of modules and identify the type of modules.
    // If someone, in most cases the module container, wants a new instance of a module with a given prototype, it asks the factory class for it,
    // which uses the prototype's factory() method. Since the method is virtual, it returns a module instance, created with the correct type.
    // A prototype itself is an instance of your module, with the constructor run, as well as connectors() and properties(). What does this mean
    // to your module? Unlike the real "Prototype"- Design pattern, the module prototypes do not get cloned to retrieve a new instance,
    // they get constructed using "new" and this factory method.
    //
    // Here is a short overview of the lifetime of a module instance:
    //
    //    * constructor
    //    * connectors()
    //    * properties()
    //    * now isInitialized() will return true
    //    * the module will be associated with a container
    //    * now isAssociated() will return true
    //          o isUsable() will return true
    //    * after it got added, moduleMain() will be called
    //    * run, run, run, run
    //    * notifyStop gets called
    //    * moduleMain() should end
    //    * destructor
    //
    // So you always have to write this method and always return a valid pointer to an object of your module class.
    // Never initialize something else in here!
126 127 128
    return boost::shared_ptr< WModule >( new WMTemplate() );
}

Alexander Wiebel's avatar
Alexander Wiebel committed
129 130
const char** WMTemplate::getXPMIcon() const
{
131
    // This is deprecated! You can still use it as fallback if you do not specify a META file.
132 133 134 135
    //
    // This was used to provide an icon for your module. You should use the META file in your resource directory. This file is commented and
    // explains each entry in detail.
    return NULL;
Alexander Wiebel's avatar
Alexander Wiebel committed
136 137
}

138 139
const std::string WMTemplate::getName() const
{
140
    // Specify your module name here. This name must be UNIQUE!
141 142 143 144 145
    return "Template";
}

const std::string WMTemplate::getDescription() const
{
146
    // Specify your module description here. Be detailed. This text is read by the user.
147 148 149 150 151
    return "This module is intended to be a module template and an example for writing modules.";
}

void WMTemplate::connectors()
{
152 153 154 155 156 157 158 159 160 161 162 163 164 165
    // How will your module know on which data it should work? Through its input connector(s). How will other modules get to know about your
    // calculated output data? Through your output connector(s). Simple isn't it? You may assume your module as some kind of function, as in
    // common programming languages, where your connectors denote its function signature. The method "connectors()" is for initializing your
    // connectors, your function signature. Now, a short excursion on how the module container and kernel knows which connector can be connected
    // to which. Generally, there are only two types of connectors available for your usage: WModuleInputData and WModuleOutputData and they can
    // only be connected to each other. So, it is not possible to connect an input with an input, nor an output with an output. Both of them are
    // template classes and therefore are associated with a type. This type determines if an input connector is compatible with an output connector.
    // A simple example: assume you have a class hierarchy:
    // Initialize your connectors here. Give them proper names and use the type your module will create or rely on. Do not use types unnecessarily
    // high in class hierarchy. The list of your connectors is fixed after connectors() got called. As in common imperative programming languages
    // the function signature can not be changed during runtime (which, in our case, means after connectors() got called).

    // Here is an example of how to create connectors. This module wants to have an input connector. This connector is defined by the type of
    // data that should be transferred, an module-wide unique name and a proper description:
166
    m_input = WModuleInputData< WDataSetSingle >::createAndAdd( shared_from_this(), "in", "The dataset to display" );
167

168 169
    // This creates an input connector which can receive WDataSetSingle. It will never be able to connect to output connectors providing just a
    // WDataSet (which is the father class of WDataSetSingle), but it will be able to be connected to an output connector with a type derived
170
    // from WDataSetSingle (like WDataSetScalar or WDataSetVector)
171

172 173 174
    // Now, lets add an output connector. We want to provide data calculated here to other modules. The output connector is initialized the same
    // way as input connectors. You need the type, the module-wide unique name and the description. The type you specify here also determines
    // which input connectors can be connected to this output connector: only connectors with a type equal or lower in class hierarchy.
175
    m_output = WModuleOutputData < WDataSetSingle  >::createAndAdd( shared_from_this(), "out", "The calculated dataset" );
176

Sebastian Eichelbaum's avatar
Sebastian Eichelbaum committed
177
    // call WModule's initialization
178 179 180 181 182 183 184 185 186 187 188 189 190 191
    WModule::connectors();
}

void WMTemplate::properties()
{
    // Every module can provide properties to the outside world. These properties can be changed by the user in the GUI or simply by other
    // modules using yours. Properties NEED to be created and added here. Doing this outside this function will lead to severe problems.
    //
    // Theoretically, you can specify properties of every type possible in C++. Therefore, see WPropertyVariable. But in most cases, the
    // predefined properties (WPropertyTypes.h) are enough, besides being the only properties shown and supported by the GUI.
    //
    // To create and add a new property, every module has a member m_properties. It is a set of properties this module provides to the outer
    // world. As with connectors, a property which not has been added to m_properties is not visible for others. Now, how to add a new property?

192
    m_propCondition = boost::shared_ptr< WCondition >( new WCondition() );
193
    m_aTrigger         = m_properties->addProperty( "Do it now!",               "Trigger Button Text.", WPVBaseTypes::PV_TRIGGER_READY,
194
                                                    m_propCondition );
195

196 197 198
    m_enableFeature    = m_properties->addProperty( "Enable feature",           "Description.", true );
    m_anInteger        = m_properties->addProperty( "Number of shape rows",     "Number of shape rows.", 10, m_propCondition );
    m_anIntegerClone   = m_properties->addProperty( "CLONE!Number of shape rows",
199
                                                    "A property which gets modified if \"Number of shape rows\" gets modified.", 10 );
200 201
    m_aDouble          = m_properties->addProperty( "Shape radii",              "Shape radii.", 20.0, m_propCondition );
    m_aString          = m_properties->addProperty( "A string",                 "Something.", std::string( "hello" ), m_propCondition );
Alexander Wiebel's avatar
Alexander Wiebel committed
202
    m_aFile            = m_properties->addProperty( "A filename",              "Description.", WPathHelper::getAppPath(), m_propCondition );
203
    m_aColor           = m_properties->addProperty( "A color",                  "Description.", WColor( 1.0, 0.0, 0.0, 1.0 ) );
204
    m_aPosition        = m_properties->addProperty( "Somewhere",                "Description.", WPosition( 0.0, 0.0, 0.0 ) );
205 206 207

    // These lines create some new properties and add them to the property list of this module. The specific type to create is determined by the
    // initial value specified in the third argument. The first argument is the name of the property, which needs to be unique among all
208 209 210
    // properties of this group and must not contain any slashes (/). The second argument is a description. A nice feature is the possibility
    // to specify an own condition, which gets fired when the property gets modified. This is especially useful to wake up the module's thread
    // on property changes. So, the property m_anInteger will wake the module thread on changes. m_enableFeature and m_aColor should not wake up
211 212 213 214 215
    // the module thread. They get read by the update callback of this modules OSG node, to update the color. m_aTrigger is a property which can
    // be used to trigger costly operations. The GUI shows them as buttons with the description as button text. The user can then press them and
    // the WPropTrigger will change its state to PV_TRIGGER_TRIGGERED. In the moduleMain documentation, you'll find a more detailed description
    // of how to use trigger properties. Be aware, that these kind of properties should be used carefully. They somehow inhibit the update flow
    // through the module graph.
216 217 218 219
    //
    // m_anIntegerClone has a special purpose in this example. It shows that you can simply update properties from within your module whilst the
    // GUI updates itself. You can, for example, set constraints or simply modify values depending on input data, most probably useful to set
    // nice default values or min/max constraints.
220

221
    // All these above properties are not that usable for selections. Assume the following situation. Your module allows two different kinds of
Sebastian Eichelbaum's avatar
Sebastian Eichelbaum committed
222
    // algorithms to run on some data and you want the user to select which one should do the work. This might be done with integer properties but it
223 224
    // is simply ugly. Therefore, properties of type WPropSelection are available. First you need to define a list of alternatives:
    m_possibleSelections = boost::shared_ptr< WItemSelection >( new WItemSelection() );
225
    m_possibleSelections->addItem( "Beer", "Cold and fresh.", template_bier_xpm );          // NOTE: you can add XPM images here.
226 227
    m_possibleSelections->addItem( "Steaks", "Medium please.",  template_steak_xpm );
    m_possibleSelections->addItem( "Sausages", "With Sauerkraut.", template_wurst_xpm );
228 229 230 231 232

    // This list of alternatives is NOT the actual property value. It is the list on which so called "WItemSelector" instances work. These
    // selectors are the actual property. After you created the first selector instance from the list, it can't be modified anymore. This ensures
    // that it is consistent among multiple threads and selection instances. The following two lines create two selectors as initial value and
    // create the property:
233 234 235 236
    m_aSingleSelection = m_properties->addProperty( "I like most",  "Do you like the most?", m_possibleSelections->getSelectorFirst(),
                                                    m_propCondition );
    m_aMultiSelection  = m_properties->addProperty( "I like", "What do you like.", m_possibleSelections->getSelectorAll(),
                                                    m_propCondition );
237

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
    // The last examples showed you how to create more or less complex selections. You where able to define a name, description and some icon for
    // each selectable item. But maybe you want to store additional information, a class implementing some function (like a strategy pattern),
    // and similar. To achieve this, we provide a special item class called WItemSelectionItemTyped. It is a templatized class which allows you
    // to add a value of an arbitrary type to each item. This value of an arbitrary type might be a pointer, string, int, or a custom class'
    // instance.
    m_possibleSelectionsUsingTypes = WItemSelection::SPtr( new WItemSelection() );
    m_possibleSelectionsUsingTypes->addItem( MyItemType::create(
                "The value 1",  // this is the value for the type we specified: std::string.
                "Option 1",
                "Description for the first option"
        )
    );
    m_possibleSelectionsUsingTypes->addItem( MyItemType::create(
                "The value 2",  // this is the value for the type we specified: std::string.
                "Option 2",
                "Description for the second option"
        )
    );
    // This now created the selections. We store a string inside each item. But remember again: this can by ANY type.
    // Finally, add a property by specifying the selector of the selection:
    m_aSingleSelectionUsingTypes = m_properties->addProperty( "Choose one", "Choose on of these and watch the console output.",
                                                              m_possibleSelectionsUsingTypes->getSelectorFirst(), m_propCondition );

261 262 263 264 265
    // There are more property types available. Check out core/common/WPropertyTypes.h. One last to mention here is the WPropInterval. This
    // property type allows you to define intervals. You can create one in the same way you created the other properties:
    m_anInterval = m_properties->addProperty( "Interval", "Choose some interval please", WIntervalDouble( -1.0, 100.0 ), m_propCondition );
    // Important to know is that you can only use WIntervalDouble. You can also use make_interval, which works similar to std::make_pair.

266 267 268
    // Adding a lot of properties might confuse the user. Using WPropGroup, you have the possibility to group your properties together. A
    // WPropGroup needs a name and can provide a description. As with properties, the name should not contain any "/" and must be unique.

269
    m_group1        = m_properties->addPropertyGroup( "First Group",  "A nice group for grouping stuff." );
270
    m_group1a       = m_group1->addPropertyGroup(     "Group 1a", "A group nested into \"Group 1\"." );
271
    m_group2        = m_properties->addPropertyGroup( "Second Group",  "Another nice group for grouping stuff." );
272 273 274 275 276 277 278 279

    // To understand how the groups can be used, you should consider that m_properties itself is a WPropGroup! This means, you can use your newly
    // created groups exactly in the same way as you would use m_properties.
    m_group1Bool    = m_group1->addProperty( "Funny stuff", "A grouped property", true );

    // You even can add one property multiple times to different groups:
    m_group2->addProperty( m_aColor );
    m_group1a->addProperty( m_aDouble );
280
    m_group1a->addProperty( m_enableFeature );
281

282 283 284
    // Properties can be hidden on the fly. The GUI updates automatically. This is a very useful feature. You can create properties which depend
    // on a current selection and blend them in our out accordingly.
    m_aHiddenInt = m_group2->addProperty( "Hide me please", "A property used to demonstrate the hidden feature.", 1, true );
285
    m_aHiddenGroup = m_group2->addPropertyGroup( "Hide me please too", "A property used to demonstrate the hidden feature.", true );
286 287

    // Add another button to group2. But this time, we do not want to wake up the main thread. We handle this directly. Fortunately,
288 289
    // WPropertyVariable offers you the possibility to specify your own change callback. This callback is used for hiding the m_aColor property
    // on the fly.
290
    m_hideButton = m_group2->addProperty( "(Un-)Hide", "Trigger Button Text.", WPVBaseTypes::PV_TRIGGER_READY,
291 292
                                          boost::bind( &WMTemplate::hideButtonPressed, this ) );

293 294 295 296 297
    // How can the values of the properties be changed? You can take a look at moduleMain where this is shown. For short: m_anInteger->set( 2 )
    // and m_anInteger->get().

    // The properties offer another nice feature: property constraints. You can enforce your properties to be in a special range, to not be
    // empty, to contain a valid directory name and so on. This is done with the class WPropertyVariable< T >::WPropertyConstraint. There are
298 299 300 301 302
    // several predefined you can use directly: WPropertyConstraintTypes.h. The constants defined there can be used as namespace in
    // WPropertyHelper. As an example, we want the property m_aFile to only contain existing directories:
    WPropertyHelper::PC_PATHEXISTS::addTo( m_aFile );
    WPropertyHelper::PC_ISDIRECTORY::addTo( m_aFile );

303 304 305 306 307 308 309
    // Thats it. To set minimum and maximum value for a property the convenience methods setMin and setMax are defined. setMin and setMax are
    // allowed for all property types with defined <= and >= operator.
    m_anInteger->setMin( 1 );
    m_anInteger->setMax( 15 );
    m_aDouble->setMin( 5.0 );
    m_aDouble->setMax( 50.0 );

310 311 312 313
    // we also want to constraint the both selection properties. One should not allow selecting multiple elements. But both require at least one
    // element to be selected:
    WPropertyHelper::PC_SELECTONLYONE::addTo( m_aSingleSelection );
    WPropertyHelper::PC_NOTEMPTY::addTo( m_aSingleSelection );
314 315
    WPropertyHelper::PC_SELECTONLYONE::addTo( m_aSingleSelectionUsingTypes );
    WPropertyHelper::PC_NOTEMPTY::addTo( m_aSingleSelectionUsingTypes );
316 317
    WPropertyHelper::PC_NOTEMPTY::addTo( m_aMultiSelection );

318
    // The most amazing feature is: custom constraints. Similar to OSG update callbacks, you just need to write your own PropertyConstraint class
319
    // to define the allowed values for your constraint. Take a look at the StringLength class in this module's code on how to do it.
320
    m_aString->addConstraint( boost::shared_ptr< StringLength >( new StringLength ) );
321
    WPropertyHelper::PC_NOTEMPTY::addTo( m_aString );
322 323

    // One last thing to mention is the active property. This property is available in all modules and represents the activation state of the
Alexander Wiebel's avatar
Alexander Wiebel committed
324
    // module. In the GUI this is simply a checkbox beneath the module. The active property should be taken into account in ALL modules.
325 326 327 328 329 330 331
    // Visualization modules should turn off their graphics. There are basically three ways to react on changes in m_active, which is the member
    // variable for this property.
    // 1: overwrite WModule::activate() in your module
    // 2: register your own handler: m_active->getCondition()->subscribeSignal( boost::bind( &WMTemplate::myCustomActiveHandler, this ) );
    // 3: react during your module main loop using the moduleState: m_moduleState.add( m_active->getCondition );
    // Additionally, your can also use the m_active variable directly in your update callbacks to en-/disable some OSG nodes.
    // This template module uses method number 1. This might be the easiest and most commonly used way.
332

333 334 335 336 337 338
    // Sometimes it is desirable to provide some values, statistics, counts, times, ... to the user. This would be possible by using a property
    // and set the value to the value you want to show the user. Nice, but the user can change this value. PropertyConstraints can't help here,
    // as they would forbid writing any value to the property (regardless if the module or the user wants to set it). In other words, these
    // special properties serve another purpose. They are used for information output. Your module already provides another property list only
    // for these kind of properties. m_infoProperties can be used in the same way as m_properties. The only difference is that each property and
    // property group added here can't be modified from the outside world. Here is an example:
339
    m_aIntegerOutput = m_infoProperties->addProperty( "Run count", "Number of run cycles the module made so far.", 0 );
340 341 342
    // Later on, we will use this property to provide the number of run cycles to the user.
    // In more detail, the purpose type of the property gets set to PV_PURPOSE_INFORMATION automatically by m_infoProperties. You can, of course,
    // add information properties to your custom groups or m_properties too. There, you need to set the purpose flag of the property manually:
343 344
    std::string message = std::string( "Hey you! Besides all these parameters, you also can print values, " ) +
                          std::string( "<font color=\"#00f\" size=15>html</font> formatted strings, colors and " ) +
345
                          std::string( "so on using <font color=\"#ff0000\">properties</font>! Isn't it <b>amazing</b>?" );
346

347
    m_aStringOutput = m_group1a->addProperty( "A message", "A message to the user.", message );
348 349
    m_aStringOutput->setPurpose( PV_PURPOSE_INFORMATION );
    // This adds the property m_aStringOutput to your group and sets its purpose. The default purpose for all properties is always
Alexander Wiebel's avatar
Alexander Wiebel committed
350
    // "PV_PURPOSE_PARAMETER". It simply denotes the meaning of the property - its meant to be used as modifier for the module's behavior; a
351
    // parameter.
352 353
    //
    // Some more examples. Please note: Although every property type can be used as information property, not everything is really useful.
354
    m_infoProperties->addProperty( m_aStringOutput );   // we can also re-add properties
355 356
    m_aTriggerOutput = m_infoProperties->addProperty( "A trigger", "Trigger As String", WPVBaseTypes::PV_TRIGGER_READY );
    m_aDoubleOutput = m_infoProperties->addProperty( "Some double", "a Double. Nice isn't it?", 3.1415 );
357
    m_aIntOutput = m_infoProperties->addProperty( "Some int", "a int. Nice isn't it?", 123456 );
358 359 360
    m_aColorOutput = m_infoProperties->addProperty( "A color", "Some Color. Nice isn't it?", WColor( 0.5, 0.5, 1.0, 1.0 ) );
    m_aFilenameOutput = m_infoProperties->addProperty( "Nice file", "a Double. Nice isn't it?", WPathHelper::getAppPath() );
    m_aSelectionOutput = m_infoProperties->addProperty( "A selection", "Selection As String",  m_possibleSelections->getSelectorFirst() );
361 362 363
    // One important note regarding information properties. If a property gets added in a group which is an information property-group, then
    // each added property does NOT contain any constraints. If a property gets an information property AFTER its creation, like m_aStringOutput,
    // then it keeps its constraints!
364

365 366 367 368 369
    // We now add another trigger. Pressing this button will cause an exception to be thrown. This demonstrates how the GUI and OpenWalnut
    // handles modules which throw an exception without catching it.
    m_exceptionTrigger = m_properties->addProperty( "Press to crash Module", "Pressing this button lets the module intentionally crash.",
                                                    WPVBaseTypes::PV_TRIGGER_READY, m_propCondition );

370
    WModule::properties();
371 372
}

373 374 375 376 377 378 379 380 381 382
void WMTemplate::requirements()
{
    // This method allows modules to specify what they need to run properly. This module, for example, needs the WGE. It therefore adds the
    // WGERequirement to the list of requirements. Modules only get started if all the requirements of it are met by the current running
    // OpenWalnut. This is a very handy tool for NO-GUI versions or script versions of OpenWalnut where there simply is no graphics engine
    // running. This way, the kernel can ensure that only modules are allowed to run who do not require the WGE.
    // Another useful example are module containers. Usually, they need several other modules to work properly.
    m_requirements.push_back( new WGERequirement() );
}

383 384 385 386 387 388 389
void WMTemplate::moduleMain()
{
    // This is the modules working thread. Its the most important part of your module.
    // When you enter this method, all connectors and properties the module provides are fixed. They get initialized in connectors() and
    // properties(). You always can assume the kernel, the GUI, the graphics engine and the data handler to be initialized and ready. Please keep
    // in mind, that this method is running in its own thread.

Alexander Wiebel's avatar
Alexander Wiebel committed
390
    // You can output log messages everywhere and any time in your module. The WModule base class therefore provides debugLog, infoLog, warnLog
391 392 393 394 395 396 397
    // and errorLog. You can use them very similar to the common std::cout streams.
    debugLog() << "Entering moduleMain()";

    // Your module can notify everybody that it is ready to be used. The member function ready() does this for you. The ready state is especially
    // useful whenever your module needs to do long operations to initialize. No other module can connect to your module before it signals its
    // ready state. You can assume the code before ready() to be some kind of initialization code.
    debugLog() << "Doing time consuming operations";
398
    sleep( 2 );
399 400 401 402 403 404 405

    // Your module can use an moduleState variable to wait for certain events. Most commonly, these events are new data on input connectors or
    // changed properties. You can decide which events the moduleState should handle. Therefore, use m_moduleState.add( ... ) to insert every
    // condition you want to wait on. As every input connector provides an changeCondition, we now add this condition to the moduleState:
    m_moduleState.setResetable( true, true );
    m_moduleState.add( m_input->getDataChangedCondition() );
    // Remember the condition provided to some properties in properties()? The condition can now be used with this condition set.
406
    m_moduleState.add( m_propCondition );
407
    // One note about "setResetable": It might happen, that a condition fires and your thread does not currently waits on it. This would mean,
Alexander Wiebel's avatar
Alexander Wiebel committed
408
    // that your thread misses the event. The resettable flag for those condition sets can help here. Whenever a condition, managed by the
409
    // condition set, fires, the moduleState variable remembers it. So, the next call to m_moduleState.wait() will immediately return and reset
410 411
    // the "memory" of the moduleState. For more details, see:
    // http://www.openwalnut.org/projects/openwalnut/wiki/MultithreadingHowto#How-to-wait-correctly
412 413 414 415 416

    // Signal ready state. Now your module can be connected by the container, which owns the module.
    ready();
    debugLog() << "Module is now ready.";

417 418 419 420 421 422 423
    // After your module has signalled that it is ready, OpenWalnut allows the project loader to set the restored properties and connections. For
    // you this means that your module now runs but WHILE running, the project loader might change properties and connections. Usually, this is
    // no problem as you write interactive modules, handling these changes fast. But if you need to ensure that you do not continue your module
    // until the project file loader has completely restored your module, you will need to call this:
    waitRestored();
    // This always returns if you manually add your module and no project file loader or something similar has to restore any values.

424 425 426
    // Most probably, your module will be a module providing some kind of visual output. In this case, the WGEManagedGroupNode is very handy.
    // It allows you to insert several nodes and transform them as the WGEGroupNode (from which WGEManagedGroupNode is derived from) is also
    // an osg::MatrixTransform. The transformation feature comes in handy if you want to transform your whole geometry according to a dataset
427 428 429
    // coordinate system for example. Another nice feature in WGEManagedGroupNode is that it can handle the m_active flag for you. Read the
    // documentation of WMTemplate::activate for more details.
    // First, create the node and add it to the main scene. If you insert something into the scene, you HAVE TO remove it after your module
430
    // has finished!
431
    m_rootNode = new WGEManagedGroupNode( m_active );
432 433 434 435 436 437 438
    // Set a new callback for this node which basically transforms the geometry according to m_aPosition. Update callbacks are the thread safe
    // way to manipulate an OSG node while it is inside the scene. This module contains several of these callbacks as an example. The one used
    // here is to translate the root node coordinate system in space according to m_aPosition:
    m_rootNode->addUpdateCallback( new TranslateCallback( this ) );
    // Insert to the scene:
    WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_rootNode );

439 440 441
    // Normally, you will have a loop which runs as long as the module should not shutdown. In this loop you can react on changing data on input
    // connectors or on changed in your properties.
    debugLog() << "Entering main loop";
442
    while( !m_shutdownFlag() )
443 444 445 446 447 448
    {
        // Now, the moduleState variable comes into play. The module can wait for the condition, which gets fired whenever the input receives data
        // or an property changes. The main loop now waits until something happens.
        debugLog() << "Waiting ...";
        m_moduleState.wait();

449
        // As you might remember, this property is an information property to provide the number of run cycles to the outside world. It won't be
450 451 452
        // modified but the module can modify it. This is useful to provide statistics, counts, times or even a "hello world" string to the user
        // as an information or status report. Please do not abuse these information properties as progress indicators. A short overview on how
        // to make progress indicators is provided some lines below. Here, we simply increase the value.
453
        m_aIntegerOutput->set( m_aIntegerOutput->get() + 1 );
454

455
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
456 457 458
        // After waking up, the module has to check whether the shutdownFlag fired. If yes, simply quit the module.

        // woke up since the module is requested to finish
459
        if( m_shutdownFlag() )
460 461 462 463 464
        {
            break;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
465 466 467 468 469
        // The next part is the collection part. We collect the information we need and check whether they changed.
        // Do not recalculate everything in every loop. Always check whether the data changed or some property and handle those cases properly.
        // After collection, the calculation work can be done.


470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
        // Now, we can check the input, whether there is an update enqueued for us. But first, we need to understand the meaning of an update on
        // an input connector:
        //  * a module updates its output connector with some new data -> updated
        //  * a module triggers an update on its output connector without an actual data change -> updated
        //  * our own input connector got connected to an output connector -> updated
        //  * our own input connector got DISconnected from an output connector -> NO update
        // You now might ask: "Why can modules trigger updates if they did not change the data?". The answer is simple. Some modules change the
        // grid without actually changing the data for example. They translate the grid in space. This results in an update although the actual
        // data stayed the same.

        // To query whether an input was updated, simply ask the input:
        bool dataUpdated = m_input->updated();

        // Remember the above criteria. We now need to check if the data is valid. After a connect-update, it might be NULL.
        boost::shared_ptr< WDataSetSingle > dataSet = m_input->getData();
        bool dataValid = ( dataSet );
        // After calling getData(), the update flag is reset and false again. Please keep in mind, that the module lives in an multi-threaded
        // world where the update flag and data can change at any time. DO NEVER use getData directly in several places of your module as the
        // data returned may change between two consecutive calls! Always grab it into a local variable and use this variable.

        // Another important hint. For grabbing the data, use a local variable wherever possible. If you use a member variable, the data might
        // never be freed if nobody uses the data anymore because your module holds the last reference. If you need to use a member variable for
        // the received data, subscribe the your input's disconnect signal or overwrite WModule::notifyConnectionClosed and reset your variable
        // there to ensure its proper deletion.

        // do something with the data
496
        if( dataUpdated && dataValid )
497
        {
498
            // The data is valid and we received an update. The data is not NULL but may be the same as in previous loops.
499 500 501
            debugLog() << "Received Data.";
        }

502 503 504 505 506 507 508 509 510 511
        // If there is no data, this might have the following reasons: the connector never has been connected or it got disconnected. Especially
        // in the case of a disconnect, you should always clean up your renderings and internal states. A disconnected module should not render
        // anything anymore. Locally stored referenced to the old input data have to be reset to. Only this way, it is guaranteed that not used
        // data gets deleted properly.
        if( !dataValid )
        {
            debugLog() << "Data changed. No valid data anymore. Cleaning up.";
            WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_rootNode );
        }

512
        // Here we collect our properties. You, as with input connectors, always check if a property really has changed. You most probably do not
513 514
        // want to check properties which are used exclusively inside the update callback of your OSG node. As the properties are thread-safe, the
        // update callback can check them and apply it correctly to your visualization.
515 516 517
        //
        // To check whether a property changed, WPropertyVariable provides a changed() method which is true whenever the property has changed.
        // Please note: creating the property with addProperty( ... ) will set changed to true.
518
        if( m_aFile->changed() )
519 520 521 522 523
        {
            // To reset the changed flag, supply a "true" to the get method. This resets the changed-flag and next loop you can again check
            // whether it has been changed externally.

            // This is a simple example for doing an operation which is not depending on any other property.
524
            debugLog() << "Doing an operation on the file \"" << m_aFile->get( true ).string() << "\".";
525

526
            // NOTE: be careful if you want to use dataSet here, as it might be unset. Verify data validity using dataUpdated && dataValid.
527 528 529
        }

        // m_aFile got handled above. Now, handle two properties which together are used as parameters for an operation.
530
        if( m_aString->changed() )
531
        {
Alexander Wiebel's avatar
Alexander Wiebel committed
532
            // This is a simple example for doing an operation which is depends on all, but m_aFile,  properties.
533 534
            debugLog() << "Doing an operation basing on m_aString ... ";
            debugLog() << "m_aString: " << m_aString->get( true );
535

536
            // NOTE: be careful if you want to use dataSet here, as it might be unset. Verify data validity using dataUpdated && dataValid.
537 538 539
        }

        // This example code now shows how to modify your OSG nodes basing on changes in your dataset or properties.
540
        // The if statement also checks for data validity as it uses the data! You should also always do that.
541
        if( ( m_anInteger->changed() || m_aDouble->changed() || dataUpdated  ) && dataValid )
542 543 544 545 546 547 548
        {
            debugLog() << "Creating new OSG node";

            // You should grab your values at the beginning of such calculation blocks, since the property might change at any time!
            int rows = m_anInteger->get( true );
            double radii = m_aDouble->get( true );

549 550 551 552 553 554
            // You can set other properties here. This example simply sets the value of m_anIntegerClone. The set command allows an additional
            // parameter. If it is true, the specified property condition does not fire if it is set. This is useful if your module main loop
            // waits for the condition of the property you want to set. Setting the property without suppressing the notification would cause
            // another loop in your module.
            m_anIntegerClone->set( m_anInteger->get(), true );

555 556
            debugLog() << "Number of Rows: " << rows;
            debugLog() << "Radii: " << radii;
557
            debugLog() << "Current dataset: " << dataSet->getFilename() << " with name: " << dataSet->getName();
558 559 560

            // This block will be executed whenever we have a new dataset or the m_anInteger property has changed. This example codes produces
            // some shapes and replaces the existing root node by a new (updated) one. Therefore, a new root node is needed:
561
            osg::ref_ptr< osg::Geode > newGeode = new osg::Geode();
562 563
            // When working with the OpenSceneGraph, always use ref_ptr to store pointers to OSG objects. This allows OpenSceneGraph to manage
            // its resources automatically.
564
            for( int32_t i = 0; i < rows; ++i )
565
            {
566
                newGeode->addDrawable(
567
                        new osg::ShapeDrawable( new osg::Box(             osg::Vec3(  25, 128, i * 15 ), radii ) ) );
568
                newGeode->addDrawable(
569
                        new osg::ShapeDrawable( new osg::Sphere(          osg::Vec3(  75, 128, i * 15 ), radii ) ) );
570
                newGeode->addDrawable(
571
                        new osg::ShapeDrawable( new osg::Cone(            osg::Vec3( 125, 128, i * 15 ), radii, radii ) ) );
572
                newGeode->addDrawable(
573
                        new osg::ShapeDrawable( new osg::Cylinder(        osg::Vec3( 175, 128, i * 15 ), radii, radii ) ) );
574
                newGeode->addDrawable(
575 576 577
                        new osg::ShapeDrawable( new osg::Capsule(         osg::Vec3( 225, 128, i * 15 ), radii, radii ) ) );
            }

Alexander Wiebel's avatar
Alexander Wiebel committed
578
            // The old root node needs to be removed safely. The OpenSceneGraph traverses the graph at every frame. This traversal is done in a
579 580 581 582 583
            // separate thread. Therefore, adding a node directly may cause the OpenSceneGraph to crash. Thats why the Group node (WGEGroupNode)
            // offers safe remove and insert methods. Use them to manipulate the scene node.
            // First remove the old node:
            m_rootNode->remove( m_geode );
            m_geode = newGeode;
584

585 586 587
            // OSG allows you to add custom callbacks. These callbacks get executed on each update traversal. They can be used to modify several
            // attributes and modes of existing nodes. You do not want to remove the node and recreate another one to simply change some color,
            // right? Setting the color can be done in such an update callback. See in the header file, how this class is defined.
588
            m_geode->addUpdateCallback( new SafeUpdateCallback( this ) );
589

590 591
            // And insert the new node
            m_rootNode->insert( m_geode );
592
        }
593 594 595

        // Now we updated the visualization after the dataset has changed. Your module might also calculate some other datasets basing on the
        // input data.
596
        // To ensure that all datasets are valid, check dataUpdated and dataValid. If both are true, you can safely use the data.
597
        if( dataUpdated && dataValid )
598 599 600 601
        {
            debugLog() << "Data changed. Recalculating output.";

            // Calculate your new data here. This example just forwards the input to the output ;-).
602
            boost::shared_ptr< WDataSetSingle > newData = dataSet;
603 604 605 606 607 608 609 610

            // Doing a lot of work without notifying the user visually is not a good idea. So how is it possible to report progress? Therefore,
            // the WModule class provides a member m_progress which is of type WPropgressCombiner. You can create own progress objects and count
            // them individually. The m_progress combiner provides this information to the GUI and the user.
            // Here is a simple example:
            int steps = 10;
            boost::shared_ptr< WProgress > progress1 = boost::shared_ptr< WProgress >( new WProgress( "Doing work 1", steps ) );
            m_progress->addSubProgress( progress1 );
611
            for( int i = 0; i < steps; ++i )
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
            {
                ++*progress1;
                sleep( 1 );
            }
            progress1->finish();
            // This creates a progress object with a name and a given number of steps. Your work loop can now increment the progress object. The
            // progress combiner m_progress collects the progress and provides it to the GUI. When finished, the progress MUST be marked as
            // finished using finish(). It is no problem to have several progress objects at the same time!

            // Sometimes, the number of steps is not known. WProgress can also handle this. Simply leave away the last parameter (the number of
            // steps. As with the other progress, you need to add it to the modules progress combiner and you need to mark it as finished with
            // finish() if you are done with your work.
            boost::shared_ptr< WProgress > progress2 = boost::shared_ptr< WProgress >( new WProgress( "Doing work 2" ) );
            m_progress->addSubProgress( progress2 );
            sleep( 2 );
            progress2->finish();

            // How to set the data to the output and how to notify other modules about it?
            m_output->updateData( newData );
            // This sets the new data to the output connector and automatically notifies all modules connected to your output.
        }
633 634 635

        // As we provided our condition to m_aTrigger too, the main thread will wake up if it changes. The GUI can change the trigger only to the
        // state "PV_TRIGGER_TRIGGERED" (this is the case if the user presses the button).
636
        if( m_aTrigger->get( true ) == WPVBaseTypes::PV_TRIGGER_TRIGGERED )
637 638 639 640
        {
            // Now that the trigger has the state "triggered", a time consuming operation can be done here.
            debugLog() << "User triggered an important and time consuming operation.";

641 642
            // We can exchange the list used for selection properties. This of course invalidates the current user selection. You should avoid
            // changing this too often and too fast as it might confuse the user.
643 644 645 646 647
            m_possibleSelections->addItem( "Beer2", "Cold and fresh.", template_bier_xpm );          // NOTE: you can add XPM images here.
            m_possibleSelections->addItem( "Steaks2", "Medium please.",  template_steak_xpm );
            m_possibleSelections->addItem( "Sausages2", "With Sauerkraut.", template_wurst_xpm );
            // Each of the above write operations trigger an invalidation of all currently exiting selectors. You can create a new selector
            // basing on an old invalid one and set it as new value for the selections:
648

649 650 651 652
            // Now we set the new selection and selector. Calling newSelector without any argument copies the old selector and tries to resize
            // the selection to match the new size
            m_aSingleSelection->set( m_aSingleSelection->get().newSelector() );
            m_aMultiSelection->set( m_aMultiSelection->get().newSelector() );
653

654 655 656
            // Update the output property
            m_aTriggerOutput->set( WPVBaseTypes::PV_TRIGGER_TRIGGERED );

657 658 659 660
            // Do something here. As above, do not forget to inform the user about your progress.
            int steps = 10;
            boost::shared_ptr< WProgress > progress1 = boost::shared_ptr< WProgress >( new WProgress( "Doing something important", steps ) );
            m_progress->addSubProgress( progress1 );
661
            for( int i = 0; i < steps; ++i )
662 663 664 665 666 667 668 669 670 671 672 673
            {
                ++*progress1;
                sleep( 1 );
            }
            progress1->finish();

            // As long as the module does not reset the trigger to "ready", the GUI disables the trigger button. This is very useful to avoid
            // that a user presses the button multiple times during an operation. When setting the property back to "ready", the GUI re-enables
            // the button and the user can press it again.
            // To avoid the moduleMain- loop to awake every time we reset the trigger, provide a second parameter to the set() method. It denotes
            // whether the change notification should be fired or not. In our case, we avoid this by providing false to the second parameter.
            m_aTrigger->set( WPVBaseTypes::PV_TRIGGER_READY, false );
674 675 676

            // Also update the information property.
            m_aTriggerOutput->set( WPVBaseTypes::PV_TRIGGER_READY );
677
        }
678 679

        // This checks the selections.
680
        if( m_aMultiSelection->changed() ||  m_aSingleSelection->changed() )
681 682 683 684
        {
            // The single selector allows only one selected item and requires one item to be selected all the time. So accessing it by index
            // is trivial:
            WItemSelector s = m_aSingleSelection->get( true );
685
            infoLog() << "The user likes " << s.at( 0 )->getName() << " the most.";
686 687 688

            // The multi property allows the selection of several items. So, iteration needs to be done here:
            s = m_aMultiSelection->get( true );
689
            for( size_t i = 0; i < s.size(); ++i )
690
            {
691
                infoLog() << "The user likes " << s.at( i )->getName();
692 693
            }
        }
694

695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
        // This checks the selections with our additional value.
        if( m_aSingleSelectionUsingTypes->changed() )
        {
            // The single selector allows only one selected item and requires one item to be selected all the time. So accessing it by index
            // is trivial:
            WItemSelector s = m_aSingleSelectionUsingTypes->get( true );
            infoLog() << "The item value is: " << s.at( 0 )->getAs< MyItemType >()->getValue() <<
                         ". Length: " << s.at( 0 )->getAs< MyItemType >()->getValue().length();

            // This showed that you can add values of your arbitrary type into the selection. Assume this is an object providing a ()-operator.
            // You can then use s.at( 0 )->getAs< MyItemType >()->getValue()() to call this operator. This< is very handy for implementing
            // strategies. But before you get too excited about this, for strategies with automatic, complex property handling, you should have
            // a look at WStrategyHelper!
        }

710 711 712 713 714 715 716 717 718 719 720
        // Trigger an exception? We do this whenever the user pressed the exception-button
        if( m_exceptionTrigger->get( true ) == WPVBaseTypes::PV_TRIGGER_TRIGGERED )
        {
            // Throw an exception and do not catch it. Please note that OpenWalnut provides several exceptions which usually cover the most
            // needs. If not, derive your own exceptions from WException. Using WExceptions has one nice advantage: it provides a backtrace on
            // systems which support this.
            throw WFileNotFound( "This is a demonstration of an exception being thrown from within a module." );
            // OpenWalnut then automatically catches it and transports it to the kernel and the registered callbacks. This usually is the GUI
            // which shows a dialog or something similar. Additionally, the m_isCrashed flag is set to true. Once a module is crahsed, it cannot
            // be "restored".
        }
721 722 723 724 725 726 727 728 729

        // Check if the interval was modified.
        if( m_anInterval->changed() )
        {
            // Grab the value as you have learned above:
            WIntervalDouble i = m_anInterval->get( true );
            // And use it somehow:
            infoLog() << "You have selected this interval: [" << i.getLower() << ", " << i.getUpper() << "].";
        }
730 731
    }

Alexander Wiebel's avatar
Alexander Wiebel committed
732
    // At this point, the container managing this module signaled to shutdown. The main loop has ended and you should clean up:
733 734 735 736 737
    //
    //  * remove allocated memory
    //  * remove all OSG nodes
    //  * stop any pending threads you may have started earlier
    //  * ...
738
    //  NOTE: as the module gets disconnected prior to shutdown, most of the cleanup should have been done already.
739 740 741 742
}

void WMTemplate::SafeUpdateCallback::operator()( osg::Node* node, osg::NodeVisitor* nv )
{
743 744 745
    // One note about m_aColor: As you might have notices, changing one of the properties, which recreate the OSG node, causes the material to be
    // gray again. This is simply caused by m_aColor->changed() is still false! To resolve this problem, use some m_osgNeedsUpdate boolean which
    // gets set in your thread main and checked here or, as done in this module, by checking whether the callback is called the first time.
746
    if( m_module->m_aColor->changed() || m_initialUpdate )
747 748 749
    {
        // Set the diffuse color and material:
        osg::ref_ptr< osg::Material > mat = new osg::Material();
750
        mat->setDiffuse( osg::Material::FRONT, m_module->m_aColor->get( true ) );
751 752 753 754 755
        node->getOrCreateStateSet()->setAttribute( mat, osg::StateAttribute::ON );
    }
    traverse( node, nv );
}

756 757 758
void WMTemplate::TranslateCallback::operator()( osg::Node* node, osg::NodeVisitor* nv )
{
    // Update the transformation matrix according to m_aPosition if it has changed.
759
    if( m_module->m_aPosition->changed() || m_initialUpdate )
760 761 762 763
    {
        // The node to which this callback has been attached needs to be an osg::MatrixTransform:
        osg::ref_ptr< osg::MatrixTransform > transform = static_cast< osg::MatrixTransform* >( node );

764
        // Build a translation matrix (to comfortably convert between WPosition and osg::Vec3 use the WVector3XXX methods)
765
        osg::Matrixd translate = osg::Matrixd::translate( m_module->m_aPosition->get( true  ).as< osg::Vec3d >() );
766 767 768 769 770 771 772 773 774 775

        // and set the translation matrix
        transform->setMatrix( translate );

        // First update done
        m_initialUpdate = false;
    }
    traverse( node, nv );
}

776
bool WMTemplate::StringLength::accept( boost::shared_ptr< WPropertyVariable< WPVBaseTypes::PV_STRING > > /* property */,
777
                                       const WPVBaseTypes::PV_STRING& value )
778
{
Alexander Wiebel's avatar
Alexander Wiebel committed
779
    // This method gets called every time the m_aString property is going to be changed. It can decide whether the new value is valid or not. If
780 781 782 783
    // the method returns true, the new value is set. If it returns false, the value is rejected.
    //
    // Note: always use WPVBaseTypes when specializing the WPropertyVariable template.

784 785 786 787
    // simple example: just accept string which are at least 5 chars long and at most 10.
    return ( value.length() <= 10 ) && ( value.length() >= 5 );
}

788 789 790 791 792 793 794
boost::shared_ptr< WPropertyVariable< WPVBaseTypes::PV_STRING >::PropertyConstraint > WMTemplate::StringLength::clone()
{
    // If you write your own constraints, you NEED to provide a clone function. It creates a new copied instance of the constraints with the
    // correct runtime type.
    return boost::shared_ptr< WPropertyVariable< WPVBaseTypes::PV_STRING >::PropertyConstraint >( new WMTemplate::StringLength( *this ) );
}

795 796
void WMTemplate::activate()
{
797 798
    // This method gets called, whenever the m_active property changes. Your module should always handle this if you do not use the
    // WGEManagedGroupNode for your scene. The user can (de-)activate modules in his GUI and you can handle this case here:
799
    if( m_active->get() )
800
    {
801
        debugLog() << "Activate.";
802
    }
803 804 805 806 807 808
    else
    {
        debugLog() << "Deactivate.";
    }

    // The simpler way is by using WGEManagedGroupNode which deactivates itself according to m_active. See moduleMain for details.
809 810 811 812 813

    // Always call WModule's activate!
    WModule::activate();
}

814 815 816 817 818
void WMTemplate::hideButtonPressed()
{
    // This method is called whenever m_hideButton changes its value. You can use such callbacks to avoid waking-up or disturbing the module
    // thread for certain operations.

819
    // If the button was triggered, switch the hide-state of m_aColor and m_aHiddenInt.
820
    if( m_hideButton->get( true ) == WPVBaseTypes::PV_TRIGGER_TRIGGERED )
821 822 823
    {
        // switch the hide flag of the color prop.
        m_aColor->setHidden( !m_aColor->isHidden() );
824
        m_aHiddenInt->setHidden( !m_aHiddenInt->isHidden() );
825
        m_aHiddenGroup->setHidden( !m_aHiddenGroup->isHidden() );
826 827 828 829 830 831

        // never forget to reset a trigger. If not done, the trigger is disabled in the GUI and can't be used again.
        m_hideButton->set( WPVBaseTypes::PV_TRIGGER_READY );
        // NOTE: this again triggers an update, which is why we need to check the state of the trigger in this if-clause.
    }
}