在 PyQt5 中打开多个窗口时遇到问题

Posted

技术标签:

【中文标题】在 PyQt5 中打开多个窗口时遇到问题【英文标题】:Having trouble opening multiple windows in PyQt5 【发布时间】:2018-10-13 20:59:30 【问题描述】:

我正在使用 PyQt5 设计器制作一个小应用程序。我主要与设计师合作,而不是从头开始构建,所以这可能是一个愚蠢的问题。基本上我有一个主菜单和一个“创建”按钮,单击该按钮将打开我一直在处理的另一个 pyqt5 .py 文件(也在设计器中创建)。

到目前为止,我将 ui 文件编译成 .py 文件并导入了我希望能够生成多个的弹出窗口,然后我使用此代码取得了一些成功:

我没有在转换后的 ui 文件中添加任何附加代码,除了这个函数:

以下是我的 mainmenu 和 popoutwindow .py 文件中的一些代码:

这是我试图复制的文件中调用的 setupUi

class BookPopout(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(772, 685)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.lastNameLabel = QtWidgets.QLabel(self.centralwidget)
        self.lastNameLabel.setGeometry(QtCore.QRect(20, 110, 60, 17))
        self.lastNameLabel.setObjectName("lastNameLabel")
        self.cityEntry = QtWidgets.QLineEdit(self.centralwidget)
        self.cityEntry.setGeometry(QtCore.QRect(20, 310, 391, 27))
        self.cityEntry.setReadOnly(True)
        self.cityEntry.setObjectName("cityEntry")
        self.bookTable = QtWidgets.QTableWidget(self.centralwidget)
        self.bookTable.setGeometry(QtCore.QRect(470, 10, 281, 481))
        font = QtGui.QFont()
        font.setPointSize(7)
        self.bookTable.setFont(font)
        self.bookTable.setAutoFillBackground(False)
        self.bookTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.bookTable.setRowCount(1)
        self.bookTable.setColumnCount(1)
        self.bookTable.setObjectName("bookTable")
        item = QtWidgets.QTableWidgetItem()
        self.bookTable.setItem(0, 0, item)
        self.bookTable.horizontalHeader().setVisible(False)
        self.bookTable.horizontalHeader().setCascadingSectionResizes(False)
        self.bookTable.horizontalHeader().setDefaultSectionSize(100)
        self.bookTable.horizontalHeader().setStretchLastSection(True)
        self.addressLineOneEntry = QtWidgets.QLineEdit(self.centralwidget)
        self.addressLineOneEntry.setGeometry(QtCore.QRect(20, 190, 391, 27))
        self.addressLineOneEntry.setReadOnly(True)
        self.addressLineOneEntry.setObjectName("addressLineOneEntry")
        self.stateEntry = QtWidgets.QLineEdit(self.centralwidget)
        self.stateEntry.setGeometry(QtCore.QRect(20, 380, 391, 27))
        self.stateEntry.setReadOnly(True)
        self.stateEntry.setObjectName("stateEntry")
        self.firstNameEntry = QtWidgets.QLineEdit(self.centralwidget)
        self.firstNameEntry.setGeometry(QtCore.QRect(20, 70, 391, 27))
        self.firstNameEntry.setReadOnly(True)
        self.firstNameEntry.setObjectName("firstNameEntry")
        self.streetAddressLabel = QtWidgets.QLabel(self.centralwidget)
        self.streetAddressLabel.setGeometry(QtCore.QRect(20, 170, 83, 17))
        self.streetAddressLabel.setObjectName("streetAddressLabel")
        self.streetAddressTwoLabel = QtWidgets.QLabel(self.centralwidget)
        self.streetAddressTwoLabel.setGeometry(QtCore.QRect(20, 240, 220, 17))
        self.streetAddressTwoLabel.setObjectName("streetAddressTwoLabel")
        self.phoneNumberLabel = QtWidgets.QLabel(self.centralwidget)
        self.phoneNumberLabel.setGeometry(QtCore.QRect(20, 480, 87, 17))
        self.phoneNumberLabel.setObjectName("phoneNumberLabel")
        self.addressLineTwoEntry = QtWidgets.QLineEdit(self.centralwidget)
        self.addressLineTwoEntry.setGeometry(QtCore.QRect(20, 260, 391, 27))
        self.addressLineTwoEntry.setReadOnly(True)
        self.addressLineTwoEntry.setObjectName("addressLineTwoEntry")
        self.phoneNumberEntry = QtWidgets.QLineEdit(self.centralwidget)
        self.phoneNumberEntry.setGeometry(QtCore.QRect(20, 500, 391, 27))
        self.phoneNumberEntry.setReadOnly(True)
        self.phoneNumberEntry.setObjectName("phoneNumberEntry")
        self.firstNameLabel = QtWidgets.QLabel(self.centralwidget)
        self.firstNameLabel.setGeometry(QtCore.QRect(20, 40, 62, 17))
        self.firstNameLabel.setObjectName("firstNameLabel")
        self.cityLabel = QtWidgets.QLabel(self.centralwidget)
        self.cityLabel.setGeometry(QtCore.QRect(20, 290, 23, 17))
        self.cityLabel.setObjectName("cityLabel")
        self.stateLabel = QtWidgets.QLabel(self.centralwidget)
        self.stateLabel.setGeometry(QtCore.QRect(20, 350, 29, 17))
        self.stateLabel.setObjectName("stateLabel")
        self.zipEntry = QtWidgets.QLineEdit(self.centralwidget)
        self.zipEntry.setGeometry(QtCore.QRect(20, 440, 391, 27))
        self.zipEntry.setReadOnly(True)
        self.zipEntry.setObjectName("zipEntry")
        self.zipLabel = QtWidgets.QLabel(self.centralwidget)
        self.zipLabel.setGeometry(QtCore.QRect(20, 410, 18, 17))
        self.zipLabel.setObjectName("zipLabel")
        self.emailEntry = QtWidgets.QLineEdit(self.centralwidget)
        self.emailEntry.setGeometry(QtCore.QRect(20, 550, 391, 27))
        self.emailEntry.setObjectName("emailEntry")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(20, 530, 80, 17))
        self.label.setObjectName("label")
        self.confirmNewPersonButton = QtWidgets.QPushButton(self.centralwidget)
        self.confirmNewPersonButton.setGeometry(QtCore.QRect(20, 590, 85, 27))
        self.confirmNewPersonButton.setObjectName("confirmNewPersonButton")
        self.lastNameEntry = QtWidgets.QLineEdit(self.centralwidget)
        self.lastNameEntry.setEnabled(True)
        self.lastNameEntry.setGeometry(QtCore.QRect(20, 130, 391, 27))
        self.lastNameEntry.setText("")
        self.lastNameEntry.setReadOnly(True)
        self.lastNameEntry.setObjectName("lastNameEntry")
        self.sortComboBox = QtWidgets.QComboBox(self.centralwidget)
        self.sortComboBox.setGeometry(QtCore.QRect(500, 530, 101, 31))
        self.sortComboBox.setObjectName("sortComboBox")
        self.sortByLabel = QtWidgets.QLabel(self.centralwidget)
        self.sortByLabel.setGeometry(QtCore.QRect(570, 500, 71, 31))
        font = QtGui.QFont()
        font.setPointSize(14)
        self.sortByLabel.setFont(font)
        self.sortByLabel.setObjectName("sortByLabel")
        self.sortButton = QtWidgets.QPushButton(self.centralwidget)
        self.sortButton.setGeometry(QtCore.QRect(600, 530, 111, 31))
        font = QtGui.QFont()
        font.setPointSize(14)
        self.sortButton.setFont(font)
        self.sortButton.setObjectName("sortButton")
        self.addNewPersonButton = QtWidgets.QPushButton(self.centralwidget)
        self.addNewPersonButton.setGeometry(QtCore.QRect(40, 10, 101, 27))
        self.addNewPersonButton.setObjectName("addNewPersonButton")
        self.editEntryButton = QtWidgets.QPushButton(self.centralwidget)
        self.editEntryButton.setGeometry(QtCore.QRect(170, 10, 111, 27))
        self.editEntryButton.setObjectName("editEntryButton")
        self.confirmEditButton = QtWidgets.QPushButton(self.centralwidget)
        self.confirmEditButton.setGeometry(QtCore.QRect(20, 590, 111, 27))
        self.confirmEditButton.setObjectName("confirmEditButton")
        self.deleteEntryButton = QtWidgets.QPushButton(self.centralwidget)
        self.deleteEntryButton.setGeometry(QtCore.QRect(300, 10, 111, 27))
        self.deleteEntryButton.setObjectName("deleteEntryButton")
        self.sortByLabel_2 = QtWidgets.QLabel(self.centralwidget)
        self.sortByLabel_2.setGeometry(QtCore.QRect(530, 570, 131, 31))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.sortByLabel_2.setFont(font)
        self.sortByLabel_2.setObjectName("sortByLabel_2")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(500, 600, 101, 31))
        self.lineEdit.setObjectName("lineEdit")
        self.sortButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.sortButton_2.setGeometry(QtCore.QRect(600, 600, 111, 31))
        font = QtGui.QFont()
        font.setPointSize(14)
        self.sortButton_2.setFont(font)
        self.sortButton_2.setObjectName("sortButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setEnabled(True)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 772, 27))
        self.menubar.setObjectName("menubar")
        self.menuOpen = QtWidgets.QMenu(self.menubar)
        self.menuOpen.setObjectName("menuOpen")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionSave = QtWidgets.QAction(MainWindow)
        self.actionSave.setObjectName("actionSave")
        self.actionSave_as = QtWidgets.QAction(MainWindow)
        self.actionSave_as.setObjectName("actionSave_as")
        self.actionClose = QtWidgets.QAction(MainWindow)
        self.actionClose.setObjectName("actionClose")
        self.menuOpen.addAction(self.actionSave)
        self.menuOpen.addSeparator()
        self.menuOpen.addAction(self.actionSave_as)
        self.menuOpen.addSeparator()
        self.menuOpen.addAction(self.actionClose)
        self.menubar.addAction(self.menuOpen.menuAction())
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

这是我的主菜单 py 文件:

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(255, 410)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.createNewBookButton = QtWidgets.QPushButton(self.centralwidget)
        self.createNewBookButton.setGeometry(QtCore.QRect(40, 220, 171, 41))
        self.createNewBookButton.setObjectName("createNewBookButton")
        self.createNewBookButton.clicked.connect(self.openNewBook)

        self.openExistingBookButton = QtWidgets.QPushButton(self.centralwidget)
        self.openExistingBookButton.setGeometry(QtCore.QRect(40, 280, 171, 41))
        self.openExistingBookButton.setObjectName("openExistingBookButton")
        self.quitProgramButton = QtWidgets.QPushButton(self.centralwidget)
        self.quitProgramButton.setGeometry(QtCore.QRect(40, 340, 171, 41))
        self.quitProgramButton.setObjectName("quitProgramButton")
        self.mainImage = QtWidgets.QGraphicsView(self.centralwidget)
        self.mainImage.setGeometry(QtCore.QRect(10, 10, 231, 192))
        self.mainImage.setObjectName("mainImage")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def openNewBook(self):
        self.popWin = QtWidgets.QMainWindow()
        self.bookUI = bookPopout.BookPopout()
        self.bookUI.setupUi(self.popWin)
        self.popWin.show()  

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

这对于打开一个额外的窗口非常有用,它在我的主窗口打开时保持打开,这是我想要的,但是当我点击以显示另一个窗口时,它只会替换我已经拥有的那个,所以我似乎一次只能多出一个。

我认为我的旧窗口被 self.popWin 引用,然后被一遍又一遍地替换,所以我想我的问题是“生成”窗口的最佳方法是什么,而不是仅仅引用同一个窗口并且结束了..我试图删除“自我”部分但是当我去创建一个新窗口时窗口甚至出现了所以我有点迷路了

例如,我希望能够写出这样的东西,但我不确定如何去做

currentWindows = []

newPopWin = QtWidgets.QMainWindow()
newBookUI = BookPopout()
newBookUI.setupUi(newPopWin)
newPopWin.show()

currentWindows.append(newPopWin)

这将是理想的,因此我可以跟踪所有当前打开的窗口,以便在我关闭主菜单后将它们全部终止。

如果能帮助我走上正轨,我将不胜感激,我已尝试查找解决方案,但许多示例并未使用从 UI 文件转换的代码。

【问题讨论】:

@eyllanesc 抱歉,我试图保持最小化,不包括从 PyQt 自动生成的内容,我应该发布这些类文件吗? 感谢您的回复/阅读@eyllanesc,我添加了一些额外的代码来测试和复制这种情况 【参考方案1】:

问题很简单,我们正在覆盖一个属性,此时前一个元素被删除,因此只有一个窗口。正如您所指出的,解决方案是将其存储在一个列表中。

但在此之前,我建议您不要在 Qt Designer 生成的 .py 中编写逻辑,因为例如,假设您要修改设计的某些内容,然后在生成新的 .py 时,它将删除您的所有逻辑。 PyQt 建议使用创建另一个文件,在其中生成带有消息的逻辑 adviritiendote:# WARNING! All changes made in this file will be lost!。因此,通过删除 openNewBook 来恢复我之前称为 bookPopup.py 和 mainmenu.py 的文件。根据docs 的建议,我们获得了以下信息:

ma​​in.py

from PyQt5 import QtCore, QtGui, QtWidgets

import bookPopout
import mainmenu


class BookPopoutWindow(QtWidgets.QMainWindow, bookPopout.BookPopout):
    def __init__(self, parent=None):
        super(BookPopoutWindow, self).__init__(parent)
        self.setupUi(self)


class MainMenu(QtWidgets.QMainWindow, mainmenu.Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainMenu, self).__init__(parent)
        self.setupUi(self)
        self.createNewBookButton.clicked.connect(self.openNewBook)
        self.popups = []

    @QtCore.pyqtSlot()
    def openNewBook(self):
        popWin = BookPopoutWindow()
        popWin.show()
        self.popups.append(popWin)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MainMenu()
    w.show()
    sys.exit(app.exec_())

【讨论】:

非常感谢您对我的帮助,尽管我在遵循正确的发布格式时遇到了麻烦,但您仍然提供了帮助,并发布了一个漂亮的解决方案,我将擅长于我的应用程序。再次感谢您:) 嘿,所以这个解决方案可以打开额外的窗口,我想打开多少就打开多少。但是由于某种原因,当我单击按钮生成窗口时,它一次打开其中两个。我在 openNewBook() 函数之前进行了打印检查,当我单击一次按钮时,它确实被调用了两次。这是否也发生在您身上,还是仅在我身边?再次感谢 ! @eyllanesc 呃,我太傻了,对不起,我忘了从我的旧主菜单 .py 文件中删除 self.createNewBookButton.clicked.connect(self.openNewBook) .. 我想这就是你不编辑那些转换后的 ui 文件的原因!.. 是的,你在我之前就想通了,哈哈,我不会再编辑这些文件了,太难跟踪了【参考方案2】:

PyQt5 打开多个窗口/小部件并关闭它们

import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QAction
import random


class Widget2(QWidget):
    def __init__(self):
        super().__init__()
        w2btn = QPushButton('Another Widget ' +
                            str(random.randrange(5, 100, 5)), self)
        w2btn.clicked.connect(self.w2btnclicked)
        w2btn.resize(w2btn.sizeHint())
        w2btn.move(50, 50)
        
    def w2btnclicked(self):
        print("Widget 2 btn clicked")
class Widget1(QWidget):

    def __init__(self):
        super().__init__()
        self.start = 50
        self.end = 50
        quit = QAction("Quit", self)
        quit.triggered.connect(self.close)
                
        addbtn = QPushButton('Add Window', self)
        addbtn.clicked.connect(self.addbtnclicked)
        addbtn.resize(addbtn.sizeHint())
        addbtn.move(50, 50)

        quitbtn = QPushButton('Quit', self)
        quitbtn.clicked.connect(QApplication.instance().quit)
        quitbtn.resize(quitbtn.sizeHint())
        quitbtn.move(50, 100)        
        self.popups = []
        
    def addbtnclicked(self):        
        print("Add Button Clicked!!")
        wdgt2 = Widget2()
        wdgt2.show()
        
        if self.start > 1600:
            self.start = 50
            self.end = self.end + 250
        wdgt2.setGeometry(self.start, self.end, 200, 200)
        self.popups.append(wdgt2)
        self.start = self.start + 250
    
    def closeEvent(self, event):
        print("In Close Event")
        QApplication.closeAllWindows()

def main():
    app = QApplication(sys.argv)
    ex = Widget1()
    ex.show()
    ex.setGeometry(800, 600, 200, 200)
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

【讨论】:

以上是关于在 PyQt5 中打开多个窗口时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

无法在 PyQt5 中打开多个窗口

能够在 PyQt5 中一次打开多个对话框的单窗口模式?

pyqt5 继承python类可以传递信号函数吗

pyqt5,pycharm,Process finished with exit code -1073741819 (0xC0000005)

PyQt5 - 使用 Line Edit 从编辑窗口更新标签

在 PyQt5 中打开一个窗口和关闭一个窗口