Raising a child window (QWidget) from QDialog that can be moved?

When reading https://www.pythonguis.com/tutorials/creating-multiple-windows/ I may have understood the following too literally:

In Qt any widget without a parent is a window. This means, to show a new window you just need to create a new instance of a widget.

… which is why I find the behavior of the code below somewhat surprising.

In the code below, I want to be able to start/raise a QDialog window by clicking a button in the main application; and then I want to be able to start/raise yet another window from the QDialog:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget, QDialog
from PyQt5.QtCore import Qt

class MainWindow(QMainWindow):

  def __init__(self):
    super().__init__()
    self.title = QLabel("MainWindow")
    self.button = QPushButton("Click for SubWindow")
    self.button.clicked.connect(self.show_sub_window)
    self.layout = QVBoxLayout()
    self.layout.addWidget(self.title)
    self.layout.addWidget(self.button)
    self.central_widget = QWidget()
    self.central_widget.setLayout(self.layout)
    self.setCentralWidget(self.central_widget)

  def show_sub_window(self):
    print("show_sub_window")
    self.sub_win = QDialog() # if instantiating with (self), subsubwindow ends up created behind main window, but then we cannot move main window to show it!
    self.sub_win.title = QLabel("SubWindow")
    self.sub_win.button = QPushButton("Click for SubSubWindow")
    self.sub_win.button.clicked.connect(self.show_sub_sub_window)
    self.sub_win.layout = QVBoxLayout()
    self.sub_win.layout.addWidget(self.sub_win.title)
    self.sub_win.layout.addWidget(self.sub_win.button)
    self.sub_win.setLayout(self.sub_win.layout)
    self.sub_win.exec_()
    print("after sub_win.show()")

  def show_sub_sub_window(self):
    print("show_sub_sub_window")
    self.sub_sub_win = QWidget()
    self.sub_sub_win.title = QLabel("SubSubWindow")
    self.sub_sub_win.button = QPushButton("(Stop clicking.)")
    self.sub_sub_win.button.setEnabled(False)
    self.sub_sub_win.layout = QVBoxLayout()
    self.sub_sub_win.layout.addWidget(self.sub_sub_win.title)
    self.sub_sub_win.layout.addWidget(self.sub_sub_win.button)
    self.sub_sub_win.setLayout(self.sub_sub_win.layout)
    self.sub_sub_win.show()

app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

When I execute the clicks as noted above, I get something like this:

pyqt5 gui result

That is, a click on “Click for SubWindow” raises SubWindow, which is modal, so it prevents interaction with MainWindow (for some reason, I always thought that modality depends on whether or not you set a parent in the QDialog constructor, but that is not the case). Then, a click on “Click for SubSubWindow” raises SubSubWindow – but interaction with this one is also disabled (actually, cannot even close it by clicking top right X)?!

So, how can I setup this code, so that SubSubWindow can be moved and interacted with independently?

It turns out, things with modality were a lot more complicated; I guess so far I’ve only had situations where I did not have to dig deeply in this. But to get a fuller picture of modality, one must consult:

  • https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QDialog.html
  • A modal dialog is a dialog that blocks input to other visible windows in the same application.
  • Dialogs can be application modal (the default) or window modal.
  • When an application modal dialog is opened, the user must finish interacting with the dialog and close it before they can access any other window in the application. Window modal dialogs only block access to the window associated with the dialog, allowing the user to continue to use other windows in an application.
  • The most common way to display a modal dialog is to call its exec() function.
    • An alternative is to call setModal (true) or setWindowModality(), then show(). Unlike exec(), show() returns control to the caller immediately. Calling setModal (true) is especially useful for progress dialogs, where the user must have the ability to interact with the dialog, e.g. to cancel a long running operation. If you use show() and setModal (true) together to perform a long operation, you must call processEvents() periodically during processing to enable the user to interact with the dialog.
  • A modeless dialog is a dialog that operates independently of other windows in the same application. Modeless dialogs are displayed using show(), which returns control to the caller immediately.

… but also:

  • https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QWidget.html
  • QWidget.setWindowModality(windowModality) – This property holds which windows are blocked by the modal widget. This property only makes sense for windows. A modal widget prevents widgets in other windows from getting input. The value of this property controls which windows are blocked when the widget is visible. Changing this property while the window is visible has no effect; you must hide() the widget first, then show() it again. By default, this property is NonModal.
  • Qt.WindowModality – Qt.NonModal: The window is not modal and does not block input to other windows.; Qt.WindowModal: The window is modal to a single window hierarchy and blocks input to its parent window, all grandparent windows, and all siblings of its parent and grandparent windows.; Qt.ApplicationModal – The window is modal to the application and blocks input to all windows.

So for the application I imagined with the OP example, I find WindowModal modality to be most appropriate; however do not forget “Changing this property while the window is visible has no effect; you must hide() the widget first, then show() it again.”.

So, the changes needed to make the SubSubWindow movable (and interactable), are:

# ...
  def show_sub_window(self):
    print("show_sub_window")
    self.sub_win = QDialog() # if instantiating with (self), subsubwindow ends up created behind main window, but then we cannot move main window to show it!
    self.sub_win.title = QLabel("SubWindow")
    self.sub_win.button = QPushButton("Click for SubSubWindow")
    self.sub_win.button.clicked.connect(self.show_sub_sub_window)
    self.sub_win.layout = QVBoxLayout()
    self.sub_win.layout.addWidget(self.sub_win.title)
    self.sub_win.layout.addWidget(self.sub_win.button)
    self.sub_win.setLayout(self.sub_win.layout)
    self.setWindowModality(Qt.WindowModal) # ADDED; "Changing this property while the window is visible has no effect; you must hide() the widget first, then show() it again."
    self.sub_win.show()                    # ADDED; ... actually, must .show() even when windows is not created yet - just to have it recognize the setWindowModality!
    self.sub_win.exec_()
    print("after sub_win.show()")
# ...

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật