date: 2022-11-30 14:23
status: public
title: ‘pyqt 显示图片的若干方法’
单张图片
使用lable 显示图片
特点是最简单,但功能也最少。
#!/usr/bin/env python# -*- coding: utf-8 -*-import sysfrom PyQt5.QtGui import QPixmapfrom PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabelclass ImageLabel(QWidget): def __init__(self, parent=None): super().__init__(parent) self.resize(600, 400) self.setWindowTitle("label image") pix = QPixmap(r'C:\fruits.jpg') label = QLabel(self) label.setPixmap(pix) label.setScaledContents(True) # 自适应QLabel大小 layout = QVBoxLayout() layout.addWidget(label) self.setLayout(layout)if __name__ == '__main__': app = QApplication(sys.argv) mainWidget = ImageLabel() mainWidget.show() sys.exit(app.exec_())
使用pyqtgraph 控件显示图片
来源 【PyQtGraph】显示图像
特点 可以对图片进行缩放操作,继承了pyqtgraph 的一些特点功能。
"""安装依赖库:1. Pillow2. PySide23. PyQtGraphfrom https://blog.csdn.net/zhy29563/article/details/119754910"""import sysimport numpy as npimport pyqtgraph as pgfrom PIL import Imagefrom PyQt5.QtWidgets import QApplication, QVBoxLayout, QPushButton, QWidget, QFileDialogfrom pyqtgraph import ImageView# 设置 PyQtGraph 显示配置######################################################################################################################### 设置显示背景色为白色,默认为黑色pg.setConfigOption('background', 'w')# 设置显示前景色为黑色,默认为灰色pg.setConfigOption('foreground', 'k')# 设置图像显示以行为主,默认以列为主pg.setConfigOption('imageAxisOrder', 'row-major')class PyQtGraphicDemo(QWidget): def __init__(self, parent=None): super(PyQtGraphicDemo, self).__init__(parent) self.resize(600, 400) # 图像显示控件 self.graphicsView = ImageView(self) # 隐藏直方图,菜单按钮,ROI self.graphicsView.ui.histogram.hide() self.graphicsView.ui.menuBtn.hide() self.graphicsView.ui.roiBtn.hide() image = Image.open(r'C:\fruits.jpg') if image is not None: # 如果之前未设置显示选项以行为主,这里需要对显示图像进行转置 self.graphicsView.setImage(np.array(image)) self.verticalLayout = QVBoxLayout(self) self.verticalLayout.addWidget(self.graphicsView) # 设置窗口布局 self.setLayout(self.verticalLayout)if __name__ == '__main__': app = QApplication(sys.argv) window = PyQtGraphicDemo() window.show() sys.exit(app.exec_())
多张图片
使用scrollArea 显示多张图片
来源:PyQt5-使用scrollArea实现图片查看器功能
特点是当窗口大小小于scrollArea 区域大小时有滑动条显示,可以拖动滑动条滑动界面。
但是这份代码有个缺点,就是当窗口大小大于scrollArea 区域大小时,你会发现scrollArea 以外的区域是空白的,也就是scrollArea 是固定大小的,区域外不会显示内容。注释掉 self.setFixedSize(850, 600) 可以测试看到。
这份代码的显示原理大致如下:创建一个scrollArea控件,对多张图像依次执行下面循环的操作:1. 创建一个label 显示image;2. label 添加到 一个QVBoxLayout 中,3. QVBoxLayout 作为一个临时的QWidget layout,4. 移动这个临时的 QWidget 到指定坐标。emmm 就不是很优雅。
# from https://blog.csdn.net/HG0724/article/details/116702824import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *from PyQt5.uic import loadUiclass Picture(QMainWindow): def __init__(self, parent=None, url=None): super().__init__(parent) self.url = url self.ui() def ui(self): loadUi('./show_pic.ui', self) # self.setFixedSize(850, 600) total = len(self.url) self.qw = QWidget() if total % 5 == 0: rows = int(total / 5) else: rows = int(total / 5) + 1 self.qw.setMinimumSize(850, 230 * rows) for i in range(total): photo = QPixmap(self.url[i]) # print('photo:',photo) # photo.loadFromData(req.content) width = photo.width() height = photo.height() print('width:', width, ' ', 'height:', height) if width == 0 or height == 0: continue tmp_image = photo.toImage() # 将QPixmap对象转换为QImage对象 size = QSize(width, height) # photo.convertFromImage(tmp_image.scaled(size, Qt.IgnoreAspectRatio)) photo = photo.fromImage(tmp_image.scaled(size, Qt.IgnoreAspectRatio)) # 为每个图片设置QLabel容器 label = QLabel() label.setFixedSize(150, 200) label.setStyleSheet("border:1px solid gray") label.setPixmap(photo) label.setScaledContents(True) # 图像自适应窗口大小 vl = QVBoxLayout() vl.addWidget(label) tmp = QWidget(self.qw) tmp.setLayout(vl) tmp.move(160 * (i % 5), 230 * int(i / 5)) self.scrollArea.setWidget(self.qw) # 和ui文件中名字相同if __name__ == '__main__': app = QApplication(sys.argv) # 这是我的文件夹中图片的路径 import glob url = glob.glob(r"C:\waDump\*.jpg") pic = Picture(url=url) pic.show() sys.exit(app.exec_())
使用scrollArea + gridLayout 显示多张图片
可以缩放窗口,图像可以随着窗口变化,但只是图像间距拉伸,每行的图片数量没有变化
# -*- coding: utf-8 -*-import globimport timefrom PyQt5 import QtWidgetsfrom PyQt5.QtCore import QSize, Qtfrom PyQt5.QtGui import QPixmapfrom PyQt5.QtWidgets import QApplication, QLabel, QGridLayoutclass Picture(QtWidgets.QWidget): def __init__(self, parent=None): super(Picture, self).__init__(parent) print('Picture init') self.setWindowTitle('All Images') self.resize(800, 600) # ui components self.scrollArea = QtWidgets.QScrollArea() self.scrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QtWidgets.QWidget() # self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.gridLayout = QGridLayout(self.scrollAreaWidgetContents) self.v_layout = QtWidgets.QVBoxLayout(self) self.v_layout.addWidget(self.scrollArea) self.setLayout(self.v_layout) # vars self.max_columns = 5 def load_images(self, paths): print('load images --start') total = len(paths) col = 0 row = 0 for i in range(total): self.max_columns = total if total < 5 else 5 photo = QPixmap(paths[i]) width = photo.width() height = photo.height() if width == 0 or height == 0: continue tmp_image = photo.toImage() # 将QPixmap对象转换为QImage对象 size = QSize(width, height) # photo.convertFromImage(tmp_image.scaled(size, Qt.IgnoreAspectRatio)) photo = photo.fromImage(tmp_image.scaled(size, Qt.IgnoreAspectRatio)) # 为每个图片设置QLabel容器 label = QLabel() w = int(self.width() / self.max_columns * 0.8) h = int(w * photo.height() / photo.width()) label.setFixedSize(w, h) label.setStyleSheet("border:1px solid gray") label.setPixmap(photo) label.setScaledContents(True) # 图像自适应窗口大小 self.gridLayout.addWidget(label, row, col) # 计算下一个label 位置 if col < self.max_columns - 1: col = col + 1 else: col = 0 row += 1 print('load images --end')if __name__ == '__main__': start_time = time.time() print('main layout show') app = QApplication([]) main_window = Picture() main_window.show() image_list = url = glob.glob(r"C:\waDump\*.jpg") # 加载图像显示 main_window.load_images(image_list) print("耗时: {:.3f}秒".format(time.time() - start_time)) app.exec_()
使用 QListWidget + 自定义的 QListWidgetItem 显示多张图片
可以缩放窗口,图像可以随着窗口重新排列,自定义的QListWidgetItem 可以灵活自定义显示样式。
代码参考这两个博客 PyQt使用笔记(六) 可多选, 有右键复制删除功能的ListWidget 2021.03.23 和 [pyqt] 使用自定义QListWidgetItem
# -*- coding: utf-8 -*-import timefrom PyQt5 import QtWidgets, QtCore, QtGuifrom PyQt5.QtCore import QSizefrom PyQt5.QtCore import Qtfrom PyQt5.QtCore import pyqtSignalfrom PyQt5.QtGui import QCursorfrom PyQt5.QtGui import QPixmapfrom PyQt5.QtWidgets import QApplicationfrom PyQt5.QtWidgets import QMenu, QAbstractItemView, QListWidgetItem, QListViewfrom PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayoutclass ImageListWidget(QtWidgets.QListWidget): signal = pyqtSignal(list) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.image_cmp_widget = None self.single_image = None self.setWindowTitle('All Images') self.resize(1400, 700) self.setContextMenuPolicy(Qt.CustomContextMenu) # 创建QMenu信号事件 self.customContextMenuRequested.connect(self.showMenu) self.contextMenu = QMenu(self) self.CMP = self.contextMenu.addAction('比较') # self.CP = self.contextMenu.addAction('复制') self.DL = self.contextMenu.addAction('删除') # self.CP.triggered.connect(self.copy) self.DL.triggered.connect(self.del_text) # 设置每个item size self.setGridSize(QtCore.QSize(220, 190)) # 设置横向list self.setFlow(QListView.LeftToRight) # 设置换行 self.setWrapping(True) # 窗口size 变化后重新计算列数 self.setResizeMode(QtWidgets.QListView.Adjust) # 设置选择模式 self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.setIconSize(QSize(200, 150)) # 显示右键菜单 def showMenu(self, pos): # pos 鼠标位置 # 菜单显示前,将它移动到鼠标点击的位置 self.contextMenu.exec_(QCursor.pos()) # 在鼠标位置显示 # 获取选择行的内容 def selected_text(self): try: selected = self.selectedItems() texts = '' for item in selected: if texts: texts = texts + '\n' + item.text() else: texts = item.text() except BaseException as e: print(e) return print('selected_text texts', texts) return texts def copy(self): text = self.selected_text() if text: clipboard = QApplication.clipboard() clipboard.setText(text) def del_text(self): try: index = self.selectedIndexes() row = [] for i in index: r = i.row() row.append(r) for i in sorted(row, reverse=True): self.takeItem(i) except BaseException as e: print(e) return self.signal.emit(row) def mouseDoubleClickEvent(self, e: QtGui.QMouseEvent) -> None: super().mouseDoubleClickEvent(e) print('double click') selected = self.selectedItems() img_path = '' for item in selected: img_path = item.image_path() if len(img_path) > 0: # 打开新窗口显示单张图片 # self.single_image = SingleImageView(image=img_path, background=Qt.white) # self.single_image.show() pass pass def load_images(self, paths): for i in range(len(paths)): img_item = ImageQListWidgetItem("dump image ***", paths[i]) self.addItem(img_item) self.setItemWidget(img_item, img_item.widget) # 刷新界面 QApplication.processEvents()# 自定义的item 继承自QListWidgetItemclass ImageQListWidgetItem(QListWidgetItem): def __init__(self, name, img_path): super().__init__() self.img_path = img_path # 自定义item中的widget 用来显示自定义的内容 self.widget = QWidget() # 用来显示name self.nameLabel = QLabel() self.nameLabel.setText(name) # 用来显示avator(图像) self.avatorLabel = QLabel() # 设置图像源 和 图像大小 img_obg = QPixmap(img_path) width = img_obg.width() height = img_obg.height() scale_size = QSize(200, 150) if width < height: scale_size = QSize(150, 200) self.avatorLabel.setPixmap(QPixmap(img_path).scaled(scale_size)) # 图像自适应窗口大小 self.avatorLabel.setScaledContents(True) # 设置布局用来对nameLabel和avatorLabel进行布局 self.hbox = QVBoxLayout() self.hbox.addWidget(self.avatorLabel) self.hbox.addWidget(self.nameLabel) self.hbox.addStretch(1) # 设置widget的布局 self.widget.setLayout(self.hbox) # 设置自定义的QListWidgetItem的sizeHint,不然无法显示 self.setSizeHint(self.widget.sizeHint()) def image_path(self): return self.img_pathif __name__ == '__main__': print('main layout show') now = time.time() app = QApplication([]) main_window = ImageListWidget() main_window.show() image_list = ['icon.jpg', 'icon.jpg', 'icon.jpg'] # 数据扩充 image_list = image_list + image_list + image_list + image_list main_window.load_images(image_list) # 绑定点击槽函数 点击显示对应item中的name main_window.itemClicked.connect(lambda item: print('clicked item label:', item.nameLabel.text())) print("ImageListWidget 耗时: {:.2f}秒".format(time.time() - now)) app.exec_()