草庐IT

pyqt 显示图片的若干方法

十橙 2023-07-18 原文

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


单张图片

使用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_())

使用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_())

多张图片

使用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_())

使用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_()

使用 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_()

有关pyqt 显示图片的若干方法的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  5. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  6. Ruby 方法() 方法 - 2

    我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

  7. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  8. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  9. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

随机推荐