我创建了一个简单的应用程序,该应用程序使用PyQt5在QLabel中显示图像。图像可以是静态的(例如:png,jpeg,bmp),也可以是gif。
下面的示例代码的说明如下:
问题: ImageDisplayer类按预期工作,但是,当我尝试从单独的线程调用update_image()方法时,gif图像没有动画。例如,当我使用QThreadPool在单独的线程中运行test_image_sequence()方法时,静态图像将按预期显示,但gif不会显示动画。
import os, sys, time, pathlib
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt, QRunnable, QThreadPool
from PyQt5.QtGui import QColor, QPixmap, QMovie
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
CURRENT_PATH = str(pathlib.Path(__file__).parent.absolute())
def main():
app = QtWidgets.QApplication(sys.argv)
my_image_window = ImageDisplayer(monitor_num=0,)
# Method 1: When not using threads, the gif animates as expected
# my_image_window.update_image(CURRENT_PATH + r'\test_images\gif_image_2.gif')
# Method 2: When using threads, gif does NOT animate
thread = QThreadPool.globalInstance()
worker = Worker(test_image_sequence, my_image_window)
thread.start(worker)
app.exec_()
def test_image_sequence(widget):
print('Will start testing seq. of images')
time.sleep(1)
images = []
images.append(CURRENT_PATH + r'\test_images\static_image_1.png')
images.append(CURRENT_PATH + r'\test_images\static_image_2.png')
images.append(CURRENT_PATH + r'\test_images\gif_image_1.gif')
images.append(CURRENT_PATH + r'\test_images\gif_image_2.gif')
for i in images:
print('Updating image to:', i)
widget.update_image(pattern_file=i)
time.sleep(3)
class ImageDisplayer():
def __init__(self, monitor_num=0,):
# Get instance of the current QApplication
self.app = QtWidgets.QApplication.instance() #https://stackoverflow.com/a/53387775/4988010
# Gather info on avaliable monitor and select the desired one
self.screen = self.app.screens()[monitor_num]
self.screen_width = self.screen.size().width()
self.screen_height = self.screen.size().height()
# Init class attributes
self.pattern_file = None # Set a default pattern if given during init
self.pixmap = None # Static image content
self.pixmap_mv = None # Movie content
self.scale_window = 2 # For debugging: If not full screen the window will be scaled by half of screen size
# Define a constant color images when no image displayed
self.pixmap_blank = QPixmap(self.screen_width, self.screen_height)
self.pixmap_blank.fill(QColor('green'))
self.pixmap = self.pixmap_blank # Default during init
self.app_widget = None # QLabel widget object
self.setupGUI() # Setup and show the widget
def setupGUI(self):
print('Setting up the QLabel')
# Create QLabel object
self.app_widget = QLabel()
self.app_widget.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.app_widget.setStyleSheet('QLabel { background-color: green;}')
self.app_widget.setCursor(Qt.BlankCursor) # A blank/invisible cursor, typically used when the cursor shape needs to be hidden.
# Scale the widget to the size of the screen
self.app_widget.setGeometry(0, 0, self.screen_width/self.scale_window , self.screen_height/self.scale_window) # Set the size of Qlabel to size of the screen
# Set a default pattern during init
self.app_widget.setPixmap(self.pixmap)
self.app_widget.show()
# Move window to topleft corner of the selected screen
self.app_widget.windowHandle().setScreen(self.screen)
self.app_widget.move(self.screen.geometry().topLeft())
def update_image(self, pattern_file):
self.pattern_file = pattern_file
print('Pattern 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.pixmap = QPixmap(self.pattern_file)
if (file_extension == '.png') or (file_extension == '.jpg') or (file_extension == '.jpeg') or (file_extension == '.bmp'):
# File is a static image
# https://doc.qt.io/qt-5/qpixmap.html
print('Image is a static')
self.app_widget.setPixmap(self.pixmap)
elif (file_extension == '.gif'):
# File is a movie
print('Image is movie')
self.pixmap_mv = QMovie(self.pattern_file)
# Connect the "finished() signal to movie_finished() slot"
self.pixmap_mv.finished.connect(self.movie_finished)
# Debugging text
print('Movie is valid: ', self.pixmap_mv.isValid())
print('loopCount: ', self.pixmap_mv.loopCount())
print('frameCount: ', self.pixmap_mv.frameCount())
print('Default speed: ', self.pixmap_mv.speed())
self.app_widget.setMovie(self.pixmap_mv)
self.pixmap_mv.start()
def movie_finished(self):
print('Movie finished')
# After movie is finished, show blank screen
self.app_widget.setPixmap(self.pixmap_blank)
class Worker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
def run(self):
self.fn(*self.args, **self.kwargs)
if __name__ == "__main__":
main()
最佳答案
基本的Qt规则:您不应直接从另一个线程修改GUI,因为它不是线程安全的,而应使用信号。例如,在这种情况下,您可以创建一个作为代理的类:
# ...
class Signaller(QObject):
imageChanged = pyqtSignal(str)
def update_image(self, pattern_file):
self.imageChanged.emit(pattern_file)
def main():
app = QtWidgets.QApplication(sys.argv)
my_image_window = ImageDisplayer(monitor_num=0,)
signaller = Signaller()
signaller.imageChanged.connect(my_image_window.update_image)
thread = QThreadPool.globalInstance()
worker = Worker(test_image_sequence, signaller)
thread.start(worker)
app.exec_()
# ...
关于python - 从另一个线程更新QLabel的内容时,GIF不会进行动画处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62976727/