Create dock widgets over Slicer views

Hi.

I’m having problems implementing a floating (unmovable) widget container over the views like in these images:





As you can see the floating widgets appear over the views (3D and slices) and I think they should have a fixed relative position with reference to the corresponding view in case of mainWindow moveEvent or resizeEvent.
They should appear on click of the toolbar on the left but that I think I know how to do.

Let me show you the approaches I tried without success:

#sliceViewMenu
toolButton = qt.QToolButton()
toolButton.setText('R')
slicer.app.layoutManager().sliceWidget("Red").sliceController().barLayout().insertWidget(0,toolButton)

myMenu = qt.QMenu(toolButton)
toolButton.setMenu(myMenu)

toolButton.setPopupMode(qt.QToolButton.InstantPopup)

widget = qt.QWidget()
layout = qt.QVBoxLayout(widget)
pushButtonPlus = qt.QPushButton('+',widget)
slider = qt.QSlider(widget)
pushButtonMinus = qt.QPushButton('-',widget)
layout.addWidget(pushButtonPlus)
layout.addWidget(slider)
layout.setAlignment(slider, qt.Qt.AlignHCenter)
layout.addWidget(pushButtonMinus)

widgetAction = qt.QWidgetAction(myMenu)
widgetAction.setDefaultWidget(widget)

myMenu.addAction(widgetAction)

But you can’t have more that one menu shown at a time. So this doesn’t work.

  • Use ctkPopUpWidget (like the one on the pinButton of the sliceController) and set True to PinUp

ControllerLayout = slicer.app.layoutManager().sliceWidget("Yellow").sliceController().layout()

PopupWidget = ctk.ctkPopupWidget(slicer.app.layoutManager().sliceWidget("Yellow").sliceController())
PopupWidget.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Minimum)
ControllerLayout.addWidget(PopupWidget)
PopupWidget.setWindowFlags(PopupWidget.windowFlags() & ~qt.Qt.ToolTip)

layout = qt.QVBoxLayout(PopupWidget)
pushButtonPlus = qt.QPushButton('+',PopupWidget)
slider = qt.QSlider(PopupWidget)
pushButtonMinus = qt.QPushButton('-',PopupWidget)
layout.addWidget(pushButtonPlus)
layout.addWidget(slider)
layout.setAlignment(slider, qt.Qt.AlignHCenter)
layout.addWidget(pushButtonMinus)
PopupWidget.pinPopup(True)

But the horizontalLayout the popup widget is in makes it as large as the sliceController widget. So this approach doesn’t work either.

There were suggestions to derive the mainWindow class to change resizeEvent and moveEvent, or create a showDockWidgets function but I can’t follow them because the mainWindow is owned by Slicer. This one may also be handy.

QgsFloatingWidget widget appears to describe what I need.

Any guidance regarding the implementation of this would be greatly appreciated.

Floating QWidgets I think is something not primarily in the design structure for Qt. It seems like QWidgets are primarily designed to have a parent that is some QLayout and floating widgets don’t really have a layout parent. I can really only think about using actual QDockWidgets such as the behavior of the Module panel area or the Python Interactor. Those are dock widgets which can be docked or set to float. They would float over the entire main window though and just over some specific widget like an individual slice view.

You showcase actions for

  • Adjusting Window/Level
  • Panning the volume in the view
  • Zooming the volume in the view
  • Rotating the volume in the view
  • Flipping the volume in the view

What are the motivations for the design of floating widgets?

  • Is it to establish familiarity to the other program you showcase?
  • Is it not acceptable to use Slicer’s left module panel area for these actions? The other program simply doesn’t have a design with an area for other widgets so they have to be temporarily floated on top of the slice view.
  • Are you hiding the Slicer left module panel area to maximize the Slice viewers area which then requires you to float widgets on top of the slice views?
  • Is a 3 button mouse not being used which would otherwise support zoom/pan/etc actions without visible widgets?
  • Are you optimizing for a touch screen interface where a mouse is not present?

Can the containers (QDockWidget) be moved, if the mainWindow of Slicer is moved, automatically? or if it is resized adapt to that?

Another way could be to derive qMRMLSliceWidget and be set up the floating widget there. Can you give guidance regarding the implementation of this? I think I would need to remake all the layouts to use my_qMRMLSliceWidget also.

To answer your questions:

Is it to establish familiarity to the other program you showcase?

I think so.

Is it not acceptable to use Slicer’s left module panel area for these actions? The other program simply doesn’t have a design with an area for other widgets so they have to be temporarily floated on top of the slice view.

I think design is more comfortable to the users if they use a touchscreen

Are you hiding the Slicer left module panel area to maximize the Slice viewers area which then requires you to float widgets on top of the slice views?

The left module panel area is already used for the workflow. I will add the toolbar (left-side) that is shown in the images, that will steal little bit of space but is acceptable. We cannot have widgets for the showcase actions that are not floating over the views because there is not space.

Is a 3 button mouse not being used which would otherwise support zoom/pan/etc actions without visible widgets?

The GUI must be compatible with touchscreens and it would require users to buy specific hardware.

Are you optimizing for a touch screen interface where a mouse is not present?

Not optimizing but making it usable enough. So the software is good for mouse and good for touchscreen.

It’s an interesting use case of Slicer if touch is the primary input. I don’t think a lot of Slicer design has considered this in the past.

Does the touchscreen support multi touch gestures? Or is it simple input only?

For example in the following linked post there is a video of pinch-to-zoom and rotating working which may avoid the need for individual buttons that do the same.

On Windows, multitouch gestures work very well in 3D views to rotate, pan, zoom, spin. @Sunderlandkyl worked quite a lot on optimizing usability with a pen, too. On macOS, the rotate, pan, zoom, spin multitouch gestures are available on the touchpad.

It is a very interesting discussion. There are a number of projects where ease of use for newcomers is one of the most important design driver. For these cases, discoverability of the features via large, impossible-to-miss buttons is useful.

Rendering a Qt widget over a VTK render window will be always risky (may break with any VTK or Qt update), but it might work, so it is worth a try. We know that floating windows are problematic (the current floating window has problems with some window managers in certain situations - for example when alt-tabbing between applications). It would be much safer to add a button bar on the top or side of the render window.

@ungi you have implemented a few nice designs that addresses similar requirements. Could you tell about your experiences?

The solution I’ve seen from @Sunderlandkyl is to add a QToolButton to the view controller area, and then create a ctkPopupWidget with the tool button as parent. Then the popup widget does not span across the width of the view but shows floating like in the photos of @mau_igna_06. This is the same as the pin button on the 3D views that pops up a floating widget and the size is limited to its contents.
The problems are of course the same too. Sometimes the floating widget appears on the main monitor when Slicer is opened on the second monitor, they sometimes stay on the screen when Slicer is minimized, they show up earlier/later compared to the view, etc.
These problems can be so annoying that in another project we added “floating” widgets on a widget that has the same background color as the view and positioned right above the view. So unless you have something behind the widgets in the view, it gives the impression that that the buttons are floating in the view. I know that’s not an ideal solution either, but it looks better.
It would be nice if either Qt or VTK offered a stable solution to float widgets in views. It’s quite common in commercial applications. It is possible that those applications don’t have a stable solution either, but they have better control over what application is opened on what screen, they don’t switch between applications, and they keep underlying libraries on the same version.

3 Likes

Hi, sorry to interrupt. I encountered the exactly same problem as you do. I wonder if the problem was fixed in the end? Thank you so much.

It was done like it was explained here by Tamas

1 Like