Add dockable widgets in quadrants using PyQt

775 Views Asked by At

I'm creating a GUI using PyQt, to display 4 images in a window, positioned in this way:

  • Top left
  • Top right
  • Bottom left
  • Bottom right

I'd like to be able to undock them, but also to redock them back to any of the 4 available space.

My final goal is to set it up so that moving an undocked image where another one is already placed, would move that second image out of the way (docking it in another free quadrant or undocking it), or that placing it in the center would make it occupy all the 4 quadrants (undocking all the others).

I've tried achieving this with QDockWidget, but so far I'm not achieving good results.

My current code:

class MainWindow(QMainWindow):
def __init__(self):
    super().__init__()

    self.setWindowTitle("Window")

    grid_layout = QGridLayout()

    dock_window_1 = QMainWindow()
    docked = QDockWidget("Dockable", self)
    dock_window_1.addDockWidget(Qt.DockWidgetArea.TopDockWidgetArea, docked)
    dockedWidget = QWidget(self)
    docked.setWidget(dockedWidget)
    dockedWidget.setLayout(QVBoxLayout())
    dockedWidget.layout().addWidget(QPushButton("1"))

    dock_window_2 = QMainWindow()
    docked_2 = QDockWidget("Dockable_2", self)
    dock_window_2.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, docked_2)
    dockedWidget_2 = QWidget(self)
    docked.setWidget(dockedWidget_2)
    dockedWidget_2.setLayout(QVBoxLayout())
    dockedWidget_2.layout().addWidget(QPushButton("2"))

    dock_window_3 = QMainWindow()
    docked_3 = QDockWidget("Dockable_3", self)
    dock_window_3.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, docked_3)
    dockedWidget_3 = QWidget(self)
    docked.setWidget(dockedWidget_3)
    dockedWidget_3.setLayout(QVBoxLayout())
    dockedWidget_3.layout().addWidget(QPushButton("3"))

    dock_window_4 = QMainWindow()
    docked_4 = QDockWidget("Dockable_4", self)
    dock_window_4.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, docked_4)
    dockedWidget_4 = QWidget(self)
    docked.setWidget(dockedWidget_4)
    dockedWidget_4.setLayout(QVBoxLayout())
    dockedWidget_4.layout().addWidget(QPushButton("4"))

    grid_layout.addWidget(dock_window_1, 0, 0)
    grid_layout.addWidget(dock_window_2, 1, 0)
    grid_layout.addWidget(dock_window_3, 0, 1)
    grid_layout.addWidget(dock_window_4, 1, 1)

    widget = QWidget()
    widget.setLayout(grid_layout)
    self.setCentralWidget(widget)

This kind of works, but I'm only able to redock a widget in its original place.

Can anyone help me getting on the right road here?

Thanks in advance!

1

There are 1 best solutions below

7
Alexander On

Most of the behavior you are describing is actually already implemented by Qt using the QMainWindow and the QDockWidget.

The issue with your code is you are creating a unique QMainWindow for each of the QDockWidgets when the program only needs one QMainWindow. Additionally you are adding each of the QDockWidgets to the layout after you have already added them using the standard dockAreas surrounding the central widget.

If you eliminate the unneeded QMainWindows and instead assign each of the QDockWidgets to the your MainWindow, your desired functionality should work automatically.

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window")
        grid_layout = QGridLayout()

        docked = QDockWidget("Dockable", self)
        docked.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        dockedWidget = QWidget(self)
        docked.setWidget(dockedWidget)
        dockedWidget.setLayout(QVBoxLayout())
        dockedWidget.layout().addWidget(QPushButton("1"))

        docked_2 = QDockWidget("Dockable_2", self)
        docked_2.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        dockedWidget_2 = QWidget(self)
        docked_2.setWidget(dockedWidget_2)
        dockedWidget_2.setLayout(QVBoxLayout())
        dockedWidget_2.layout().addWidget(QPushButton("2"))

        docked_3 = QDockWidget("Dockable_3", self)
        docked_3.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        dockedWidget_3 = QWidget(self)
        docked_3.setWidget(dockedWidget_3)
        dockedWidget_3.setLayout(QVBoxLayout())
        dockedWidget_3.layout().addWidget(QPushButton("3"))

        docked_4 = QDockWidget("Dockable_4", self)
        docked_4.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        dockedWidget_4 = QWidget(self)
        docked_4.setWidget(dockedWidget_4)
        dockedWidget_4.setLayout(QVBoxLayout())
        dockedWidget_4.layout().addWidget(QPushButton("4"))

        self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, docked)
        self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, docked_2)
        self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, docked_3)
        self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, docked_4)
        widget = QWidget()
        widget.setLayout(grid_layout)
        self.setCentralWidget(widget)