百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

实战PyQt5: 121-使用QImage实现一个看图应用

ztj100 2025-07-10 22:13 5 浏览 0 评论

QImage简介

QImage类提供了独立于硬件的图像表示形式,该图像表示形式可以直接访问像素数据,并且可以用作绘制设备。QImage是QPaintDevice子类,因此可以使用QPainter直接在图像上绘制。在QImage上使用QPainter时,可以在当前GUI线程以外的其他线程中执行绘制。

QImage提供了多种方式来读取图像文件,在创建QImage对象时可以加载图像文件,也可以在创建对象之后,使用load()或者loadFrameData()函数来加载图像。加载图像时,文件名可以是磁盘上的实际文件,也可以是嵌入到应用程序的资源。

QImage类支持包括单色,8位,32位和alpha混合图像。QImage提供了一组函数用于获取有关图像的各种信息和实现图像转换。

  • 几何尺寸信息: 函数size(), width(), height(), dotsPerMeterX()和dotsPerMeterY()提供了图像的尺寸和纵横比信息。rect()函数返回图像的包围矩形,vaild()函数用于判断一组坐标是否位于这个包围矩形内。
  • 色彩信息: 使用pixel()函数可以获得指定像素的颜色,该颜色是一个独立于文件格式的QRgb值。对于单色或者8位图像,colorCount()和colorTable() 函数提供了有关用于存储图数据的颜色分量的信息。hasAlphaChannel()返回函数是否有alpha通道的信息。
  • 文本信息: text() 函数返回给定文本关键值的图像文本信息。函数textKeys()返回图像的文本关键值信息。
  • 底层信息: depth()函数返回图像的位深度信息。图像支持的深度信息把包括1(单色),8、16、24和32位。bitPlaneCount()返回图像的位平面数。

像素操作

QImage的像素操作功能取决于图像格式而有所不同,原因是单色和8位图像的颜色是基于索引值,使用颜色查找表来获得,而32位图像则直接存储ARGB值。

如果是32位图像,则可以使用setPixel()函数为给定的像素设置一个ARGB颜色值。例如:

image = QImage(3, 3, QImage.Format_RGB32)
 
value = qRgb(180, 149, 39)  # 0xffbd9527
image.setPixel(1, 1, value)
 
value = qRgb(122, 163, 39)  # 0xff7aa327
image.setPixel(0, 1, value)
image.setPixel(1, 0, value)
 
value = qRgb(237, 187, 51)  # 0xffedba31
image.setPixel(2, 1, value)

用一个方格表示一个像素,上面代码生成的图像可以用下图来描述:

对于8位和单色图像,像素值为图像颜色表中的索引值,因此setPixel()函数只能用于将给定位置的像素颜色设置为图像颜色表中的预定义的颜色值,即是说,它只能改变像素的索引值。若需要更改颜色或者将颜色添加到图像的颜色表中,可以使用setColor()函数来完成。颜色表中的一项为一个ARGB颜色值,使用qRgb()和qRgba()函数可以为setColor()提供需要的QRgb值。例如:

image = QImage(3, 3, QImage.Format_Indexed8)
 
value = qRgb(122, 163, 39)  # 0xff7aa327
image.setColor(0, value)
 
value = qRgb(237, 187, 51)  # 0xffedba31
image.setColor(1, value)
 
value = qRgb(180, 149, 39)  # 0xffbd9527
image.setColor(2, value)
 
image.setPixel(0, 1, 0)
image.setPixel(1, 0,0)
image.setPixel(1,1,2)
image.setPixel(2,1,1)

用一个方格表示一个像素,上面代码生成的图像可以用下图来描述:

图像格式

QImage中存储的每个像素都由整数表示。整数的大小取决于格式。QImage支持QImage.Format枚举描述的图像格式。Qt提供以下图像格式:

  • QImage.Format_Invalid (0): 无效图片。
  • QImage.Format_Mono (1): 每像素使用1位存储图像。字节首先用高位(MSB方式打包)。
  • QImage.Format_MonoLSB (2): 每像素使用1位存储图像。字节首先用低位(LSB方式打包)。
  • QImage.Format_Indexed8 (3): 使用8位索引将图像存储到颜色表中。
  • QImage.Format_RGB32 (4): 图像使用32位RGB格式(0xffRRGGBB)存储。
  • QImage.Format_ARGB32 (5): 图像使用32位ARGB格式(0xAARRGGBB)存储。
  • QImage.Format_ARGB32_Premultiplied (6): 使用预乘的32位ARGB格式(0xAARRGGBB)存储图像,即红色,绿色和蓝色通道乘以除以255的alpha分量。(如果RR,GG或BB的值大于alpha使用预乘ARGB32的某些操作(例如使用alpha混合的图像合成)要比使用普通ARGB32更快。
  • QImage.Format_RGB16 (7): 图像以16位RGB格式(5-6-5)存储。
  • QImage.Format_ARGB8565_Premultiplied (8): 使用预乘的24位ARGB格式(8-5-6-5)存储图像。
  • QImage.Format_RGB666 (9): 图像以24位RGB格式(6-6-6)存储。未使用的最高有效位始终为零。
  • QImage.Format_ARGB6666_Premultiplied (10): 使用预乘的24位ARGB格式(6-6-6-6)存储图像。
  • QImage.Format_RGB555 (11): 图像以16位RGB格式(5-5-5)存储。未使用的最高有效位始终为零。
  • QImage.Format_ARGB8555_Premultiplied (12): 使用预乘的24位ARGB格式(8-5-5-5)存储图像。
  • QImage.Format_RGB888 (13): 图像以24位RGB格式(8-8-8)存储。
  • QImage.Format_RGB444 (14): 图像以16位RGB格式(4-4-4)存储。未使用的位始终为零。
  • QImage.Format_ARGB4444_Premultiplied (15): 使用预乘的16位ARGB格式(4-4-4-4)存储图像。
  • QImage.Format_RGBX8888 (16): 图像使用32位字节排序的RGB(x)格式(8-8-8-8)存储。这与Format_RGBA8888相同,除了alpha必须始终为255。(在Qt 5.2中添加)
  • QImage.Format_RGBA8888 (17): 图像使用32位字节顺序RGBA格式(8-8-8-8)存储。与ARGB32不同,这是一种字节排序格式,这意味着32位编码在大字节序和小字节序架构之间有所不同,分别为(0xRRGGBBAA)和(0xAABBGGRR)。如果读取为字节0xRR,0xGG,0xBB,0xAA,则颜色顺序在任何体系结构上都是相同的。(在Qt 5.2中添加)。
  • QImage.Format_RGBA8888_Premultiplied (18): 使用预乘的32位字节排序RGBA格式(8-8-8-8)存储图像。(在Qt 5.2中添加)。
  • QImage.Format_BGR30 (19): 图像使用32位BGR格式(x-10-10-10)存储。(在Qt 5.4中添加)。
  • QImage.Format_A2BGR30_Premultiplied (20): 图像使用32位预乘ABGR格式(2-10-10-10)存储。(在Qt 5.4中添加)。
  • QImage.Format_RGB30 (21): 图像以32位RGB格式(x-10-10-10)存储。(在Qt 5.4中添加)。
  • QImage.Format_A2RGB30_Premultiplied (22): 图像使用32位预乘ARGB格式(2-10-10-10)存储。(在Qt 5.4中添加)。
  • QImage.Format_Alpha8 (23): 图像使用仅8位alpha格式存储。(在Qt 5.5中添加)。
  • QImage.Format_Grayscale8 (24): 图像使用8位灰度格式存储。(在Qt 5.5中添加)。
  • QImage.Format_Grayscale16 (28): 图像使用16位灰度格式存储。(在Qt 5.13中添加)。
  • QImage.Format_RGBX64 (25): 使用64位半字排序的RGB(x)格式(16-16-16-16)存储图像。这与Format_RGBA64相同,除了alpha必须始终为65535。(在Qt 5.12中添加)。
  • QImage.Format_RGBA64 (26): 图像使用64位半字排序RGBA格式(16-16-16-16)存储。(在Qt 5.12中添加)。
  • QImage.Format_RGBA64_Premultiplied (27): 使用预乘的64位半字排序RGBA格式(16-16-16-16)存储图像。(在Qt 5.12中添加)。
  • QImage.Format_BGR888 (29): 图像使用24位BGR格式存储。(在Qt 5.14中添加)。

图像变换和图像属性修改

QImage支持许多用于创建新图像的功能:

  • createAlphaMask(): 从该图像的alpha缓冲区生成并返回一个1-bpp的蒙版
  • mirrored(): 生成一个径向图像
  • scaled(): 生成一个缩放图像
  • rgbSwapped(): 从RGB图像构造一个BGR图像
  • scaledToWidth(): 返回缩放到指定宽度的图像
  • caledToHeight(): 返回缩放到指定高度的图像
  • transformed():返回使用给定的转换矩阵和转换模式转换的图像

一些用于就地图像属性的函数:

  • setDotsPerMeterX(): 设置物理单位的水平像素数来定义纵横比。
  • setDotsPerMeterY(): 设置物理单位的垂直像素数来定义纵横比。
  • fill(): 用给定的像素值填充整个图像。
  • invertPixels(): 使用给定的InvertMode值反转图像中的所有像素值。
  • setColorTable(): 设置用于转换颜色索引的颜色表。仅单色和8位格式。
  • setColorCount(): 调整颜色表的大小。仅单色和8位格式。

创建一个看图应用

参考pyqt5-examples样例文件imageviewer.py, 实现一个简单的看图程序,完整代码如下:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QDir
from PyQt5.QtGui import QImage, QPainter, QPixmap, QPalette
from PyQt5.QtWidgets import (QApplication, QMainWindow, QMenuBar, QMenu, QAction,
                             QFileDialog, QMessageBox, QLabel, QScrollArea, QSizePolicy)
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter
 
class ImageViewer(QMainWindow):
    def __init__(self, parent=None):
        super(ImageViewer, self).__init__(parent)   
        
         # 设置窗口标题
        self.setWindowTitle('实战Qt for Python: QImage实现一个看图应用')      
        # 设置窗口大小
        self.resize(500, 400)
      
        self.initUi()
        
    def initUi(self):
        #打印
        self.printer = QPrinter()
        #缩放因子
        self.scaleFactor = 0.0
       
        #创建显示图片的窗口 
        self.imgLabel = QLabel()
        self.imgLabel.setBackgroundRole(QPalette.Base)
        self.imgLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imgLabel.setScaledContents(True)
        
        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imgLabel)
        
        self.setCentralWidget(self.scrollArea)
        
        self.initMenuBar()
    
    def initMenuBar(self):
        menuBar = self.menuBar() 
        menuFile = menuBar.addMenu('文件(&F)')
        menuView = menuBar.addMenu('视图(&V)')
        
        actionOpen = QAction('打开(&O)...', self, shortcut='Ctrl+O', triggered=self.onFileOpen)
        self.actionPrint = QAction('打印(&P)...', self, shortcut='Ctrl+P', enabled=False, triggered=self.onFilePrint)  
        actionExit = QAction('退出(&X)', self, shortcut='Ctrl+Q', triggered=QApplication.instance().quit)
        
        self.actionZoomIn = QAction('放大(25%)(&I)', self, shortcut='Ctrl++', enabled=False, triggered=self.onViewZoomIn)
        self.actionZoomOut = QAction('缩小(25%)(&O)', self, shortcut='Ctrl++', enabled=False, triggered=self.onViewZoomOut)
        self.actionNormalSize = QAction('原始尺寸(&N)', self, shortcut='Ctrl+S', enabled=False, triggered=self.onViewNormalSize)
        self.actionFitToWindow = QAction('适应窗口(&F)', self, shortcut='Ctrl+F', enabled=False, checkable=True, triggered=self.onViewFitToWindow)
        
        menuFile.addAction(actionOpen)
        menuFile.addAction(self.actionPrint)
        menuFile.addSeparator()
        menuFile.addAction(actionExit)  
        
        menuView.addAction(self.actionZoomIn)
        menuView.addAction(self.actionZoomOut)
        menuView.addAction(self.actionNormalSize)
        menuView.addSeparator()
        menuView.addAction(self.actionFitToWindow)
    
    #打开文件
    def onFileOpen(self):
        filename,_ = QFileDialog.getOpenFileName(self, '打开文件', QDir.currentPath()) 
        if filename:
            image = QImage(filename)
            if image.isNull():
                QMessageBox.information(self, '图像浏览器', '不能加载文件%s.' % filename)
                return
            
            self.imgLabel.setPixmap(QPixmap.fromImage(image))
            self.scaleFactor = 1.0
            
            self.actionPrint.setEnabled(True)
            self.actionFitToWindow.setEnabled(True)
            self.updateActions()
            
            if not self.actionFitToWindow.isChecked():
                self.imgLabel.adjustSize()
    
    #打印
    def onFilePrint(self):
        dlg = QPrintDialog(self.printer, self)
        if dlg.exec():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.imgLabel.pixmap().size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
            painter.setWindow(self.imgLabel.pixmap().rect())
            painter.drawPixmap(0, 0, self.imgLabel.pixmap())
    
    #放大图像
    def onViewZoomIn(self):
        self.scaleIamge(1.25)   
    
    def onViewZoomOut(self):
        self.scaleIamge(0.8)   
    
    def onViewNormalSize(self):
        self.imgLabel.adjustSize()
        self.scaleFactor = 1.0
    
    def onViewFitToWindow(self):
        fitToWindow = self.actionFitToWindow.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.onViewNormalSize()
            
        self.updateActions()
    
    def updateActions(self):
        checked = not self.actionFitToWindow.isChecked()
        self.actionZoomIn.setEnabled(checked)
        self.actionZoomOut.setEnabled(checked)
        self.actionNormalSize.setEnabled(checked)
        
    def scaleIamge(self, factor):
        self.scaleFactor *= factor
        self.imgLabel.resize(self.scaleFactor * self.imgLabel.pixmap().size())
        
        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)
        
        self.actionZoomIn.setEnabled(self.scaleFactor < 4.0)
        self.actionZoomOut.setEnabled(self.scaleFactor > 0.25)
        
    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(int(factor * scrollBar.value() + ((factor - 1) * scrollBar.pageStep()/2)))
        
   
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = ImageViewer()
    window.show()
    sys.exit(app.exec())   

运行结果如下图:

本文知识点

  • QImage支持的图像格式
  • 使用QImage进行图像变换
  • 创建一个简单的图像浏览应用

前一篇: 实战PyQt5:120-像素图QPixmap和QBitmap


请多多关注,评论,收藏,点赞,和转发。

相关推荐

Sublime Text 4 稳定版 Build 4113 发布

IT之家7月18日消息知名编辑器SublimeText4近日发布了Build4113版本,是SublimeText4的第二个稳定版。IT之家了解到,SublimeTe...

【小白课程】openKylin便签贴的设计与实现

openKylin便签贴作为侧边栏的一个小插件,提供便捷的文本记录和灵活的页面展示。openKylin便签贴分为两个部分:便签列表...

“告别 Neovim!为什么我投奔了刚开源的 Zed 编辑器?”

...

壹啦罐罐 Android 手机里的 Xposed 都装了啥

这是少数派推出的系列专题,叫做「我的手机里都装了啥」。这个系列将邀请到不同的玩家,从他们各自的角度介绍手机中最爱的或是日常使用最频繁的App。文章将以「每周一篇」的频率更新,内容范围会包括iOS、...

电气自动化专业词汇中英文对照表(电气自动化专业英语单词)

专业词汇中英文对照表...

Python界面设计Tkinter模块的核心组件

我们使用一个模块,我们要熟悉这个模块的主要元件。如我们设计一个窗口,我们可以用Tk()来完成创建;一些交互元素,按钮、标签、编辑框用到控件;怎么去布局你的界面,我们可以用到pack()、grid()...

以色列发现“死海古卷”新残片(死海古卷是真的吗)

编译|陈家琦据艺术新闻网(artnews.com)报道,3月16日,以色列考古学家发现了死海古卷(DeadSeaScrolls)新残片。新出土的羊皮纸残片中包括以希腊文书写的《十二先知书》段落,这...

鸿蒙Next仓颉语言开发实战教程:订单列表

大家上午好,最近不断有友友反馈仓颉语言和ArkTs很像,所以要注意不要混淆。今天要分享的是仓颉语言开发商城应用的订单列表页。首先来分析一下这个页面,它分为三大部分,分别是导航栏、订单类型和订单列表部分...

哪些模块可以用在 Xposed for Lollipop 上?Xposed 模块兼容性解答

虽然已经有了XposedforLollipop的安装教程,但由于其还处在alpha阶段,一些Xposed模块能不能依赖其正常工作还未可知。为了解决大家对于模块兼容性的疑惑,笔者尽可能多...

利用 Fluid 自制 Mac 版 Overcast 应用

我喜爱收听播客,健身、上/下班途中,工作中,甚至是忙着做家务时。大多数情况下我会用MarcoArment开发的Overcast(Freemium)在iPhone上收听,这是我目前最喜爱的Po...

Avalonia日志组件实现与优化指南(ar日志表扣)

...

浅色Al云食堂APP代码(三)(手机云食堂)

以下是进一步优化完善后的浅色AI云食堂APP完整代码,新增了数据可视化、用户反馈、智能推荐等功能,并优化了代码结构和性能。项目结构...

实战PyQt5: 121-使用QImage实现一个看图应用

QImage简介QImage类提供了独立于硬件的图像表示形式,该图像表示形式可以直接访问像素数据,并且可以用作绘制设备。QImage是QPaintDevice子类,因此可以使用QPainter直接在图...

滚动条隐藏及美化(滚动条隐藏但是可以滚动)

1、滚动条隐藏背景/场景:在移动端,滑动的时候,会显示默认滚动条,如图1://隐藏代码:/*隐藏滚轮*/.ul-scrool-box::-webkit-scrollbar,.ul-scrool...

浅色AI云食堂APP完整代码(二)(ai 食堂)

以下是整合后的浅色AI云食堂APP完整代码,包含后端核心功能、前端界面以及优化增强功能。项目采用Django框架开发,支持库存管理、订单处理、财务管理等核心功能,并包含库存预警、数据导出、权限管理等增...

取消回复欢迎 发表评论: