我們接下來的討論,會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計
如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)
建議先閱讀 day5 文章後再來閱讀此文。
https://www.wongwonggoods.com/python/pyqt5-5/
https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day13_scroll_area
這篇是延續 Day 12 顯示圖片 zoom in, zoom out 功能的後續開發,
只有 zoom in, zoom out 有時還不足以應付我們處理細節,
因此我們需要一個捲軸,幫助我們能更自由的移動圖片。
注意順序,先新增 Vertical Layout,疊加上 Scroll Area,再疊加上 Qlabel
。注意這些物件彼此之間的階層關係,一樣我們可以先修改一些物件名稱,方便我們等等使用
。我們在介面的右下角新增能夠顯示目前圖片的解析度的 Qlabel,
新增這個功能主要是能方便我們能夠確定現在圖片已經被我們縮放到什麼程度了。
讀者們可以開始自行設計自己的介面囉,以上為我的示範。
一樣的編譯指令,我們加上 -x (也可不加),
我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。
pyuic5 -x day13.ui -o UI.py
一樣,這程式只有介面 (視覺上的呈現),沒有任何互動功能
python UI.py
這樣我們的介面就大致出來囉!
我們先觀察一下剛剛在 QtDesigner 中的物件階層關係,
其中紅色框框的地方有多出一個我們不要的東西,scrollAreaWidgetContents,
這個東西在 QtDesigner 中預設是會與 QscrollArea 一起被建立,
但實際上因為我們已經很清楚我們需要的是 Qlabel 顯示的圖片,
因此我們直接去改 UI.py 裡面的一些內容。
我們可以透過搜尋功能幫助我們快速找到相關的段落,這些都是要刪掉的
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 667, 427))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.label = QtWidgets.QLabel(self.scrollAreaWidgetContents)
self.label.setGeometry(QtCore.QRect(0, 0, 1920, 1080))
self.label.setObjectName("label")
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
我們觀察一下,
# self.scrollAreaWidgetContents = QtWidgets.QWidget()
# self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 800, 400))
# self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.label = QtWidgets.QLabel()
self.label.setGeometry(QtCore.QRect(0, 0, 0, 0))
self.label.setObjectName("label")
self.scrollArea.setWidget(self.label)
為何不使用 self.scrollAreaWidgetContents?
目前測試的結果是不會成功的顯示出捲軸,可能的原因是因為 Qlabel 才有存在超過視窗範圍的大小,而 self.scrollAreaWidgetContents 作為容器,並沒有辦法以超過的大小觸發 self.scrollArea 的捲軸事件,因此功能失效。
不過這部份原因目前只是我的猜測,總之捲軸的功能是無法正常運行的。
這次除了 day12 既有的功能之外,我們新增了一些物件,
我們繼續修改我們 day12 的程式碼
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.ui.scrollArea.setWidgetResizable(True)
self.ui.label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
# self.ui.label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) # 將圖片置中
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.img_resize()
def func_zoom_out(self):
self.qpixmap_height += 100
self.img_resize()
def img_resize(self):
scaled_pixmap = self.qpixmap.scaledToHeight(self.qpixmap_height)
print(f"current img shape = ({scaled_pixmap.width()}, {scaled_pixmap.height()})")
self.ui.img_shape.setText(f"current img shape = ({scaled_pixmap.width()}, {scaled_pixmap.height()})")
self.ui.label.setPixmap(scaled_pixmap)
與 day12 的不同是,我們主要新增了這兩行
self.ui.scrollArea.setWidgetResizable(True)
self.ui.label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
# self.ui.label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) # 將圖片置中
但是如果為了好看,想讓圖片置中,可以改為以下敘述:
self.ui.label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
因為我們新增了 UI 優化的功能,稍微想一下就可以知道,
這段程式碼基本上會跟著我們圖片變化一起改變,
因此我們把「顯示圖片現在解析度」的功能新增在此處。
照我們 day5 的程式架構,我們執行
python start.py
★ 本文也同步發於我的個人網站(會有內容目錄與顯示各個小節,閱讀起來更流暢):【PyQt5】Day 13 - 使用 QVBoxLayout, QscrollArea 製作出捲軸,以高解析度檢視圖片 (基於 QImage 使用 OpenCV) PyQt5 scrollable image