0

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!

Mdp11
  • 552
  • 4
  • 13

1 Answers1

0

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)
Alexander
  • 16,091
  • 5
  • 13
  • 29
  • Thanks for your reply. Unfortunately this results in the 4 widgets being top, bottom, left and right, when I'd like them to be placed in a 2x2 square, so one top-right, one top-left, one bottom-right and one bottom-left. – Mdp11 Sep 03 '22 at 13:27
  • @Mdp11 Try it now – Alexander Sep 03 '22 at 13:35
  • That's already starting to look the way I want. Thank you! – Mdp11 Sep 03 '22 at 15:01
  • Do you know how to make it so that docked widgets gets replaced instead of getting stacked? And also how to enable to place it in center taking up all 4 slots? I'm not pretending you to write the code for me, also pointing me in the right direction would be greatly appreciated! – Mdp11 Sep 03 '22 at 15:02
  • @Mdp11 And what would happen to the other 3 when you put one in the center? – musicamante Sep 03 '22 at 18:35
  • @Mdp11 Honestly not really. I suggest opening a new question that focuses solely on that because I don't really know where to start for that – Alexander Sep 03 '22 at 18:54
  • All you need to do is remove the central widget, and the docks will take up all the space. – ekhumoro Sep 04 '22 at 10:47