Как сделать форму загрузки в PyQt5?
Запуск программы на PyQt5 занимает какое-то время и в это время ничего не происходит, то есть можно подумать, что программа попросту не запускается.
Поэтому я решила на время загрузки добавить отдельную форму, как это сделано во многих приложениях, которая будет висеть, пока не загрузится основная форма.
Но после многих попыток у меня не вышло правильно реализовать свою задумку, если поместить инициализацию основной формы в def __init__(self), то ни одна форма не запускается, пока обе не прогрузятся полностью, это логично:
class LoadingForm(QWidget):
def __init__(self):
super().__init__()
...
self.main_window = Window()
self.main_window.show()
self.hide()
Далее я попробовала сделать запуск основной формы после нажатия на форму загрузки:
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.main_window = Window()
self.main_window.show()
self.hide()
Этот вариант сработал, но вся обработка происходит уже после нажатия кнопки и поэтому форма загрузки просто зависает, а также это не совсем подходящий вариант, так как нужно нажать кнопкой на форму. Я использую qasync и, чтобы избавиться от зависания, попробовала такой вариант:
@asyncSlot
async def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.main_window = Window()
self.main_window.show()
self.hide()
Такой код выдаёт ошибку:
TypeError: outer_decorator() takes 1 positional argument but 2 were given
Как правильно реализовать форму загрузки?
Через qasync или другим способом. Загрузка формы не очень долгая 1-3 секунды, можно было бы и сделать таймер и после истечения времени, показывать уже загруженную форму.
Насколько такой вариант правильный?
Вот весь код:
import sys
import PyQt5
from qasync import asyncSlot, asyncClose, QApplication
import asyncio
import functools
import qasync
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.Qt import *
class LoadingForm(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Loading Form')
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.setWindowFlags(Qt.FramelessWindowHint)
self.setGeometry(300, 300, 620, 300)
self.press = False
self.last_pos = QPoint(0, 0)
box = QHBoxLayout(self)
movie = PyQt5.QtGui.QMovie('loading.gif')
lbl = QLabel(self)
lbl.setMovie(movie)
movie.start()
box.addWidget(lbl)
self.setWindowOpacity(0.6)
self.centerOnScreen()
def centerOnScreen(self):
resolution = QDesktopWidget().screenGeometry()
self.move((resolution.width() / 2) - (self.frameSize().width() / 2),
(resolution.height() / 2) - (self.frameSize().height() / 2))
def mouseMoveEvent(self, event):
if self.press:
self.move(event.globalPos() - self.last_pos)
@asyncSlot # Тут ошибка
async def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.press = True
self.main_window = Window()
self.main_window.show()
self.hide()
self.last_pos = event.pos()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.press = False
class Window(QMainWindow):
def __init__(self):
# super(Window, self).__init__()
# uic.loadUi('untitled.ui', self)
super().__init__()
self.setWindowTitle('Main Form')
self.setGeometry(300, 300, 620, 300)
async def main():
def close_future(future, loop):
loop.call_later(10, future.cancel)
future.cancel("Close Application")
loop = asyncio.get_event_loop()
future = asyncio.Future()
app = QApplication.instance()
app.setStyle("fusion")
if hasattr(app, 'aboutToQuit'):
getattr(app, 'aboutToQuit').connect(functools.partial(close_future, future, loop))
window = LoadingForm()
window.show()
await future
return True
if __name__ == '__main__':
try:
qasync.run(main())
except asyncio.exceptions.CancelledError:
sys.exit(0)
Ответы (1 шт):
Класс QSplashScreen позволяет нам создавать заставки.
Заставка обеспечивает обратную связь с пользователем о том, что приложение загружается.
Как вариант:
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QMovie
from PyQt5.QtWidgets import QApplication, QSplashScreen, QWidget, \
QMainWindow, QLabel, QGridLayout
class GifSplashScreen(QSplashScreen):
def __init__(self, *args, **kwargs):
super(GifSplashScreen, self).__init__(*args, **kwargs)
self.movie = QMovie('Data/splash.gif')
self.movie.frameChanged.connect(self.onFrameChanged)
self.movie.start()
def onFrameChanged(self, _):
self.setPixmap(self.movie.currentPixmap())
def finish(self, widget):
self.movie.stop()
super(GifSplashScreen, self).finish(widget)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Main Form')
self.resize(640, 480)
self.centralwidget = QWidget()
self.setCentralWidget(self.centralwidget)
self.label = QLabel('<h1>Hello World.</h1>')
self.layout = QGridLayout(self.centralwidget)
self.layout.addWidget(self.label, 0, 0, alignment=Qt.AlignCenter)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
splash = GifSplashScreen()
splash.show()
def createWindow():
app.w = MainWindow()
# Имитация начального отображения через 3 секунды
splash.showMessage(
'Ожидание отображения интерфейса',
Qt.AlignHCenter | Qt.AlignBottom, Qt.white
)
QTimer.singleShot(3000, lambda: (
splash.showMessage(
'Загрузка завершена',
Qt.AlignHCenter | Qt.AlignBottom, Qt.white
),
app.w.show(),
splash.finish(app.w))
)
# Симуляция занимает 6 секунд. Вы не можете использовать sleep.
# Можете использовать дочерние потоки для загрузки трудоемких данных.
# Интерфейс настройки цикла в основном потоке может взаимодействовать с
# QApplication.instance().processEvents()
splash.showMessage(
'Ожидание создания интерфейса',
Qt.AlignHCenter | Qt.AlignBottom, Qt.white
)
QTimer.singleShot(3000, createWindow)
sys.exit(app.exec_())
