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

一、说明

        据我所知,内置的 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 路径。

10-17 06:03