当前位置: 首页 > news >正文

PYQT5实现图片显示、通过滚轮缩放图片、通过鼠标拖动图片移动,搞懂所有细节

本文在这篇文章的基础上进行优化:

pyqt5实现图片显示、图片放大/缩小(通过滚轮)、图片移动(鼠标拖动)_小郁同学的博客-CSDN博客_pyqt5 放大图片

 首先要说明2个重要细节:
一般触发paintEvent事件的有如下几种原因:

窗口显隐导致重画
窗口大小(重新调整)改变,或者重新排布(布局)导致重画
调用update 或者 repaint重画
当窗口第一次显示时,系统会自动产生绘图事件
当窗口部件被其他部件遮挡时,然后又再次显示出来,会对隐藏区域进行重绘事件
————————————————
原文链接:https://blog.csdn.net/liunanya/article/details/93844597

在案例中,如果窗口已打开图片,再次打开图片时,会弹出图片选择框,图片选择框会遮挡图片显示控件,这个操作将触发paintEvent事件。如果设置图片左上角的点QPoint为(0,0)倒没什么问题,一旦不是(0,0)将会出错,因为在其它分支会重新计算QPoint。

很多意想不到的地方会触发paintEvent事件

第2个细节:

painter.drawPixmap(self.point, self.scaled_img)

中,self.point不是图片显示的左上角真实值,需要除以scale才是,形如self.point / self.scale,参考文献中似乎也未留意到这一点。

我的案例优化点:

1.初始图片居中显示

2.缩放:如鼠标点在图上,则根据这一点顶点缩放,否则按图此前的左上角点进行缩放

3.图片拖动时,左键按住的点不会漂移

代码如下:

# -*- coding: utf-8 -*-
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QImageReader
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFileDialog
from PyQt5.Qt import QPixmap, QPoint, Qt, QPainter, QIcon
from PyQt5.QtCore import QSize


class ImageBox(QWidget):
    def __init__(self):
        super(ImageBox, self).__init__()
        self.img = None
        self.scaled_img = None
        self.start_pos = None
        self.end_pos = None
        self.left_click = False
        self.wheel_flag = False

        self.scale = 1
        self.old_scale = 1
        self.point = QPoint(0, 0)
        self.x = -1
        self.y = -1
        self.new_height = -1
        self.new_width = -1

    def init_ui(self):
        self.setWindowTitle("ImageBox")

    def set_image(self, img_path):
        self.img = QPixmap(img_path)
        width, height = self.img.width(), self.img.height()
        if height / width > 990 / 660:
            new_height = 990
            new_width = width * 990 / height
        else:
            new_height = height * 660 / width
            new_width = 660
        self.point = QPoint(int((660 - new_width) * 0.5), int((990 - new_height) * 0.5))
        self.img = self.img.scaled(new_width, new_height, Qt.KeepAspectRatio)
        self.scaled_img = self.img

        self.new_height = new_height
        self.new_width = new_width
        self.scale = 1

    def paintEvent(self, e):
        if self.scaled_img:
            painter = QPainter()
            painter.begin(self)
            painter.scale(self.scale, self.scale)
            if self.wheel_flag:        # 定点缩放
                self.wheel_flag = False
                # 判断当前鼠标pos在不在图上
                this_left_x = self.point.x() * self.old_scale
                this_left_y = self.point.y() * self.old_scale
                this_scale_width = self.new_width * self.old_scale
                this_scale_height = self.new_height * self.old_scale

                # 鼠标点在图上,以鼠标点为中心动作
                gap_x = self.x - this_left_x
                gap_y = self.y - this_left_y
                if 0 < gap_x < this_scale_width and 0 < gap_y < this_scale_height:
                    new_left_x = int(self.x / self.scale - gap_x / self.old_scale)
                    new_left_y = int(self.y / self.scale - gap_y / self.old_scale)
                    self.point = QPoint(new_left_x, new_left_y)
                # 鼠标点不在图上,固定左上角进行缩放
                else:
                    true_left_x = int(self.point.x() * self.old_scale / self.scale)
                    true_left_y = int(self.point.y() * self.old_scale / self.scale)
                    self.point = QPoint(true_left_x, true_left_y)
            painter.drawPixmap(self.point, self.scaled_img)  # 此函数中还会用scale对point进行处理
            painter.end()

    def wheelEvent(self, event):
        angle = event.angleDelta() / 8  # 返回QPoint对象,为滚轮转过的数值,单位为1/8度
        angleY = angle.y()
        self.old_scale = self.scale
        self.x, self.y = event.x(), event.y()
        self.wheel_flag = True
        # 获取当前鼠标相对于view的位置
        if angleY > 0:
            self.scale *= 1.08
        else:  # 滚轮下滚
            self.scale *= 0.92
        if self.scale < 0.3:
            self.scale = 0.3
        self.adjustSize()
        self.update()

    def mouseMoveEvent(self, e):
        if self.left_click:
            self.end_pos = e.pos() - self.start_pos                    # 当前位置-起始位置=差值
            self.point = self.point + self.end_pos / self.scale        # 左上角的距离变化
            self.start_pos = e.pos()
            self.repaint()

    def mousePressEvent(self, e):
        if e.button() == Qt.LeftButton:
            self.left_click = True
            self.start_pos = e.pos()

    def mouseReleaseEvent(self, e):
        if e.button() == Qt.LeftButton:
            self.left_click = False


class Ui_Form(QWidget):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(900, 1080)
        self.scrollArea = QtWidgets.QScrollArea(Form)
        self.scrollArea.setGeometry(QtCore.QRect(150, 10, 680, 990))
        self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        # self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")

        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.box = ImageBox()

        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 680, 990))
        self.scrollAreaWidgetContents.setMinimumSize(QtCore.QSize(100, 100))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.gridLayout = QtWidgets.QGridLayout(self.scrollAreaWidgetContents)
        self.gridLayout.setObjectName("gridLayout")

        self.gridLayout.addWidget(self.box, 0, 0, 1, 1)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)

        self.open_file = QtWidgets.QPushButton(Form)
        self.open_file.setGeometry(QtCore.QRect(30, 100, 81, 41))
        font = QtGui.QFont()
        font.setFamily("Aharoni")
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)
        self.open_file.setFont(font)
        self.open_file.setObjectName("open_file")
        self.open_file.clicked.connect(self.open_image)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.open_file.setText(_translate("Form", "选择文件"))

    def open_image(self):
        img_name, _ = QFileDialog.getOpenFileName(None, "Open Image File","","All Files(*);;*.jpg;;*.png;;*.jpeg")
        self.box.set_image(img_name)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_Form()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

 

相关文章:

  • 利用MATLAB绘制箱线图—箱线图在高光谱图像处理中的应用
  • ERPS协议学习笔记
  • node.js基于微信小程序的校园失物招领系统毕业设计源码072343
  • 1.4 Elasticsearch索引的mappings映射
  • ams启动过程以及App的启动过程
  • Java毕业设计-新闻段子发布管理系统
  • maven依赖冲突导致tomcat启动失败
  • Mysql基础(四)——约束与表关系
  • 聚苯乙烯微球表面原位接枝含糖聚合物/pH响应性磁性聚苯乙烯基多孔微球制备方法
  • Curator使用手册
  • x86汇编_MUL/IMUL乘法指令_笔记52
  • CSP-J1 CSP-S1第1轮 初赛 如何拿到好成绩(60分及以上)
  • Package | 解决Could NOT find GLEW (missing: GLEW_INCLUDE_DIRS GLEW_LIBRARIES)
  • Maven的配置与安装
  • 阿里云 OSS
  • 分享一款快速APP功能测试工具
  • [数据结构]链表的实现在PHP中
  • 【5+】跨webview多页面 触发事件(二)
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • Angular 响应式表单 基础例子
  • ES6核心特性
  • java 多线程基础, 我觉得还是有必要看看的
  • JavaScript设计模式之工厂模式
  • JavaWeb(学习笔记二)
  • JSDuck 与 AngularJS 融合技巧
  • KMP算法及优化
  • laravel with 查询列表限制条数
  • mongodb--安装和初步使用教程
  • MQ框架的比较
  • Theano - 导数
  • vue数据传递--我有特殊的实现技巧
  • vue--为什么data属性必须是一个函数
  • 阿里云前端周刊 - 第 26 期
  • 分布式熔断降级平台aegis
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 使用Swoole加速Laravel(正式环境中)
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 自定义函数
  • 【云吞铺子】性能抖动剖析(二)
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • ​决定德拉瓦州地区版图的关键历史事件
  • # 安徽锐锋科技IDMS系统简介
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • #每天一道面试题# 什么是MySQL的回表查询
  • (2)MFC+openGL单文档框架glFrame
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (笔试题)合法字符串
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (超详细)语音信号处理之特征提取
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (六)Hibernate的二级缓存
  • (十)T检验-第一部分
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models