HowToCreateNewWorkingArea

From user's Wiki!
Jump to: navigation, search

This page provides some tips on how to create non standard working areas and set it as default Working Area in your plugin

Types

There are two types of Working area as you can see from the two pictures below, the first on is based on the standard working areas we already have and the second one is a completly new layout that includes section that are not render windows. Go to section 'Standard Working Area' to know how to create and set the first one and 'Customized Working Area' for the second one.

Standard
Customized

Standard Working Area

Create xml WorkingArea

First of all build your xml configuration using the WorkingAreaConfiguration options:

WorkingAreaConfiguration icon in the Toolbar Menu
ConfigureWorkingArea panel widget

In order to do this:

1. click on Working Area Manager >>> and you will create your empty working area cliking on New and then Rename.

2. click on <<< Configure Working Area where you can chose which type of windows you want to add and to remove

3. go back to the previous tab and Save your workingArea.

Remark: you can also try to create your workingArea taking as an example the existing ones in 'UserName\Application Data\gimias\v1.3.0\WorkingAreas'.

Add your WorkingArea to the WidgetCollective

Let's suppose now that you have a xml configuration for your working area: MyWorkingArea.xml. Also you have developed a plug-in with a plug-in tab that may be called MyPlugin, and you want that when you select the plug-in tab of your plug-in in gimias, your custom workin area directly appears as the selected one, and not only, that it can be choosed amongst the rest of the default working areas existing in Gimias.

To do this you need to add some code in your widget collective .cxx file, just in the constructor function.


WidgetCollective::WidgetCollective() 
{
       // Create plugin tab page
	Core::Runtime::Kernel::GetGraphicalInterface()->CreatePluginTab( "Dicom" );
	
	if ( GetPluginTab( ) != NULL )
	{
               // Use the caption of the working area
		GetPluginTab( )->ShowWindow( "DICOM working area" );
	}
}

Now, whether we select our plug-ing tab, we'll see that our current working area is shown automatically. You can find an example in DICOMPlugin.

Install xml in the Binary Folder

What is left to do is how to get our working area to be put automatically in or plug-in directory, so that we don't have to put it by hand just by copying the file and pasting it. This is a work that has to be done in the python file used by csnake that is just in the root of the plug-in source directory. The file may have a name like csnMyPlugin.py. You only need to add the next lines at the end of that file. Then, by using cSnake application, you can click on "Install files to build folder" button and automatically the new files will be added on the corresponding places. The needed lines for the csnake file are theese:

installFolder = "%s/debug" % MyPlugin.installSubFolder
MyPlugin.installManager.AddFilesToInstall( MyPlugin.Glob( "resource/WorkingAreas" ), installFolder, _debugOnly = 1, _WIN32 = 1 )
installFolder = "%s/release" % MyPlugin.installSubFolder
MyPlugin.installManager.AddFilesToInstall( MyPluginGlob( "resource/WorkingAreas" ), installFolder, _releaseOnly = 1, _WIN32 = 1 )
installFolder = "%s" % MyPlugin.installSubFolder
MyPlugin.installManager.AddFilesToInstall( MyPlugin.Glob( "resource/WorkingAreas" ), installFolder, _NOT_WIN32 = 1 )


Notice that our working area is located in our <MyPluginSourceRootPath>/resource/WorkingAreas, but you can change it accordingly to your project needs.

Customized Working Area

This type of working area is the more complex one, you may want to have different kind of information plot on the same window ( look at picture Customized on the top of this page).

Follow these steps in order to create it correctly:

1. Create wgGlade with MultiRenderWindowMITK

2. Opt 1: Connect custom working area with dataTreeList

3. Opt 2: Connect custom working area with a specific DataHolder disconnected from the dataTreeList

4. Register custom working area in your plugin


Create wxGlade with MultiRenderWindowMITK

In order to do this, start by creating as usual a new GimiasPlugin PanelWidget using the StartNewModule tool ( a specific option for the working area will come asap in the future). If you want you can replace in every file 'PanelWidget' with 'WorkingArea'.For this example we just decided to create a panel widget typing 'CustomWorkingArea' in the name field of StartNewModule.

Start by modifing the wxglade file and create the configuration you want. As an example we choosed to create a working area like the one in the picture.

wxGlade file modified
Tabs of added class

Click on "Add custom Widget" option represented by the icon with the question mark and enter the Core::Widgets::MultiRenderWindowMitk class in it. Add an Id in the common properties, then add the code to define the new id and the '#include "coreMultiRenderWindowMITK.h"' of the class ( see ConfigureClass picture for details).

Remember that it's better to previously erase the automatic generated files PanelWidgetUI.cpp and .h that came with the template in order not to generate conflicts with this completely new version of the interface.

By default the template comes with two inputs, if you don't want to have inputs for your working area just remove the lines that are connected to these inputs in the processor ( in the constructor function and in the update one, and the connection to them in the panel widget OnInit function) or you can just hide these inputs by editing the OnInit function of the panel widget ( see HowToCommunicateBtwProcessorAndPanelWidget for more details).

Once you removed the automatically input widget you wanted to use you have to define which type of MultiRenderWindow you are going to put in the empty space. This can be done in the OnInit() function of the Panelwidget using these lines:

#include "wxMitkOrthoSliceFactory.h"
#include "wxMitkMultiRenderWindowLayout.h"
[...] 
mitk::wxMitkOrthoSliceFactory::InitRenderWindows( window_1 );
window_1->GetMetadata()->AddTag( "LayoutType", int( mitk::Single3D) );
window_1->EnableDisplayPlaneSubtree( false );

Where window_1 is the name of the CostumWidget we created in wxGlade. From the following pictures you can see different options of layoutType that are availble only by changing the layout type or by changing the factory of the rendering window, the following lines correspond to the last picture:

#include "wxMitkMultiSliceFactory.h"
[...]
mitk::wxMitkMultiSliceFactory::InitRenderWindows( window_1,
	mitk::SliceNavigationController::Transversal, 5);
window_1->EnableDisplayPlaneSubtree( true );


"LayoutType", int( mitk::Only2D)
"LayoutType", int( mitk::Default2xD)
MultiSliceFactory ->Transversal

Opt 1: Connect custom working area with dataListTree

First of all you need to initialize the window with the selectedDataEntity you can do this by adding the first line in the OnInit function of the PanelWidget ( after or before the initialization of the window we have seen in the previous paragraph), and you also have to initialize the BaseWindow pointers (like the public renderingTree) with the second line

window_1->Init( GetListBrowser()->GetSelectedDataEntityHolder());
GetPluginTab()->InitBaseWindow(window_1);


Then you have to activate the renderingTree of the window as the one that will be active in the plugin and you will also set the ActiveMultiRenderWindow to the one you initialized. This is done by adding these lines to the Enable function, inside if ( enable ) code block and immediately after UpdateWidget();

if ( window_1 )
{
    GetPluginTab()->GetRenderingTreeManager()->SetActiveTree( window_1->GetPrivateRenderingTree( ) );
    GetPluginTab()->GetWorkingAreaManager()->GetActiveMultiRenderWindowHolder( )->SetSubject( window_1 );
}

You will be able then to use the toolbar to enable and disable the axis, and to render directly the dataEntities you have in the list as you're used to do in the other working areas. You can also directly use the Visual Properties widget to change the properties of your node. If you want to change this properties internally you should do as in a normal panel widget calling the function GetRenderingTree().

Opt 2: Connect custom working area with a specific DataHolder disconnected from the dataTreeList

First of all you need to initialize the window with the selectedDataEntity you can do this by adding this line in the OnInit funcion of the PanelWidget ( after or before the initialization of the window we have seen in the correspondent paragraph)

window_1->Init( GetListBrowser()->GetSelectedDataEntityHolder());


In this option you will need to add manually everything to the private renderingTree of your window, this can be done by using

window_1->GetPrivateRenderingTree()

instead of what we usually do

GetRenderingTree()

So let's imagine that we just set a new dataEntity to an internal DataEntityHolder and we want to add/show it to the rendering tree we will do:

window_1->GetPrivateRenderingTree()->Add(dataEntity,true,true);
window_1->GetPrivateRenderingTree()->Show(dataEntity);

The same if you want to add or change its properties:

[...]
Core::CastAnyProcessingData(
     window_1->GetPrivateRenderingTree()->GetNode(dataEntity), node);
node->SetProperty(materialProperty,"material");

Register custom working area in your plugin

To register and set you working area as the default one in your plugin you have to edit the WidgetCollective file like this:

Core::Runtime::Kernel::GetGraphicalInterface()->RegisterFactory( 
	CustomWorkingAreaPanelWidget::Factory::NewBase( ), 
	Core::WindowConfig( ).WorkingArea().Id( wxID_CustomWorkingAreaPanelWidget).
       Caption( "Custom Working Area" ) );
GetPluginTab()->ShowWindow( wxID_CustomWorkingAreaPanelWidget );
GetPluginTab()->SetCurrentWorkingArea(wxID_CustomWorkingAreaPanelWidget);

Remark that this can be done just by changing the automatically created 'config' to 'Core::WindowConfig( ).WorkingArea()' in the registration of the panel widget.

Custom Working Area added to menu


Another Example : WorkingArea with plotWindow

This type of working area is relatevily easier because you don't need to connect any RenderingTree but you can give a different layout to your Signal dataEntity instead of the usual one you have in the SignalViewer. In otder to do this you just have to do these two steps:

1. Create wgGlade with mpWindow

2. Use wxMathPlot functions to customize your graph

Create wxGlade with mpWindow

Follow exactly the same steps as it is explained in the paragraph above just changing the class name for 'mpWindow' and adding the '#include "mathplot.h" in the properties window

Wxglade file for plotwindow

Once you generate the files proceed as in the Register custom working area in your plugin paragraph in order to have the current working area as the default one.

If you only have a plot window and no multi render window you will also have to add these lines in the if(enable) block of the Enable function of Panel Widget in order to initialize the rendering tree and allow the signals to be visualized in the normal signalViewer too.

#include "coreRenderingTreeMITK.h"
[...]
In your working area class:
 Core::RenderingTreeMITK::Pointer m_RenderingTree;

[...]
In your constructor:
 m_RenderingTree = Core::RenderingTreeMITK::New()

[...]
bool myWorkingArea::Enable( bool enable = /*True*/ )
{
  bool returnCode = myWorkingAreaUI::Enable( enable );

  if ( enable )
  {
    GetPluginTab()->GetRenderingTreeManager()->SetActiveTree( m_RenderingTree.GetPointer() );
  }

  return returnCode;
}

Use wxMathPlot functions to customize your graph

We have now to modify the panel widget and the processor classes of our working area in order to take the information of a dataEntity input signal. First modify the processor in order to have one input of type Core::SignalTypeId and remove every template default in the Update() function.

BaseProcessor::SetNumberOfInputs( INPUTS_NUMBER );
GetInputPort(INPUT_0)->SetName(  "Input Signal1" );
GetInputPort(INPUT_0)->SetDataEntityType( Core::SignalTypeId);

Now in the Panel Widget class you can add in the header these attributes that will be our x and y axis.

//! Used for setting the scales on the plot screen
	mpScaleX*		m_xscale;
	mpScaleY*		m_yscale;

And you can initialize them in the constructor of the Panel Widget like this

//Font
wxFont plotFont = wxFont(8, wxDEFAULT, wxNORMAL, wxNORMAL, 0);
//Initial axis settings
m_xscale = new mpScaleX("x label",mpALIGN_BORDER_BOTTOM, false);
m_yscale = new mpScaleY("y label",mpALIGN_LEFT, false);
m_xscale->SetFont(plotFont);
m_yscale->SetFont(plotFont);
m_xscale->SetTicks(true);
m_yscale->SetTicks(true);

Then you can connect the plot to the button event function (i.e. OnBtnApply() ) or directly to the change of your input (i.e. OnModifiedInputDataEntity() )

// Plot signals on the same plot:
mpFXYVector*	dataPlotLayer;
mpFXYVector*	dataPlotLayer2;

std::vector<float>	time1,time2;
blSignalCollective::Pointer signal1;
m_Processor->GetProcessingData(0,signal1);
               
if (signal1->GetNumberOfSignals()< 2)
   return;

for (int I=0;I<signal1->GetSignal(0)->GetNumberOfValues();I++)
{
  time1.push_back(static_cast<float>(signal1->GetSignal(0)->GetValueX(I)));
  time2.push_back(static_cast<float>(signal1->GetSignal(1)->GetValueX(I)));
}

dataPlotLayer = new mpFXYVector("Signal 1");
dataPlotLayer2 = new mpFXYVector("Signal 2" );
//First Remove existing layers
window_1->DelLayer(m_xscale);
window_1->DelLayer(m_yscale);
window_1->AddLayer(m_xscale);
m_yscale->SetName(signal1->GetSignal(0)->GetName());	
window_1->AddLayer(m_yscale);
dataPlotLayer->SetData(
	time1,
	signal1->GetSignal(0)->GetYVector());
dataPlotLayer->SetContinuity(true);
dataPlotLayer->SetPen(wxPen(*wxRED, 1));
dataPlotLayer2->SetData(	
       time2,
	signal1->GetSignal(1)->GetYVector());
dataPlotLayer2->SetContinuity(false);
dataPlotLayer2->SetPen(wxPen(*wxBLUE, 1));
window_1->AddLayer(dataPlotLayer);
window_1->AddLayer(dataPlotLayer2);
window_1->Fit();

An example of how it could appear the Plot window

Show your working area

Using GetPluginTab( ) to customize your plugin

You can use GetPluginTab( ) to customized the widgets that will be shown by default when loading your plugin. Before calling the function GetPluginTab( ) you need to check that the return value is not NULL. For example taking a look at this piece of code (from DICOMPlugin), you can see that the command panel and all toolbars are hidden and the IO toolbar is shown:

if ( GetPluginTab( ) != NULL )
{
  GetPluginTab( )->EnableWindow( wxID_CommandPanel, false);
  GetPluginTab( )->EnableAllToolbars( false );
  GetPluginTab( )->ShowWindow( wxID_IOToolbar );
}

The function GetPluginTab( ) will return the last active plugin tab. When working in plugin perspective, this is the plugin tab of the current plugin, for example, in DICOMPlugin, the first thing it does is to create a plugin tab:

Core::Runtime::Kernel::GetGraphicalInterface()->CreatePluginTab( "Dicom" );

When working in workflow perspective, this line of code doesn't have any effect. All plugin tabs are created after loading all plugins and using the XML description of the workflow. For each workflow step, a new plugin tab will be created.

To show your custom working area

In workflow perspective, you can set your custom working area as the default one if you remove all working areas from the workflow step and keep only your customized one. In plugin perspective, you can show your custom working area by default if you set the property Show( ) to WindowConfig( ) parameter (you can see DICOM example).

Go back to Developers