【发布时间】:2020-02-05 17:00:30
【问题描述】:
我有一个小的 GUI,在显示的图像上显示不同的层。在某些时候,我想将带有所有可视化图层的当前图像存储到磁盘,同时继续使用 GUI。图像相当大(存储大约需要 5 秒),所以我想将保存的内容卸载到后台线程中。
我尝试了不同的方法,但都没有奏效。最小的工作示例(仍然需要 PNG 进行测试,抱歉):
import sys
import threading
from PIL import Image
from PIL.ImageQt import ImageQt
from PyQt5.QtCore import QThread, pyqtSignal, QRunnable, QThreadPool
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel, QSizePolicy, QAction, QToolBar
class StorageQRunnable(QRunnable):
def __init__(self,
pixmap: QPixmap,
target_path: str):
super(StorageQRunnable, self).__init__()
self.pixmap = pixmap
self.target_path = target_path
def run(self):
print("Starting to write image in QRunnable.")
self.pixmap.save(self.target_path, "PNG")
print("Done writing image in QRunnable.")
class StorageQThread(QThread):
signal = pyqtSignal("PyQt_PyObject")
def __init__(self,
pixmap: QPixmap,
target_path: str):
super(StorageQThread, self).__init__()
self.pixmap = pixmap
self.target_path = target_path
def run(self):
print("Starting to write image in QThread.")
self.pixmap.save(self.target_path, "PNG")
print("Done writing image in QThread.")
self.signal.emit(0)
class StorageThread(threading.Thread):
def __init__(self,
pixmap: QPixmap,
target_path: str):
super(StorageThread, self).__init__()
self.pixmap = pixmap
self.target_path = target_path
def run(self):
print("Starting to write image in threading.Thread.")
self.pixmap.save(self.target_path, "PNG")
print("Done writing image in threading.Thread.")
class TrialWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(TrialWindow, self).__init__(*args, **kwargs)
self.imageLabel = QLabel()
self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.imageLabel.setScaledContents(True)
self.setCentralWidget(self.imageLabel)
toolbar = QToolBar("Controls")
toolbar.addAction(QAction("Do", self, shortcut="Ctrl+F", triggered=self.continue_in_foreground))
toolbar.addAction(QAction("To background", self, shortcut="Ctrl+B", triggered=self.to_background))
toolbar.addAction(QAction("Exit", self, shortcut="Ctrl+Q", triggered=self.close))
self.addToolBar(toolbar)
self.bg_task_started = False
def continue_in_foreground(self):
print("Doing.")
def thread_done(self, status: int):
if status == 0:
print(":)")
def to_background(self):
print("Background.")
if not self.bg_task_started:
# print("I Pushing threading.Thread to background.")
# StorageThread(pixmap=self.imageLabel.pixmap().copy(), target_path="/tmp/target1.png").start()
# print("II Pushing QThread to background.")
# self.w2 = StorageQThread(pixmap=self.imageLabel.pixmap().copy(), target_path="/tmp/target1.png")
# self.w2.signal.connect(self.thread_done)
# self.w2.start()
print("III Pushing QRunnable to background.")
r = StorageQRunnable(pixmap=pixmap.copy(), target_path="/tmp/target1.png")
QThreadPool.globalInstance().start(r)
self.bg_task_started = True
def visualize(self, pxmp: QPixmap):
self.imageLabel.setPixmap(pxmp)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = TrialWindow()
# load pixmap
img = Image.open("/tmp/sample.png")
image = ImageQt(img)
pixmap = QPixmap.fromImage(image)
window.show()
window.visualize(pixmap)
sys.exit(app.exec_())
简短手册:
- 开始
- 按 Ctrl+F 可查看来自 GUI 线程的控制台输出(一次或多次)
- 按 Ctrl+B 开始在后台线程中存储大 PNG
- 继续按Ctrl+F,看到图像被存储之前什么都没有发生,所有事件都只在之后处理(GUI不可用)
- 通过Ctrl+Q退出
随意评论不同的方法,即threading.Thread的用法,QThread的用法或QRunnable的用法,所有结果都相同:将像素图存储为PNG(这实际上应该发生在后台线程中)会阻塞 GUI。
【问题讨论】:
-
我知道它失败了,因为 GUI 没有响应,而线程应该在后台运行。无论我(尝试)是否在后台线程中运行像素图存储,程序的行为都是相同的; pixmap-storage 调用被阻塞。这是我不想要的。
-
18MegaPixel, RGB
-
upload.wikimedia.org/wikipedia/commons/9/90/… 这应该可以解决问题,至少在将其存储为 PNG 之后。否则我明天一回来就必须提供一个 png。
标签: python python-3.x multithreading pyqt pyqt5