Main Window 中的 Qt Dialog,使背景变暗

Qt Dialog in Main Window, make the background dimmed

我正在尝试创建动态自定义对话框 class。这将中央小部件作为参数,并且该中央小部件将成为此自定义对话框的主要小部件。 (对于动态)

当它显示自己时,它会制作背景dark/dimmed。 可能我应该使用 exec_ 函数来防止在对话框区域之外点击。

重要的是将自己添加到主窗口的布局中,这样当主窗口的size/position发生变化时,它可以自动调整自己。

我可以通过挂钩来做到这一点 resizeEvent/moveEvent 但我正在寻找更好的方法来做到这一点。如果我将这个自定义对话框添加到主窗口的布局中,它会更好。

谢谢。

创建一个 child 在 window 的当前内容上 绘制并在 resizeEvent() 覆盖中调整大小的小部件绝对不是一个问题。事实上,每次使用布局管理器调整小部件的大小时,这实际上就是 Qt 所做的。这种做法的好处是可以完全“覆盖”window的所有内容,包括菜单栏、状态栏和任何dock/toolbar。

如果您仍然希望它们可用并且只想覆盖主要小部件,您可以通过将“覆盖”设置为主要小部件本身的 child 而不是使用window 作为 parent.

另一种方法是使用 QStackedWidget 作为中央小部件,并将布局(即 QStackedLayout)设置为使用 StackAll stackingMode,这将允许您显示所有“页”的堆叠布局叠加。

请注意,此方法有一个重要缺点:您必须 处理tab-focus。由于显示并启用了所有小部件(包括属于另一个“页面”的小部件),因此通过 Tab 更改焦点将允许将焦点更改为不属于“对话框”的小部件。

我会给你一个基本的例子,中央小部件是一个 QTableWidget,它会在双击项目时显示“弹出窗口”。

请仔细研究并尝试理解它的作用。

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()