Skip to content

Conversation

@jellybean2004
Copy link
Contributor

@jellybean2004 jellybean2004 commented Nov 14, 2025

Description

Implemented support for multiple simultaneous slicers on 2D plots, allowing users to create, manage, and switch between different slicer instances.

  • A unique ID is assigned to each slicer instance so they can be viewed simultaneously on the 2D fitting plot, and each generates an independent plot.
  • A list of all active slicers is added in the Slicer Parameters window so users can select and tweak them individually. The selected slicer gets automatically updated when a slicer is added or modified in the 2D plot. Users can also delete individual slicers from there.
  • Slicers have different colours to help differentiate them.

How Has This Been Tested?

Functionality tested in the program, works as expected.

Review Checklist:

[if using the editor, use [x] in place of [ ] to check a box]

Documentation (check at least one)

  • There is nothing that needs documenting
  • Documentation changes are in this PR
  • There is an issue open for the documentation (link?)

Installers

  • There is a chance this will affect the installers, if so
    • Windows installer (GH artifact) has been tested (installed and worked)
    • MacOSX installer (GH artifact) has been tested (installed and worked)
    • Wheels installer (GH artifact) has been tested (installed and worked)

Licensing (untick if necessary)

  • The introduced changes comply with SasView license (BSD 3-Clause)

@krzywon
Copy link
Contributor

krzywon commented Nov 14, 2025

I'm testing this locally and think this is looking incredibly promising! I can easily add any number of slicers to a 2D plot.

Questions and notes I have:

  • Should multiple slicers be added together, treated separately, or should there be a user option to switch? In the current implementation, they are all treated separately.
  • Is it possible to add a list of active slicers to the context menu, similar to how 1D plots list individual plots? This would help enable features (including ones I'm suggesting below), like removing a single slicer, changing the color of the slicer, etc.
  • Is it possible to remove only one of the slicers? This would allow a user to compare two slices, and only keep the one they want without having to keep another slicer visible.
  • Previous versions replaced the existing slicer when a new slicer was selected. This changes that paradigm. Is this desired, or should we include a way for the user to either add or replace a slicer?
  • Selecting a slicer on the plots highlights that slicer in the slicer parameters window, which is a nice feature!
  • Multiple, overlapping slicers are difficult to differentiate. Is it possible to make them all different colors or even give the user an option for colors?

@jellybean2004
Copy link
Contributor Author

I'm testing this locally and think this is looking incredibly promising! I can easily add any number of slicers to a 2D plot.

Questions and notes I have:

  • Should multiple slicers be added together, treated separately, or should there be a user option to switch? In the current implementation, they are all treated separately.
  • Is it possible to add a list of active slicers to the context menu, similar to how 1D plots list individual plots? This would help enable features (including ones I'm suggesting below), like removing a single slicer, changing the color of the slicer, etc.
  • Is it possible to remove only one of the slicers? This would allow a user to compare two slices, and only keep the one they want without having to keep another slicer visible.
  • Previous versions replaced the existing slicer when a new slicer was selected. This changes that paradigm. Is this desired, or should we include a way for the user to either add or replace a slicer?
  • Selecting a slicer on the plots highlights that slicer in the slicer parameters window, which is a nice feature!
  • Multiple, overlapping slicers are difficult to differentiate. Is it possible to make them all different colors or even give the user an option for colors?

Hi @krzywon! Welcome back, and thank you for your comments.

So this is the first step in the whole project I have at hand, which is just to enable the addition of multiple slicers at once, bringing it closer to the features available in Grasp. The next step is to plot them on the same plot. Finally, to link the slicers so that cases like peaks symmetrically distributed around the centre can be handled more easily.

I've just pushed a commit that implements deleting a single slicer.

Regarding the list of active slicers, I have updated the Slicer Parameters window to include a list of all the slicers shown in the plot. There, you can also check a particular slicer to tweak the parameters, and with the new commit, you can delete just the selected slicer too. Is this what you mean?

I could add a checkbox somewhere so users can either have the newly added multi-slicers or keep the previously supported single slicer, if that is something useful to keep.

You did raise a good point about the slicers being the same colour, and therefore being hard to differentiate (I was ignoring that, but I guess I have been caught 😅) I will give a shot at implementing that.

Copy link
Member

@rozyczko rozyczko left a comment

Choose a reason for hiding this comment

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

A few minor comments to the source and questions about implementation.

@jellybean2004
Copy link
Contributor Author

@krzywon, I have changed the slicer colours to be different, so it is easier to differentiate. I have also added the functionality of individual slicer deletion, which can be done from the slicer parameter window.

@rozyczko, I have implemented most of your suggestions. I will now work on refactoring out the ID generation, as it is the same for each slicer.

Please let me know if there is anything else I could add, or something I missed.

Copy link
Member

@butlerpd butlerpd left a comment

Choose a reason for hiding this comment

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

Nice work though I have not looked closely at the code yet. This is mainly a functionality review based on window 11 testing of the installer.

  • Clear slicers does not clear the box sum box on the plot so once a box sum is slicer is activated, the box on the 2D plot is permanent and we have to close the whole plot before another slicer can be used cleanly. Not sure if this predates this PR in which case another issue might be appropriate, but prior to this PR a new slicer removed the old anyway?
  • There are really irritating white lines that randomly appear (apparently when another window passes over the plot?) but this seem to be there from before this pull request? In which case an issue might be the better place to worry about this.
  • I like that all slicers, even the annulus (and box sum though IMO we should eventually remove that as a thing) allows for multiple slicers. The color coding helps, but it is a bit unintuitive that the new slicer falls directly on top of the old one. To the newbie it looks like nothing happened? maybe just offset the interactors by a small fraction from the first one each time. If the third is on top of the second it won't matter as by that point it should be clear? and most people will move the second to where they want it before asking for a third slicer?
  • Alternatively I guess the offset could increment each time? This is more complicated for the annulus which does not have a center that ties the two sides together. Not sure how to deal with that, but I think the colloid orientation people will use the multiple annulus a lot.
  • Related, the top color is the color for the last slice. However, if one grabs the center point and moves the ROI, it is the very first one that gets moved. This is very counterintuitive. At first I thought that resizing the one I moved was not working as I assumed it should be updating the last 1D plot (the one on top).
  • Clearing slicers does not clear the 1D plots. Should it? I think so, specially now that we may have several plots from one 2D plot. Also @ChristianWoegerbauer work to promote the slicer plot to a regular data set, if the user wants to maitain that data they presumably will have pressed send to fitting?
  • It is not clear to me that there is a use case for having two different types of slicers on the plot at once? And if the normal desire is to change slicer types it might be useful to revert that behavior to clearing the slicers and creating the new one. That said, all the user has to learn to do is to clear slicers before asking for a different slicer type so maybe it is OK to leave as is for now?

@butlerpd
Copy link
Member

Another question: what happens if I add enough slicers to go through the list of 15 defined colors? do the colors recycle back at the beginning?

@jellybean2004
Copy link
Contributor Author

Another question: what happens if I add enough slicers to go through the list of 15 defined colors? do the colors recycle back at the beginning?

Yes, the colours cycle back, but hopefully no one is adding more than 15 slicers 😅

Copy link
Contributor

@krzywon krzywon left a comment

Choose a reason for hiding this comment

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

Adding a BoxSum deletes all other slicers, except existing BoxSum slicers, but previous BoxSum slicer interactors are no longer draggable on the plot. Clear Slicers does not remove any BoxSum, including the one the is still interactive.

Otherwise, see code comments.

Thanks for making the individual slicers removable and for the color work!

# TODO: generalize this and put it in GuiUtils so that we can use it elsewhere
tempPlotsToRemove = []
slicer_type_id = 'Slicer' + self.data0.name
for itemIndex in range(item.rowCount()):
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm almost certain this could be simplified into a single loop and a more compact if tree. CodeScene will gate on something like this, which is why I am noting it. I'll try to find a cleaner solution today.

self.slicer_widget.checkSlicerByName(name)
break
except Exception:
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

What exceptions could get a user here? Swallowing exceptions like this seems unsafe.


def onSlicerReplaceChanged(self, index):
"""replace the slicer with the one chosen"""
if index == 0: # No interactor
Copy link
Contributor

Choose a reason for hiding this comment

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

Are there any cases where index might be a non-integer value, like False, None, or empty strings? If not, this is fine, but if so, those Falsey values will fall through.

Side note: Type hints on this method would help clarify this situation.

try:
slicer_obj.clear()
except (ValueError, AttributeError):
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

Were you seeing these errors? In what cases? Why are you swallowing them?

Comment on lines +124 to +125
# new_plot.name = "AnnulusPhi" + "(" + self.data.name + ")"
# new_plot.title = "AnnulusPhi" + "(" + self.data.name + ")"
Copy link
Contributor

Choose a reason for hiding this comment

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

Another instance of code commented out that should probably be removed instead.

Utility functions for slicers
"""

def generate_unique_plot_id(base_id, item):
Copy link
Contributor

Choose a reason for hiding this comment

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

Type hints, please.

parent_item = item if item.parent() is None else item.parent()

existing = 0
for i in range(parent_item.rowCount()):
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we assuming this tree only has a single set of children? That might change in the future.

Perhaps an intermediate, recursive method that returns the count for each level with this for loop would be better.

Suggested change
for i in range(parent_item.rowCount()):
def _row_count_for_level(parent_item, count: int):
for i in range(parent_item.rowCount()):
it = parent_item.child(i)
d = it.data()
if hasattr(d, "id") and isinstance(d.id, str) and d.id.startswith(base_id):
count += 1
if it.rowCount() > 0:
count += _row_count_for_level(it, count)
return count

Copy link
Member

@butlerpd butlerpd left a comment

Choose a reason for hiding this comment

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

having looked at the code a bit and the other comments I think this is essentially ready to go after fixing the issues named or pushing them to new issues for a future PR. Probably don't want this PR to grow too much?

@krzywon
Copy link
Contributor

krzywon commented Nov 19, 2025

@butlerpd and @jellybean2004 - The box sum issues I've mentioned are new and should be addressed before I will approve. The one that is required is the box sum not being cleared when slicers are cleared so multiple box sum images end up on the plot. The box sum clearing all other slicers when a new box sum is generated is how it currently works, but is also different than the new functionality, so I'm conflicted on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants