I am trying to create a dynamic custom dialog class. That takes the central widget as a parameter and that centralwidget will be the main widget of this custom dialog. (for dynamic)
When It will show itself, it will make the background dark/dimmed. Probably I should use exec_ function for not clickable out of dialog area.
The important thing is to add himself to the layout of the mainwindow, so it can automatically adjust itself when the mainwindow's size/position changes.
I can do this with hooking resizeEvent/moveEvent but I am looking better way to do this. If I add this custom dialog to mainwindow's layout, it is gonna be better.
Thanks.
Creating a child widget that is drawn over the current content of the window and resized in the resizeEvent()
override is absolutely not a problem. In fact, it's practically what Qt does every time a widget using a layout manager is resized. The benefit of this approach is that you can completely "cover" all contents of the window, including the menu bar, the status bar and any dock/toolbar.
If you still want them to be usable and only want to cover the main widget, you can do the same by setting the "cover" as a child of the main widget itself, instead of using the window as a parent.
An alternative could be to use a QStackedWidget as a central widget, and set the layout (which is a QStackedLayout) to use the StackAll
stackingMode
, which will allow you to show all "pages" of the stacked layout superimposed.
Note that this approach has an important drawback: you must take care of the tab-focus. Since all widgets (including widgets that belong to another "page") are shown and enabled, changing focus through Tab will allow changing the focus to widgets that are not part of your "dialog".
I'll leave you with a basic example, the central widget is a QTableWidget, and it shows the "popup" whenever an item is doubleclicked.
Please carefully study it and try to understand what it does.
from PyQt5 import QtCore, QtWidgets
class Container(QtWidgets.QWidget):
def showEvent(self, event):
if not event.spontaneous():
self.setFocus()
# certain widgets might want to keep focus on tab
# so we delay the focusNextChild
QtCore.QTimer.singleShot(0, self.focusNextChild)
def focusNextPrevChild(self, isNext):
# keep tab focus on this widget
super().focusNextPrevChild(isNext)
return self.isAncestorOf(QtWidgets.QApplication.focusWidget())
def paintEvent(self, event):
# stylesheets set on QWidget subclasses need this
qp = QtWidgets.QStylePainter(self)
opt = QtWidgets.QStyleOption()
opt.initFrom(self)
qp.drawPrimitive(QtWidgets.QStyle.PE_Widget, opt)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.menuBar().addMenu('Test').addAction('Action')
self.stack = QtWidgets.QStackedWidget(self)
self.setCentralWidget(self.stack)
self.stack.layout().setStackingMode(QtWidgets.QStackedLayout.StackAll)
table = QtWidgets.QTableWidget(20, 30)
self.stack.addWidget(table)
table.cellDoubleClicked.connect(self.showDialog)
self.resize(QtWidgets.QApplication.primaryScreen().size() * 2 / 3)
def showDialog(self, row, column):
background = QtWidgets.QWidget(objectName='background')
background.setStyleSheet('''
#background {
background: rgba(64, 64, 64, 64);
}
Container {
background: palette(window);
border: 1px outset palette(window);
border-radius: 5px;
}
''')
backLayout = QtWidgets.QVBoxLayout(background)
container = Container()
backLayout.addWidget(container, alignment=QtCore.Qt.AlignCenter)
container.setAutoFillBackground(True)
layout = QtWidgets.QVBoxLayout(container)
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(20)
font = self.font()
font.setPointSize(font.pointSize() * 3)
layout.addWidget(QtWidgets.QLabel(
'Hello!', font=font, alignment=QtCore.Qt.AlignCenter))
layout.addWidget(QtWidgets.QLabel(
'You doubleclicked cell {}, {}'.format(row + 1, column + 1)))
button = QtWidgets.QPushButton('Close')
layout.addWidget(button)
self.centralWidget().addWidget(background)
self.centralWidget().setCurrentWidget(background)
# Important! you must always delete the widget when you don't need it
# anymore. Alternatively, hide it if you want to reuse it again later
button.clicked.connect(background.deleteLater)
app = QtWidgets.QApplication([])
win = MainWindow()
win.show()
app.exec()