Featured image of post 【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)

【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)

紀錄 PyQt5 文字輸入框 — QLineEdit、QTextEdit、文字編輯、使用者輸入控制。

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

【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)

前言

我們接下來的討論,會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計
如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)
建議先閱讀 day5 文章後再來閱讀此文。
【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)
https://wongwongnotes.com/posts/python/gui/pyqt/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 可以直接改變整個視窗的大小。

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

【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)

新增 Qlabel 作為圖片顯示

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

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

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

這邊有兩件事情要注意:

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

【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)

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

轉換成 UI.py

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

轉換 day11.ui -> UI.py

pyuic5 -x day11.ui -o UI.py

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

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

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

【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)

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

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()」 表示將R, G, B 3 個 channel 轉換成 B, G, R (因為 OpenCV 是以 BGR 的方式儲存圖片,需要先進行轉換)
  • 「self.ui.label.setPixmap(QPixmap.fromImage(self.qimg))」:將圖片在 label 中顯示

感謝網友 JSON 幫忙糾正錯誤!

執行結果

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

python start.py

正常顯示圖片的情形

【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)

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

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

【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)

Reference

使用 Hugo 建立
主題 StackJimmy 設計