динамическое создание окон и присвоение им идентификатора
Есть программа, в которой по нажатию на кнопку создаются окна соответствующего класса. Окна являются элементами списка.
Надо сделать, чтобы каждому окну присваивался свой 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 шт):
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()
Лучший учебник - это документация, но вам потребуется время, чтобы ее научиться читать.
Вы выбрали класс 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()
