如何从PySide Qt的tabWidget弹出单独窗口

9 投票
2 回答
4042 浏览
提问于 2025-04-17 15:52

我有一个扩展的主窗口,里面添加了一个 QtGui.QTabWidget。我正在创建几个从 QtGui.QWidget 扩展出来的小部件,这些小部件可以添加到标签窗口中,也可以从中移除。

我想要做的是有一个“弹出”按钮,点击后可以把小部件从标签窗口中移除,并让它变成一个独立的窗口(还有一个“弹回”按钮可以把它放回主窗口)。这个想法有点像Gtalk在Gmail中的实现。需要注意的是,如果我关闭主窗口,其他的“标签”或“窗口”也应该关闭,而且我希望能够把所有窗口并排放置,让它们都能同时显示和更新(我会展示接近实时的数据)。

我对Qt还不太熟悉,但如果我没理解错的话,如果一个小部件没有父级,它就会独立显示。这是可行的,但我不知道怎么把这个窗口“弹回”去。

class TCWindow(QtGui.QMainWindow): 
    .
    .
    .
    def popOutWidget(self, child):
        i = self.tabHolder.indexOf(child)
        if not i == -1:
             self.tabCloseRequested(i)
        self.widgets[i].setParent(None)
        self.widgets[i].show()

我感觉这两个之间应该还是有父子关系。

有没有办法保持父子关系,同时让窗口独立出来,还是我对Qt的理解有误?

另外,在子窗口中创建一个变量来保存与主窗口的链接(比如 self.parentalUnit = self.parent())是个好主意,还是说这只是个临时的解决办法?

2 个回答

0

在Qt中,布局会管理添加到其中的控件,所以你可以让它来处理父子关系。你可以创建一个没有父控件的另一个控件,这个控件在你按下弹出按钮之前是隐藏的。当你按下这个按钮时,就把“弹出控件”从原来的布局中移除,然后添加到隐藏控件的布局中。当你按下收回按钮时,就把控件放回它原来的布局中。为了在关闭主窗口时关闭这个隐藏的窗口,你需要重新定义一下 closeEvent(QCloseEvent* ev),可以像这样做(抱歉用的是C++,但我敢打赌在Python中也是类似的):

void MainWindow::closeEvent(QCloseEvent* ev)
{
   dw->setVisible(false); // independent of mainwindow widget
   sw->setVisible(false); // independent of mainwindow widget
   QWidget::closeEvent(ev); //invoking close event after all the other windows are hidden
}
13

保持 parent 不变。如果你去掉 parent,那么关闭主窗口时就不会关闭那些“浮动”的标签页,因为它们现在变成了顶级窗口。windowFlags 用来定义一个小部件是窗口还是子部件。简单来说,你需要在 QtCore.Qt.WindowQtCore.Qt.Widget 之间切换。

下面是一个小而完整的例子:

#!/usr/bin/env python
# -.- coding: utf-8 -.-
import sys
from PySide import QtGui, QtCore


class Tab(QtGui.QWidget):
    popOut = QtCore.Signal(QtGui.QWidget)
    popIn = QtCore.Signal(QtGui.QWidget)

    def __init__(self, parent=None):
        super(Tab, self).__init__(parent)

        popOutButton = QtGui.QPushButton('Pop Out')
        popOutButton.clicked.connect(lambda: self.popOut.emit(self))
        popInButton = QtGui.QPushButton('Pop In')
        popInButton.clicked.connect(lambda: self.popIn.emit(self))

        layout = QtGui.QHBoxLayout(self)
        layout.addWidget(popOutButton)
        layout.addWidget(popInButton)


class Window(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__()

        self.button = QtGui.QPushButton('Add Tab')
        self.button.clicked.connect(self.createTab)
        self._count = 0
        self.tab = QtGui.QTabWidget()
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.button)
        layout.addWidget(self.tab)

    def createTab(self):
        tab = Tab()
        tab.setWindowTitle('%d' % self._count)
        tab.popIn.connect(self.addTab)
        tab.popOut.connect(self.removeTab)
        self.tab.addTab(tab, '%d' % self._count)
        self._count += 1

    def addTab(self, widget):
        if self.tab.indexOf(widget) == -1:
            widget.setWindowFlags(QtCore.Qt.Widget)
            self.tab.addTab(widget, widget.windowTitle())

    def removeTab(self, widget):
        index = self.tab.indexOf(widget)
        if index != -1:
            self.tab.removeTab(index)
            widget.setWindowFlags(QtCore.Qt.Window)
            widget.show()


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)

    w = Window()
    w.show()

    sys.exit(app.exec_())

撰写回答