Qt小組件 - 8 圖片瀏覽器

一個自制的圖片瀏覽器,如果不想安裝qfluentwidgetsCommandBarView可以使用QWidget+QPushButton替代

安裝 qfluentwidgets

pip install PySide6-Fluent-Widgets[full]

代碼示例

# coding: utf-8
from typing import Unionfrom PySide6.QtCore import Qt, QRectF, QSize, Signal, QPropertyAnimation, QEasingCurve, Property
from PySide6.QtGui import QPainter, QPixmap, QWheelEvent, QResizeEvent, QImage
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsItem, QFileDialog, \QVBoxLayout
from qfluentwidgets import FluentIcon as FIF, InfoBar, CommandBarView, MaskDialogBase, Actionfrom common import imageRequestclass PictureBrowserView(QGraphicsView):"""圖片查看器"""closeSignal = Signal(bool)def __init__(self, parent=None):super().__init__(parent)self._rotationAngle = 0.0self.zoomInTimes = 0self.maxZoomInTimes = 22self.displayedImageSize = QSize(0, 0)self.verticalLayout = QVBoxLayout(self)self.toolsBar = CommandBarView(self)self.graphicsScene = QGraphicsScene(self)self.pixmapItem = QGraphicsPixmapItem()self.__animation = QPropertyAnimation(self, b'rotation', self)self.__animation.setDuration(100)self.__animation.setEasingCurve(QEasingCurve.Type.InOutQuart)self.__animation.finished.connect(self.setAdaptation)self.__initWidgets()self.__initSignals()def __initWidgets(self):self.toolsBar.move(0, 0)self.toolsBar.resize(self.width(), 50)self.setAlignment(Qt.AlignmentFlag.AlignCenter)self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)  # 隱藏水平滾動條self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)  # 隱藏垂直滾動條self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)  # 以鼠標所在位置為錨點進行縮放self.pixmapItem.setTransformationMode(Qt.TransformationMode.SmoothTransformation)  # 平滑轉型self.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.LosslessImageRendering | QPainter.RenderHint.SmoothPixmapTransform)  # 平滑像素圖變換self.setContentsMargins(0, 0, 0, 0)self.setViewportMargins(0, 0, 0, 0)# 設置布局self.verticalLayout.setContentsMargins(10, 10, 10, 10)self.verticalLayout.setSpacing(0)self.verticalLayout.addWidget(self.toolsBar, 0, Qt.AlignTop | Qt.AlignRight)# 添加圖片標簽self.graphicsScene.addItem(self.pixmapItem)self.setScene(self.graphicsScene)# 設置控件樣式self.setStyleSheet('QGraphicsView{background-color: transparent; border: none;}')def __initSignals(self):self.toolsBar.addAction(Action(FIF.ROTATE, '旋轉圖片', triggered=lambda: self.setRotationByAnimation()))self.toolsBar.addAction(Action(FIF.ZOOM_IN, '放大圖片', triggered=lambda: self.enlargePicture()))self.toolsBar.addAction(Action(FIF.ZOOM_OUT, '縮小圖片', triggered=lambda: self.shrinkPicture()))self.toolsBar.addAction(Action(FIF.SAVE, '保存圖片', triggered=self.__saveImage))self.toolsBar.addAction(Action(FIF.CLOSE, '關閉窗口', triggered=self.closeSignal.emit))self.toolsBar.resizeToSuitableWidth()def __saveImage(self):fileName, t = QFileDialog.getSaveFileName(self, self.tr("保存圖片"), "", self.tr("圖片 (*.png)"))if fileName:self.pixmapItem.pixmap().save(fileName, 'PNG')InfoBar.success('', self.tr("圖片已保存到") + fileName, self.window())def __getScaleRatio(self):"""獲取顯示的圖像和原始圖像的縮放比例:return:"""pm = self.pixmapItem.pixmap()if pm.isNull():return 1pw = pm.width()ph = pm.height()rw = min(1, self.width() / pw)rh = min(1, self.height() / ph)return min(rw, rh)def __setDragEnabled(self, isEnabled: bool):"""設置拖拽是否啟動:param isEnabled: bool:return:"""if isEnabled:self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)else:self.setDragMode(QGraphicsView.DragMode.NoDrag)def __isEnableDrag(self):"""根據圖片的尺寸決定是否啟動拖拽功能:return:"""v = self.verticalScrollBar().maximum() > 0h = self.horizontalScrollBar().maximum() > 0return v or hdef getRotation(self) -> float:return self.pixmapItem.rotation()def setRotation(self, stepSize: Union[float, int] = 90.0):"""順時針旋轉:param stepSize: 步長,旋轉角度:return:"""if self.pixmapItem.pixmap().isNull():return# self.pixmapItem.setTransformOriginPoint(self.pixmapItem.boundingRect().center())  # 指定圖片旋轉中心點self.pixmapItem.setRotation(stepSize)def setRotationByAnimation(self, stepSize: int = 90):"""順時針旋轉動畫:param stepSize: 步長,旋轉角度:return:"""if self.__animation.state() == QPropertyAnimation.State.Running:returnself.__animation.setStartValue(self._rotationAngle)self._rotationAngle += stepSizeself.__animation.setEndValue(self._rotationAngle)self.__animation.start()def resetTransform(self):"""重置變換:return:"""self.zoomInTimes = 0self.__setDragEnabled(False)super().resetTransform()def setAdaptation(self):"""縮放以適應:return:"""self.setSceneRect(QRectF(self.pixmapItem.pixmap().rect()))self.fitInView(self.pixmapItem)self.__setDragEnabled(False)self.zoomInTimes = 0def setOriginalSize(self):"""設置 1:1 大小:return:"""self.resetTransform()self.setSceneRect(QRectF(self.pixmapItem.pixmap().rect()))self.__setDragEnabled(self.__isEnableDrag())self.zoomInTimes = self.getZoomInTimes(self.pixmapItem.pixmap().width())def enlargePicture(self, anchor=QGraphicsView.ViewportAnchor.AnchorUnderMouse):"""放大圖片:return:"""if self.zoomInTimes == self.maxZoomInTimes:returnself.setTransformationAnchor(anchor)self.zoomInTimes += 1self.scale(1.1, 1.1)self.__setDragEnabled(self.__isEnableDrag())# 還原 anchorself.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)def shrinkPicture(self, anchor=QGraphicsView.ViewportAnchor.AnchorUnderMouse):"""縮小圖片:return:"""if self.zoomInTimes == 0 and not self.__isEnableDrag():returnself.setTransformationAnchor(anchor)self.zoomInTimes -= 1# 原始圖像的大小pm = self.pixmapItem.pixmap()pw = pm.width()ph = pm.height()# 實際顯示的圖像寬度w = self.displayedImageSize.width() * 1.1 ** self.zoomInTimesh = self.displayedImageSize.height() * 1.1 ** self.zoomInTimesif pw > self.width() or ph > self.height():# 在窗口尺寸小于原始圖像時禁止繼續縮小圖像比窗口還小if w <= self.width() and h <= self.height():self.fitInView(self.pixmapItem)else:self.scale(1 / 1.1, 1 / 1.1)else:# 在窗口尺寸大于圖像時不允許縮小的比原始圖像小if w <= pw:self.resetTransform()else:self.scale(1 / 1.1, 1 / 1.1)self.__setDragEnabled(self.__isEnableDrag())# 還原 anchorself.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)def getZoomInTimes(self, width: int, step: int = 100):for i in range(0, self.maxZoomInTimes):if width - self.displayedImageSize.width() * 1.1 ** i <= step:return ireturn self.maxZoomInTimesdef fitInView(self, item: QGraphicsItem, mode=Qt.AspectRatioMode.KeepAspectRatio):"""縮放場景使其適應窗口大小:param item::param mode::return:"""super().fitInView(item, mode)self.displayedImageSize = self.__getScaleRatio() * self.pixmapItem.pixmap().size()self.zoomInTimes = 0def resizeEvent(self, event: QResizeEvent):"""重寫 resizeEvent 事件,調整圖片大小:param event::return:"""# super().resizeEvent(event)if self.zoomInTimes > 0:return# 調整圖片大小ratio = self.__getScaleRatio()self.displayedImageSize = self.pixmapItem.pixmap().size() * ratioif ratio < 1:self.fitInView(self.pixmapItem)else:self.resetTransform()def wheelEvent(self, e: QWheelEvent):"""滾動鼠標滾輪縮放圖片:param e::return:"""if e.angleDelta().y() > 0:self.enlargePicture()else:self.shrinkPicture()def setPixmap(self, pixmap: Union[str, QPixmap, QImage]):"""設置圖片:param pixmap::return:"""if not pixmap:returnif isinstance(pixmap, str):pixmap = QPixmap(pixmap)elif isinstance(pixmap, QImage):pixmap = QPixmap.fromImage(pixmap)# 設置圖片, 并設置場景大小self.pixmapItem.setPixmap(pixmap)# 縮放圖片ratio = self.__getScaleRatio()self.displayedImageSize = pixmap.size() * ratioself.pixmapItem.setTransformOriginPoint(self.pixmapItem.boundingRect().center())self.setSceneRect(QRectF(pixmap.rect()))self.setAdaptation()def setUrl(self, url: str):imageRequest(self, url)rotation = Property(float, getRotation, setRotation)class PictureBrowserDialog(MaskDialogBase):"""圖片瀏覽器"""def __init__(self, parent=None):super().__init__(parent)self.vBoxLayout = QVBoxLayout(self.widget)self.view = PictureBrowserView(self.widget)self.view.closeSignal.connect(self.close)self.vBoxLayout.setContentsMargins(0, 0, 0, 0)self._hBoxLayout.setContentsMargins(0, 0, 0, 0)self.vBoxLayout.addWidget(self.view)self.setClosableOnMaskClicked(True)def setUrl(self, url: str):self.view.setUrl(url)
main.py

QImageQPixmap不太支持大文件瀏覽,或者未知圖片類型,使用pillow嵌套使用

# coding: utf-8
import sys
from PIL import Image
from PIL.ImageQt import toqpixmap
from PySide6.QtWidgets import QApplicationfrom components import PictureBrowserViewapp = QApplication(sys.argv)
view = PictureBrowserView()
# view.setPixmap(r"G:\手機\壁紙\20250616153644.png")
view.setPixmap(toqpixmap(Image.open(r"G:\手機\壁紙\0250616153644.png")))
view.show()
sys.exit(app.exec())

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/91066.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/91066.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/91066.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

R study notes[1]

文章目錄introducing to Rreferencesintroducing to R R is an integrated suite involved data handling,storage facility,calculations on arrays,tools for data analysis and so on.running the command R in the terminal of OS can start R software.in R terminal ,to…

由于主庫切換歸檔路徑導致的 Oracle DG 無法同步問題的解決過程

由于主庫切換歸檔路徑導致的 Oracle DG 無法同步問題的解決過程 在上一篇文章中&#xff0c;由于 Oracle 數據庫的歸檔日志空間耗盡導致客戶端無法連接數據庫。在解決的過程中臨時修改了歸檔路徑。后來通過修改參數db_recovery_file_dest_size的值解決了問題。 但該操作導致DG無…

密碼學與加密貨幣:構建去中心化信任的技術基石與未來挑戰

密碼學是加密貨幣的技術基石&#xff0c;兩者通過數學原理構建去中心化信任體系。以下從技術原理、應用場景及未來挑戰三方面展開分析&#xff1a;一、密碼學基礎&#xff1a;加密貨幣的安全基石非對稱加密體系公鑰與私鑰&#xff1a;基于橢圓曲線密碼學&#xff08;ECC&#x…

用于 Web 認證的 抗量子簽名——ML-DSA 草案

1. 引言 本文描述了在 Web Authentication (WebAuthn) 中實現無密碼認證&#xff08;Passwordless authentication&#xff09;的方法&#xff0c;該方法使用模塊格&#xff08;Module-Lattice&#xff09;為基礎的數字簽名標準&#xff08;ML-DSA&#xff09;&#xff0c;即 …

ubuntu18.04解壓大的tar.gz文件失敗

1. 問題描述 我在vmware的虛擬機裝有petalinux環境&#xff0c;需要解壓downloads_2020.2.tar.gz這個大的壓縮包文件&#xff0c;但是總是失敗&#xff0c;而且過程很漫長 tar: downloads/git2/github.com.vim.vim.git/objects/pack/pack-f7f2e2add0c8972a9141b557ef725c38069…

App拉起:喚醒即達,告別繁瑣操作

在移動互聯網進入存量競爭的今天&#xff0c;“讓用戶少點一次、少等一秒”往往意味著20%以上的轉化率差異。openinstall把這套體驗總結成一套可落地的App拉起方案&#xff1a;一套SDK一組鏈接跳轉規則一個可自定義的落地頁&#xff0c;就能把Web→App的整條動線縮成一次點擊。…

開發指南125-HTML DOM事件

1、onload和onunload在頁面或某個元素加載完成后或離開后觸發事件。2、onchange用于在元素的值發生變化時觸發事件。一般用于<input>, <select>, <textarea>等元素3、onfocus 和 onblur激活或失去焦點時觸發4、onmouseover 和 onmouseout鼠標移入或移除時觸發…

使用redis 作為消息隊列時, 如何保證消息的可靠性

使用Redis作為消息隊列時&#xff0c;如何保證消息的可靠性 在分布式系統中&#xff0c;消息隊列扮演著不可或缺的角色&#xff0c;它能夠有效地實現服務間的解耦和異步通信。Redis憑借其出色的性能&#xff0c;常常被用作輕量級的消息隊列。然而&#xff0c;Redis本質上是一個…

CentOS7 安裝和配置教程

CentOS7 安裝和配置教程第一部分&#xff1a;安裝準備1. 下載CentOS 7鏡像2. 創建安裝介質第二部分&#xff1a;安裝步驟1. 在VMeare上安裝CentOS-7-x86_64-Minimal2. 安裝配置3. 安裝過程第三部分&#xff1a;初始配置1. 首次啟動設置2. 網絡配置3. 防火墻配置第四部分&#x…

clock_getres系統調用及示例

39. clock_getres - 獲取時鐘精度 函數介紹 clock_getres系統調用用于獲取指定時鐘的精度&#xff08;分辨率&#xff09;。它返回時鐘能夠表示的最小時間間隔。 函數原型 #include <time.h>int clock_getres(clockid_t clk_id, struct timespec *res);功能 獲取指定時鐘…

MCU+RTOS調試

1. 引言在做項目時&#xff0c;百分之三十的時間寫代碼&#xff0c;還有百分之70的時間用于調試。本期將以Keil為例進行調試章節的講解&#xff0c;目的在于做出一個標準化的調試步驟&#xff0c;方便大家學習如何調試代碼。內容分為基礎調試、中級調試及進階調試三部分&#x…

Redis的數據淘汰策略是什么?有哪些?

1.監測設置了TTL的數據volatile-lru&#xff1a;淘汰最近最少使用的數據volatile-lfu&#xff1a;淘汰最近使用次數最少的數據volatile-ttl&#xff1b;淘汰將要過期的數據volatile-random&#xff1a;隨機淘汰2.監測全庫數據allkeys-lru&#xff1a;淘汰最近最少使用的數據all…

相控陣波束躍度指向誤差Matlab仿真

波束躍度影響&#xff1a;TR芯片移相器位數、陣元數量、校準后陣元初始相位、TR芯片移相器精度、波控計算精度等。用MATLAB進行TR芯片移相器位數、陣元數量對指向誤差進行仿真。 close all; %線陣波束躍度仿真 20250726 %beam displacement % 波束躍度影響&#xff1a;TR芯片移…

板凳-------Mysql cookbook學習 (十二--------6)

MySQL 8 導入二進制文件(trailer.ogv)操作指南 在MySQL中導入二進制文件(如trailer.ogv視頻文件)通常有幾種方法&#xff0c;我將詳細介紹每種方法的操作步驟。 方法一&#xff1a;使用LOAD_FILE函數導入BLOB字段 這是最直接的方法&#xff0c;適合中小型二進制文件。sql - 1. …

昇思學習營-【模型推理和性能優化】學習心得_20250730

一、權重的加載 模型包含兩部分&#xff1a; base model 和 LoRA adapter 其中base model的權重在微調時被凍結&#xff0c; 推理時加載原權重即可&#xff0c;LoRA adapter可通過PeftModel.from_pretrained進行加載。 二、啟動推理 通過model.generate&#xff0c;啟動推理…

[AI8051U入門第十一步]W5500-服務端

學習目標: 1、連接TCP/IP 2、學習W5500作為服務端代碼一、TCP/IP介紹 TCP/IP 協議棧介紹 TCP/IP(Transmission Control Protocol / Internet Protocol)是互聯網通信的核心協議族,定義了數據如何在網絡中進行傳輸和路由。它由多個協議組成,采用分層架構,確保不同設備之間…

C 標準庫 <time.h> 函數詳解

目錄 概述 1 核心數據類型 1.1 time_t 1.2 clock_t 1.3 struct tm 1.4 size_t 2 核心函數 2.1 時間獲取函數 2.2 時間轉換函數 2.3 時間差計算 2.4 時間格式化函數 3 線程安全版本&#xff08;POSIX 擴展&#xff09; 3.1 函數列表 3.2 時間處理完整示例 4 重要…

基于BEKK-GARCH模型的參數估計、最大似然估計以及參數標準誤估計的MATLAB實現

基于BEKK-GARCH模型的參數估計、最大似然估計以及參數標準誤估計的MATLAB實現。BEKK-GARCH模型是一種多變量GARCH模型&#xff0c;用于估計多個時間序列的條件方差和協方差矩陣。 MATLAB實現BEKK-GARCH模型 1. 準備數據 假設你已經有一個時間序列數據矩陣 returns&#xff0c;每…

TDengine 中 TDgpt 用于異常檢測

介紹 TDgpt 內置時序數據異常檢測模型 TDengine 中定義了異常&#xff08;狀態&#xff09;窗口來提供異常檢測服務。異常窗口可以視為一種特殊的事件窗口&#xff08;Event Window&#xff09;&#xff0c;即異常檢測算法確定的連續異常時間序列數據所在的時間窗口。與普通事件…

統計學08:概率分布

一、隨機變量隨機變量是一個將 隨機事件 映射到 數值 的數學函數&#xff0c;用于描述事件的結果。隨機變量可以是離散的&#xff08;如骰子&#xff09;或連續的&#xff08;如人的身高、體重&#xff09;。1&#xff09;概率質量函數PMF——離散隨機變量P(X x) 對應于某個值…