Форум программистов, компьютерный форум, киберфорум
iamvic
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
Путевые заметки в процессе познания Python и PyQt/PySide.
Помни - только тег CODE не портит код добавлением пробела в начало пустой строки.

К вопросу о закрытии окна нажатием на значок в полосе заголовка.

Запись от iamvic размещена 11.05.2021 в 21:10
Показов 7372 Комментарии 2
Метки pyqt5, python, python 3, qt5

В теме Изменение событий дочернего окна,
поднятой katyuhma, неожиданно вспомнилась ещё одна проблемка,
с которой когда-то пришлось повозиться, но решение так и не оформилось
в виде текста. Восполняю пробел.

Суть-то простая - открытое окно приложения, имеющее стандартное оформление,
можно закрыть по крайней мере двумя способами:

- вызовом close() в коде приложения,
- нажатием на значок закрытия в заголовке окна (сюда же относятся выбор
пункта <Закрыть> в выпадающем меню после нажатия на значок приложения,
а также нажатие комбинации клавиш Alt-F4).

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

Перед написанием пробника было полезно освежить знания чтением документации
https://doc.qt.io/qt-5/qwidget.html#close (кой-чего не знал, да и забыл).

Итак, слот QWidget.close():
- закрывает виджет,
- возвращает true, если виджет был закрыт; в противном случае возвращается false.
Слот? Возвращает значение? Это как? Надо проверить.
- сначала он отправляет виджету QCloseEvent. Виджет прячется, если принимает
событие закрытия. Если он игнорирует событие, ничего не происходит. Реализация
QWidget.closeEvent() по умолчанию принимает событие закрытия.

Тут уже становится понятно ЧТО надо проверить и КАК...
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QAction
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
 
APP_NAME = 'The Close Probe v0.001'
 
class ProbeMainWindow(QMainWindow):
    '''Главное окно приложения
    '''
    def __init__(self, parent=None):
        super(ProbeMainWindow, self).__init__(parent)
        self.initUI()
 
    def initUI(self):
        '''Строим графический интерфейс
        '''
        self.setWindowTitle(APP_NAME)
        self.statusBar()
 
        exitAction = QAction(
            'Выход', self, shortcut='Ctrl+Q',
            statusTip='Завершить работу приложения',
            triggered=self.to_close, enabled=True)
 
        taskMenu = self.menuBar().addMenu('Файл')
        taskMenu.addAction(exitAction)
        print('')
 
    def to_close(self):
        print('вызов ProbeMainWindow.to_close()')
        rc = self.close()
        print('self.close() -->', rc)
 
    def closeEvent(self, event):
        print('вызов ProbeMainWindow.closeEvent()')
        print('self -->', self)
        print('self.sender() -->', self.sender(), '\n')
        super(ProbeMainWindow, self).closeEvent(event)
 
    def close(self):
        print('вызов ProbeMainWindow.close()')
        print('self -->', self)
        print('self.sender() -->', self.sender(), '\n')
        super(ProbeMainWindow, self).close()
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
 
    mwin = ProbeMainWindow()
    mwin.show()
    sys.exit(app.exec_())
Запускаем и смотрим результаты:

1. выход выбором пункта меню Файл --> Выход (или нажатием Ctrl-Q):
Code
1
2
3
4
5
6
7
8
9
10
11
12
user@linux:~> ./closeprobe.py
 
вызов ProbeMainWindow.to_close()
вызов ProbeMainWindow.close()
self --> <__main__.ProbeMainWindow object at 0x7fe4d72edca8>
self.sender() --> <PyQt5.QtWidgets.QAction object at 0x7fe4d72eddc8>
 
вызов ProbeMainWindow.closeEvent()
self --> <__main__.ProbeMainWindow object at 0x7fe4d72edca8>
self.sender() --> <PyQt5.QtWidgets.QAction object at 0x7fe4d72eddc8>
 
self.close() --> None
2. выход нажатием на значок закрытия в заголовке окна (или выбором
пункта <Закрыть> в выпадающем меню после нажатия на значок приложения,
или нажатием комбинации клавиш Alt-F4):
Code
1
2
3
4
5
user@linux:~> ./closeprobe.py
 
вызов ProbeMainWindow.closeEvent()
self --> <__main__.ProbeMainWindow object at 0x7f9ff7e71ca8>
self.sender() --> None
А результаты заставляют задуматься. С одной стороны, вроде бы логично
выполнять все действия по закрытию в обработчике closeEvent(), поскольку
события QCloseEvent от внешних источников поступают на обработку только туда.
И, наверняка, для большинства случаев подойдёт именно этот вариант.
С другой стороны, хотелось бы немного большей свободы действий в случае
необходимости выполнения тяжёлых критических операций, например,
по сохранению текущего состояния и т.д. Впрочем, возможность отфильтровать
события QCloseEvent, поступающие от внешних источников, есть.

А вот ситуацию с невозвратом значений из QWidget.close() пока
интерпретировать правильно не могу.

Добавлю пример, чтобы стало понятнее.
Вот таким нехитрым способом можно запретить закрывать окно нажатием на значок закрытия в заголовке:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QAction
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
 
APP_NAME = 'The Close Probe v0.002'
 
class ProbeMainWindow(QMainWindow):
    '''Главное окно приложения
    '''
    def __init__(self, parent=None):
        super(ProbeMainWindow, self).__init__(parent)
        self.initUI()
 
    def initUI(self):
        self.setWindowTitle(APP_NAME)
        self.statusBar()
 
        exitAction = QAction(
            'Выход', self, shortcut='Ctrl+Q',
            statusTip='Завершить работу приложения',
            triggered=self.to_close, enabled=True)
 
        taskMenu = self.menuBar().addMenu('Файл')
        taskMenu.addAction(exitAction)
 
    def to_close(self):
        print('self.to_close() --> освобождаем ресурсы, сохраняем состояние и т.д.')
        self.close()
 
    def closeEvent(self, event):
        print('self.closeEvent() --> self.sender() is', self.sender())
        if self.sender() is None:
            # игнорируем QCloseEvent от внешних источников:
            # - значок закрытия в заголовке окна,
            # - пункт <Закрыть> в выпадающем меню в заголовке
            # - комбинация клавиш Alt-F4
            event.ignore()
        else:
            event.accept()
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
 
    mwin = ProbeMainWindow()
    mwin.show()
    sys.exit(app.exec_())
Пример того, как можно вынести обработку закрытия из обработчика closeEvent(),
тоже не будет лишним, наверное:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QAction
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
 
APP_NAME = 'The Close Probe v0.003'
 
class ProbeMainWindow(QMainWindow):
    '''Главное окно приложения
    '''
    dont_repeat_closing = False
 
    def __init__(self, parent=None):
        super(ProbeMainWindow, self).__init__(parent)
        self.initUI()
 
    def initUI(self):
        self.setWindowTitle(APP_NAME)
        self.statusBar()
 
        self.exitAction = QAction(
            'Выход', self, shortcut='Ctrl+Q',
            statusTip='Завершить работу приложения',
            triggered=self.to_close, enabled=True)
 
        taskMenu = self.menuBar().addMenu('Файл')
        taskMenu.addAction(self.exitAction)
 
    def to_close(self):
        # запрещаем обработку повторных закрытий
        self.dont_repeat_closing = True
 
        # деактивируем меню, чтобы пользователь не баловался
        self.exitAction.setEnabled(False)
 
        # Здесь уже можно хоть чего - интерфейс-то не заморожен.
        # Можно, например, организовать визуальную демонстрацию
        # процесса закрытия.
        # А мы просто изобразим бурную бездеятельность в течение 5 секунд
        # (можно даже, например, понажимать на значок закрытия в заголовке,
        # чтобы убедиться, что обработка событий происходит нормально)
        print('self.to_close() --> освобождаем ресурсы, сохраняем состояние и т.д.')
        QTimer.singleShot(5000, self.close)
        # В реальных условиях таймер не нужен. Достаточно вызвать self.close()
        # по завершению всех операций.
 
    def closeEvent(self, event):
        print('self.closeEvent() --> self.sender() is', self.sender())
        if self.sender() is None:
            # игнорируем QCloseEvent от внешних источников:
            # - значок закрытия в заголовке окна,
            # - пункт <Закрыть> в выпадающем меню в заголовке
            # - комбинация клавиш Alt-F4
            event.ignore()
            if not self.dont_repeat_closing:
                # на закрытие отправляем только после первого QCloseEvent
                # от внешнего источника, на повторные не реагируем
                self.dont_repeat_closing = True
 
                # если здесь просто вызвать self.to_close(), то из обработчика
                # self.closeEvent() мы всё-таки не выйдем, а узнавать чем это
                # потом обернётся...
                QTimer.singleShot(10, self.to_close)
                print('self.closeEvent() --> первый раз --> на выход')
            else:
                print('self.closeEvent() --> не первый раз --> отказ')
        else:
            event.accept()
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
 
    mwin = ProbeMainWindow()
    mwin.show()
    sys.exit(app.exec_())
Метки pyqt5, python, python 3, qt5
Размещено в Памятка
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 2
Комментарии
  1. Старый комментарий
    Аватар для Fudthhh
    Все хорошо, но, есть пару замечаний:
    1) Это PEP-8, стоит хорошенько его вызубрить чтоб код более читабельный писать.
    2) Лучше написать код, а ниже расписывать каждую строчку, так будет более понятно и читабельнее.

    Немного кода от себя:
    Кликните здесь для просмотра всего текста

    Python
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    import sys
    from PySide2 import QtWidgets, QtGui
     
     
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self, parent: QtWidgets.QWidget = None):
            QtWidgets.QMainWindow.__init__(self, parent)
            self.setWindowTitle("Example: [Close Event]")
     
            QtWidgets.QShortcut(QtGui.QKeySequence("Alt+F4"), self, self.close)
     
            if not parent:
                self.show()
     
        def closeEvent(self, event: QtGui.QCloseEvent) -> None:
            if QtWidgets.QMessageBox.question(
                    self, "Closing the program",
                    "Are you sure you want to exit the program?",
                    (QtWidgets.QMessageBox.Yes
                     | QtWidgets.QMessageBox.Cancel)) != QtWidgets.QMessageBox.Yes:
                return event.ignore()
            # Действия при закрытии главного окна
            return event.ignore()
     
     
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        main = MainWindow()
        sys.exit(app.exec_())
    Запись от Fudthhh размещена 18.05.2021 в 08:00 Fudthhh вне форума
  2. Старый комментарий
    Fudthhh, спасибо за добрые слова и пример правильного
    оформления текста программ . Моя беда в том, что опыт мой отягощён
    давними прочными беспорядочными связями с другими языками программирования
    и, соответственно, иной стилистикой. Это несколько мешает усвоению
    рекомендаций PEP 8 и постоянно прорывается, несмотря на то, что я работаю
    над этим недостатком. Утешаю себя мыслью, что пункт 2 из PEP 8 в своих проектах
    и в ответах на форуме старюсь блюсти строго
    Запись от iamvic размещена 19.05.2021 в 17:27 iamvic вне форума
 
Новые блоги и статьи
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru