pyqt 显示图片的若干方法


date: 2022-11-30 14:23
status: public
title: ‘pyqt 显示图片的若干方法’


1 单张图片

1.1 使用lable 显示图片

特点是最简单,但功能也最少。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys

from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel


class 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_())

1.2 使用pyqtgraph 控件显示图片

来源 【PyQtGraph】显示图像
特点 可以对图片进行缩放操作,继承了pyqtgraph 的一些特点功能。

"""
安装依赖库:
1. Pillow
2. PySide2
3. PyQtGraph
from https://blog.csdn.net/zhy29563/article/details/119754910
"""

import sys

import numpy as np
import pyqtgraph as pg
from PIL import Image
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QPushButton, QWidget, QFileDialog
from 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_())

2 多张图片

2.1 使用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/116702824
import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.uic import loadUi


class 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_())

2.2 使用scrollArea + gridLayout 显示多张图片

可以缩放窗口,图像可以随着窗口变化,但只是图像间距拉伸,每行的图片数量没有变化

# -*- coding: utf-8 -*-
import glob
import time

from PyQt5 import QtWidgets
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication, QLabel, QGridLayout


class 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_()

2.3 使用 QListWidget + 自定义的 QListWidgetItem 显示多张图片

可以缩放窗口,图像可以随着窗口重新排列,自定义的QListWidgetItem 可以灵活自定义显示样式。
代码参考这两个博客 PyQt使用笔记(六) 可多选, 有右键复制删除功能的ListWidget 2021.03.23[pyqt] 使用自定义QListWidgetItem

# -*- coding: utf-8 -*-
import time

from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QSize
from PyQt5.QtCore import Qt
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QCursor
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMenu, QAbstractItemView, QListWidgetItem, QListView
from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout


class 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 继承自QListWidgetItem
class 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_path


if __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_()

代码仓库:https://github.com/hcz017/pyqt_show_img

<think>好的,我现在需要在PyQt中实现批量导入图像文件的功能。用户想要一个方法教程和示例代码。首先,我应该回忆一下PyQt相关的知识,特别是文件操作和图像显示的组件。 首先,批量导入文件通常使用QFileDialog,这个类可以打开文件选择对话框。用户可能需要选择多个文件,所以应该用getOpenFileNames而不是单个文件选择。记得设置文件过滤器,比如只允许图片格式,如.png、.jpg等。 接下来,加载图像文件的话,用户可能用的是PIL库的Image.open,或者是PyQt自己的QPixmap。不过根据用户提供的引用[1],他们提到使用PIL的Image.open方法,所以可能需要将PIL图像转换为QPixmap以便在Qt中显示。或者也可以直接用QPixmap加载文件路径,但需要注意格式支持。 然后,显示多个图像的话,可能需要一个滚动区域,里面放一个QWidget或者QGridLayout来排列图片。比如使用QScrollArea作为容器,里面放一个QWidget,再在QWidget里用QGridLayout来管理各个图像的标签和缩略图。每个图像可能需要用QLabel来显示,设置QPixmap进去,并调整大小适应。 另外,考虑到性能,如果图片数量很大,直接加载所有可能会占用过多内存。所以可能需要分页或者懒加载,但用户的问题可能暂时不需要这么复杂,先实现基础功能。 步骤大概是这样的: 1. 创建主窗口,添加按钮和滚动区域。 2. 按钮点击触发文件选择对话框,获取多个文件路径。 3. 遍历这些路径,用PIL或QPixmap加载图像,生成缩略图。 4. 将每个缩略图添加到布局中,可能用网格布局,每行显示若干张。 5. 处理图像过大问题,调整显示大小,保持比例。 示例代码方面,需要导入必要的模块,比如PyQt5的QApplication, QMainWindow, QPushButton, QLabel, QScrollArea, QFileDialog等。可能还需要用PIL的Image来处理图像,转换成QPixmap。 需要注意线程问题,如果图片很多,加载可能会阻塞界面,但用户的问题可能暂时不考虑异步加载,先同步处理。 根据引用[3],PyQt结合设计师可以提高效率,但用户这里可能需要直接编写代码的方式。所以用代码构建界面。 测试的时候,确保点击按钮能选择多个图片,正确显示在滚动区域里,布局合理,缩略图显示正确,不会卡死界面。 可能的问题:文件路径中有无法加载的图片,需要异常处理;图片方向问题;内存管理。但用户示例代码可能简化这些,先完成基本功能。 现在整理示例代码的结构: 主窗口继承自QMainWindow,中间放一个QScrollArea,里面包含一个QWidget,用网格布局。顶部有按钮,点击后触发选择文件,然后循环处理每个文件,加载并显示。 将每个图像的QLabel添加到布局中,可能需要设置固定大小,比如200x200,并保持比例缩放。 代码的大致步骤: 1. 导入模块。 2. 创建MainWindow类,初始化UI:按钮、滚动区域、布局。 3. 按钮点击连接槽函数,调用QFileDialog.getOpenFileNames。 4. 清空之前的图片(如果有的话)。 5. 遍历选中的文件,用Image.open打开,调整大小,转成QPixmap,再显示在QLabel中,添加到布局。 6. 显示窗口并运行应用。 可能需要注意PIL Image到QPixmap的转换,比如通过保存为临时字节,或者使用fromImage,但可能需要先转换成QImage。例如,使用ImageQt模块将PIL图像转为QImage,再转QPixmap。 或者,直接使用QPixmap加载文件路径,这样可能更简单,但可能不支持所有格式。需要根据用户需求,但引用[1]用了PIL,所以示例中可能使用PIL来处理,再转换。 所以,具体转换步骤: pil_image = Image.open(file_path) pil_image = pil_image.convert("RGB") # 如果需要的话 data = pil_image.tobytes("raw", "RGB") q_image = QImage(data, pil_image.width, pil_image.height, QImage.Format_RGB888) pixmap = QPixmap.fromImage(q_image) 或者更简单的方式,如果文件格式是支持的,直接用QPixmap.load(file_path)。但为了通用性,可能还是用PIL更好,比如处理不同的图像格式。 或者使用引用[1]中的方法,用PIL打开图像,然后转换为QPixmap。比如: from PIL import Image from PyQt5.QtGui import QPixmap from PyQt5.QtWidgets import QLabel input_image = Image.open(file_path) # 转换为QPixmap # 可能需要先将PIL图像转为QImage from PIL.ImageQt import ImageQt q_image = ImageQt(input_image) pixmap = QPixmap.fromImage(q_image) 这样可能更可靠。 综上,示例代码的结构应该包括这些部分。同时,为了显示多个图像,可能需要动态生成QLabel并添加到布局中。布局使用QGridLayout,每行放几列,比如每行4张图,根据窗口宽度调整。 另外,在用户提供的引用[4]中,提到了打开图像、视频等按钮的功能,可能可以参考其界面设计,添加按钮和滚动区域。 最后,确保代码能够正确运行,并处理基本的异常,比如文件无法打开的情况,但示例中可以简化处理,假设用户选择的都是有效文件。 这样,就能完成一个批量加载和显示多个图像文件的PyQt应用示例。</think>### 使用 PyQt 实现批量加载和显示图像文件的方法 #### 实现步骤 1. **创建主窗口**:添加按钮和滚动区域容器 2. **文件选择功能**:通过 `QFileDialog` 选择多个图像文件 3. **图像加载与转换**:使用 PIL 库加载图像并转换为 `QPixmap` 格式[^1] 4. **动态布局管理**:使用 `QGridLayout` 自动排列图像缩略图 5. **滚动显示优化**:通过 `QScrollArea` 支持大量图像的滚动浏览 #### 示例代码 ```python import sys from PyQt5.QtWidgets import ( QApplication, QMainWindow, QPushButton, QLabel, QScrollArea, QWidget, QGridLayout, QFileDialog ) from PyQt5.QtGui import QPixmap from PyQt5.QtCore import Qt from PIL import Image from PIL.ImageQt import ImageQt class ImageViewer(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): # 窗口基础设置 self.setWindowTitle("批量图像查看器") self.setGeometry(100, 100, 800, 600) # 创建滚动区域 scroll_area = QScrollArea() scroll_widget = QWidget() self.grid_layout = QGridLayout(scroll_widget) scroll_area.setWidget(scroll_widget) scroll_area.setWidgetResizable(True) self.setCentralWidget(scroll_area) # 添加操作按钮 btn_load = QPushButton("导入图像", self) btn_load.clicked.connect(self.load_images) self.grid_layout.addWidget(btn_load, 0, 0) def load_images(self): # 清空现有内容(保留按钮) for i in reversed(range(self.grid_layout.count())): if i != 0: # 跳过第一个按钮 self.grid_layout.itemAt(i).widget().deleteLater() # 选择多个文件 files, _ = QFileDialog.getOpenFileNames( self, "选择图像文件", "", "图像文件 (*.png *.jpg *.jpeg *.bmp)" ) # 动态加载图像 row, col = 1, 0 max_columns = 4 # 每行显示4张图 for idx, file_path in enumerate(files): try: # 使用PIL加载图像 pil_image = Image.open(file_path) pil_image.thumbnail((200, 200)) # 生成缩略图 # 转换为QPixmap qimage = ImageQt(pil_image.convert("RGBA")) pixmap = QPixmap.fromImage(qimage) # 创建显示标签 label = QLabel() label.setPixmap(pixmap) label.setAlignment(Qt.AlignCenter) label.setStyleSheet("border: 1px solid #999; margin: 5px;") # 添加到网格布局 self.grid_layout.addWidget(label, row, col) col = (col + 1) % max_columns if col == 0: row += 1 except Exception as e: print(f"加载失败:{file_path}\n错误信息:{str(e)}") if __name__ == "__main__": app = QApplication(sys.argv) window = ImageViewer() window.show() sys.exit(app.exec_()) ``` #### 关键功能说明 1. **文件选择对话框**: - 使用 `QFileDialog.getOpenFileNames` 实现多选 - 过滤非图像文件类型(支持PNG/JPG/BMP等) 2. **图像处理优化**: - 通过 `pil_image.thumbnail((200,200))` 生成统一尺寸的缩略图 - 使用 `QGridLayout` 自动换行布局[^3] 3. **滚动显示机制**: - `QScrollArea` 容器支持大量图像的垂直滚动 - `setWidgetResizable(True)` 确保布局自适应窗口大小
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值