当一个方法计算时,我试图显示一个带有gif和进度条的SplashScreen。
因此,我有一个带有PyQt5MainWindow应用程序的main.py。在这个应用程序方法启动时,请参阅我的calc.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("nn====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("nn====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)
显示了启动屏幕和进度条,但随后gui冻结并崩溃。
因此,从main.py开始计算.py。main.py是一个带有app=Qapplication((和MainWindow((的gui。看起来像:
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_())
我尝试在calc.py中使用dapp.exit(exec_(((而不是dapp.deleteLater((,但它仍然崩溃了。
您混淆了概念:
-
如果任务正在处理,那么耗时的任务必须在另一个线程中执行。
-
如果你想用在另一个线程中获得的信息控制GUI,那么你必须使用信号,在这种情况下,我创建了两个信号,发送显示窗口的QProgressBar的值,另一个发送结果。
-
如果要执行定期任务,请使用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("nn====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(
"nn====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_())