Disable rotations & single handle rescaling in shape layer


I am trying to make a squared ROI with a shape layer with 4 handles. My ROI shouldn’t be rotated and only rescaled (as a whole but not individual handles, so that the square/rectangle ratio is maintained). “s” key has a default binding that does most of this (only allows lateral displacement and whole-shape rescaling but not individual handles rescaling) however it still allows rotations of the shape. Is there an easy way to block this from the code?

Hi @Diego_AC - do you mean the “shift” key not the “s” key in the above?

If I hold done shift before I start drawing a rectangle then it will be forced to be a square.

If I have a shape, select it, and then I hold down shift and start adjusting from one of the bottom handles it maintains aspect ratio. If I adjust from a side handle it does look like it just “stretches” which isn’t so good (see end of gif). We could maintain a fixed aspect ratio even from a resize there, or those handles could disappear when you press shift.

Right now there isn’t a way to disable rotation, stretching, and lock in fixed aspect ratio from the code, but that could in theory be added. Is that what you want? Can you explain a little more about what you’re trying to achieve, why you want the functionality to be this way and then we can try and figure out what’s best!! A screenshot or gif might help too.

Hi Nicholas,

Thanks for the response! I got confused with the key, it is shift as you point out. The reason why I confused it is because I accessed it from my code as


And naïvely I though this was s, not shift. But I see now I was wrong. Let me explain here better what I am trying to achieve:

I have an image layer displaying data and a shape layer for the ROI. The shape layer is instantiated like a 500x500 square:

self.roi = self.viewer.add_shapes(
            [np.array([[0, 0], [500, 0], [500, 500], [0, 500]])],

Now, the idea is that this rectangular ROI can be scaled and translated but not rotated or deformed so one can select a rectangular region of the data and an extra button adds functionality to, once happy with the ROI, proceed to cropping the data in the image layer and display it accordingly (together with the ROI dissapearing).

roi_pos = (
        roi_size = (
            int(self.wid_display.roi.data[0][3][1] - self.wid_display.roi.data[0][0][1]),
            int(self.wid_display.roi.data[0][1][0] - self.wid_display.roi.data[0][0][0]),
        self.state.camera_settings.subarray = tuple(
            [roi_pos[1], roi_pos[0], roi_size[1], roi_size[0]]
        self.update_roi_info(width=roi_size[0], height=roi_size[1])
        if self.wid_display.roi.visible:
            self.wid_display.roi.visible = False

The reason behind deactivating rotations is that the region has to necessarily be parallel to the edges of the image. So we want to avoid mistakenly rotating the shape. I hope this helps to understand what I was looking for and why but happy to clarify further. Thanks!

This was incredibly helpful - thanks for the detailed explanation. I understand the use case and need for finer grained control over the interactivity. Now we just need to think about the best and most natural way to expose this control. I think it should probably be at the per-layer level and really be something that is modifying the allowed interactivity. Maybe the simplest way to start is just a way to disable rotation?

There has also been some other discussion of cropping in napari too which you might find useful https://github.com/napari/napari/issues/1341 and we could also think about providing a more dedicated way to do cropping without creating a shapes layer, if that was of interest too?

Uh oh! This is so nice! I think it might work and it is way cleaner than having a shape layer. I will reply later once I have tried this. Thanks

1 Like