Commit 58affc00 authored by promayon's avatar promayon
Browse files

NEW CamiTK

FIXED


git-svn-id: svn+ssh://scm.forge.imag.fr/var/lib/gforge/chroot/scmrepos/svn/camitk/trunk/camitk@387 ec899d31-69d1-42ba-9299-647d76f65fb3
parent 3c35beef
......@@ -41,15 +41,18 @@ OpenAction::OpenAction (ActionExtension * extension) : Action (extension) {
setEmbedded (false);
setDescription ("Open data (component) from a file");
setComponent ("");
setIcon (QPixmap (":/fileOpen"));
if (qApp->type() != QApplication::Tty)
setIcon (QPixmap (":/fileOpen"));
// Setting classification family and tags
setFamily ("Application");
addTag ("Open");
// add a shortcut
getQAction()->setShortcut( QKeySequence::Open );
getQAction()->setShortcutContext(Qt::ApplicationShortcut);
if (qApp->type() != QApplication::Tty) {
getQAction()->setShortcut( QKeySequence::Open );
getQAction()->setShortcutContext(Qt::ApplicationShortcut);
}
}
// --------------- destructor -------------------
......
......@@ -40,7 +40,7 @@ using namespace camitk;
// --------------- constructor -------------------
OtsuFilter::OtsuFilter(ActionExtension * extension) : Action(extension) {
// Setting name, description and input component
setName("Otsu Threshod Filter");
setName("Otsu Threshold Filter");
setDescription("<p>This filters classifies pixels to minimize the error of misclassification. The goal is to find a threshold that classifies the image into two clusters such that we minimize the area under the histogram for one cluster that lies on the other cluster's side of the threshold. This is equivalent to minimizing the within class variance or equivalently maximizing the between class variance.</p>");
setComponent("ImageComponent");
......
......@@ -152,3 +152,4 @@ Action::ApplyStatus MeshQuality::apply() {
return SUCCESS;
}
......@@ -29,6 +29,7 @@
#include "Application.h"
#include "Component.h"
#include "ActionWidget.h"
#include "Log.h"
#include <QHBoxLayout>
#include <QGroupBox>
......@@ -43,7 +44,8 @@ Action::Action(ActionExtension* extension): QObject() {
qAction = NULL;
isEmbedded = true;
actionWidget = NULL;
nbAliveComponents = 0;
nbAliveComponents = 0;
autoUpdateProperties = false;
}
// -------------------- destructor --------------------
......@@ -96,7 +98,7 @@ QPixmap Action::getIcon() {
return icon;
}
// -------------------- getWidget --------------------
// -------------------- getTargets --------------------
const ComponentList Action::getTargets() const {
return targetComponents;
}
......@@ -105,13 +107,17 @@ const ComponentList Action::getTargets() const {
QWidget* Action::getWidget() {
// build or update the widget
if (!actionWidget) {
// Setting the widget containing the parameters, using the default widget
// Setting the widget containing the parameters, using the default widget
actionWidget = new ActionWidget(this);
}
else {
// make sure the widget has updated targets
// make sure the widget has updated targets
dynamic_cast<ActionWidget*>(actionWidget)->updateTargets();
}
// make sure the widget updates its parameters
dynamic_cast<ActionWidget*>(actionWidget)->setAutoUpdateProperty(autoUpdateProperties);
return actionWidget;
}
......@@ -122,7 +128,7 @@ QAction* Action::getQAction() {
qAction = new QAction(getIcon(), getName(), this);
qAction->setStatusTip(getDescription());
qAction->setWhatsThis(getName() + "\n" + getDescription());
// connect it to the trigger slot
// connect it to the trigger slot
connect ( qAction, SIGNAL ( triggered() ), this, SLOT ( trigger() ) );
}
......@@ -134,6 +140,7 @@ Action::ApplyStatus Action::trigger(QWidget * parent) {
//-- build the list of valid targets
targetComponents.clear();
ComponentList selComp = Application::getSelectedComponents();
foreach (Component * comp, selComp) {
// check compatibility
if (comp->isInstanceOf(this->getComponent()))
......@@ -173,67 +180,89 @@ Action::ApplyStatus Action::trigger(QWidget * parent) {
}
// -------------------- applyInPipeline --------------------
Action::ApplyStatus Action::applyInPipeline() {
ApplyStatus status;
ApplyStatus status;
preProcess();
status = apply();
postProcess();
preProcess();
status = apply();
postProcess();
return status;
return status;
}
// -------------------- setInputComponents --------------------
void Action::setInputComponents(ComponentList inputs) {
//-- build the list of valid targets
inputComponents.clear();
inputComponents.clear();
foreach (Component * comp, inputs) {
// check compatibility
if (comp->isInstanceOf(this->getComponent()))
inputComponents.append(comp);
}
inputComponents.append(comp);
}
}
// -------------------- setInputComponent --------------------
void Action::setInputComponent(Component * input) {
this->inputComponents.clear();
if (input->isInstanceOf(this->getComponent()))
inputComponents.append(input);
}
this->inputComponents.clear();
if (input->isInstanceOf(this->getComponent()))
inputComponents.append(input);
}
// -------------------- getOutputComponents --------------------
ComponentList Action::getOutputComponents() {
return this->outputComponents;
return this->outputComponents;
}
// -------------------- getOutputComponent --------------------
Component * Action::getOutputComponent() {
if (outputComponents.isEmpty())
return NULL;
else
return outputComponents.first();
if (outputComponents.isEmpty())
return NULL;
else
return outputComponents.first();
}
// -------------------- preProcess --------------------
void Action::preProcess() {
// unselect all components
ComponentList selComp = Application::getSelectedComponents();
// unselect all components
ComponentList selComp = Application::getSelectedComponents();
foreach (Component * comp, selComp) {
comp->setSelected(false);
}
comp->setSelected(false);
}
// select the right components (those set by setInputComponent(s)
foreach (Component * comp, inputComponents) {
comp->setSelected(true);
}
// select the right components (those set by setInputComponent(s)
foreach (Component * comp, inputComponents) {
comp->setSelected(true);
}
}
// -------------------- postProcess --------------------
void Action::postProcess() {
outputComponents.clear();
ComponentList allComp = Application::getAllComponents();
foreach (Component * comp, allComp) {
if (comp->getModified()) {
outputComponents.append(comp);
}
}
// TODO record history
outputComponents.clear();
ComponentList allComp = Application::getAllComponents();
foreach (Component * comp, allComp) {
if (comp->getModified()) {
outputComponents.append(comp);
}
}
// TODO record history
}
// -------------------- getAutoUpdateProperties --------------------
bool Action::getAutoUpdateProperties() const {
return autoUpdateProperties;
}
// -------------------- setAutoUpdateProperties --------------------
void Action::setAutoUpdateProperties(bool autoUpdateProperties) {
this->autoUpdateProperties = autoUpdateProperties;
if (actionWidget)
dynamic_cast<ActionWidget*>(actionWidget)->setAutoUpdateProperty(autoUpdateProperties);
}
}
......
......@@ -83,6 +83,10 @@ class ActionExtension;
* This means that, if there is a widget, the action algorithm is controlled by the action widget, i.e. apply() is not
* called by trigger() but should be called by one of the action widget's button.
*
* An Action generally is used to wrap an algorithm in CamiTK. If this algorithm has parameters, it is very easy to
* get these parameters accessible to the user through the ActionWidget.
* These parameters are in fact defined as Qt dynamic properties.
*
* By default an action has a widget, instance of ActionWidget.
* If ActionWidget does not correspond to what you need, just create a new class inheriting from QWidget, or
* even better, inheriting from ActionWidget.
......@@ -128,6 +132,13 @@ class ActionExtension;
* a default widget for an action.
* If your action does not have any GUI/parameters, just override the getWidget() method in order to return NULL.
*
* By default the properties/parameters are not automatically updated when the user change the default widget,
* they are updated only when the user click on the apply button of the default widget.
* Use setAutoUpdateProperties(true) to automatically update the action's properties.
*
* \note if you use your own MyVerySpecialActionWidget class, make sure it conforms to this behaviour
* (you can get the desired behaviour by calling getAutoUpdateProperty()
*
* By default the action's widget is embedded. If you do not want to embed your action's widget, use setEmbedded(false)
* in the constructor.
* When embedded, the parent widget has to be given at triggered time.
......@@ -140,11 +151,11 @@ class ActionExtension;
* targetComponents is filtered so that it only contains compatible components (i.e.,
* instances of getComponent()).
*
* The method applyInPipeline() perform some preprcessing and post processing around the mehtod apply().
* It has to be used within a pipeline (a chain of actions) where setInputComponents()
* The method applyInPipeline() performs some pre- and post-processing around the method apply().
* It has to be used within a pipeline (a chain of actions) where setInputComponents()
* and getOutputComponents() are needed. preProcess() only selects the right components,
* and postProcess() sets output components and record history (TODO).
*
*
*
* For a simple example of an embedded action @see RenderingOption
* For a simple example of a non-embedded action @see RigidTransform
......@@ -201,11 +212,13 @@ public:
/// Specify the input Component(s)
void setInputComponents(ComponentList inputs);
/// Specify the input Components in case of only one Component.
void setInputComponent(Component * input);
/// Returns the output Component(s)
ComponentList getOutputComponents();
/// Returns the output Components in case of only one Component.
Component * getOutputComponent();
//@}
......@@ -290,6 +303,12 @@ public:
const ComponentList getTargets() const;
///@}
/// auto update properties
bool getAutoUpdateProperties() const;
/// are the properties to be udpated every time the user makes a change in the widget (default is false)?
void setAutoUpdateProperties(bool);
protected:
/// @name Generic action attributes setters
/// These methods can not be redefined in subclasses but have to used to ensure name/description unicity
......@@ -366,6 +385,9 @@ private:
/// the currently selected and valid (regarding the component property) components, for which this action is called (private list, use getTargets() is subclasses)
ComponentList targetComponents;
/// Should the properties/parameters of this action be automatically updated when the user change something in the GUI
bool autoUpdateProperties;
/// @name Pipeline execuction of the Action
///@{
/// List field with setInputComponents() or setInputComponent()
......
......@@ -81,6 +81,7 @@ ActionWidget::ActionWidget(Action *action): QFrame() {
buttonFrame->setLayout(buttonLayout);
widgetLayout->addWidget(buttonFrame);
setLayout(widgetLayout);
// connect the buttons
QObject::connect(applyButton, SIGNAL(clicked()), objectController, SLOT(apply()));
......@@ -91,9 +92,7 @@ ActionWidget::ActionWidget(Action *action): QFrame() {
// and then call the apply() method
QObject::connect(applyButton, SIGNAL(clicked()), myAction, SLOT(apply()));
QObject::connect(revertButton, SIGNAL(clicked()), objectController, SLOT(revert()));
setLayout(widgetLayout);
// Now tell the ObjectController that this Action itself is the one to manage
objectController->setObject(myAction);
}
......@@ -135,4 +134,9 @@ QString ActionWidget::getTargetLabel() {
return targetNames;
}
// -------------------- setAutoUpdateProperty --------------------
void ActionWidget::setAutoUpdateProperty(bool autoUpdate) {
objectController->setAutoUpdateProperty(autoUpdate);
}
}
......@@ -50,13 +50,19 @@ class ObjectController;
* signal is connected the the action apply() slot.
* The revert button resets the values in GUI to the initial values (all modification in the GUI
* are cancelled).
* You can easily show or hide the button frame usint setButtonVisibility()
* You can easily show or hide the button frame using setButtonVisibility()
*
* updateTargets() updates the widget using the actual/current properties and target of the action.
* This method should be called by an action in the overriden getWidget method.
* A typical getWidget() method of an action should use the lazy instanciation pattern to instanciate
* ActionWidget the first time it is called, and call the ActionWidget instance updateTargets() method
* for any subsequent calls.
*
* If you cannot wait for the user to click on the apply button (or if this button is hidden)
* and you want the value of the action's property/parameter to be updated everytime
* there is a change in the GUI, call setAutoUpdateProperty(true)
* (default is false);
*
*/
class CAMITK_API ActionWidget : public QFrame {
public:
......@@ -79,6 +85,9 @@ public:
/// true by default
void setDescriptionVisibility(bool visible);
/// automatically update the action properties whenever there is a change in the GUI
void setAutoUpdateProperty(bool autoUpdate);
protected:
/// return a text with the target component names followed by their class names
QString getTargetLabel();
......
......@@ -274,6 +274,10 @@ class Viewer;
* It is thus very <b>important</b> to rewrite these methods in your Component subclass to takes the actions' modification
* into account in your low-level data.
*
* Dynamic properties: if your Component defines some dynamic property, you might want to override updateProperty() in order
* to update the internal state of your object when a dynamic property has been changed.
* @see ObjComponent for a good example
*
* It is extensively using Qt Meta-Object system (concepts and implementation).
* see http://doc.qt.nokia.com/latest/metaobjects.html
*
......
/*****************************************************************************
* $CAMITK_LICENCE_BEGIN$
*
* CamiTK - Computer Assisted Medical Intervention ToolKit
* (c) 2001-2012 UJF-Grenoble 1, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
*
* Visit http://camitk.imag.fr for more information
*
* This file is part of CamiTK.
*
* CamiTK is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* CamiTK 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 version 3 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with CamiTK. If not, see <http://www.gnu.org/licenses/>.
*
* $CAMITK_LICENCE_END$
/*****************************************************************************
* $CAMITK_LICENCE_BEGIN$
*
* CamiTK - Computer Assisted Medical Intervention ToolKit
* (c) 2001-2012 UJF-Grenoble 1, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
*
* Visit http://camitk.imag.fr for more information
*
* This file is part of CamiTK.
*
* CamiTK is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* CamiTK 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 version 3 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with CamiTK. If not, see <http://www.gnu.org/licenses/>.
*
* $CAMITK_LICENCE_END$
****************************************************************************/
......@@ -47,49 +47,60 @@
namespace camitk
{
namespace camitk {
int ObjectControllerPrivate::enumToInt ( const QMetaEnum &metaEnum, int enumValue ) const {
QMap<int, int> valueMap; // dont show multiple enum values which have the same values
int pos = 0;
for ( int i = 0; i < metaEnum.keyCount(); i++ ) {
int value = metaEnum.value ( i );
if ( !valueMap.contains ( value ) ) {
if ( value == enumValue )
return pos;
valueMap[value] = pos++;
}
}
return -1;
}
int ObjectControllerPrivate::intToEnum ( const QMetaEnum &metaEnum, int intValue ) const {
QMap<int, bool> valueMap; // dont show multiple enum values which have the same values
QList<int> values;
for ( int i = 0; i < metaEnum.keyCount(); i++ ) {
int value = metaEnum.value ( i );
if ( !valueMap.contains ( value ) ) {
valueMap[value] = true;
values.append ( value );
}
}
if ( intValue >= values.count() )
return -1;
return values.at ( intValue );
}
bool ObjectControllerPrivate::isSubValue ( int value, int subValue ) const {
if ( value == subValue )
return true;
int i = 0;
while ( subValue ) {
if ( ! ( value & ( 1 << i ) ) ) {
if ( subValue & 1 )
return false;
}
i++;
subValue = subValue >> 1;
}
return true;
}
......@@ -98,49 +109,63 @@ bool ObjectControllerPrivate::isPowerOf2 ( int value ) const {
if ( value & 1 ) {
return value == 1;
}
value = value >> 1;
}
return false;
}
int ObjectControllerPrivate::flagToInt ( const QMetaEnum &metaEnum, int flagValue ) const {
if ( !flagValue )
return 0;
int intValue = 0;
QMap<int, int> valueMap; // dont show multiple enum values which have the same values
int pos = 0;
for ( int i = 0; i < metaEnum.keyCount(); i++ ) {
int value = metaEnum.value ( i );
if ( !valueMap.contains ( value ) && isPowerOf2 ( value ) ) {
if ( isSubValue ( flagValue, value ) )
intValue |= ( 1 << pos );
valueMap[value] = pos++;
}
}
return intValue;
}
int ObjectControllerPrivate::intToFlag ( const QMetaEnum &metaEnum, int intValue ) const {
QMap<int, bool> valueMap; // dont show multiple enum values which have the same values
QList<int> values;
for ( int i = 0; i < metaEnum.keyCount(); i++ ) {
int value = metaEnum.value ( i );
if ( !valueMap.contains ( value ) && isPowerOf2 ( value ) ) {
valueMap[value] = true;
values.append ( value );
}
}
int flagValue = 0;
int temp = intValue;
int i = 0;
while ( temp ) {
if ( i >= values.count() )
return -1;
if ( temp & 1 )
flagValue |= values.at ( i );
i++;
temp = temp >> 1;
}
return flagValue;
}
......@@ -153,15 +178,18 @@ void ObjectControllerPrivate::updateClassProperties ( const QMetaObject *metaObj
for ( int idx = metaObject->propertyOffset(); idx < metaObject->propertyCount(); idx++ ) {
QMetaProperty metaProperty = metaObject->property ( idx );
if ( metaProperty.isReadable() ) {
if ( m_classToIndexToProperty.contains ( metaObject ) && m_classToIndexToProperty[metaObject].contains ( idx ) ) {
QtVariantProperty *subProperty = m_classToIndexToProperty[metaObject][idx];
if ( metaProperty.isEnumType() ) {
if ( metaProperty.isFlagType() )
subProperty->setValue ( flagToInt ( metaProperty.enumerator(), metaProperty.read ( m_object ).toInt() ) );
else
subProperty->setValue ( enumToInt ( metaProperty.enumerator(), metaProperty.read ( m_object ).toInt() ) );
} else {
}
else {
subProperty->setValue ( metaProperty.read ( m_object ) );
}
}
......@@ -171,6 +199,7 @@ void ObjectControllerPrivate::updateClassProperties ( const QMetaObject *metaObj
QtVariantProperty * ObjectControllerPrivate::buildQtVariantProperty(QString name, QMetaType::Type type, QVariant value, bool isReadable, bool isWritable, bool isEnumType, bool isFlagType, bool isDesignable, QMetaEnum* metaEnum) {
QtVariantProperty *buildProperty = 0;
// hide objectName property
if ( !isReadable ) {
buildProperty = m_readOnlyManager->addProperty ( QVariant::String, name);
......@@ -182,26 +211,33 @@ QtVariantProperty * ObjectControllerPrivate::buildQtVariantProperty(QString name
buildProperty = m_manager->addProperty ( QtVariantPropertyManager::flagTypeId(), name );
QMap<int, bool> valueMap;
QStringList flagNames;
for ( int i = 0; i < metaEnum->keyCount(); i++ ) {
int enumValue = metaEnum->value ( i );
if ( !valueMap.contains ( enumValue ) && isPowerOf2 ( enumValue ) ) {
valueMap[enumValue] = true;
flagNames.append ( QLatin1String ( metaEnum->key ( i ) ) );
}
buildProperty->setAttribute ( QLatin1String ( "flagNames" ), flagNames );
buildProperty->setValue ( flagToInt ( *metaEnum, value.toInt() ) );
}
} else {
}
else {
buildProperty = m_manager->addProperty ( QtVariantPropertyManager::enumTypeId(), name);
QMap<int, bool> valueMap; // dont show multiple enum values which have the same values
QStringList enumNames;
for ( int i = 0; i < metaEnum->keyCount(); i++ ) {
int value = metaEnum->value ( i );
if ( !valueMap.contains ( value ) ) {
valueMap[value] = true;
enumNames.append ( QLatin1String ( metaEnum->key ( i ) ) );
}
}
buildProperty->setAttribute ( QLatin1String ( "enumNames" ), enumNames );
buildProperty->setValue ( enumToInt ( *metaEnum, value.toInt() ) );
}
......@@ -216,17 +252,22 @@ QtVariantProperty * ObjectControllerPrivate::buildQtVariantProperty(QString name
buildProperty = m_readOnlyManager->addProperty ( type, name + " (Non Designable)" );
else
buildProperty = m_manager->addProperty ( type, name );
buildProperty->setValue ( value );
} else {
}
else {
// a map is managed as a group
if ( type == QMetaType::QVariantMap) {
QString mapName = name;
if ( !isWritable ) {
mapName += "*";
}
buildProperty = m_manager->addProperty(QtVariantPropertyManager::groupTypeId(), mapName);