Salome HOME
PARAVIS main menus and toolbars.
[modules/paravis.git] / src / PVGUI / PVGUI_Module.cxx
1 // LIGHT : sample (no-corba-engine) SALOME module
2 //
3 // Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 // File   : PVGUI_Module.cxx
23 // Author : Julia DOROVSKIKH
24 //
25
26 #include "PVGUI_Module.h"
27 #include "PVGUI_Module_impl.h"
28 #include "PVGUI_ProcessModuleHelper.h"
29 #include "PVGUI_ViewModel.h"
30 #include "PVGUI_ViewManager.h"
31 #include "PVGUI_ViewWindow.h"
32
33 #include <SUIT_Desktop.h>
34 #include <SUIT_MessageBox.h>
35 #include <SUIT_ResourceMgr.h>
36 #include <SUIT_Session.h>
37 #include <LightApp_Application.h>
38 #include <LightApp_SelectionMgr.h>
39 #include <QtxActionToolMgr.h>
40
41 #include <QAction>
42 #include <QApplication>
43 #include <QIcon>
44 #include <QInputDialog>
45 #include <QString>
46 #include <QStringList>
47 #include <QToolBar>
48
49 #include <pqApplicationCore.h>
50 #include <pqActiveServer.h>
51 #include <pqActiveView.h>
52 #include <pqClientAboutDialog.h>
53 #include <pqObjectBuilder.h>
54 #include <pqOptions.h>
55 #include <pqRenderView.h>
56 #include <pqRubberBandHelper.h>
57 #include <pqServer.h>
58 #include <pqServerManagerModel.h>
59 #include <pqServerResource.h>
60 #include <pqUndoStack.h>
61 #include <pqVCRController.h>
62 #include <pqViewManager.h>
63 #include <vtkPVMain.h>
64 #include <vtkProcessModule.h>
65
66 /*
67  * Make sure all the kits register their classes with vtkInstantiator.
68  * Since ParaView uses Tcl wrapping, all of VTK is already compiled in
69  * anyway.  The instantiators will add no more code for the linker to
70  * collect.
71  */
72
73 #include <vtkCommonInstantiator.h>
74 #include <vtkFilteringInstantiator.h>
75 #include <vtkGenericFilteringInstantiator.h>
76 #include <vtkIOInstantiator.h>
77 #include <vtkImagingInstantiator.h>
78 #include <vtkInfovisInstantiator.h>
79 #include <vtkGraphicsInstantiator.h>
80
81 #include <vtkRenderingInstantiator.h>
82 #include <vtkVolumeRenderingInstantiator.h>
83 #include <vtkHybridInstantiator.h>
84 #include <vtkParallelInstantiator.h>
85
86 #include <vtkPVServerCommonInstantiator.h>
87 #include <vtkPVFiltersInstantiator.h>
88 #include <vtkPVServerManagerInstantiator.h>
89 #include <vtkClientServerInterpreter.h>
90
91
92 //----------------------------------------------------------------------------
93 // ClientServer wrapper initialization functions.
94 // Taken from ParaView sources (file pqMain.cxx)
95 extern "C" void vtkCommonCS_Initialize(vtkClientServerInterpreter*);
96 extern "C" void vtkFilteringCS_Initialize(vtkClientServerInterpreter*);
97 extern "C" void vtkGenericFilteringCS_Initialize(vtkClientServerInterpreter*);
98 extern "C" void vtkImagingCS_Initialize(vtkClientServerInterpreter*);
99 extern "C" void vtkInfovisCS_Initialize(vtkClientServerInterpreter*);
100 extern "C" void vtkGraphicsCS_Initialize(vtkClientServerInterpreter*);
101 extern "C" void vtkIOCS_Initialize(vtkClientServerInterpreter*);
102 extern "C" void vtkRenderingCS_Initialize(vtkClientServerInterpreter*);
103 extern "C" void vtkVolumeRenderingCS_Initialize(vtkClientServerInterpreter*);
104 extern "C" void vtkHybridCS_Initialize(vtkClientServerInterpreter*);
105 extern "C" void vtkWidgetsCS_Initialize(vtkClientServerInterpreter*);
106 extern "C" void vtkParallelCS_Initialize(vtkClientServerInterpreter*);
107 extern "C" void vtkPVServerCommonCS_Initialize(vtkClientServerInterpreter*);
108 extern "C" void vtkPVFiltersCS_Initialize(vtkClientServerInterpreter*);
109 extern "C" void vtkXdmfCS_Initialize(vtkClientServerInterpreter *);
110
111 //----------------------------------------------------------------------------
112 void ParaViewInitializeInterpreter(vtkProcessModule* pm)
113 {
114   // Initialize built-in wrapper modules.
115   vtkCommonCS_Initialize(pm->GetInterpreter());
116   vtkFilteringCS_Initialize(pm->GetInterpreter());
117   vtkGenericFilteringCS_Initialize(pm->GetInterpreter());
118   vtkImagingCS_Initialize(pm->GetInterpreter());
119   vtkInfovisCS_Initialize(pm->GetInterpreter());
120   vtkGraphicsCS_Initialize(pm->GetInterpreter());
121   vtkIOCS_Initialize(pm->GetInterpreter());
122   vtkRenderingCS_Initialize(pm->GetInterpreter());
123   vtkVolumeRenderingCS_Initialize(pm->GetInterpreter());
124   vtkHybridCS_Initialize(pm->GetInterpreter());
125   vtkWidgetsCS_Initialize(pm->GetInterpreter());
126   vtkParallelCS_Initialize(pm->GetInterpreter());
127   vtkPVServerCommonCS_Initialize(pm->GetInterpreter());
128   vtkPVFiltersCS_Initialize(pm->GetInterpreter());
129   vtkXdmfCS_Initialize(pm->GetInterpreter());
130 }
131
132 vtkPVMain*                 PVGUI_Module::pqImplementation::myPVMain = 0;
133 pqOptions*                 PVGUI_Module::pqImplementation::myPVOptions = 0;
134 PVGUI_ProcessModuleHelper* PVGUI_Module::pqImplementation::myPVHelper = 0;
135
136 /*!
137   \class PVGUI_Module
138   \brief Implementation of light (no-CORBA-engine) 
139          SALOME module wrapping ParaView GUI.
140 */
141
142 /*!
143   \brief Constructor. Sets the default name for the module.
144 */
145 PVGUI_Module::PVGUI_Module()
146   : LightApp_Module( "PARAVIS" ),
147     Implementation( 0 )
148 {
149 }
150
151 /*!
152   \brief Destructor.
153 */
154 PVGUI_Module::~PVGUI_Module()
155 {
156 }
157
158 /*!
159   \brief Initialize module. Creates menus, prepares context menu, etc.
160   \param app application instance
161 */
162 void PVGUI_Module::initialize( CAM_Application* app )
163 {
164   LightApp_Module::initialize( app );
165
166   /*
167   int i = 1;
168   while( i ){
169     i = i;
170   }
171   */
172
173   pvInit();
174
175   /*
176   createAction( lgLoadFile, tr( "TOP_LOAD_FILE" ), QIcon(), tr( "MEN_LOAD_FILE" ),
177                 tr( "STB_LOAD_FILE" ), 0, desk, false, this, SLOT( onLoadFile() ) );
178   createAction( lgDisplayLine, tr( "TOP_DISPLAY_LINE" ), QIcon(), tr( "MEN_DISPLAY_LINE" ),
179                 tr( "STB_DISPLAY_LINE" ), 0, desk, false, this, SLOT( onDisplayLine() ) );
180   createAction( lgEraseLine, tr( "TOP_ERASE_LINE" ), QIcon(), tr( "MEN_ERASE_LINE" ),
181                 tr( "STB_ERASE_LINE" ), 0, desk, false, this, SLOT( onEraseLine() ) );
182   createAction( lgSaveFile, tr( "TOP_SAVE_FILE" ), QIcon(), tr( "MEN_SAVE_FILE" ),
183                 tr( "STB_SAVE_FILE" ), 0, desk, false, this, SLOT( onSaveFile() ) );
184   createAction( lgEditLine, tr( "TOP_EDIT_LINE" ), QIcon(), tr( "MEN_EDIT_LINE" ),
185                 tr( "STB_EDIT_LINE" ), 0, desk, false, this, SLOT( onEditLine() ) );
186   createAction( lgAddLine,  tr( "TOP_ADD_LINE" ),  QIcon(), tr( "MEN_ADD_LINE" ),
187                 tr( "STB_ADD_LINE" ),  0, desk, false, this, SLOT( onAddLine() ) );
188   createAction( lgDelLine,  tr( "TOP_DEL_LINE" ),  QIcon(), tr( "MEN_DEL_LINE" ),
189                 tr( "STB_DEL_LINE" ),  0, desk, false, this, SLOT( onDelLine() ) );
190   createAction( lgClear,    tr( "TOP_CLEAR_ALL" ), QIcon(), tr( "MEN_CLEAR_ALL" ),
191                 tr( "STB_CLEAR_ALL" ), 0, desk, false, this, SLOT( onClear() ) );
192
193   int aFileMnu = createMenu( tr( "MEN_FILE" ), -1, -1 );
194   createMenu( separator(), aFileMnu, -1, 10 );
195   createMenu( lgLoadFile,  aFileMnu, 10 );
196   createMenu( lgSaveFile,  aFileMnu, 10 );
197
198   int aLightMnu = createMenu( tr( "MEN_LIGHT" ), -1, -1, 50 );
199   createMenu( lgAddLine,      aLightMnu, 10 );
200   createMenu( lgEditLine,     aLightMnu, 10 );
201   createMenu( lgDelLine,      aLightMnu, 10 );
202   createMenu( separator(),    aLightMnu, -1, 10 );
203   createMenu( lgClear,        aLightMnu, 10 );
204
205   QString rule = "(client='ObjectBrowser' or client='OCCViewer') and selcount=1 and type='TextLine' and !empty";
206
207   popupMgr()->insert ( action( lgDisplayLine ), -1, 0 );
208   popupMgr()->setRule( action( lgDisplayLine ), rule + " and !visible"  );
209
210   popupMgr()->insert ( action( lgEraseLine ), -1, 0 );
211   popupMgr()->setRule( action( lgEraseLine ), rule + " and activeView='OCCViewer' and visible"  );
212
213   rule = "client='ObjectBrowser' and selcount=1 and type='TextLine'";
214
215   popupMgr()->insert ( action( lgEditLine ), -1, 0 );
216   popupMgr()->setRule( action( lgEditLine ), rule  );
217
218   popupMgr()->insert ( action( lgAddLine ),  -1, 0 );
219   popupMgr()->setRule( action( lgAddLine ),  rule );
220
221   popupMgr()->insert ( separator(),          -1, 0 );
222
223   popupMgr()->insert ( action( lgDelLine ),  -1, 0 );
224   popupMgr()->setRule( action( lgDelLine ),  rule );
225
226   rule = "client='ObjectBrowser'";
227
228   popupMgr()->insert ( action( lgClear ),    -1, 0 );
229   popupMgr()->setRule( action( lgClear ),    rule );*/
230 }
231
232 /*!
233   \brief Get list of compliant dockable GUI elements
234   \param m map to be filled in ("type":"default_position")
235 */
236 void PVGUI_Module::windows( QMap<int, int>& m ) const
237 {
238   m.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
239   // TODO: creation of Python console leads to SIGSEGV on Python initialization...
240   //m.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
241   // ParaView diagnostic output redirected here
242   m.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
243 }
244
245 /*!
246   \brief Create custom popup menu selection object.
247   \return new selected object
248 */
249 /*LightApp_Selection* PVGUI_Module::createSelection() const
250 {
251   return new PVGUI_Selection();
252 }*/
253
254 /*!
255   \brief Create data model.
256   \return module specific data model
257 */
258 /*CAM_DataModel* PVGUI_Module::createDataModel()
259 {
260   return new PVGUI_DataModel( this );
261 }*/
262
263 /*!
264   \brief Static method, performs initialization of ParaView session.
265   \return \c true if ParaView has been initialized successfully, otherwise false
266 */
267 bool PVGUI_Module::pvInit()
268 {
269   if ( !pqImplementation::myPVMain ){
270     // Obtain command-line arguments
271     int argc = 0;
272     QStringList args = QApplication::arguments();
273     char** argv = new char*[args.size()];
274     for ( QStringList::const_iterator it = args.begin(); argc < 1 && it != args.end(); it++, argc++ )
275       argv[argc] = strdup( (*it).toLatin1().constData() );
276
277     vtkPVMain::SetInitializeMPI(0);  // pvClient never runs with MPI.
278     vtkPVMain::Initialize(&argc, &argv); // Perform any initializations.
279
280     // TODO: Set plugin dir from preferences
281     //QApplication::setLibraryPaths(QStringList(dir.absolutePath()));
282
283     pqImplementation::myPVMain = vtkPVMain::New();
284     if ( !pqImplementation::myPVOptions )
285       pqImplementation::myPVOptions = pqOptions::New();
286     if ( !pqImplementation::myPVHelper )
287       pqImplementation::myPVHelper = PVGUI_ProcessModuleHelper::New();
288
289     pqImplementation::myPVOptions->SetProcessType(vtkPVOptions::PVCLIENT);
290
291     // This creates the Process Module and initializes it.
292     int ret = pqImplementation::myPVMain->Initialize(pqImplementation::myPVOptions, 
293                                                      pqImplementation::myPVHelper, 
294                                                      ParaViewInitializeInterpreter,
295                                                      argc, argv);
296     if (!ret){
297       // Tell process module that we support Multiple connections.
298       // This must be set before starting the event loop.
299       vtkProcessModule::GetProcessModule()->SupportMultipleConnectionsOn();
300       ret = pqImplementation::myPVHelper->Run(pqImplementation::myPVOptions);
301     }
302
303     delete[] argv;
304     return !ret;
305   }
306   
307   return true;
308 }
309  
310 /*!
311   \brief Static method, cleans up ParaView session at application exit.
312 */
313 void PVGUI_Module::pvShutdown()
314 {
315   // TODO...
316 }  
317
318 /*!
319   \brief Shows (toShow = true) or hides ParaView view window
320 */
321 void PVGUI_Module::showView( bool toShow )
322 {
323   // TODO: check if ParaView view already exists
324   if ( !Implementation ){
325     LightApp_Application* anApp = getApp();
326     SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
327     PVGUI_ViewManager* viewMgr = new PVGUI_ViewManager( anApp->activeStudy(), anApp->desktop() );
328     anApp->addViewManager( viewMgr );
329     connect( viewMgr, SIGNAL( lastViewClosed( SUIT_ViewManager* ) ),
330              anApp, SLOT( onCloseView( SUIT_ViewManager* ) ) );
331     //connect( viewMgr, SIGNAL( viewCreated( SUIT_ViewWindow* ) ), vm, SLOT( onViewCreated( SUIT_ViewWindow* ) ) );
332     //connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ), this, SLOT( onViewDeleted( SUIT_ViewWindow* ) ) );
333     SUIT_ViewWindow* wnd = viewMgr->createViewWindow();  
334
335     // Simulate ParaView client main window
336     Implementation = new pqImplementation( anApp->desktop() );
337     PVGUI_ViewWindow* pvWnd = dynamic_cast<PVGUI_ViewWindow*>( wnd );
338     pvWnd->setMultiViewManager( &Implementation->Core.multiViewManager() );
339
340     setupDockWidgets();
341     
342     pvCreateActions();
343     pvCreateMenus();
344     pvCreateToolBars();
345     
346     setupDockWidgetsContextMenu();
347
348     // Now that we're ready, initialize everything ...
349     Implementation->Core.initializeStates();
350   }
351 }
352
353 /*!
354   \brief Manage the label of Undo operation.
355 */
356 void PVGUI_Module::onUndoLabel(const QString& label)
357 {
358   action(UndoId)->setText(
359     label.isEmpty() ? tr("Can't Undo") : QString(tr("&Undo %1")).arg(label));
360   action(UndoId)->setStatusTip(
361     label.isEmpty() ? tr("Can't Undo") : QString(tr("Undo %1")).arg(label));
362 }
363
364 /*!
365   \brief Manage the label of Redo operation.
366 */
367 void PVGUI_Module::onRedoLabel(const QString& label)
368 {
369   action(RedoId)->setText(
370     label.isEmpty() ? tr("Can't Redo") : QString(tr("&Redo %1")).arg(label));
371   action(RedoId)->setStatusTip(
372     label.isEmpty() ? tr("Can't Redo") : QString(tr("Redo %1")).arg(label));
373 }
374
375 /*!
376   \brief Manage the label of Undo Camera operation.
377 */
378 void PVGUI_Module::onCameraUndoLabel(const QString& label)
379 {
380   action(CameraUndoId)->setText(
381     label.isEmpty() ? tr("Can't Undo Camera") : QString(tr("U&ndo %1")).arg(label));
382   action(CameraUndoId)->setStatusTip(
383     label.isEmpty() ? tr("Can't Undo Camera") : QString(tr("Undo %1")).arg(label));
384 }
385
386 /*!
387   \brief Manage the label of Redo Camera operation.
388 */
389 void PVGUI_Module::onCameraRedoLabel(const QString& label)
390 {
391   action(CameraRedoId)->setText(
392     label.isEmpty() ? tr("Can't Redo Camera") : QString(tr("R&edo %1")).arg(label));
393   action(CameraRedoId)->setStatusTip(
394     label.isEmpty() ? tr("Can't Redo Camera") : QString(tr("Redo %1")).arg(label));
395 }
396
397 /*!
398   \brief Slot to delete all objects.
399 */
400 void PVGUI_Module::onDeleteAll()
401 {
402   pqObjectBuilder* builder = pqApplicationCore::instance()->getObjectBuilder();
403   Implementation->Core.getApplicationUndoStack()->beginUndoSet("Delete All");
404   builder->destroyPipelineProxies();
405   Implementation->Core.getApplicationUndoStack()->endUndoSet();
406 }
407
408 /*!
409   \brief Slot to check/uncheck the action for corresponding selection mode.
410 */
411 void PVGUI_Module::onSelectionModeChanged(int mode)
412 {
413   if( toolMgr()->toolBar( mySelectionControlsTb )->isEnabled() ) {
414     if(mode == pqRubberBandHelper::SELECT) //surface selection
415       action(SelectCellsOnId)->setChecked(true);
416     else if(mode == pqRubberBandHelper::SELECT_POINTS) //surface selection
417       action(SelectPointsOnId)->setChecked(true);
418     else if(mode == pqRubberBandHelper::FRUSTUM)
419       action(SelectCellsThroughId)->setChecked(true);
420     else if(mode == pqRubberBandHelper::FRUSTUM_POINTS)
421       action(SelectPointsThroughId)->setChecked(true);
422     else if (mode == pqRubberBandHelper::BLOCKS)
423       action(SelectBlockId)->setChecked(true);
424     else // INTERACT
425       action(InteractId)->setChecked(true);
426   }
427 }
428
429 /*!
430   \brief Slot to manage the change of axis center.
431 */
432 void PVGUI_Module::onShowCenterAxisChanged(bool enabled)
433 {
434   action(ShowCenterId)->setEnabled(enabled);
435   action(ShowCenterId)->blockSignals(true);
436   pqRenderView* renView = qobject_cast<pqRenderView*>(
437     pqActiveView::instance().current());
438   action(ShowCenterId)->setChecked( renView ? renView->getCenterAxesVisibility() : false);
439   action(ShowCenterId)->blockSignals(false);
440 }
441
442 /*!
443   \brief Slot to set tooltips for the first anf the last frames, i.e. a time range of animation.
444 */
445 void PVGUI_Module::setTimeRanges(double start, double end)
446 {
447   action(FirstFrameId)->setToolTip(QString("First Frame (%1)").arg(start, 0, 'g'));
448   action(LastFrameId)->setToolTip(QString("Last Frame (%1)").arg(end, 0, 'g'));
449 }
450
451 /*!
452   \brief Slot to manage the plaing process of animation.
453 */
454 void PVGUI_Module::onPlaying(bool playing)
455 {
456   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
457   if(playing) {
458     disconnect( action(PlayId),                        SIGNAL( triggered() ),
459                 &Implementation->Core.VCRController(), SLOT( onPlay() ) );
460     connect( action(PlayId),                        SIGNAL( triggered() ),
461              &Implementation->Core.VCRController(), SLOT( onPause() ) );
462     action(PlayId)->setIcon(QIcon(resMgr->loadPixmap("ParaView",tr("ICON_PAUSE"),false)));
463     action(PlayId)->setText("Pa&use");
464   }
465   else {
466     connect( action(PlayId),                        SIGNAL( triggered() ),
467              &Implementation->Core.VCRController(), SLOT( onPlay() ) );
468     disconnect( action(PlayId),                        SIGNAL( triggered() ),
469                 &Implementation->Core.VCRController(), SLOT( onPause() ) );
470     action(PlayId)->setIcon(QIcon(resMgr->loadPixmap("ParaView",tr("ICON_PLAY"),false)));
471     action(PlayId)->setText("&Play");
472   }
473
474   Implementation->Core.setSelectiveEnabledState(!playing);
475 }
476
477 /*!
478   \brief Slot to add camera link.
479 */
480 void PVGUI_Module::onAddCameraLink()
481 {
482   pqView* vm = pqActiveView::instance().current();
483   pqRenderView* rm = qobject_cast<pqRenderView*>(vm);
484   if(rm) rm->linkToOtherView();
485   else SUIT_MessageBox::warning(getApp()->desktop(),
486                                 tr("WARNING"), tr("WRN_ADD_CAMERA_LINK"));
487 }
488
489 /*!
490   \brief Slot to show information about ParaView.
491 */
492 void PVGUI_Module::onHelpAbout()
493 {
494   pqClientAboutDialog* const dialog = new pqClientAboutDialog(getApp()->desktop());
495   dialog->setAttribute(Qt::WA_DeleteOnClose);
496   dialog->show();
497 }
498
499 /*!
500   \brief Returns the ParaView multi-view manager.
501 */
502 pqViewManager* PVGUI_Module::getMultiViewManager() const
503 {
504   pqViewManager* aMVM = 0; 
505   if ( Implementation )
506     aMVM = &Implementation->Core.multiViewManager();
507   return aMVM;
508 }
509
510 /*!
511   \brief Activate module.
512   \param study current study
513   \return \c true if activaion is done successfully or 0 to prevent
514   activation on error
515 */
516 bool PVGUI_Module::activateModule( SUIT_Study* study )
517 {
518   bool isDone = LightApp_Module::activateModule( study );
519   if ( !isDone ) return false;
520
521   setMenuShown( true );
522
523   showView( true );
524
525   // Make default server connection
526   if ( Implementation )
527     Implementation->Core.makeDefaultConnectionIfNoneExists();
528
529   return isDone;
530 }
531
532
533 /*!
534   \brief Deactivate module.
535   \param study current study
536   \return \c true if deactivaion is done successfully or 0 to prevent
537   deactivation on error
538 */
539 bool PVGUI_Module::deactivateModule( SUIT_Study* study )
540 {
541   // hide menus
542   setMenuShown( false );
543
544   return LightApp_Module::deactivateModule( study );
545 }
546
547 /*!
548   \fn CAM_Module* createModule();
549   \brief Export module instance (factory function).
550   \return new created instance of the module
551 */
552
553 #ifdef WNT
554 #define PVGUI_EXPORT __declspec(dllexport)
555 #else   // WNT
556 #define PVGUI_EXPORT
557 #endif  // WNT
558
559 extern "C" {
560   PVGUI_EXPORT CAM_Module* createModule() {
561     return new PVGUI_Module();
562   }
563 }