Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Allow for custom presets prefix and tooltips #1052

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

vovythevov
Copy link

Although Slicer allowed to create custom presets for the Slice views, it
did not allow the user to customize the tooltip nor the prefix that would
show in the view.
It is now possible to customize each of the slice views preset to have
a custom prefix and/or tooltip.

Although Slicer allowed to create custom presets for the Slice views, it
did not allow the user to customize the tooltip nor the prefix that would
show in the view.
It is now possible to customize each of the slice views preset to have
a custom prefix and/or tooltip.
Copy link
Member

@jcfr jcfr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done 👍 I will make use of this right away in an other project 😄

Few nitpicks, and should be good to go.

@lassoan

vtkErrorMacro("GetSliceOrientationPreset: invalid orientation preset name: " << name);
return NULL;
OrientationPresetType* preset = this->GetOrientationPreset(name);
return preset ? preset->Orientation : nullptr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullptr -> NULL, since we will most likely create 4.10.1 ... let's continue support c++98

vtkErrorMacro("GetOrientationPreset: The orientation preset "
"'" << name << "' does NOT exist.");
}
return nullptr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> NULL

@@ -460,6 +477,26 @@ bool vtkMRMLSliceNode::RemoveSliceOrientationPreset(const std::string &name)
return false;
}

//----------------------------------------------------------------------------
OrientationPresetType* vtkMRMLSliceNode::GetOrientationPreset(const std::string &name, bool error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bool error -> bool displayError

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead, what do you think of displaying the error on the user side ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a protected method. It only has this option so you can query the preset existence in HasSliceOrientationPreset without an error.

return false;
}

//----------------------------------------------------------------------------
bool vtkMRMLSliceNode::HasSliceOrientationPreset(const std::string &name)
bool vtkMRMLSliceNode::RenameSliceOrientationPresetPrefix(const std::string &name, const std::string &prefix)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about RenameSliceOrientationPresetPrefix -> SetSliceOrientationPresetPrefix ?

std::vector< OrientationPresetType >::iterator it;
for (it = this->OrientationMatrices.begin(); it != this->OrientationMatrices.end(); ++it)
//----------------------------------------------------------------------------
bool vtkMRMLSliceNode::RenameSliceOrientationPresetTooltip(const std::string &name, const std::string &tip)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RenameSliceOrientationPresetTooltip -> SetSliceOrientationPresetTooltip

@@ -23,6 +23,15 @@ class vtkMRMLVolumeNode;
class vtkMatrix3x3;
class vtkMatrix4x4;

/// \brief Structure to store preset orientation information.
struct OrientationPresetType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct VTK_MRML_EXPORT OrientationPresetType

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead, I suggest to move the struct into the vtkMRMLSliceNode class, that way it will be available from python.

/// \sa AddSliceOrientationPreset(const std::string& name, vtkMatrix4x4* sliceToRAS)
/// \sa AddSliceOrientationPreset(
/// const std::string& name, vtkMatrix3x3* sliceToRAS,
/// const std::string& prefix, const std::string& tooltip)
/// \sa UpdateMatrices()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// \sa GetSliceOrientationPresetPrefix()
/// \sa GetSliceOrientationPresetTooltip()

@@ -186,6 +197,16 @@ class VTK_MRML_EXPORT vtkMRMLSliceNode : public vtkMRMLAbstractViewNode
/// preset.
std::string GetSliceOrientationPresetName(vtkMatrix3x3* orientationMatrix);

/// \brief Return the preset prefix corresponding to a preset \a name.
///
/// Returns an empty string if the preset with the given \a name doesn't exist.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\sa SetSliceOrientationPresetPrefix()
\sa SetSliceOrientationPresetTooltip()

@@ -197,21 +218,46 @@ class VTK_MRML_EXPORT vtkMRMLSliceNode : public vtkMRMLAbstractViewNode
///
/// \sa RenameSliceOrientationPreset(const std::string& name, const std::string& updatedName)
/// \sa RemoveSliceOrientationPreset(const std::string& name)
bool AddSliceOrientationPreset(const std::string& name, vtkMatrix3x3* orientationMatrix);
/// \sa RenameSliceOrientationPresetPrefix(const std::string& name, const std::string& prefix)
/// \sa RenameSliceOrientationPresetTooltip(const std::string& name, const std::string& tip)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there are no ambiguity on the signature, I suggest to reference the function without arguments ... less chance for it become out of the sync

\sa SetSliceOrientationPresetPrefix()
\sa SetSliceOrientationPresetTooltip()

@pieper
Copy link
Member

pieper commented Dec 6, 2018

Can you add a note about why (in what use case) you want to use these options?

@vovythevov
Copy link
Author

@pieper:
Sure !
When you add a custom preset, the tooltip always shows "Oblique" as if you were in a "Reformat" view, which is weird for users.
As an example:
image

Before this, the only way to modify this would have been to find the qMRMLSliderWidget for each qMRMLSliceControllerWidget and manually change each tooltip and each prefix. This would have to be repeated whenever you change layout as well.

@pieper
Copy link
Member

pieper commented Dec 6, 2018

Ah - 👍 thanks!

Copy link
Contributor

@lassoan lassoan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, it will be useful to remove hardcoded offset label and slider tooltip. However, there are some concepts to be cleaned up.

Slice node should not store strings that the application must blindly display in very specific places, as we don't know how exactly the application wants to present slice views. Instead, it should store properties that the application can interpret and decide how to display.

For example, instead of storing preformatted prefix ("S: ") and tooltip ("I <-----> S"), we should just store axis labels for positive and negative directions "S" and "I". Then the application may decide to use axis labels to generate prefix and tooltip strings.

Since axis labels are already defined in the base class (vtkMRMLAbstractViewNode, Get/SetAxisLabel), it is not necessary to define labels again. We can determine the axis index from the slice normal direction (3rd column of the orientation matrix). If the normal is aligned exactly with an axis then you can use the corresponding axis labels. If it is not aligned exactly then we can use multiple directions (non-zero components of the slice normal vector; for example: LA <-----> RP; or LAI <-----> RPS) or just not use a label at all.

While we can always compute axis labels, it may be useful to specify custom axis labels for custom orientations. For example, an organ-oriented preset we might want to use "medial<----->lateral" labels instead of some automatically generated labels.

To summarize: I would recommend to replace "prefix" and "tooltip" members by "LabelAxisPositive" and "LabelAxisNegative". Generate prefix and tooltip strings in the GUI widget class. We can decide if we want to compute axis index from direction matrix to look up axis label or we always rely on "LabelAxisPositive" and "LabelAxisNegative" strings.

/// \brief Structure to store preset orientation information.
struct OrientationPresetType
{
std::string Name;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add documentation for each member. FOr example, I don't know what "Prefix" is used for, how it is different from "Name" and why it is called prefix (prefix to what?).

std::string Name;
vtkSmartPointer<vtkMatrix3x3> Orientation;
std::string Prefix;
std::string Tooltip;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"tooltip" describes too specifically how this information must be used for. Instead, the variable should be called "description" or something similar and the application could decide how to present it to the user (for example, the application can show it as a tooltip).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lassoan What about changing Tooltip into Description ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My general comment above essentially supersedes this comment. We should not store preformatted display strings here. Instead, we should just store axis labels (which by the way are optional because we already define axis labels for the 3 main axes in the abstract view node).

For example just store "S" and "I" axis labels and let the GUI widget generate "S: " prefix and "I <-----> S" tooltip.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. It makes sense to update qMRMLViewControllerBar to generate reasonable default for Prefix, Tooltip, ...

That said, to support custom value per presets:

  • for a custom preset named Foo, add user setting entry (e.g ViewControllerBar/OrientationPresets/Foo/Tooltip, ViewControllerBar/OrientationPresets/Foo/Prefix)
  • the qMRMLViewControllerBar would be updated to check if settings are set and associated values

Finally, if in a custom application more advanced customization are still needed, making sure the "widgets" of the viewer controller are associated with an objectName allow getting their reference and update them. (for example to hide the pin button, set prefix to empty string, ...)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to support custom value per presets

Allowing setting custom positive/negative axis labels should be enough customization. Do you have any use case where this would not give good results?

Finally, if in a custom application more advanced customization are still needed, making sure the "widgets" of the viewer controller are associated with an objectName allow getting their reference and update them.

I agree. This would allow fine-grained customization without overcomplicating the API. For common customizations, such as hiding the pin button and adding custom buttons next to the slider, we should also add explicit method calls.

@@ -460,6 +477,26 @@ bool vtkMRMLSliceNode::RemoveSliceOrientationPreset(const std::string &name)
return false;
}

//----------------------------------------------------------------------------
OrientationPresetType* vtkMRMLSliceNode::GetOrientationPreset(const std::string &name, bool error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not complicate the API with with this "bool error" flag. Since the caller can detect if a preset is not found (by getting NULL as result), it is not essential to log an error..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
4 participants