【没钱买ps,PyQt自己写】Day 12 - 建立一个可以缩放图片大小的显示器 (基於 QImage 使用 OpenCV)

看完这篇文章你会得到的成果图

zoom in

zoom out

前言

我们接下来的讨论,会基於读者已经先读过我 day5 文章 的架构下去进行程序设计
如果还不清楚我程序设计的逻辑 (UI.py、controller.py、start.py 分别在干麻)
建议先阅读 day5 文章後再来阅读此文。

https://www.wongwonggoods.com/python/pyqt5-5/

此篇文章的范例程序码 github

https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day12_img_resize

以 Qlabel 在 PyQt 中显示图片

这篇是延续 Day 11 显示图片的後续开发,
在 day11 我们只是单纯的显示图片,
但有碰到了「图片解析度太大,无法完全显示」的问题,
今天我们就要加入「zoom in」、「zoom out」的功能,来改善这个问题。

UI 设计部份 (UI.py)

以 Qlabel 作为图片显示

我们与之前一样新增一个 Qlabel,文字可以先不用管他,
这个 Qlabel 是我们等等要作为显示图片使用,
因此我们记得要去修改图片大小,以免显示的范围太小。

  • 另外我们新增两个按钮,作为等等缩放图片使用。

注意上图中的图片大小,另外变数名称也建议修改。

读者们可以开始自行设计自己的介面罗,以上为我的示范。

转换成 UI.py

一样的编译指令,我们加上 -x (也可不加),
我们就可以先检视看看转换後的视窗是不是跟我们想像的一样。

转换 day12.ui -> UI.py

pyuic5 -x day12.ui -o UI.py

执行看看 UI.py 画面是否如同我们想像

一样,这程序只有介面 (视觉上的呈现),没有任何互动功能

  • 看看我们制作出来的介面
python UI.py

这样我们的介面就大致出来罗!

controller 设计部份 (controller.py)

从 UI.py 中找出物件名称

这次我们有三个物件

  • 分别是 zoom in, zoom out 的按钮:self.btn_zoom_in、self.btn_zoom_out
  • 显示图片的 Qlabel:self.label

取得名称後,去修改 controller.py

还记得我们在 day5 中的模板吗?这边我们直接复制过来使用并修改。

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QFileDialog
import cv2

from UI import Ui_MainWindow

class MainWindow_controller(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__() # in python3, super(Class, self).xxx = super().xxx
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setup_control()


    def setup_control(self):
        # TODO        
        self.img_path = 'cat.jpg'
        self.ui.btn_zoom_in.clicked.connect(self.func_zoom_in) 
        self.ui.btn_zoom_out.clicked.connect(self.func_zoom_out)
        self.display_img()

    def display_img(self):
        self.img = cv2.imread(self.img_path)
        height, width, channel = self.img.shape
        bytesPerline = 3 * width
        self.qimg = QImage(self.img, width, height, bytesPerline, QImage.Format_RGB888).rgbSwapped()
        self.qpixmap = QPixmap.fromImage(self.qimg)
        self.qpixmap_height = self.qpixmap.height()
        self.ui.label.setPixmap(QPixmap.fromImage(self.qimg))

    def func_zoom_in(self):
        self.qpixmap_height -= 100
        self.resize_image()

    def func_zoom_out(self):
        self.qpixmap_height += 100
        self.resize_image()

    def resize_image(self):
        scaled_pixmap = self.qpixmap.scaledToHeight(self.qpixmap_height)
        self.ui.label.setPixmap(scaled_pixmap)

setup_control() 修改的部份

  • 「self.img_path = 'cat.jpg'」:要显示图片的路径
  • 「self.ui.btn_zoom_in.clicked.connect(self.func_zoom_in)」:使按键连结 zoom in 的功能
  • 「self.ui.btn_zoom_out.clicked.connect(self.func_zoom_out)」:使按键连结 zoom out 的功能
  • 「self.display_img()」:等等会去 call 我们写好的显示图片的 function

display_img() 的部份

相关的功能我们在 day11
有介绍的很详细了,这边只说我们有修改的部份

会修改的原因主要是我们新增了 zoom in 的功能,
因此我们显示图片函式需要调整成可以随时变动的 (不写死、保有弹性,可重复使用)

我们把原先的写的内容拆解,
特别其中的这四行:

self.qimg = QImage(self.img, width, height, bytesPerline, QImage.Format_RGB888).rgbSwapped()
self.qpixmap = QPixmap.fromImage(self.qimg)
self.qpixmap_height = self.qpixmap.height()
self.ui.label.setPixmap(QPixmap.fromImage(self.qimg))

第一行一样我们透过 OpenCV 的方式,转换成我们要的 Qimage
第二行我们改成独立从 Qimage 提取出 QPixmap,
这边我们可以理解为

  • QImage 是实际上图片的内容
  • QPixmap 是我们最终用来显示再画面上的内容 (可能为了显示有缩放调整),但不会更改到原图

我们在第三行取得 QPixmap 的高度,作为我们等等缩放使用
最後第一行一样就是透过 label 显示出来。

func_zoom_in()、func_zoom_out() 的部份

这边我们是去调整「我们想要呈现的」QPixmap 高度,
在接下来的 resize_image() ,让他依据这个高度自动比例缩放。

resize_image() 的部份

scaled_pixmap = self.qpixmap.scaledToHeight(self.qpixmap_height)
self.ui.label.setPixmap(scaled_pixmap)

这边的第一行就是让图片 (Qpixmap) 自己去适应我们要的图片高度,做出对应的图片缩放。
而第二行就是把缩放後的图片 (Qpixmap) 塞进去我们的 label 当中。

注意:我们从头到尾都只有更改到 Qpixmap 而没有改动到 Qimg,
这也表示我们的原图是一直有被保存下来的。
我们所做的都只是「显示上的更动」。

执行结果

照我们 day5 的程序架构,我们执行

python start.py

zoom in 的功能

zoom out 的功能

Reference


★ 本文也同步发於我的个人网站(会有内容目录与显示各个小节,阅读起来更流畅):【PyQt5】Day 12 - 建立一个可以缩放图片大小的显示器 (基於 QImage 使用 OpenCV)


<<:  Unity自主学习(十三):认识Unity介面(4)

>>:  D13 - 「类比×电压×输入」

DAY08 - [CSS+RWD] 图文交错排版,资料不打结!

今日文章目录 应用情境 事前准备 CSS 说明 参考资料 应用情境 针对重复性的资料流中,指定其中...

开发过程必备除错基本知识 - 内部模组与架构

本篇介绍开发过程中,除错的必要知识,让你之後我在介绍除错方法时会更容易了解。 从 zul 到 HTM...

统整先前的小缺漏

补上缺漏和元素 games, economy之类的先补上 @commands.command() a...

[Day 29] Leetcode 15. 3Sum (C++)

前言 到了倒数第二天啦~ 大概Day 21开始有一系列的sum题目,一直说要接续完成,终於今天又回归...

第26车厢-眼睛眨啊眨~登入密码的显示/隐藏应用篇

本篇介绍现行登入密码栏位,旁边都有一个小眼睛,是如何点一下就秀出密码的呢? ▼ 完成图如下 首先先...