如何从主窗口打开子窗口

ubu*_*ntu 3 python window pyqt childwindow

我的问题很难解释,但我尽力了。请在这方面帮助我。

我在 QtDesigner 中设计了一个 gui,并将 .ui 文件转换为 .py,例如 main_window.py。现在为了避免在 main_window.py 中进行更改,我为侦听器创建了另一个类。

class Main():    
    window = None
    app = None

    def __init__(self):
        self.launch()
        self.attach_listener()
        self.execute()

    ''' Launch GUI '''
    def launch(self):
        self.app = QtGui.QApplication(sys.argv)
        self.window = Ui_MainWindow()
        self.window.show()

    ''' Execute Window '''
    def execute(self):
        sys.exit(self.app.exec_())

    ''' Attach Listeners '''
    def attach_listener(self):
        self.window.add_button.clicked.connect(self.add_listener)
        self.window.delete_button.clicked.connect(self.delete_listener)
        self.window.update_button.clicked.connect(self.update_listener)
        self.window.connect(self.window.combo_box, QtCore.SIGNAL('activated(QString)'), self.logout_listener)
Run Code Online (Sandbox Code Playgroud)

我有另一个具有相同结构的 child_window.py,但由于 QApplication,我无法从这个窗口打开该窗口。我搜索了答案,但无法应用于我的代码。当类从 QtGui.QMainWindow 或 QtGui.QWidget 扩展时,这些答案是适用的,但我的情况不同。

Mbo*_*o42 5

您将 Ui_MainWindow 对象与实际的窗口对象(QMainWindow、QDialog、QWidget 等)混在一起 self.window = Ui_MainWindow() 没有做任何事情,因为您将其附加到的类不是 Window。您需要创建一个窗口并将 Ui_MainWindow 应用到它。

显然你可以让这个工作,但它看起来并不漂亮。您需要通过 findChild 访问您的小部件。我能看到的唯一好处是在设计器中更改表单后您不会运行 pyside-uic,这非常简单。

更简单的方法

当您使用 pyuic / pyside-uic 时,它会将 .ui 文件转换为 .py 文件。您不应该编辑 .py,因为它们将在您下次使用 QtDesigner 时被覆盖。您需要创建一个窗口类并将 UI 类应用于它。

设置新表格

  • 在 QtDesigner 中生成文件 mainWinui.ui - 类名 MainWindow
  • 使用 pyside-uic -o mainWinui.py mainWinui.ui 进行转换。mainWinui.py 从不手动编辑
  • 创建 mainWinClass.py 以加载 ui.py 并完成所有自定义 UI 工作
  • 在 mainWinClass.py 中声明信号和槽,使用 .connect 等

其中一些模块名称可能看起来有点尴尬,但我选择了它们,因为过去我在模块和类之间的名称冲突方面遇到了麻烦;由于我不明白 Qt Designer 将如何处理它的命名。

如果您查看 pyside-uic 创建的文件,它的顶部包含您需要在 mainWinClass.py 中使用的正确类和方法名称

主Winui.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'mainWinui.ui'
#
# Created: Sun Feb  7 14:22:09 2016
#      by: pyside-uic 0.2.15 running on PySide 1.2.4
#
# WARNING! All changes made in this file will be lost!

from PySide import QtCore, QtGui

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
Run Code Online (Sandbox Code Playgroud)

创建一个新的 mainwinClass.py 并将正确的导入和类名复制到它,加上一些样板来加载 .ui。

它看起来像这样:

主WinClass.py

from mainWinui import Ui_MainWindow
from PySide import QtGui


class MainWin(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setup_signals()

# Is this is the same as your Listeners ??
def setup_signals(self):
    # Signal for updating the lineedits from the grid
    self.ui.tabWidget.currentChanged.connect(self.onTabChanged)
    # Connect the "Add Staff" button to the addStaffMember method
    self.ui.btnAddStaff.clicked.connect(self.addStaffMember)
Run Code Online (Sandbox Code Playgroud)

然后使用另一个文件来启动应用程序本身,并维护应用程序的一些非 GUI 方面,例如更新程序或全局日志记录。我已经看到所有子窗口都在这里实例化的代码,但我(通常)不会那样做。我将它们保留在主窗体中。这取决于您打算如何设计应用程序。

应用名称.py

from PySide import QtGui, QtCore
from mainwinClass import MainWin
import sys


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

    mainWin.show()
    sys.exit(app.exec_())

    # Nothing else _needed_ in here
Run Code Online (Sandbox Code Playgroud)

现在对于任何子窗口再次遵循相同的方法。

模态形式

在 Qt Designer 中创建一个新的“底部带有按钮的对话框”。根据需要添加小部件并另存为 dialogAddStaffui.ui。

pyside-uic -o dialogAddStaffui.py dialogAddStaffui.ui.
Run Code Online (Sandbox Code Playgroud)

创建一个名为 dialogAddStaffClass.py 的新的空文本文档,并使用 dialogAddStaffui.ui 作为类名等的参考。将 dialogAddStaffClass.py 编辑为如下所示:

对话框添加员工类

from dialogAddStaffui import Ui_DialogAddStaff
from PySide import QtCore, QtGui


class DialogAddStaff(QtGui.QDialog):
    def __init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent)
        self.ui = Ui_DialogAddStaff()
        self.ui.setupUi(self)
        # Your own init stuff
Run Code Online (Sandbox Code Playgroud)

这两个进口是这里唯一需要的。如果你想复制这个,意识到在 Qt 设计器中我有

windowModality = ApplicationModal,表单是一个“底部有按钮的对话框”

对于这些简单的表单,它们有一个 accept 方法来检查用户输入的数据的有效性,并使用 self.done(1) 关闭。如果您想查看如何处理验证和关闭:

对话框添加员工类

def validate(self):
    retval = True
    if not self.ui.editLname.text():
        retval = False
        QtGui.QMessageBox.information(self, 'Invalid Last name',
                                      "Last Name must not be blank")
        self.ui.editLname.setFocus()
    return retval

def accept(self):
    if self.validate():
        self.done(1)
Run Code Online (Sandbox Code Playgroud)

通过这些对话框形式,Qt 自动设置了 OK 按钮以触发接受。我只是覆盖了那个方法。

如果您想在父子之间进行通信,您可以在引用父项的子项上设置一个属性,或者在子项关闭后但在其变量被垃圾收集之前读取子项的属性。创建循环引用可能会出现问题,所以要小心。

由于新窗体是模态的,用户在关闭子窗体之前不能与主窗体交互,并且启动子窗口的函数将停止直到子窗口关闭,因此可以使用本地变量来保存子类。

“添加员工”按钮连接到 addStaffMember 函数。

主WinClass.py

from dialogAddStaffClass import DialogAddStaff


def addStaffMember(self):
    addStaffForm = DialogAddStaff()
        res = addStaffForm.exec_()   # exec_ waits, show would continue
        # Function waits here for the Modal form to close.
        if res:   # child was closed with OK and validated correctly
            print(addStaffForm.ui.editLname.text())
            # Saveing other Staff data happens here
Run Code Online (Sandbox Code Playgroud)

因为子窗体是用 exec_ 运行的,所以主窗体会等到子窗体关闭后再继续。当函数退出时 addStaffForm 变量被垃圾收集,因此不再有对子表单属性的任何引用。(可能没有形式......)

如果你想打开一个长期存在的表单,你可以在更持久的地方实例化它。

非模态形式

这是 SisterForm 的示例。它是在设计器中从“主窗口”类型创建的(它有自己的菜单和状态栏等)。如果您不需要这些装饰,请使用 Dialog 窗体,但将其 windowModality 设置为 NonModal。

  • 在Qt Designer中创建sisterWinui.ui - 设置objectName SisterWin
  • pyside-uic -o 姐妹Winui.py 姐妹Winui.ui
  • 创建一个文件sisterwinClass.py - 设置它的导入和初始化
  • 制作一个变量以将其保存在一个长期存在的地方(MainWin 本身,使用 self.ref 属性)
  • 为它制作一个启动按钮并将其连接到 MainWin 类中的方法

姐姐Winui.ui

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'sisterWinui.ui'
#
# Created: Mon Feb  8 12:05:37 2016
#      by: pyside-uic 0.2.15 running on PySide 1.2.4
#
# WARNING! All changes made in this file will be lost!

from PySide import QtCore, QtGui

class Ui_SisterWin(object):
    def setupUi(self, SisterWin):
        SisterWin.setObjectName("SisterWin")
Run Code Online (Sandbox Code Playgroud)

运行 uic

pyside-uic -o sisterWinui.py sisterWinui.ui
Run Code Online (Sandbox Code Playgroud)

创建一个文件sisterwinClass.py - 设置它的导入和初始化

姐妹winClass.py

from sisterWinui import Ui_SisterWin
from PySide import QtCore, QtGui


class SisterWin(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        self.ui = Ui_SisterWin()
        self.ui.setupUi(self)
        # Your custom stuff after this
Run Code Online (Sandbox Code Playgroud)

在 Qt Designer 中,向主窗体添加一个按钮或其他任何东西以启动sisterForm。然后对 mainwinClass 进行一些编辑。制作一个变量以将其保存在一个长期存在的地方

主赢类

from sisterwinClass import SisterWin
# no other new imports needed, just the stuff you had before


class MainWin(QtGui.QMainWindow):
    def __init__(self, parent=None):
        # These three lines were already there
        QtGui.QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        # Add a long lived attribute to hold the class instance
        self.sisterWin = None
        # Next line was already there
        self.setup_signals()

def setup_signals(self):
    # Connect button to openSisterWin
    self.ui.btnSisterwin.clicked.connect(self.openSisterWin)
    # You probably have other connects after this


# This toggles the window
def openSisterWin(self):
    if not self.sisterWin:
        self.sisterWin = SisterWin()
    if self.sisterWin.isVisible():
        print('Hiding')
        self.sisterWin.hide()
        # hide or close, it's your choice
        # self.sisterWin.close()
    else:
        print('Showing')
        self.sisterWin.show()
Run Code Online (Sandbox Code Playgroud)

我希望这涵盖了您现在正在寻找的内容?如果想知道如何隐藏主窗口,请看这里 Happy hacking :-)