iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
Software Development

【今年還是不夠錢買psQQ,不如我們用PyQt自己寫一個】系列 第 11

【沒錢買ps,PyQt自己寫】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)

看完這篇文章你會得到的成果圖

前言

我們接下來的討論,會基於讀者已經先讀過我 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/day11_display_image

以 Qlabel 在 PyQt 中顯示圖片

我們在先前的文章中已經有提過,我們可以使用 Qlabel 作為顯示文字,
同樣的,我們也可以使用 Qlabel 來顯示圖片。

今天我們就要來實作這個功能。

UI 設計部份 (UI.py)

先修改主程式 window 大小

我們先點擊 QMainWindow 的部份,
展開 geometry,這裡修改 Width, Height 可以直接改變整個視窗的大小。

建議修改要大一點,因為要顯示的圖片可能也會很大。

新增 Qlabel 作為圖片顯示

我們使用與之前一樣的方式新增一個 Qlabel,文字可以先不用管他,
但我們需要先改變 Qlabel 所佔的範圍大小,
展開 geometry,這裡修改 Width, Height 可以直接改變 Qlabel 顯示視窗的大小。

而上面的 X, Y 可以個人需求修改,X, Y 表示的是「相對 MainWindow」此 Label 開始顯示的位置。

(從左上角開始算,往右X、往下Y)

這邊有兩件事情要注意:

  1. 修改後的 Qlabel geometry 必須小於主程式的 QMainWindow` (畫面比視窗大不合理吧XD)
  2. 如果 Qlabel geometry 設定的解析度不夠大,有可能會只有只顯示部份圖片的情形 (從左上開始自動剪裁)

讀者們可以開始自行設計自己的介面囉,以上為我的示範。

轉換成 UI.py

一樣的編譯指令,我們加上 -x (也可不加),
我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。

轉換 day11.ui -> UI.py

pyuic5 -x day11.ui -o UI.py

執行看看 UI.py 畫面是否如同我們想像

一樣,這程式只有介面 (視覺上的呈現),沒有任何互動功能

  • 看看我們製作出來的介面
python UI.py

這邊可以看到很單純的只有一段被初始化的文字,
接下來我們要開始去改變裡面的內容。

controller 設計部份 (controller.py)

從 UI.py 中找出物件名稱

這次我們只有一個物件 self.label

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

還記得我們在 day5 中的模板嗎?這邊我們直接複製過來使用並修改。

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtGui import QImage, QPixmap
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_small.jpg'
        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.ui.label.setPixmap(QPixmap.fromImage(self.qimg))

import 新增的部份

  • 「import cv2」:我們為了要顯示圖片,會使用到 OpenCV 的 function
  • 「from PyQt5.QtGui import QImage, QPixmap」:為了要顯示圖片,會使用到 PyQt5.QtGui 中的 QImage, QPixmap 這兩個物件的定義

setup_control() 修改的部份

  • 「self.img_path = 'cat.jpg'」:要顯示圖片的路徑
  • 「self.display_img()」:等等會去 call 我們寫好的顯示圖片的 function

display_img() 的部份

  • 「self.img = cv2.imread(self.img_path)」:OpenCV 經典的讀圖 function,之前有介紹過,應該不用我們再多說
  • 「height, width, channel = self.img.shape」:讀取圖片的 shape 與 channel,等等設定參數會用到
  • 「bytesPerline = 3 * width」:設定「每一行」的影像佔用位置數量,目前因為有 3 個 channel,因此是 3 * width (如果有透明度可能就是 4 個 channel)
  • 「self.qimg = QImage(self.img, width, height, bytesPerline, QImage.Format_RGB888).rgbSwapped()」:將轉成 OpenCV (numpy) 的格式圖片轉換成 QImage 的格式,並輸入圖片對應的長寬,每一行佔用的 bytes 數,
    而 QImage.Format_RGB888 代表的是 RGB 3 個 channel,「.rgbSwapped()」 表示將 B, G, R 3 個 channel 轉換成 R, G, B (因為 OpenCV 是以 BGR 的方式儲存圖片,需要先進行轉換)
  • 「self.ui.label.setPixmap(QPixmap.fromImage(self.qimg))」:將圖片在 label 中顯示

執行結果

照我們 day5 的程式架構,我們執行

python start.py

正常顯示圖片的情形

碰到解析度太大,導致我們設定的 Qlabel 無法完全顯示的情形

就會像下圖這樣,因為圖片太大了,800*600 只能顯示部份圖片

Reference


★ 本文也同步發於我的個人網站(會有內容目錄與顯示各個小節,閱讀起來更流暢):【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)


上一篇
【沒錢買ps,PyQt自己寫】Day 10 - 以 QFileDialog 讀取系統的檔案、資料夾
下一篇
【沒錢買ps,PyQt自己寫】Day 12 - 建立一個可以縮放圖片大小的顯示器 (基於 QImage 使用 OpenCV)
系列文
【今年還是不夠錢買psQQ,不如我們用PyQt自己寫一個】30

尚未有邦友留言

立即登入留言