Hello,

Thanks, that's all really helpful information!

I was able to get a SimpleMemberCommand to fire when the node is renamed,
but I was wondering if there is a way I can attach a MemberCommand to
itk::ModifiedEvent.

See code below:
void AwesomeView::NodeNameModified(itk::Object* caller, const itk::
EventObject& event)
{
    // this seems useful, but does not execute
    QMessageBox::information(nullptr, tr("Got an event!"), tr(
"You renamed the node!"));
}

void AwesomeView::NodeNameModified_Simple()
{
    // unfortunately SimpleMemberCommand does not specify which node
sent the command
    QMessageBox::information(nullptr, tr("Got a simple event!"), tr(
"You renamed the node!"));
}

// note: this does get called when a file is loaded, too.
void AwesomeView::NodeAdded(const mitk::DataNode* node)
{
    // this does not work
    {
        itk::MemberCommand<AwesomeView>::Pointer command = itk::
MemberCommand<AwesomeView>::New();
        command->SetCallbackFunction(this, &AwesomeView::NodeNameModified);

        // Isn't this technically mutating the node? Is this UB since
node is const?
        node->GetProperty("name")->AddObserver(itk::ModifiedEvent
(), command);
    }

    // this does work
    {
        itk::SimpleMemberCommand<AwesomeView>::Pointer simpleCommand = itk::
SimpleMemberCommand<AwesomeView>::New();
        simpleCommand->SetCallbackFunction(this, &AwesomeView
::NodeNameModified_Simple);

        node->GetProperty("name")->AddObserver(itk::ModifiedEvent
(), simpleCommand);
    }

    QMessageBox::information(nullptr, tr("Node added"), tr("Node added"));


}

I looked into the MITK source but I could not find a reason why
MemberCommand does not work, but SimpleMemberCommand does.
- Does anybody know why a DataNode's ModifiedEvent does not seem to work
with MemberCommand? It seems like in theory the underlying itk code is
capable of providing a pointer to the caller and the EventObject.
- Are there some restrictions on what kinds of commands a certain
event/Observer supports? How do I know what type of Commands I can use?

Now, to reply to specific things that came up in the email chain,

>From Stefan:

> I do not know your context, e.g. if you want to modify MITK code like the
> Data Manager plugin, do not want to touch MITK code, or want to write your
> own application from scratch based on or not based on BlueBerry.
>

I would prefer to not modify the MITK code, we just use it in our
application.

I didn't check but you should be able to also hook into the data storage to
> easily get notified when a new data node was added or removed from the
> storage.
>

Are you referring to the same method that Amir was talking about,
QmitkAbstractView::NodeChanged(...)? Or something else?

>From Amir:

Thank you for the class names to check out, looking at those sample
implementations was very helpful!

I think I will first try using a DataNode property observer, and I'll save
the QmitkAbstractView::NodeChanged() option as a plan B if it's not
possible to use MemberCommand on a DataNode property observer.

>From Ralf:

1. What is the purpose of the name property in your workflow? Is it used as
> an Identifier?
>

Yes, it's technically an identifier. A variable of sorts, but not a
programming variable. It's not the only way we uniquely identify the data,
we have our own setup for managing the data behind the treeview.

For child nodes in certain places in the hierarchy, the name must be unique
and not empty space (to make it easy to display) for all the nodes at that
level of the hierarchy.  I may need to add more restrictions on what a
valid name is in the future, too.

I could allow the displayed name to desync from the identifier the node
represents, but this seems like it would make for a more confusing UI that
would require users to manually click on and view properties a lot more.
I'd prefer to avoid that.

If so, wouldn't be the Identifiable-Interface a better option?
>

I'm not familiar with this, do you have a link?

Note that an interface that expects all the data entries to have unique
names won't work well, the rest of the nodes in the program do allow for
duplicate names. Only nodes holding a certain data type will require their
child nodes to have unique names.

So you will know that a node as a changed name, but you will only be able
> to directly query the new name. The old name is already lost then. So you
> would need to somewhere else store the last known node names additionally.
>

I think this might be ok, the data the nodes represent is stored somewhere
else in the code.
- I think I can just compare the new node name to the old name stored
elsewhere in the program before applying the node name to the identifier.
- If the new node name is invalid, I could then show a message and revert
the node's name back to the identifier.

It's not perfect, but the only other alternative I can think of is to
somehow completely disable the ability to double click and rename any node
(which might not be possible or desirable).

2. What is the purpose of the Data Manager plugin in in your workflow? Do
> you need it at all (we currently migrating more and more views to work
> without the Data Manager but with DataStorageInspectors and
> SelectionWidgets)?
>

We use it extensively, it displays all the data we have, and I'd guess it's
here to stay. But, I appreciate the suggestion. I'll look into those
classes when I get a bit more time.

Thanks,

- Alex

>

On Tue, Sep 1, 2020 at 4:12 AM Floca, Ralf Omar <r.fl...@dkfz-heidelberg.de>
wrote:

> Hi Alex,
>
> just an addition to Stefan's comment. He has already pointed you in the
> right direction. Going that path one challenge will be, that the Modified
> events are triggered *after* the modification. So you will know that a node
> as a changed name, but you will only be able to directly query the new
> name. The old name is already lost then. So you would need to somewhere
> else store the last known node names additionally.
>
> Before you following the path let me add some comments/questions:
> 1. What is the purpose of the name property in your workflow? Why do you
> need to control it? Is it used as an Identifier? If so, wouldn't be the
> Identifiable-Interface a better option?
> 2. What is the purpose of the Data Manager plugin in in your workflow? Do
> you need it at all (we currently migrating more and more views to work
> without the Data Manager but with DataStorageInspectors and
> SelectionWidgets)?
>
> Best Ralf
>
> ________________________________
> Date: Tue, 1 Sep 2020 07:21:16 +0000
> From: "Dinkelacker, Stefan" <s.dinkelac...@dkfz-heidelberg.de>
> To: Alex Melville <amelv...@umich.edu>,
>         "mitk-users@lists.sourceforge.net" <
> mitk-users@lists.sourceforge.net>
> Subject: Re: [mitk-users] Event to handle Data Manager node renames
> Message-ID: <1598944874273.67...@dkfz-heidelberg.de>
> Content-Type: text/plain; charset="utf-8"
>
> ?Hi Alex,
>
>
> I do not know your context, e.g. if you want to modify MITK code like the
> Data Manager plugin, do not want to touch MITK code, or want to write your
> own application from scratch based on or not based on BlueBerry. But in
> general, you are on the right track with ITK events. You want to hook into
> the name property which also is an ITK object and which is the actual
> location the name is eventually stored (single source of truth). I didn't
> check but you should be able to also hook into the data storage to easily
> get notified when a new data node was added or removed from the storage. In
> these cases you want to add/remove an observer to that data at least for
> the modified event of the name property.
>
>
> Best,
>
> Stefan
>
> ________________________________
> Von: Alex Melville <amelv...@umich.edu>
> Gesendet: Montag, 31. August 2020 17:06
> An: mitk-users@lists.sourceforge.net
> Betreff: Re: [mitk-users] Event to handle Data Manager node renames
>
> Hello, I still haven't been able to find a solution for this. I figured
> I'd try again since there weren't any replies, I'll post my progress on
> investigating this in case this helps somebody.
>
> It looks like if there is a way to do this, it would involve hooking into
> something on the ITK side.
>
> A possible workaround for this would be to have some kind of periodic
> checker function that goes through everything in the treeview and looks for
> node names that changed and roll back invalid / duplicate names, but I
> would rather not do that.
>
> MITK\Modules\Core\src\DataManagement\mitkDataStorage.cpp
> void mitk::DataStorage::OnNodeModifiedOrDeleted(const itk::Object *caller,
> const itk::EventObject &event) {  if (m_BlockNodeModifiedEvents) return;
>
>  const mitk::DataNode *_Node = dynamic_cast<const mitk::DataNode
> *>(caller);  if (_Node)  { const itk::ModifiedEvent *modEvent =
> dynamic_cast<const itk::ModifiedEvent *>(&event); if (modEvent)
> ChangedNodeEvent.Send(_Node); else  DeleteNodeEvent.Send(_Node);  }
>
> in MITK\Modules\Core\src\DataManagement\mitkDataStorage.cpp
> void mitk::DataStorage::AddListeners(const mitk::DataNode *_Node) {
> itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_MutexOne);  // node
> must not be 0 and must not be yet registered  mitk::DataNode *NonConstNode
> = const_cast<mitk::DataNode *>(_Node);  if (_Node &&
> m_NodeModifiedObserverTags.find(NonConstNode) ==
> m_NodeModifiedObserverTags.end())  {
> itk::MemberCommand<mitk::DataStorage>::Pointer nodeModifiedCommand =
> itk::MemberCommand<mitk::DataStorage>::New();
> nodeModifiedCommand->SetCallbackFunction(this,
> nodeModifiedCommand->&mitk::DataStorage::OnNodeModifiedOrDeleted);
> m_NodeModifiedObserverTags[NonConstNode] =
> NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand);
>
> itk::MemberCommand<mitk::DataStorage>::Pointer interactorChangedCommand =
> itk::MemberCommand<mitk::DataStorage>::New();
> interactorChangedCommand->SetCallbackFunction(this,
> interactorChangedCommand->&mitk::DataStorage::OnNodeInteractorChanged);
>
> m_NodeInteractorChangedObserverTags[NonConstNode] =
> NonConstNode->AddObserver(mitk::DataNode::InteractorChangedEvent(),
> interactorChangedCommand);
>
> // add itk delete listener on datastorage
> itk::MemberCommand<mitk::DataStorage>::Pointer deleteCommand =
> itk::MemberCommand<mitk::DataStorage>::New();
> deleteCommand->SetCallbackFunction(this,
> deleteCommand->&mitk::DataStorage::OnNodeModifiedOrDeleted);
> // add observer
> m_NodeDeleteObserverTags[NonConstNode] =
> NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand);  } } Now
> we're getting into ITK.
>
> Looks like maybe I should look at itk::ModifiedEvent()
>
> Seems like
> Modules/Core/Common/src/itkEventObject.cxx:70:itkEventMacroDefinition(ModifiedEvent,
> AnyEvent)
>
> #define itkEventMacroDeclaration(classname, super)                   \
>  /** \class classname */                                            \
>  class ITKEvent_EXPORT classname:public super                       \
>  {                                                                  \
> public:                                                              \
> typedef classname Self;                                          \
> typedef super     Superclass;                                    \
> classname();                                                     \
> classname(const Self &s);                                        \
> virtual ~classname();                                            \
> virtual const char *GetEventName() const;                        \
> virtual bool CheckEvent(const::itk::EventObject * e) const;      \
> virtual ::itk::EventObject *MakeObject() const;                   \
> private:                                                             \
> void operator=(const Self &);                                    \
>  };
>
> #define itkEventMacroDefinition(classname, super)
>  \
> classname::classname() {}
>  \
> classname::classname(const classname &s):super(s){};
>   \
> classname::~classname() {}
>   \
> const char * classname::GetEventName() const { return #classname; }
>   \
> bool classname::CheckEvent(const::itk::EventObject * e) const
>  \
>  { return ( dynamic_cast< const classname * >( e ) != ITK_NULLPTR ); } \
> ::itk::EventObject *classname::MakeObject() const { return new classname; }
> \
>
> I guess most of the interesting stuff is in the EventObject class, but it
> seems like this just serves as an intermediary for some other code
>
> ITK_4.9.0>grep ModifiedEvent * -RinF
> Modules/Core/Common/include/itkEventObject.h:179:itkEventMacroDeclaration(ModifiedEvent,
> AnyEvent)
>
> Modules/Core/Common/include/itkTreeChangeEvent.h:33:class
> TreeChangeEvent:public ModifiedEvent
> Modules/Core/Common/include/itkTreeChangeEvent.h:39:  typedef
> ModifiedEvent   Superclass;
> Modules/Core/Common/include/itkTreeChangeEvent.h:65:
> TreeChangeEvent(const Self & s):itk::ModifiedEvent(s) {}
>
> Modules/Core/Common/src/itkEventObject.cxx:70:itkEventMacroDefinition(ModifiedEvent,
> AnyEvent)
> Modules/Core/Common/src/itkObject.cxx:390:  InvokeEvent( ModifiedEvent() );
>
> Modules/Core/Common/wrapping/ITKCommonBase.wrap:13:itk_wrap_simple_class("itk::ModifiedEvent")
>
> Modules/Filtering/LabelMap/include/itkLabelMap.h:472:
> bool iEmitModifiedEvent );
>
> Modules/Filtering/LabelMap/include/itkLabelMap.hxx:256:        bool
> emitModifiedEvent = ( iLabel == m_BackgroundValue );
> Modules/Filtering/LabelMap/include/itkLabelMap.hxx:257:
> this->RemovePixel( tempIt, idx, emitModifiedEvent );
> Modules/Filtering/LabelMap/include/itkLabelMap.hxx:326:              bool
> iEmitModifiedEvent )
> Modules/Filtering/LabelMap/include/itkLabelMap.hxx:337:      if(
> iEmitModifiedEvent )
> Modules/Filtering/LabelMap/include/itkLabelMap.hxx:359:  bool
> emitModifiedEvent = true;
> Modules/Filtering/LabelMap/include/itkLabelMap.hxx:360:  RemovePixel( it,
> idx, emitModifiedEvent );
>
> Modules/ThirdParty/GDCM/src/gdcm/Source/Common/gdcmEvent.h:91:gdcmEventMacro(
> ModifiedEvent      , AnyEvent );
>
> Doesn't seem like this is used anywhere. Maybe that's just something
> that's treated as a ModifiedEvent, need to figure out where the event is
> created.
>
> Let me know if you have any suggestions, thanks,
>
> - Alex
>
> On Wed, Aug 26, 2020 at 11:26 AM Alex Melville <amelv...@umich.edu<mailto:
> amelv...@umich.edu>> wrote:
> Hello,
>
> Using the MITK sample project as an example, I would like to add an event
> handler for when a node in the Data Manager is renamed by double clicking
> on it.
>
> I would like to be able to:
> - Have our program do something in response to the node being renamed
> - Show a message and cancel the renaming operation if it can't be used
> (e.g., if the name is already in use, or invalid)
>
> I would also like this same behavior to happen when the "name" property of
> a node is edited in the "Properties" window, I notice that they seem to be
> tied together, so this might be the same event.
>
> If this isn't possible, I would also be OK with disabling this double
> click rename functionality completely, and the property tree rename too,
> and using my own custom UI instead for renaming nodes.
>
> I've been looking through the source, and I've seen some things like
> ModifiedEvent in itk that sound promising, but I am having trouble locating
> exactly which object / event to hook into.
>
> Thanks,
>
> - Alex
> -------------- next part --------------
> An HTML attachment was scrubbed...
>
> ------------------------------
>
>
>
> ------------------------------
>
> Subject: Digest Footer
>
> _______________________________________________
> mitk-users mailing list
> mitk-users@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/mitk-users
>
>
> ------------------------------
>
> End of mitk-users Digest, Vol 172, Issue 1
> ******************************************
>
_______________________________________________
mitk-users mailing list
mitk-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mitk-users

Reply via email to