我想使用PyQt显示选定的静态图像或animated gif using QLabel小部件。对于我的特定用例,我仅使用Qt显示图像,而我的Python代码的其余部分不需要Qt。我想要我的Qt app to run in the background (separate thread)。这是因为运行app.exec_()
后,Qt应用程序进入了连续事件循环,从而阻塞了我的Python代码中的其余逻辑。
我能够使用这两个类实现一个可行的解决方案(下面的代码)。使用此方法,我可以随时更新静态或动画图像,并且代码可以按预期工作。但是,我不想合并两个类,而是想合并为一个类(下面的代码)。一种方法的问题是显示的gif没有动画。每当我更新静态图像时,代码就会按预期工作,但是,当使用gif图像时,显示的gif不会被动画化。为什么一类方法不能为显示的gif设置动画?我为什么做错了?
工作:两课方式
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
import time
import threading
from PyQt5.QtCore import QObject, pyqtSignal
import os
def main():
print('Step 1')
print(' Some logic here without QT')
print('Step 2')
print(' Launch QT app to run in background')
myapp = myImageDisplayApp()
print('Step 3')
print(' Continue some logic while QT running in background')
time.sleep(2)
print('Step 4')
print(' Update the displayed image in the QT app running in background')
myapp.emit_image_update('qt_test_static.png')
time.sleep(1)
myapp.emit_image_update('qt_test_movie.gif')
time.sleep(1)
print('Step 5')
print(' Continue logic while QT running in background')
time.sleep(3)
class myImageDisplayApp (QObject):
# Define the custom signal
# https://www.riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html#the-pyqtslot-decorator
signal_update_image = pyqtSignal(str)
def __init__ (self):
super().__init__()
self.app = QApplication(sys.argv)
# Setup the seperate thread
# https://stackoverflow.com/a/37694109/4988010
self.thread = threading.Thread(target=self.run_app_widget_in_background)
self.thread.daemon = True
self.thread.start()
def run_app_widget_in_background(self):
self.app = QApplication(sys.argv)
self.my_bg_qt_app = qtAppWidget(qt_patterndisplay_threader=self)
self.app.exec_()
def emit_image_update(self, pattern_file=None):
print('signal_image_update')
self.pattern_file = pattern_file
self.signal_update_image.emit(self.pattern_file)
class qtAppWidget (QLabel):
def __init__ (self, qt_patterndisplay_threader):
super().__init__()
# Connect the singal to slot
qt_patterndisplay_threader.signal_update_image.connect(self.updateImage)
self.app = QApplication.instance()
# Get avaliable screens/monitors
# https://doc.qt.io/qt-5/qscreen.html
# Get info on selected screen
self.screens_available = self.app.screens()
self.screen = self.screens_available[0]
self.screen_width = self.screen.size().width()
self.screen_height = self.screen.size().height()
self.setupGUI()
def setupGUI(self):
# Define a constant color images
self.pixmap = QPixmap(self.screen_width, self.screen_height)
self.pixmap.fill(QColor('blue'))
scale_window = 2
# Create QLabel object
self.app_widget = QLabel()
self.app_widget.setGeometry(0, 0, self.screen_width/scale_window , self.screen_height/scale_window) # Set the size of Qlabel to size of the screen
self.app_widget.setWindowTitle('myImageDisplayApp')
self.app_widget.setAlignment(Qt.AlignLeft | Qt.AlignTop) #https://doc.qt.io/qt-5/qt.html#AlignmentFlag-enum
self.app_widget.setPixmap(self.pixmap)
self.app_widget.show()
def updateImage(self, pattern_file=None):
print('\nPattern file: ', pattern_file)
filename, file_extension = os.path.splitext(pattern_file) # Get filename and extension https://stackoverflow.com/a/541394/4988010
self.app_widget.clear() # Clear all existing content of the QLabel
self.pattern_file = pattern_file
self.pixmap = QPixmap(self.pattern_file)
if file_extension != '.gif':
# File is a static image
print('Image is a static')
self.app_widget.setPixmap(self.pixmap)
else:
# File is a movie
print('Image is movie')
self.pixmap_mv = QMovie(self.pattern_file)
print('Check to see if gif valid: ', self.pixmap_mv.isValid())
self.app_widget.setMovie(self.pixmap_mv)
self.pixmap_mv.start()
if __name__ == "__main__":
main()
不工作:一种课堂方法
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
import time
import threading
from PyQt5.QtCore import QObject, pyqtSignal
import os
def main():
print('Step 1')
print(' Some logic here without QT')
print('Step 2')
print(' Launch QT app to run in background')
myapp = myImageDisplayApp()
print('Step 3')
print(' Continue some logic while QT running in background')
time.sleep(2)
print('Step 4')
print(' Update the displayed image in the QT app running in background')
myapp.emit_image_update('qt_test_static.png')
time.sleep(1)
myapp.emit_image_update('qt_test_movie.gif')
time.sleep(1)
print('Step 5')
print(' Continue logic while QT running in background')
time.sleep(3)
class myImageDisplayApp (QObject):
# Define the custom signal
# https://www.riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html#the-pyqtslot-decorator
signal_update_image = pyqtSignal(str)
def __init__ (self):
super().__init__()
# Connect the singal to slot
self.signal_update_image.connect(self.updateImage)
self.app = QApplication(sys.argv)
# Get avaliable screens/monitors
# https://doc.qt.io/qt-5/qscreen.html
# Get info on selected screen
self.screens_available = self.app.screens()
self.screen = self.screens_available[0]
self.screen_width = self.screen.size().width()
self.screen_height = self.screen.size().height()
# Setup the seperate thread
# https://stackoverflow.com/a/37694109/4988010
self.thread = threading.Thread(target=self.run_app_widget_in_background)
self.thread.daemon = True
self.thread.start()
def run_app_widget_in_background(self):
self.app = QApplication(sys.argv)
# self.my_bg_qt_app = qtAppWidget(qt_patterndisplay_threader=self)
self.setupGUI()
self.app.exec_()
def setupGUI(self):
# Define a constant color images
self.pixmap = QPixmap(self.screen_width, self.screen_height)
self.pixmap.fill(QColor('red'))
scale_window = 2
# Create QLabel object
self.app_widget = QLabel()
self.app_widget.setGeometry(0, 0, self.screen_width/scale_window , self.screen_height/scale_window) # Set the size of Qlabel to size of the screen
self.app_widget.setWindowTitle('myImageDisplayApp')
self.app_widget.setAlignment(Qt.AlignLeft | Qt.AlignTop) #https://doc.qt.io/qt-5/qt.html#AlignmentFlag-enum
self.app_widget.setPixmap(self.pixmap)
self.app_widget.show()
def emit_image_update(self, pattern_file=None):
print('signal_image_update')
self.pattern_file = pattern_file
self.signal_update_image.emit(self.pattern_file)
def updateImage(self, pattern_file=None):
print('\nPattern file: ', pattern_file)
filename, file_extension = os.path.splitext(pattern_file) # Get filename and extension https://stackoverflow.com/a/541394/4988010
self.app_widget.clear() # Clear all existing content of the QLabel
self.pattern_file = pattern_file
self.pixmap = QPixmap(self.pattern_file)
if file_extension != '.gif':
# File is a static image
print('Image is a static')
self.app_widget.setPixmap(self.pixmap)
else:
# File is a movie
print('Image is movie')
self.pixmap_mv = QMovie(self.pattern_file)
print('Check to see if gif valid: ', self.pixmap_mv.isValid())
self.app_widget.setMovie(self.pixmap_mv)
self.pixmap_mv.start()
if __name__ == "__main__":
main()
类似于my previous answer,您必须在另一个线程中执行繁重的任务,并使用信号来通知更改:
import sys
import time
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt, QThread, QTimer
from PyQt5.QtGui import QColor, QPixmap, QMovie
from PyQt5.QtWidgets import QApplication, QLabel
class TaskManager(QObject):
task3Finished = pyqtSignal()
task4Finished = pyqtSignal()
task5Finished = pyqtSignal()
@pyqtSlot()
def task(self):
print("Step 3")
time.sleep(2)
self.task3Finished.emit()
print("Step 4")
time.sleep(1)
self.task4Finished.emit()
print("Step 5")
time.sleep(1)
self.task5Finished.emit()
time.sleep(3)
QApplication.quit()
class qtAppWidget(QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.app = QApplication.instance()
self.screens_available = self.app.screens()
self.screen = self.screens_available[0]
self.screen_width = self.screen.size().width()
self.screen_height = self.screen.size().height()
self.setupGUI()
def setupGUI(self):
scale_window = 2
self.setGeometry(
0, 0, self.screen_width / scale_window, self.screen_height / scale_window
)
self.setWindowTitle("myImageDisplayApp")
self.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.show()
@pyqtSlot()
def on_task3_finished(self):
pixmap = QPixmap("qt_test_static.png")
self.setPixmap(pixmap)
@pyqtSlot()
def on_task4_finished(self):
movie = QMovie("qt_test_movie.gif", parent=self)
self.setMovie(movie)
movie.start()
def main(args):
print("Step 1")
print(" Some logic here without QT")
print("Step 2")
print(" Launch QT app to run")
app = QApplication(args)
myapp = qtAppWidget()
thread = QThread()
thread.start()
manager = TaskManager()
# move the QObject to the other thread
manager.moveToThread(thread)
manager.task3Finished.connect(myapp.on_task3_finished)
manager.task4Finished.connect(myapp.on_task4_finished)
# start task
QTimer.singleShot(0, manager.task)
ret = app.exec_()
thread.quit()
thread.wait()
del thread, app
return ret
if __name__ == "__main__":
sys.exit(main(sys.argv))