Форум программистов, компьютерный форум, киберфорум
iamvic
Войти
Регистрация
Восстановить пароль
Оценить эту запись

К вопросу об экранной заставке при старте PyQt-приложений.

Запись от iamvic размещена 10.10.2021 в 23:19
Обновил(-а) iamvic 15.10.2021 в 07:45 (доработка решения)
Метки pyqt5, python, python 3, qt5

Однако, появилась ещё одна тема для памятки, поскольку неожиданные (хотя и прогнозируемые) трудности вызвала организация показа экранной заставки при старте PyQt-приложений под Linux.

Принцип-то понятен. А если не забывать, что Python - это интерпретатор и в процессе загрузки приложения может тратить значительное время на предварительную трансляцию самописных модулей и помещение результатов в рабочий каталог __pycache__, то, наверное, приемлемое решение могло бы выглядеть так:

splash_probe.py:
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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
 
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QSplashScreen
# Здесь оставляем только минимальный комплект импортов,
# необходимых для организации показа заставки.
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
 
    splash = QSplashScreen(QPixmap('splash_image.jpg'))
    splash.showMessage('Загрузка данных...',
                       Qt.AlignHCenter | Qt.AlignBottom, Qt.red)
    splash.show()
    # Показываем заставку.
 
    app.processEvents()
 
    import time
    from PyQt5.QtWidgets import QMainWindow
    # Сюда выносим все остальные импорты, требуемые для работы приложения.
 
    time.sleep(5)
    # А затем изображаем процесс инициализации и открываем главное окно.
 
    mwin = QMainWindow()
    mwin.show()
 
    splash.finish(mwin)
 
    sys.exit(app.exec())
И это прекрасно работает, но только под Windows. А под Linux экранная заставка в положенный момент не появляется, а только промелькивает перед самым открытием главного окна. Причина такого поведения тут уже упоминалась:
и событие showEvent уже произошло, и self.isVisible() возвращает True, а окно-то всё ещё не отрисовано
(см. https://www.cyberforum.ru/blog... g6887.html)
Именно задержка X11 в отрисовке окон и не даёт нормально развернуться экранной заставке. А поскольку с подобным поведением уже боролся, то и решение-то получилось довольно простое:

(PS: Первоначальное решение убрано под кат ради сохранения полноты дискуссии)
Кликните здесь для просмотра всего текста

splash_probe.py:
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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
 
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QEventLoop
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QSplashScreen
# Здесь оставляем только минимальный комплект импортов,
# необходимых для организации показа заставки.
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
 
    splash = QSplashScreen(QPixmap('splash_image.jpg'))
    splash.showMessage('Загрузка данных...',
                       Qt.AlignHCenter | Qt.AlignBottom, Qt.red)
    splash.show()
    # Показываем заставку.
 
    if sys.platform == 'win32':
        app.processEvents()
    else:
        splash_loop = QEventLoop()
        QTimer.singleShot(250, splash_loop.exit)
        splash_loop.exec()
 
    import time
    from PyQt5.QtWidgets import QMainWindow
    # Сюда выносим все остальные импорты, требуемые для работы приложения.
 
    time.sleep(5)
    # А затем изображаем процесс инициализации и открываем главное окно.
 
    mwin = QMainWindow()
    mwin.show()
 
    splash.finish(mwin)
 
    sys.exit(app.exec())


(PS: Ниже приведено окончательное решение)
splash_probe.py:
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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
 
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QEventLoop
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QSplashScreen
# Здесь оставляем только минимальный комплект импортов,
# необходимых для организации показа окна-заставки.
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
 
    splash = QSplashScreen(QPixmap('splash_image.jpg'))
    splash.showMessage('Загрузка данных...',
                       Qt.AlignHCenter | Qt.AlignBottom, Qt.red)
    splash.show()
    # Показываем заставку.
 
    if sys.platform == 'win32':
        app.processEvents()
        # Под Windows достаточно один раз прокрутить общий цикл событий,
        # чтобы картинка загрузилась и отобразилась.
    else:
        QTimer.singleShot(500, app.quit)
        # Под Linux из-за особенностей X11 общий цикл событий приходится
        # прокручивать определённое время. 500 мсек обычно хватает даже
        # на слабом железе. Как правило, изображение появляется раньше.
        # Оставшиеся доли секунды общий цикл крутится вхолостую.
        app.exec()
 
    import time
    from PyQt5.QtWidgets import QMainWindow
    # Сюда выносим все остальные импорты, необходимые для работы приложения,
    # поскольку они тоже требуют определённого времени для загрузки
    # и, возможно, предварительной трансляции.
 
    time.sleep(5)
    # Имитируем процесс инициализации в течение 5 сек.
    # И далее действуем по канону.
 
    mwin = QMainWindow()
    mwin.show()
 
    splash.finish(mwin)
 
    sys.exit(app.exec())
Не знаю насколько оно универсально, но по крайней мере даже на слабенькой Toshiba NB100 под Lubuntu 18.04 отрабатывает вполне уверенно. Длительность задержки, скорее всего, лучше увеличить. Даже потеря полсекунды на открытии заставки не особо чувствительна, если инициализация длится больше десятка секунд.
Размещено в Памятка
Показов 1320 Комментарии 16
Всего комментариев 16
Комментарии
  1. Старый комментарий
    Аватар для Avazart
    Костыли.
    У Вас уже есть эвент-луп (app.exec()) используйте его и сигналы-слоты. Главное окно создавайте но не показывайте сразу.

    P/S: Хотя конечно QSplashScreen в каком-то роде сам по себе костыль.
    Запись от Avazart размещена 11.10.2021 в 12:55 Avazart вне форума
    Обновил(-а) Avazart 11.10.2021 в 12:58 (йй)
  2. Старый комментарий
    Спасибо, Avazart!
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    Костыли.
    Сами понимаете - мера вынужденная. Всё нагугленное выглядело уж слишком устрашающим .
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    У Вас уже есть эвент-луп (app.exec()) используйте его и сигналы-слоты.
    Вы правы. Действительно, Ваше предложение использовать app.exec() и отправлять сигнал на слот app.quit выглядит предпочтительным:
    Python
    1
    2
    3
    4
    5
    
        if sys.platform == 'win32':
            app.processEvents()
        else:
            QTimer.singleShot(500, app.quit)
            app.exec()
    Попутно выяснилось, что и задержку отправки сигнала всё-таки надо увеличить, иначе тяжёлые картинки не успевают загрузиться.
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    Главное окно создавайте но не показывайте сразу.
    Вот так, с налёту, не могу ухватить смысл этого предложения. Растолкуете? Можно даже примером кода - в этот-то раз я не стал в архив паковать .
    Запись от iamvic размещена 12.10.2021 в 08:13 iamvic вне форума
  3. Старый комментарий
    Аватар для Avazart
    Цитата:
    Сами понимаете - мера вынужденная.
    Скорее от не понимания фундаментальных вещей.

    Цитата:
    Попутно выяснилось, что и задержку отправки сигнала всё-таки надо увеличить, иначе тяжёлые картинки не успевают загрузиться.
    Думаю с этого нужно начать. Чем/как качаете ? Через requests ?
    Запись от Avazart размещена 12.10.2021 в 12:29 Avazart вне форума
    Обновил(-а) Avazart 12.10.2021 в 12:30
  4. Старый комментарий
    Аватар для Avazart
    Цитата:
    Попутно выяснилось, что и задержку отправки сигнала всё-таки надо увеличить, иначе тяжёлые картинки не успевают загрузиться.
    Думаю с этого нужно начать. Чем/как качаете ? Через requests ?

    Цитата:
    Python
    1
    2
    3
    4
    5
    
        if sys.platform == 'win32':
            app.processEvents()
        else:
            QTimer.singleShot(500, app.quit)
            app.exec()
    Нет не это я имел ввиду. Тут все лишнее.
    Запись от Avazart размещена 12.10.2021 в 12:31 Avazart вне форума
  5. Старый комментарий
    Цитата:
    Думаю с этого нужно начать. Чем/как качаете ? Через requests ?
    .
    Не качаю. Никак. Ничем.
    Python
    1
    
        splash = QSplashScreen(QPixmap('splash_image.jpg'))
    Картинка просто лежит в текущем каталоге диска. Большая при первом запуске, пока Linux воюет с кэшем, не успевает отобразиться в той прокрутке цикла событий, которую Вы назвали лишней, а маленькая успевает. При повторном же пуске успевают и большая, и маленькая.
    Запись от iamvic размещена 12.10.2021 в 14:42 iamvic вне форума
  6. Старый комментарий
    Аватар для Avazart
    Ничего не понимаю. Что у Вас там блин за картинка ...
    Запись от Avazart размещена 12.10.2021 в 14:57 Avazart вне форума
  7. Старый комментарий
    Картинка-то простая, а вот железка с собой в поездку взята слабенькая. Вот и вылазят разные интересности. Простите, но пока пропаду со связи.
    Запись от iamvic размещена 12.10.2021 в 15:21 iamvic вне форума
  8. Старый комментарий
    Аватар для Avazart
    Зачем Вам вообще QSplashScreen ?
    Запись от Avazart размещена 12.10.2021 в 18:11 Avazart вне форума
  9. Старый комментарий
    Аватар для Avazart
    Зачем Вам вообще QSplashScreen ?
    Запись от Avazart размещена 12.10.2021 в 19:24 Avazart вне форума
  10. Старый комментарий
    Цитата:
    Зачем Вам вообще QSplashScreen ?
    Вопрос, конечно, философский... Отвечу развёрнуто.

    Не секрет, что общеизвестные правила хорошего тона рекомендуют после старта приложения вывесить некую заставку на экран, чтобы пользователь был уверен, что приложение всё-таки запустилось по двойному щелчку ЛКМ. Разработчики Qt предлагают использовать для этих целей QSplashScreen. Описанный в документации Qt классический способ прекрасно работает под Windows и абсолютно недееспобен под Linux. Возникшая необходимость обеспечения одинаковой функциональности приложения под разными ОС и привела к вышеуказанному решению, которое с минимальными затратами со стороны программиста решает поставленную задачу. Судя по результатам предварительных испытаний, способ рабочий. Вот и всё.
    Запись от iamvic размещена 14.10.2021 в 23:17 iamvic вне форума
  11. Старый комментарий
    Аватар для Avazart
    Цитата:
    Не секрет, что общеизвестные правила хорошего тона рекомендуют после старта приложения вывесить некую заставку на экран
    А я бы сказал это правила плохого тона. Зачем пользователя заставлять ждать? Это ожидание явно радует пользователя.
    Когда Вы запускаете QtCreator Вы видите заставку?

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

    Лично я бы даже в таких случаях не делал бы заставку. По крайней мере не показывал бы ее одну.
    В таких случаях можно показать главное окно а уже поверх него заставку или же вместо заставки показывать прогрессбар в основном окне. Так явно лучше чем заставка перекрывающая все и вся и которую не закрыть ни свернуть.
    Запись от Avazart размещена 15.10.2021 в 13:29 Avazart вне форума
    Обновил(-а) Avazart 15.10.2021 в 13:36
  12. Старый комментарий
    Уважаемый, Avazart! Да, я даже соглашусь со всеми Вашими оценками . Только поймите правильно, что моя задача в данной конкретной ситуации состояла не в том, чтоб оценочки выставлять людям и технологиям, а найти приемлемый выход при существующем положении дел. Вот в чём беда. Кто там, что там и как будет делать дальше - мне, как Вы понимаете, доложить забыли.
    Запись от iamvic размещена 15.10.2021 в 17:46 iamvic вне форума
  13. Старый комментарий
    Аватар для Avazart
    Выход только один перестать биться головой об стену и начать использовать ее по назначению т.е думать.

    Проблема XY
    Запись от Avazart размещена 15.10.2021 в 18:18 Avazart вне форума
    Обновил(-а) Avazart 15.10.2021 в 18:20
  14. Старый комментарий
    Avazart, это всё прекрасно, но очнитесь! Мы как-то всё о разных вещах толкуем. Вы ж не знаете всех обстоятельств, приведших к появлению этой заплатки. Мы живём в несовершенном мире. Когда у людей пожар-цейтнот-аврал и всё пропало, не время рассуждать о качестве проекта, надо пластырь заводить, разбираться на берегу будут.

    Ведь, все с Вами согласны, что изначально всё надо было делать по-другому. Наверное, примерно так, как я предлагал здесь https://www.cyberforum.ru/pyth... 45300.html Пусть это коряво, наивно, избыточно и вызывает у вас рвоту, но моделька всё же даёт представление о том, как можно это всё реализовать. Можно спорить, дискутировать, критиковать, но так или иначе решение проблемы существует.

    Но вписаться с этим в уже готовый горящий проект не представляется возможным. Что выберете в этой ситуации - добавить несколько строк и выжить или утонуть в объёмных переделках? По головам-то бить и руки выпрямлять имеет смысл только выжившим... По пластырю замечания есть?
    Запись от iamvic размещена 16.10.2021 в 09:31 iamvic вне форума
  15. Старый комментарий
    Аватар для Avazart
    Цитата:
    Когда у людей пожар-цейтнот-аврал и всё пропало, не время рассуждать о качестве проекта, надо пластырь заводить, разбираться на берегу будут.
    Все верно а рассуждать-матом будет другой программист пришедший на смену когда Вас уволят ...
    Запись от Avazart размещена 16.10.2021 в 19:20 Avazart вне форума
    Обновил(-а) Avazart 16.10.2021 в 20:39
  16. Старый комментарий
    Цитата:
    Вас уволят
    Ну дык-ть живём-то по сказке, куда из неё денешься-то: один - изъявляет желание "Хочу ковёр-самолёт", другой - проектирует, третий - исполняет, четвёртый - пилотирует, а когда начинают падать - зовут пятого "Делай, что хочешь, но чтоб летело - жить хотим!!!", пятый - забивает костыль и, обеспечив мягкую посадку, получает заслуженное вознаграждение . Тут и сказочке - конец.
    Запись от iamvic размещена 16.10.2021 в 20:50 iamvic вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.