Difference between revisions of "HowToCreateNewWorkingArea"

From user's Wiki!
Jump to: navigation, search
(Standard Working Area)
Line 12: Line 12:
 
= Standard Working Area =
 
= Standard Working Area =
  
 +
== Create xml WorkingArea ==
 
First of all build your xml configuration using the WorkingAreaConfiguration options:
 
First of all build your xml configuration using the WorkingAreaConfiguration options:
 
{|
 
{|
Line 19: Line 20:
  
 
In order to do this:
 
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'''.
 
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
 
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.
 
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'.
 
'''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.
 
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 file, just in the constructor function. You already may have some code in yout WidgetCollective constructor like this:
+
To do this you need to add some code in your widget collective .cxx file, just in the constructor function.
 +
This part is just used to guess where the path of the current plug-in being loaded is located. There is where we have previously deployed our xml with the configuration of our custom working area.
 +
 
 
  <span style="color:#0000ff">
 
  <span style="color:#0000ff">
 
  WidgetCollective::WidgetCollective()  
 
  WidgetCollective::WidgetCollective()  
 
  {
 
  {
    Core::Runtime::Kernel::GetGraphicalInterface()->CreatePluginTab( "MyPluginTabName" );
+
     // Widget collective constructor previous code [...]
+
    // Base common configuration
+
    Core::WindowConfig config;
+
    config.TabPage( "MyPluginTabName" ).CommandPanel();
+
+
    Core::Runtime::Kernel::GetGraphicalInterface()->RegisterFactory(
+
        MyFirstPanelWidget::Factory::NewBase(),
+
        config.Caption( "MyFirstPanelWidget" ).
+
        Id( wxID_MyFirstPanelWidget ) );
+
</span>
+
+
The next part is just used to guess where the path of the current plug-in being loaded is located. There is where we have previously deployed our xml with the configuration of our custom working area.
+
 
+
<span style="color:#0000ff">
+
     // Widget collective code continues...
+
 
   
 
   
 
     // Construct the current plug-in path
 
     // Construct the current plug-in path
Line 86: Line 79:
  
 
Now, wether we select our plug-ing tab, we'll see that our current working area is shown automatically.
 
Now, wether we select our plug-ing tab, we'll see that our current working area is shown automatically.
 +
 +
== 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:
 
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:

Revision as of 07:57, 17 March 2011

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. This part is just used to guess where the path of the current plug-in being loaded is located. There is where we have previously deployed our xml with the configuration of our custom working area.


WidgetCollective::WidgetCollective() 
{
    // Widget collective constructor previous code [...]

    // Construct the current plug-in path
    Core::Runtime::wxMitkGraphicalInterface::Pointer graphicalIface;
    graphicalIface = Core::Runtime::Kernel::GetGraphicalInterface();
    Core::Runtime::FrontEndPluginManager::Pointer pluginManager;
    pluginManager = graphicalIface->GetFrontEndPluginManager();
    Core::Runtime::FrontEndPluginLoader::Pointer pluginLoader;
    pluginLoader = pluginManager->GetPluginLoader( pluginManager->GetCurrentPluginName() );
    std::string thisPluginPath = pluginLoader->GetLibraryPath() + Core::IO::SlashChar;
 

Once we have the plug-in path location, we can make our way to the xml file and open it with the WorkingAreaStorage class. This class registers our new custom working area so it can be choosen from the menu of current available working areas. But if we want to select it automatically, we have to do this by telling our plug-in tab to show that working area window.


    // Widget collective code continues...

    Core::BaseWorkingAreaStorage::Pointer baseWorkingAreaStorage;
    baseWorkingAreaStorage = Core::Runtime::Kernel::GetGraphicalInterface()->GetWorkingAreaStorage();
    string workingAreaConfigPath = thisPluginPath + "MechanicalDyssynchronyWorkingArea.xml";

    Core::Widgets::WorkingAreaStorage* workingAreaStorage = dynamic_cast<Core::Widgets::WorkingAreaStorage*>( baseWorkingAreaStorage.GetPointer() );
    if( workingAreaStorage )
    {
        workingAreaStorage->Open( workingAreaConfigPath );
        Core::Widgets::BaseMainWindow* mainWindow;
        mainWindow = Core::Runtime::Kernel::GetGraphicalInterface()->GetMainWindow();
        if( mainWindow )
        {
            Core::Widgets::PluginTab* thisPluginTab = mainWindow->GetLastPluginTab();
            if( thisPluginTab )
            {
                thisPluginTab->ShowWindow( "MechanicalDyssynchronyWorkingArea" );
            }
        }
    }
}

Now, wether we select our plug-ing tab, we'll see that our current working area is shown automatically.

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 constructor 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

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 constructor 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.

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