Показ QSplashScreen с QMov ie и сбой QProgressBar. PyQt5 - PullRequest
0 голосов
/ 07 апреля 2020

Я пытаюсь отобразить SplashScreeen с gif и прогресс-баром в нем, пока метод вычисляет.

Поэтому у меня есть один main.py с приложением PyQt5 MainWindow. В этом приложении запускается метод, см. Мой cal c .py:

from time import sleep, time
import pandas as pd
import concurrent.futures, requests, queue, sys
from threading import Thread
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon, QFont, QKeySequence, QPalette, QBrush, QColor, QPixmap, QMovie, QPainter
from PyQt5.QtCore import Qt, QSize, QRect, QThread, pyqtSignal, QTimer

class MovieSplashScreen(QSplashScreen):

    def __init__(self, movie, parent = None):

        movie.jumpToFrame(0)
        pixmap = QPixmap(movie.frameRect().size())

        QSplashScreen.__init__(self, pixmap)
        self.movie = movie
        self.movie.frameChanged.connect(self.repaint)

    def showEvent(self, event):
        self.movie.start()

    def hideEvent(self, event):
        self.movie.stop()

    def paintEvent(self, event):

        painter = QPainter(self)
        pixmap = self.movie.currentPixmap()
        self.setMask(pixmap.mask())
        painter.drawPixmap(0, 0, pixmap)

    def sizeHint(self):

        return self.movie.scaledSize()

def splashScreen(zeit = 0):
        print('===splashScreen(self)====')


        dapp = QApplication(['a', 's'])

        # Create and display the splash screen
        movie = QMovie("img\\fuchs.gif")
        if zeit <= 2:
            gerundet = 50
        elif zeit > 2:
            gerundet = zeit * 60
        print("gerundet = ", gerundet)

        splash = MovieSplashScreen(movie) 
        width = splash.frameGeometry().width()
        height = splash.frameGeometry().height()
        x = splash.pos().x()
        y = splash.pos().y() 

        print('splash x,y: ',width, height, x, y)
        splash.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        splash.setEnabled(False)

        # adding progress bar
        palette = QPalette()
        palette.setColor(QPalette.Highlight, Qt.green)
        progressBar = QProgressBar()
        progressBar.setMaximum(gerundet)
        progressBar.setGeometry(x, y-30, width, 20)
        progressBar.setPalette(palette)
        progressBar.setWindowFlags(Qt.SplashScreen | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)

        gerundet = gerundet + 1
        #splash.setMask(splash_pix.mask())
        progressBar.show()
        splash.show()
        splash.showMessage("<h1><font color='red'></font></h1>", Qt.AlignTop | Qt.AlignCenter, Qt.black)

        for i in range(1, gerundet):
            progressBar.setValue(i)
            t = time()
            while time() < t + 0.1:
                dapp.processEvents()

        progressBar.hide()
        window = QWidget()
        splash.finish(window)
        dapp.deleteLater() # here are troubles maybe in cause of main.py with the GUI has a app = QAplllication(sys.argv) too?


def getSignals(selectedCoins, selectedCoinsText):
    print("=====getFilteredSignals====")
    dfFilter = []
    noResults = []   
    print("selectedCoins: ", selectedCoins)
    zeit = len(selectedCoins)

# Problems here?
    t = Thread(target=splashScreen, args=(zeit,))
    t.start()

    for i in range(len(selectedCoins)):
        print("i: "+str(i)+" ", selectedCoins[i])
        if i >= 1:
            sleep(6)
        result = makeSignals(selectedCoins[i])
        print("results.empty: ", result.empty)
        if result.empty == False:
            result = result.set_index('Pair', inplace=False)
            dfFilter.append(result)
        else: 
            print("selectedCoinsText"+str(i)+": ", selectedCoinsText[i])
            noResults.append(selectedCoinsText[i])


    print("\nlen(dfFilter): ", len(dfFilter))
    if len(dfFilter) == 0:
        print("\n\n====in if len(dfFilter) == 0: \n dfFilter: ", dfFilter)
        # Creating an empty Dataframe with column names only
        dfempty = pd.DataFrame(columns=['User_ID', 'UserName', 'Action'])
        print("Empty Dataframe ", dfempty,'\n dfempty.empty: ', dfempty.empty)
        return dfempty
    elif len(dfFilter) > 0:   
        for i in range(len(dfFilter)):
            print("\n\n====in for loop=== \n dfFilter ["+str(i)+"]: \n", dfFilter[i])

        filteredResults = pd.concat(dfFilter, axis=0, sort=False)        
        #filteredResults['Gain (%)'] = pd.to_numeric(filteredResults['Gain (%)'], errors='coerce') 
        filteredResults = filteredResults.sort_values(by='Gain (%)', ascending=False, inplace=False) 
        filteredResults = filteredResults.reset_index(inplace=False)
        print('\nfilteredResults: \n', filteredResults, "\n", filteredResults.dtypes)

        return filteredResults

self.results = calc.getSignals( a, aText)

Отображаются Splashscreen и Progressebar, но затем gui зависает и вылетает.

Итак, из main.py cal c .py запускается. main.py - это gui с app = Qapplication () и MainWindow (). Выглядит так:

import calc

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.font = QFont("Helvetica", 9)
        self.setFont(self.font) 
        ...
        self.getSignals(a, aText)
        ...


    def getSignals(self, a, aText):
        zeit = len(a)
        self.results = calc.getSignals(a, aText)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle('Fusion')
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec_())

Я пытаюсь использовать dapp.exit (exec_ ()) вместо dapp.deleteLater () в cal c .py , но он все равно тоже падает .

1 Ответ

0 голосов
/ 07 апреля 2020

Вы вводите в заблуждение понятия:

  • Задача, занимающая много времени, должна выполняться в другом потоке, в вашем случае эта задача обрабатывается.

  • Если вы хотите управлять GUI информацией, полученной в другом потоке, то вы должны использовать сигналы, в этом случае я создал 2 сигнала, которые отправляют значение QProgressBar, показывая windows, а другой отправляет результат.

  • Если вы хотите выполнять периодические c задачи, используйте QTimer.

С учетом вышеизложенного решение:

import os
import threading
import time

from PyQt5 import QtCore, QtGui, QtWidgets

import pandas as pd


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


class MovieSplashScreen(QtWidgets.QSplashScreen):
    def __init__(self, movie, parent=None):
        self._movie = movie
        self.movie.jumpToFrame(0)
        pixmap = QtGui.QPixmap(movie.frameRect().size())
        super(MovieSplashScreen, self).__init__(pixmap)
        self.setParent(parent)
        self.movie.frameChanged.connect(self.repaint)

    @property
    def movie(self):
        return self._movie

    def showEvent(self, event):
        self.movie.start()
        super(MovieSplashScreen, self).showEvent(event)

    def hideEvent(self, event):
        self.movie.stop()
        super(MovieSplashScreen, self).hideEvent(event)

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        pixmap = self.movie.currentPixmap()
        self.setMask(pixmap.mask())
        painter.drawPixmap(0, 0, pixmap)

    def sizeHint(self):
        return self.movie.scaledSize()


class Processor(QtCore.QObject):
    started = QtCore.pyqtSignal(int)
    filteredResultsSignal = QtCore.pyqtSignal(pd.DataFrame)

    def execute(self, selectedCoins, selectedCoinsText):
        threading.Thread(
            target=self._execute, args=(selectedCoins, selectedCoinsText)
        ).start()

    def _execute(self, selectedCoins, selectedCoinsText):
        print("=====getFilteredSignals====")
        dfFilter = []
        noResults = []
        print("selectedCoins: ", selectedCoins)
        zeit = len(selectedCoins)
        self.started.emit(zeit)

        for i, (coin, text) in enumerate(zip(selectedCoins, selectedCoinsText)):
            print("i: {} {}".format(i, coin))
            if i >= 6:
                time.sleep(6)
            result = makeSignals(selectedCoins[i])
            print("results.empty: ", result.empty)
            if not result.empty:
                result = result.set_index("Pair", inplace=False)
                dfFilter.append(result)
            else:
                print("selectedCoinsText{}: {}".format(i, text))
                noResults.append(text)

        print("\nlen(dfFilter): ", len(dfFilter))
        if len(dfFilter) == 0:
            print("\n\n====in if len(dfFilter) == 0: \n dfFilter: ", dfFilter)
            # Creating an empty Dataframe with column names only
            filteredResults = pd.DataFrame(columns=["User_ID", "UserName", "Action"])
            print(
                "Empty Dataframe ",
                filteredResults,
                "\n dfempty.empty: ",
                filteredResults.empty,
            )
        elif len(dfFilter) > 0:
            for i, df_filter in enumerate(dfFilter):
                print(
                    "\n\n====in for loop=== \n dfFilter [{}]: \n{}".format(i, df_filter)
                )

            filteredResults = pd.concat(dfFilter, axis=0, sort=False)
            # filteredResults['Gain (%)'] = pd.to_numeric(filteredResults['Gain (%)'], errors='coerce')
            filteredResults = filteredResults.sort_values(
                by="Gain (%)", ascending=False, inplace=False
            )
            filteredResults = filteredResults.reset_index(inplace=False)
            print(
                "\nfilteredResults: \n", filteredResults, "\n", filteredResults.dtypes
            )

        self.filteredResultsSignal.emit(filteredResults)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        font = QtGui.QFont("Helvetica", 9)
        self.setFont(font)

        self.processor = Processor(self)
        self.processor.started.connect(self.on_started)
        self.processor.filteredResultsSignal.connect(self.on_filteredResultsSignal)
        self.processor.execute("a", "aText")

        movie_path = os.path.join(CURRENT_DIR, "img", "fuchs.gif")

        movie = QtGui.QMovie(movie_path)
        self.splash = MovieSplashScreen(movie)
        self.splash.setWindowFlags(
            QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.FramelessWindowHint
        )
        self.splash.setEnabled(False)

        # adding progress bar
        palette = QtGui.QPalette()
        palette.setColor(QtGui.QPalette.Highlight, QtCore.Qt.green)
        self.progressBar = QtWidgets.QProgressBar()

        self.progressBar.setPalette(palette)
        self.progressBar.setWindowFlags(
            QtCore.Qt.SplashScreen
            | QtCore.Qt.FramelessWindowHint
            | QtCore.Qt.WindowStaysOnTopHint
        )
        self.progressBar.move(self.splash.pos() + QtCore.QPoint(0, -30))
        self.progressBar.resize(self.splash.width(), 30)
        self.counter = 0
        self.gerundet = 0
        self.timer = QtCore.QTimer(interval=100, timeout=self.on_timeout)

    @QtCore.pyqtSlot(pd.DataFrame)
    def on_filteredResultsSignal(self, df):
        print(df)

    @QtCore.pyqtSlot()
    @QtCore.pyqtSlot(int)
    def on_started(self, zeit=0):
        gerundet = 50 if zeit <= 2 else zeit + 60
        print("gerundet = ", gerundet)
        gerundet = gerundet + 1
        self.progressBar.setMaximum(gerundet)
        # splash.setMask(splash_pix.mask())
        self.progressBar.show()
        self.splash.show()
        self.splash.showMessage(
            "<h1><font color='red'></font></h1>",
            QtCore.Qt.AlignTop | QtCore.Qt.AlignCenter,
            QtCore.Qt.black,
        )
        self.counter = 1
        self.gerundet = gerundet
        self.timer.start()

    @QtCore.pyqtSlot()
    def on_timeout(self):
        self.progressBar.setValue(self.counter)
        self.counter += 1
        if self.counter > self.gerundet:
            self.timer.stop()
            self.progressBar.hide()
            self.splash.close()


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    app.setStyle("Fusion")
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec_())
...