使用 Tkinter Canvas 小部件添加放大镜功能?

news2025/1/12 20:58:12

一、说明

        据我所知,内置的 Tkinter Canvas 类比例不会自动缩放图像。如果您无法使用自定义小部件,则可以缩放原始图像并在调用缩放函数时将其替换在画布上。

二、实现图像放大镜技术细节

        我如何将放大和缩小添加到以下脚本中,我想将其绑定到鼠标滚轮。如果您在 Linux 上测试此脚本,请不要忘记将 MouseWheel 事件更改为 Button-4 和 Button-5。

  1. 下面的代码片段可以合并到您的原始类中。它执行以下操作:缓存 Image.open() 的结果。
  2. 添加 redraw() 函数来计算缩放后的图像并将其添加到画布中,并且还会删除先前绘制的图像(如果有)。
  3. 使用鼠标坐标作为图像放置的一部分。我只是将 x 和 y 传递给 create_image 函数,以显示图像位置如何随着鼠标移动而移动。您可以将其替换为您自己的中心/偏移计算。
  4. 这使用 Linux 鼠标滚轮按钮 4 和 5(您需要将其推广到 Windows 等)。

三、代码实现

from tkinter import *
from PIL import Image,ImageTk

class LoadImage:
    def __init__(self,root):
        frame = Frame(root)
        self.canvas = Canvas(frame,width=900,height=900)
        self.canvas.pack()
        frame.pack()
        File = "d001.jpg"
        self.orig_img = Image.open(File)
        self.img = ImageTk.PhotoImage(self.orig_img)
        self.canvas.create_image(0,0,image=self.img, anchor="nw")

        self.zoomcycle = 0
        self.zimg_id = None

        root.bind("<MouseWheel>",self.zoomer)
        self.canvas.bind("<Motion>",self.crop)

    def zoomer(self,event):
        if (event.delta > 0):
            if self.zoomcycle != 4: self.zoomcycle += 1
        elif (event.delta < 0):
            if self.zoomcycle != 0: self.zoomcycle -= 1
        self.crop(event)

    def crop(self,event):
        if self.zimg_id: self.canvas.delete(self.zimg_id)
        if (self.zoomcycle) != 0:
            x,y = event.x, event.y
            if self.zoomcycle == 1:
                tmp = self.orig_img.crop((x-45,y-30,x+45,y+30))
            elif self.zoomcycle == 2:
                tmp = self.orig_img.crop((x-30,y-20,x+30,y+20))
            elif self.zoomcycle == 3:
                tmp = self.orig_img.crop((x-15,y-10,x+15,y+10))
            elif self.zoomcycle == 4:
                tmp = self.orig_img.crop((x-6,y-4,x+6,y+4))
            size = 300,200
            self.zimg = ImageTk.PhotoImage(tmp.resize(size))
            self.zimg_id = self.canvas.create_image(event.x,event.y,image=self.zimg)

if __name__ == '__main__':
    root = Tk()
    root.title("Crop Test")
    App = LoadImage(root)
    root.mainloop()

四、关于内存问题 

        更新我对不同的比例进行了一些测试,发现调整大小/创建图像使用了相当多的内存。我在配备 32GB RAM 的 Mac Pro 上使用 540x375 JPEG 进行了测试。以下是不同比例因子使用的内存:

 1x (500, 375) 14 M
2x (1000, 750) 19 M
4x (2000, 1500) 42 M
8x (4000, 3000) 181 M
16x (8000, 6000) 640 M
32x (16000, 12000) 1606 米
64x (32000, 24000) ...
        达到约 7400 M 并耗尽内存,_memcpy 中的 EXC_BAD_ACCESS
鉴于上述情况,更有效的解决方案可能是确定将显示图像的视口的大小,计算鼠标坐标中心周围的裁剪矩形,使用矩形裁剪图像,然后仅缩放裁剪部分。这应该使用常量内存来存储临时图像。否则,您可能需要使用第 3 方 Tkinter 控件来为您执行此裁剪/窗口缩放。

        更新 2 工作但过于简化的裁剪逻辑,只是为了让您开始:

 def redraw(self, x=0, y=0):
        if self.img_id: self.canvas.delete(self.img_id)
        iw, ih = self.orig_img.size
        # calculate crop rect
        cw, ch = iw / self.scale, ih / self.scale
        if cw > iw or ch > ih:
            cw = iw
            ch = ih
        # crop it
        _x = int(iw/2 - cw/2)
        _y = int(ih/2 - ch/2)
        tmp = self.orig_img.crop((_x, _y, _x + int(cw), _y + int(ch)))
        size = int(cw * self.scale), int(ch * self.scale)
        # draw
        self.img = ImageTk.PhotoImage(tmp.resize(size))
        self.img_id = self.canvas.create_image(x, y, image=self.img)
        gc.collect()

        只是为了其他发现这个问题的人的利益,我附上了我的最终测试代码,该代码使用画中画/放大镜缩放。它基本上只是对已经发布的样本偏差的更改。看到它也很酷:)。

        正如我之前所说,如果您在 Linux 上使用此脚本,请不要忘记将 MouseWheel 事件更改为 Button-4 和 Button-5。显然,您需要在显示“INSERT JPG FILE PATH”的位置插入 .JPG 路径。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1099643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV 笔记(1):图像的读取、显示、创建

Part11. 数字图像的含义 OpenCV 中的图像&#xff0c;其实指的是数字图像。在介绍图像这个概念之前&#xff0c;先介绍几个基础的概念&#xff1a; 像素(Pixel)是图像的基本单元或者基本元素&#xff0c;亦或者是图像最小的单位。图像中的像素点包含不同的像素值。对于灰白图像…

Flume 整合 Kafka

1.背景 先说一下&#xff0c;为什么要使用 Flume Kafka&#xff1f; 以实时流处理项目为例&#xff0c;由于采集的数据量可能存在峰值和峰谷&#xff0c;假设是一个电商项目&#xff0c;那么峰值通常出现在秒杀时&#xff0c;这时如果直接将 Flume 聚合后的数据输入到 Storm 等…

Vue-props配置功能

Vue-props配置功能 props概述 功能&#xff1a;接收从其他组件传过来的数据&#xff0c;将数据从静态转为动态注意&#xff1a; 同一层组件不能使用props&#xff0c;必须是父组件传子组件的形式。父组件传数据&#xff0c;子组件接收数据。不能什么数据都接收&#xff0c;可…

【Spring Cloud】网关Gateway的请求过滤工厂RequestRateLimiterGatewayFilterFactory

概念 关于微服务网关Gateway中有几十种过滤工厂&#xff0c;这一篇博文记录的是关于请求限流过滤工厂&#xff0c;也就是标题中的RequestRateLimiterGatewayFilterFactory。这个路由过滤工厂是用来判断当前请求是否应该被处理&#xff0c;如果不会被处理就会返回HTTP状态码为42…

三相异步电机动态数学模型及矢量控制仿真

三相异步电机动态数学模型及矢量控制仿真 本文带你一步步推倒三相异步电机动态数学模型&#xff0c;按基于转子磁链定向的矢量控制进行 matlab 仿真&#xff0c;实现较好的控制效果。 1、异步电机三相方程 2、坐标变换 3、磁链3/2变换推导 4、两相静止坐标系下的方程 5、…

python 图片下面加边框TK界面

python 对图片增加边框&#xff0c;logo贴图&#xff0c;获取图片exif参数&#xff0c;填写图片文本内容-CSDN博客 import tkinter as tk from tkinter import ttk import os import glob import json import tkinter.messagebox as messagebox # 弹出提示框 from PIL import…

Vue 如何检测 data 中 数组的变化?

Vue 可以使用 watch 和 computed 监听数组的变化。 ① 使用 watch 监听数组 可以通过 deep 选项深度监听数组内部元素的变化 ② 使用 computed 监听数组 创建一个计算属性&#xff0c;返回数组的长度或者某个数组元素的值&#xff0c;当数组发生变化时&#xff0c;计算属性会…

在nodejs中实现调度任务

在nodejs中实现调度任务 node.js帮助开发人员简化了工作流程&#xff0c;创建了高效的应用程序。它的许多有用功能之一是任务调度。本文将探讨在nodejs中调度任务的重要性、各种使用第三方库的代码示例&#xff0c;以及需要遵循的一些有用的操作。 为什么我们需要安排任务 调…

#创作纪念日#我的256天创作纪念日

我的创作256天纪念日 机缘收获日常成就憧憬 机缘 机缘……好像128天的时候已经写过了…… 小升初时&#xff0c;我开始接触编程&#xff0c;进入了一个全新的世界。刚开始学习编程时&#xff0c;我只是对电脑的一些操作比较感兴趣&#xff0c;但慢慢地&#xff0c;我开始对编…

模式植物GO背景基因集制作

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 写在前面 关于GO背景基因集文件的制作&#xff0c;我们在很早以前也发过。近两天&#xff0c;自己在分析时候&#xff0c;也是被搞了头疼。想重新制作一份GO背景基因集&#xff0c;进行富集分析。但是结果&…

vueday01——ref响应式

特性&#xff1a;持续监控某个响应式变量的属性名变化&#xff0c;可以使用shallowRef来取消这一特性&#xff0c;只监控对象整体的变化 ref测试代码&#xff1a; <template><div :id"idValue" ref"myDiv">打印obj{{ obj }}</div><…

大数据Flink(九十七):EXPLAIN、USE和SHOW 子句

文章目录 EXPLAIN、USE和SHOW 子句 一、EXPLAIN 子句 二、USE 子句

QQd挂源码已更新最新加速项目程序全开源

1、99 公益日活动加速任务已全部完成适配&#xff0c;空间公益说说和评论并分享小世界内容任务在已有的功能上进行挂机&#xff0c; 其中【发小世界】功能暂时更名为【公益小世界】。 2、上线新功能【公益答题】用于完成参加 Qbox 公益答题任务&#xff0c;等级套装有任意一项…

期中考Web复现

第一题 1z_php <?php //Yeedo told you to study hard! echo !(!(!(!(include "flag.php")||(!error_reporting(0))||!isset($_GET[OoO])||!isset($_GET[0o0])||($_GET[OoO]2023)||!(intval($_GET[OoO][0])2023)||$_GET[0o0]$_GET[OoO]||!(md5($_GET[0o0])md5($_…

Java Static

Static 变量被 static 修饰 static 修饰的变量在类中只有一份&#xff0c;可以称为类变量&#xff0c;其他变量称为实例变量在方法区加载类的时候&#xff0c;会检查类中是否存在静态变量&#xff0c;如果存在则会在堆内存区域开辟一块空间用于存储静态变量。方法区中的静态变…

A114-经典赛题-Web应用程序文件包含安全攻防

实验步骤: Web应用程序文件包含安全攻防 任务环境说明&#xff1a; 服务器场景&#xff1a;WebServ2003&#xff08;用户名&#xff1a;administrator&#xff1b;密码&#xff1a;空&#xff09; 服务器场景操作系统&#xff1a;Microsoft Windows2003 Server 服务器场景…

IPv6知识概述 - ND协议

IPv6知识概述 - ND协议 参考文章&#xff1a;https://blog.csdn.net/Gina_wj/article/details/106708770 IPv6基础篇&#xff08;四&#xff09;&#xff1a;邻居发现协议NDP ND协议功能概述 ND&#xff08;Neighbor Discovery&#xff0c;邻居发现&#xff09;协议是IPv6的…

01【Git的基本使用与底层原理】

下一篇&#xff1a;02【Git的分支与数据恢复】 目录&#xff1a;【Git系列教程-目录大纲】 文章目录 一、Git概述1.1 Git简介1.2 集中式与分布式1.2.1 集中式版本控制1.2.2 分布式版本控制 1.3 Git的使用流程1.3.1 本地仓库1.3.2 协同开发 1.4 Git的配置1.4.1 Git的配置等级1…

从头开始机器学习:线性回归

从头开始机器学习&#xff1a;线性回归 跟随 16 分钟阅读 28月 <> 1 一、说明 本篇实现线性回归的先决知识是&#xff1a;基本线性代数&#xff0c;微积分&#xff08;偏导数&#xff09;、梯度和、Python &#xff08;NumPy&#xff09;&#xff1b;从线性方程入手。 代…

【LeetCode刷题(数据结构与算法)】:用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元素 int peek() 返回队列开头…