如何在更新标签 Pyqt5 上的图像(opencv)时调用其他函数?
How to call other functions while update image(opencv) on a label Pyqt5?
我想用图像更新标签,但是当我开始更新标签时,在更新完成之前没有任何效果,例如我想暂停更新或关闭 window。当我使用 tkinter 时它工作得很好但它在 Pyqt5 中不起作用。这是我的代码
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog, QDialog, QMainWindow,QMessageBox,QGroupBox,QSlider,QPushButton,QRadioButton,QLabel,QCheckBox,QFrame,QWidget,QTabWidget,QProgressBar,QTextBrowser,QTableWidget
from PyQt5.QtCore import QCoreApplication, Qt, QSize,QThread, QRect,QSize
from PyQt5.QtGui import QIcon, QFont,QPixmap,QIcon
import cv2
import os
import glob
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(800, 800)
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(165, 125, 61, 16))
self.label.setObjectName("label")
self.btn1 = QPushButton(Form)
self.image_frame = QtWidgets.QLabel(Form)
self.btn1.setGeometry(QRect(350, 200, 111, 31))
self.btn1.clicked.connect(self.load_images_from_folder)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.btn1.setText(_translate("Form","Btn"))
def load_images_from_folder(self):
for filename in glob.glob('path/*.jpg'):
img = cv2.imread(filename, cv2.IMREAD_UNCHANGED)
img = cv2.resize(img,(200,200))
print(filename)
self.showimg(img)
def showimg(self,img):
self.image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
self.image = QtGui.QImage(self.image.data, self.image.shape[1], self.image.shape[0], QtGui.QImage.Format_RGB888).rgbSwapped()
self.image_frame.setPixmap(QtGui.QPixmap.fromImage(self.image))
self.image_frame.setGeometry(QRect(70, 310, 461, 441))
Form.update()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
点击按钮标签更新后,我无法再次点击按钮,直到更新完成
在 UI 环境中,必须尽快将控制返回到主线程。如果你有很多图像处理,这意味着加载图像的函数(它也调用设置图像的函数)在完成之前不会将控制权释放给主线程。
如果不这样做,结果是 UI 将完全冻结(没有更新,没有用户输入),直到 returns。
解决方案是使用线程,但是还需要考虑到涉及UI个元素,不允许访问UI个元素从外螺纹。因此,actual 解决方案是使用 QThreads 和自定义信号:Qt 信号是线程安全的,并且它们是 queued 只要两个objects 属于不同的线程正在尝试通信。
在这种特定情况下,线程将是具有自定义信号的 QThread(每当新图像准备就绪时必须发出):
class ImageLoader(QtCore.QThread):
imageReady = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self.images = []
def run(self):
while True:
while self.images:
img = cv2.imread(self.images.pop(), cv2.IMREAD_UNCHANGED)
img = cv2.resize(img,(200,200))
self.imageReady.emit(img)
def loadImages(self, imageList):
# if you want to *clear* the current queue
self.images[:] = imageList
# otherwise, just append:
# self.images.extend(imageList)
然后实现需要创建线程实例并将图像添加到 load_images_from_folder
中的 queue:
class Ui_Form(object):
def setupUi(self, Form):
# ...
self.imageLoader = ImageLoader()
self.imageLoader.imageReady.connect(self.showimg)
self.imageLoader.start()
def load_images_from_folder(self):
self.imageLoader.loadImages(glob.glob('path/*.jpg'))
重要提示:
- 编辑使用
pyuic
生成的文件被认为是 糟糕的 做法,因为它几乎总是会导致难以追踪的意外行为和错误;这些文件的 header 中的注释非常明确:您应该 不 编辑它们;相反,创建这些文件,保持原样,然后将它们导入到您的主脚本中,如关于 using Designer; 的官方指南中所述
- 访问
Form
(Form.update()
),这基本上是一个 全局 变量,是另一种不好的做法;就像编辑 pyuic 文件一样,这应该 永远不会 完成,除非你 真的 知道你在做什么(如果你知道,你可能根本不会这样做);在任何情况下,在标签上设置像素图会自动安排更新,因此函数调用完全无用,因为我们正确使用了线程,这允许主事件循环请求 UI;[=53 上的更新=]
- 设置固定几何图形是但另一种通常不鼓励的做法;学习如何使用 layout managers and how to use them in Designer 代替;如果您希望 QLabel 具有固定大小,请使用
setFixedSize()
;
我想用图像更新标签,但是当我开始更新标签时,在更新完成之前没有任何效果,例如我想暂停更新或关闭 window。当我使用 tkinter 时它工作得很好但它在 Pyqt5 中不起作用。这是我的代码
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog, QDialog, QMainWindow,QMessageBox,QGroupBox,QSlider,QPushButton,QRadioButton,QLabel,QCheckBox,QFrame,QWidget,QTabWidget,QProgressBar,QTextBrowser,QTableWidget
from PyQt5.QtCore import QCoreApplication, Qt, QSize,QThread, QRect,QSize
from PyQt5.QtGui import QIcon, QFont,QPixmap,QIcon
import cv2
import os
import glob
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(800, 800)
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(165, 125, 61, 16))
self.label.setObjectName("label")
self.btn1 = QPushButton(Form)
self.image_frame = QtWidgets.QLabel(Form)
self.btn1.setGeometry(QRect(350, 200, 111, 31))
self.btn1.clicked.connect(self.load_images_from_folder)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.btn1.setText(_translate("Form","Btn"))
def load_images_from_folder(self):
for filename in glob.glob('path/*.jpg'):
img = cv2.imread(filename, cv2.IMREAD_UNCHANGED)
img = cv2.resize(img,(200,200))
print(filename)
self.showimg(img)
def showimg(self,img):
self.image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
self.image = QtGui.QImage(self.image.data, self.image.shape[1], self.image.shape[0], QtGui.QImage.Format_RGB888).rgbSwapped()
self.image_frame.setPixmap(QtGui.QPixmap.fromImage(self.image))
self.image_frame.setGeometry(QRect(70, 310, 461, 441))
Form.update()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
点击按钮标签更新后,我无法再次点击按钮,直到更新完成
在 UI 环境中,必须尽快将控制返回到主线程。如果你有很多图像处理,这意味着加载图像的函数(它也调用设置图像的函数)在完成之前不会将控制权释放给主线程。
如果不这样做,结果是 UI 将完全冻结(没有更新,没有用户输入),直到 returns。
解决方案是使用线程,但是还需要考虑到涉及UI个元素,不允许访问UI个元素从外螺纹。因此,actual 解决方案是使用 QThreads 和自定义信号:Qt 信号是线程安全的,并且它们是 queued 只要两个objects 属于不同的线程正在尝试通信。
在这种特定情况下,线程将是具有自定义信号的 QThread(每当新图像准备就绪时必须发出):
class ImageLoader(QtCore.QThread):
imageReady = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self.images = []
def run(self):
while True:
while self.images:
img = cv2.imread(self.images.pop(), cv2.IMREAD_UNCHANGED)
img = cv2.resize(img,(200,200))
self.imageReady.emit(img)
def loadImages(self, imageList):
# if you want to *clear* the current queue
self.images[:] = imageList
# otherwise, just append:
# self.images.extend(imageList)
然后实现需要创建线程实例并将图像添加到 load_images_from_folder
中的 queue:
class Ui_Form(object):
def setupUi(self, Form):
# ...
self.imageLoader = ImageLoader()
self.imageLoader.imageReady.connect(self.showimg)
self.imageLoader.start()
def load_images_from_folder(self):
self.imageLoader.loadImages(glob.glob('path/*.jpg'))
重要提示:
- 编辑使用
pyuic
生成的文件被认为是 糟糕的 做法,因为它几乎总是会导致难以追踪的意外行为和错误;这些文件的 header 中的注释非常明确:您应该 不 编辑它们;相反,创建这些文件,保持原样,然后将它们导入到您的主脚本中,如关于 using Designer; 的官方指南中所述
- 访问
Form
(Form.update()
),这基本上是一个 全局 变量,是另一种不好的做法;就像编辑 pyuic 文件一样,这应该 永远不会 完成,除非你 真的 知道你在做什么(如果你知道,你可能根本不会这样做);在任何情况下,在标签上设置像素图会自动安排更新,因此函数调用完全无用,因为我们正确使用了线程,这允许主事件循环请求 UI;[=53 上的更新=] - 设置固定几何图形是但另一种通常不鼓励的做法;学习如何使用 layout managers and how to use them in Designer 代替;如果您希望 QLabel 具有固定大小,请使用
setFixedSize()
;