Открыть окно при нажатии кнопки, скрыть родительский элемент и снова показать родительский элемент при закрытии нового окна pyqt

#python #user-interface #pyqt

#python #пользовательский интерфейс #pyqt

Вопрос:

Предположим, у нас есть два окна, одно с кнопкой, родительское, и одно пустое, дочернее. Коды для родительского и дочернего элементов:

test_parent.py:

 from PyQt5 import QtCore, QtGui, QtWidgets
from test_child import Ui_child

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(270, 208)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(90, 90, 89, 25))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        self.pushButton.clicked.connect(self.open_child)        

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))

    def open_child(self):
        self.child = QtWidgets.QMainWindow()
        self.ui = Ui_child()
        self.ui.setupUi(self.child)
        self.child.show()
        MainWindow.hide()

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

и test_child.py:

 from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_child(object):
    def setupUi(self, child):
        child.setObjectName("child")
        child.resize(275, 176)
        self.centralwidget = QtWidgets.QWidget(child)
        self.centralwidget.setObjectName("centralwidget")
        child.setCentralWidget(self.centralwidget)

        self.retranslateUi(child)
        QtCore.QMetaObject.connectSlotsByName(child)

    def retranslateUi(self, child):
        _translate = QtCore.QCoreApplication.translate
        child.setWindowTitle(_translate("MainWindow", "MainWindow"))

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

С помощью этого кода, когда пользователь нажимает на кнопку, родительский элемент скрывается, а дочерний отображается. Теперь я хочу снова показать родительский элемент, когда пользователи закрывают дочерний элемент. Как я могу это сделать?

Ответ №1:

Хорошим решением является создание пользовательского сигнала для дочернего элемента и выдача его при закрытии окна.

Обратите внимание, что для этого вы должны правильно использовать файлы, созданные pyuic, что вы не делаете: эти файлы никогда не должны изменяться вручную, поскольку они предназначены только для импорта и не предназначены для дальнейшей реализации: есть много причин не делать этого,и один из них заключается в том, что их структура может привести к путанице в структуре объекта. Вы можете прочитать больше о правильном использовании этих файлов в официальных рекомендациях PyQt по использованию конструктора.

Самое главное, поскольку это простые object классы python (а не классы виджетов Qt), они не допускают переопределения методов, что очень важно, особенно в подобных случаях.

Итак, прежде чем продолжить пример, перестройте эти файлы с pyuic помощью и создайте новый скрипт для своей программы.

Концепция заключается в том, что дочерний класс будет иметь closed сигнал, и сигнал будет передаваться внутри closeEvent (который вызывается каждый раз, когда окно закрывается пользователем или программно с close() помощью ); затем главное окно создает дочерний элемент (но не показывает его) прямо в __init__ , и соединяет его пользовательский сигнал сэто окно show .

 from PyQt5 import QtCore, QtGui, QtWidgets
from test_parent import Ui_MainWindow
from test_child import Ui_child

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.pushButton.clicked.connect(self.openChild)

        # create an instance of the child
        self.child = Child()
        # connect its closed signal to show() of this window
        self.child.closed.connect(self.show)

    def openChild(self):
        self.child.show()
        self.hide()


class Child(QtWidgets.QMainWindow, Ui_child):
    closed = QtCore.pyqtSignal()
    def __init__(self):
        super().__init__()
        self.setupUi(self)

    def closeEvent(self, event):
        self.closed.emit()


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.show()
    sys.exit(app.exec_())
 

Комментарии:

1. Я думаю, что я должен делать все в qt designer, но я просто видел в сети, что все делают pyuic, а затем модифицируют материал, который я не считаю уместным. Тем не менее, мне непонятно, что вы подразумеваете под «перестроить эти файлы с помощью pyuic». Вы имеете в виду вызов pyuic для каждого из них, создание файла py из пользовательского интерфейса (как я делал), а затем копирование того, что мне нужно для создания скрипта, который вы мне показали? Разве это не противоположно тому, что мы говорим, чтобы делать что-то в qt designer?

2. «все делали pyuic, а затем изменяли материал» к счастью, не все, и тот, кто это делает, почти всегда ошибается в этом (что еще хуже, предлагать это): это считается плохой практикой по множеству причин, и существует лишь очень мало случаев, для которых (очень ограниченных) модификаций требуетсяна самом деле требуется. Я имел в виду: избавьтесь от этих 2 файлов, перестроив их с помощью pyuic, используя ваши исходные файлы пользовательского интерфейса (в моем примере предполагается, что они будут называться test_parent.py и test_child.py ), затем создайте новый файл с приведенным выше кодом таким, какой он есть, это будет ваш фактический программный скрипт.

3. @musicamante в вашем примере, что возвращает self.child.parentWidget() ?

4. @pippo1980 он вернется None , поскольку дочерний элемент был создан без какого-либо родительского элемента ( __init__ у него нет аргумента) и не setParent() вызывался для него.

5. круто, вернуться к PyQt5, бороться с родительским / дочерним элементом! Это то же самое, что модальный / немодальный fromQt Designer или что-то еще?

Ответ №2:

 
from PyQt5 import QtCore, QtGui, QtWidgets
from test_parent import Ui_MainWindow

class Ui_child(object):
    def setupUi(self, child):
        child.setObjectName("child")
        child.resize(275, 176)
        self.centralwidget = QtWidgets.QWidget(child)
        self.centralwidget.setObjectName("centralwidget")
        child.setCentralWidget(self.centralwidget)

        self.retranslateUi(child)
        QtCore.QMetaObject.connectSlotsByName(child)

    def retranslateUi(self, child):
        _translate = QtCore.QCoreApplication.translate
        child.setWindowTitle(_translate("MainWindow", "MainWindow"))

    def closeEvent(self, event):
        self.parent = Ui_MainWindow()
        self.parent.setupUi(QtWidgets.QMainWindow())
        self.parent.show()
        self.hide()
        
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_child()
    ui.setupUi(child)
    child.show()
    sys.exit(app.exec_())
 

Комментарии:

1. Я не могу запустить родительский файл. В нем говорится: «из test_child import Ui_child ImportError: невозможно импортировать имя ‘Ui_child'»

2. Это неверный ответ: не только from test_parent import Ui_MainWindow строка создает циклический импорт (отсюда и ошибка импорта, отмеченная @Learningfrommasters), но он также не будет работать в любом случае: closeEvent() никогда не будет вызван, поскольку класс не наследуется от QWidget. Пожалуйста, всегда проверяйте свой код перед отправкой ответа.