【发布时间】:2020-07-19 05:44:56
【问题描述】:
我创建了一个简单的应用程序,它使用 PyQt5 在 QLabel 中显示图像。图片可以是静态的(例如:png、jpeg、bmp),也可以是 gif。
下面的示例代码说明如下:
-
类ImageDisplayer() 负责创建一个QLabel,其中包含要显示的所需图像。 update_image() 方法允许将 QLabel 中显示的图像更新为所需的新图像。 QLabel 窗口显示在所需的屏幕上(使用多台显示器时)。
-
main() 方法是一个 PyQt5 应用程序的简单演示,它使用 ImageDisplayer 类在 QLabel 上显示所需的图像。在现实世界的最终用例中,这个主要的 Qt 应用程序将具有其他复杂的小部件/逻辑来与用户交互(例如:询问用户要显示哪个图像),并且从 ImageDisplayer 显示的 QLabel 将始终全屏显示所需的图像在辅助监视器上。但是,为简单起见,我没有在下面的示例代码中展示这一点。
-
test_image_sequence() 方法是一个简单的函数,它循环遍历各种测试图像以调试/排除 ImageDisplayer() 类的开发问题。
问题: 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()
【问题讨论】:
标签: python multithreading pyqt pyqt5