динамическое создание окон и присвоение им идентификатора

Есть программа, в которой по нажатию на кнопку создаются окна соответствующего класса. Окна являются элементами списка.

Надо сделать, чтобы каждому окну присваивался свой id и по этому id, введенному в QLineEdit, можно было отправлять окну переменную, содержащую некое значение и команду, например закрытия окна, сделать его не активным или наоборот активным или изменить его цвет.

Как вообще правильно создавать и работать с дочерними окнами?

Подскажите где найти хороший урок, учебники, потому что тот Гоша Дударь это жесть! В delphi все намного проще было. Почему Python легкий? Он очень тяжелый :-( На сколько мой код безграмотный?

Код всей программы:

    from PyQt5 import QtWidgets
    from PyQt5.QtWidgets import QApplication, QMainWindow

    import sys

    child_count = 0
    child_window = []

    class main_window_class(QMainWindow):


        def __init__(self):
            super(main_window_class, self).__init__()
            self.setWindowTitle("Prog_1 Main Window")
            self.setGeometry(100, 100, 500, 500)

            self.btn1 = QtWidgets.QPushButton("Создать дочернее окно", self)
            self.btn1.clicked.connect(self.btn1_on_click)
            self.btn1.setGeometry(0,50,300,50)
            self.name_edit = QtWidgets.QLineEdit("Ваш ник",self)
            self.name_edit.setGeometry(0, 0, 300, 50)

        def print_close_message(self):
            print("кто-то сдох :-(")

        def btn1_on_click(self):
            global child_count, child_window
            child_window.append(child_window_class())
            child_window[child_count].show()
            print(len(child_window))
            child_count += 1
            print("нас стало:",child_count)
            id = child_window.index(child_window) #тут вываливается ошибка
            print("мой id:",id)

    class child_window_class(QMainWindow):
        def __init__(self):
            super(child_window_class, self).__init__()
            self.setWindowTitle("Prog_1 Main Window")
            self.setGeometry(100, 100, 500, 500)


        def closeEvent(self, event):
            global child_count
            event.accept()
            main_window_class().print_close_message()
            child_count -= 1
            print("нас осталось:",child_count)

    def main_application_init():
        app = QApplication(sys.argv)
        main_window = main_window_class()
        main_window.show()
        sys.exit(app.exec_())

    if __name__ == "__main__":
        main_application_init()

Ответы (2 шт):

Автор решения: gil9red

id = child_window.index(child_window) #тут вываливается ошибка

Функция index ищет в списке объект и если его не будет, выбросит исключение ValueError, а у вас в функцию передается число, хотя в списке объекты дочерних окон. Исключения нужно ловить через try/except. В примере покажу как обрабатывать исключения, что не были пойманы в try/except, через sys.excepthook

Насчет кода, я бы сказал выглядит неплохо. Но названия классов нужно писать с верхнего регистра и в верблюжьей нотации, и думаю лучше не писать в названии class, для примера: MainWindow.

А переменные child_count = 0, child_window = [] я бы перенес в сам класс MainWindow. Да и child_count не нужно, т.к. количество можно получить из списка child_window

Тут, кст, фигня:

main_window_class().print_close_message()

Вы создаете новое окно и у него вызываете метод print_close_message. Архитектурно, лучше окнам-детям передавать окно родителя и его использовать. В print_close_message добавил параметр для объекта дочернего окна, в него виджет пишет самого себя, так родительское окно узнает что дочернее окно закрывается

Подправил код с учетом замечаний и рекомендаций:

import sys
import traceback

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLineEdit, QMessageBox


def log_uncaught_exceptions(ex_cls, ex, tb):
    text = '{}: {}:\n'.format(ex_cls.__name__, ex)
    text += ''.join(traceback.format_tb(tb))

    print(text)
    QMessageBox.critical(None, 'Error', text)
    sys.exit(1)


sys.excepthook = log_uncaught_exceptions


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Prog_1 Main Window")
        self.setGeometry(100, 100, 500, 500)

        self.btn1 = QPushButton("Создать дочернее окно", self)
        self.btn1.clicked.connect(self.btn1_on_click)
        self.btn1.setGeometry(0, 50, 300, 50)
        self.name_edit = QLineEdit("Ваш ник", self)
        self.name_edit.setGeometry(0, 0, 300, 50)

        self.child_windows = []

    def print_close_message(self, child: 'ChildWindow'):
        self.child_windows.remove(child)

        print(f"кто-то сдох :-(\nнас осталось: {len(self.child_windows)}")

    def btn1_on_click(self):
        child_window = ChildWindow(self)
        child_window.show()

        self.child_windows.append(child_window)
        print(len(self.child_windows))

        print("Нас стало:", len(self.child_windows))
        idx = self.child_windows.index(child_window)
        print("Мой id:", idx)


class ChildWindow(QMainWindow):
    def __init__(self, parent: MainWindow):
        super().__init__()

        self.parent = parent

        self.setWindowTitle("Prog_1 ChildWindow")
        self.setGeometry(100, 100, 500, 500)

    def closeEvent(self, event):
        event.accept()
        self.parent.print_close_message(self)


def main():
    app = QApplication(sys.argv)

    main_window = MainWindow()
    main_window.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
→ Ссылка
Автор решения: S. Nick

Лучший учебник - это документация, но вам потребуется время, чтобы ее научиться читать.

Вы выбрали класс QMainWindow для создания окон приложения - это хороший выбор.
Начните с прочтения Qt Main Window Framework .

Вторую тему, которую вам надо освоить - это сигналы и слоты.
Начинаем читать Signals & Slots

Далее читаем Layout Management

Глобальные переменные - это ЗЛО.

Ваш код может выглядеть примерно так:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow


class ChildWindow(QMainWindow):
    childSignal = QtCore.pyqtSignal(int)                     # +++
    
    def __init__(self, num):
        super(ChildWindow, self).__init__()
        self.setWindowTitle(f"ChildWindow {num}")
        self.resize(300, 200)
        self.centralWidget = QtWidgets.QWidget()             # !!!
        self.setCentralWidget(self.centralWidget)            # !!!
        self.num = num

    def closeEvent(self, event):
        self.childSignal.emit(self.num)                      # +++


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.centralWidget = QtWidgets.QWidget()             # !!!
        self.setCentralWidget(self.centralWidget)            # !!!
        
        self.child_count = 1
        self.child_window = {}

        self.textEdit = QtWidgets.QTextEdit()
        
        self.btn1 = QtWidgets.QPushButton("Создать дочернее окно")
        self.btn1.clicked.connect(self.btn1_on_click)
        
        self.name_edit = QtWidgets.QLineEdit(placeholderText="Введите ваш ник")
        
        self.layout = QtWidgets.QVBoxLayout(self.centralWidget)
        self.layout.addWidget(self.textEdit)
        self.layout.addWidget(self.name_edit)
        self.layout.addWidget(self.btn1)

    def btn1_on_click(self):
        if not self.name_edit.text():
            msg = msg = QtWidgets.QMessageBox.information(
                self, 
                'Внимание', 
                'Заполните пожалуйста поле для ввода.'
            )
            return

        childWindow = ChildWindow(self.child_count)
        self.child_window[self.child_count] = childWindow
        childWindow.childSignal.connect(self.close_child)       # +++
        childWindow.show()

        self.name_edit.clear()
        self.child_count += 1

    def close_child(self, num):
#        print(f"Закрыли окно: {num} --> {self.child_window[num].windowTitle()}")
        self.textEdit.append(f"Закрыли окно: {num} --> {self.child_window[num].windowTitle()}")


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.setWindowTitle("Main Window")
    window.resize(400, 100)
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
        main()

введите сюда описание изображения

→ Ссылка