Python 使用Tkinter画布小部件添加放大和缩小功能?
如何将放大和缩小添加到以下脚本中,我想将其绑定到鼠标滚轮。如果要在linux上测试此脚本,请不要忘记将鼠标滚轮事件更改为Button-4和Button-5Python 使用Tkinter画布小部件添加放大和缩小功能?,python,tkinter,zooming,Python,Tkinter,Zooming,如何将放大和缩小添加到以下脚本中,我想将其绑定到鼠标滚轮。如果要在linux上测试此脚本,请不要忘记将鼠标滚轮事件更改为Button-4和Button-5 from Tkinter import * import Image, ImageTk class GUI: def __init__(self,root): frame = Frame(root, bd=2, relief=SUNKEN) frame.grid_rowconfigure(0, w
from Tkinter import *
import Image, ImageTk
class GUI:
def __init__(self,root):
frame = Frame(root, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscrollbar = Scrollbar(frame, orient=HORIZONTAL)
xscrollbar.grid(row=1, column=0, sticky=E+W)
yscrollbar = Scrollbar(frame)
yscrollbar.grid(row=0, column=1, sticky=N+S)
self.canvas = Canvas(frame, bd=0, xscrollcommand=xscrollbar.set, yscrollcommand=yscrollbar.set, xscrollincrement = 10, yscrollincrement = 10)
self.canvas.grid(row=0, column=0, sticky=N+S+E+W)
File = "PATH TO JPG PICTURE HERE"
self.img = ImageTk.PhotoImage(Image.open(File))
self.canvas.create_image(0,0,image=self.img, anchor="nw")
self.canvas.config(scrollregion=self.canvas.bbox(ALL))
xscrollbar.config(command=self.canvas.xview)
yscrollbar.config(command=self.canvas.yview)
frame.pack()
self.canvas.bind("<Button 3>",self.grab)
self.canvas.bind("<B3-Motion>",self.drag)
root.bind("<MouseWheel>",self.zoom)
def grab(self,event):
self._y = event.y
self._x = event.x
def drag(self,event):
if (self._y-event.y < 0): self.canvas.yview("scroll",-1,"units")
elif (self._y-event.y > 0): self.canvas.yview("scroll",1,"units")
if (self._x-event.x < 0): self.canvas.xview("scroll",-1,"units")
elif (self._x-event.x > 0): self.canvas.xview("scroll",1,"units")
self._x = event.x
self._y = event.y
def zoom(self,event):
if event.delta>0: print "ZOOM IN!"
elif event.delta<0: print "ZOOM OUT!"
root = Tk()
GUI(root)
root.mainloop()
从Tkinter导入*
导入图像
类GUI:
定义初始化(自,根):
框架=框架(根部,bd=2,浮雕=凹陷)
frame.grid_rowconfigure(0,权重=1)
frame.grid\u columnconfigure(0,权重=1)
xscrollbar=滚动条(框架,方向=水平)
xscrollbar.grid(行=1,列=0,粘性=E+W)
yscrollbar=滚动条(帧)
yscrollbar.grid(行=0,列=1,粘性=N+S)
self.canvas=canvas(frame,bd=0,xscrollcommand=xscrollbar.set,yscrollcommand=yscrollbar.set,xscrolincrement=10,yscrolincrement=10)
self.canvas.grid(行=0,列=0,粘性=N+S+E+W)
File=“此处JPG图片的路径”
self.img=ImageTk.PhotoImage(Image.open(文件))
self.canvas.create_image(0,0,image=self.img,anchor=“nw”)
self.canvas.config(scrollregion=self.canvas.bbox(全部))
xscrollbar.config(命令=self.canvas.xview)
yscrollbar.config(命令=self.canvas.yview)
frame.pack()
self.canvas.bind(“,self.grab)
self.canvas.bind(“,self.drag)
root.bind(“,self.zoom)
def抓取(自身、事件):
self.\u y=event.y
self._x=事件.x
def拖动(自身、事件):
if(self._y-event.y<0):self.canvas.yview(“滚动”、-1,“单位”)
elif(self._y-event.y>0):self.canvas.yview(“滚动”,1,“单位”)
if(self._x-event.x<0):self.canvas.xview(“滚动”、-1,“单位”)
elif(self._x-event.x>0):self.canvas.xview(“滚动”,1,“单位”)
self._x=事件.x
self.\u y=event.y
def缩放(自身、事件):
如果event.delta>0:打印“放大!”
elif event.delta查看小部件而不是简单的画布可能是一个好主意,它支持通过OpenGL进行缩放。据我所知,内置的Tkinter画布类缩放不会自动缩放图像。如果无法使用自定义小部件,则可以缩放原始图像,并在调用scale函数时在画布上替换它
下面的代码片段可以合并到原始类中。它做了以下工作:
缓存Image.open()
的结果
添加一个redraw()
函数来计算缩放图像,并将其添加到画布中,同时删除以前绘制的图像(如果有)
使用鼠标坐标作为图像放置的一部分。我只需将x和y
传递到create_image
函数,以显示图像位置在鼠标移动时如何移动。您可以使用自己的中心/偏移计算来替换此值
这使用了Linux鼠标滚轮按钮4和5(您需要将其推广到Windows上,等等)
(更新的)代码:
更新我做了一些不同比例的测试,发现调整/创建图像使用了相当多的内存。我在Mac Pro上使用540x375 JPEG和32GB RAM进行了测试。以下是用于不同比例因子的内存:
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 M
64x (32000, 24000) ...
reached around ~7400 M and ran out of memory, EXC_BAD_ACCESS in _memcpy
鉴于上述情况,一个更有效的解决方案可能是确定将显示图像的视口的大小,计算鼠标坐标中心周围的裁剪矩形,使用rect裁剪图像,然后仅缩放裁剪部分。这应该使用恒定内存来存储临时映像。否则,您可能需要使用第三方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()
为了其他发现这个问题的人的利益,我附上了我的neer最终测试代码,它使用画中画/放大镜缩放。它基本上只是对samplebias已经发布的内容的修改。看到它也很酷:)
如前所述,如果您在linux上使用此脚本,请不要忘记将鼠标滚轮事件更改为Button-4和Button-5。显然,您需要插入一个.JPG路径,其中显示“insert JPG FILE path”
从Tkinter导入*
导入图像
类LoadImage:
定义初始化(自,根):
帧=帧(根)
self.canvas=canvas(框架,宽度=900,高度=900)
self.canvas.pack()
frame.pack()
File=“插入JPG文件路径”
self.orig_img=Image.open(文件)
self.img=ImageTk.PhotoImage(self.orig\u img)
self.canvas.create_image(0,0,image=self.img,anchor=“nw”)
self.zoomcycle=0
self.zimg_id=None
root.bind(“,self.zoomer)
self.canvas.bind(“,self.crop)
def缩放器(自身、事件):
如果(event.delta>0):
如果self.zoomcycle!=4:self.zoomcycle+=1
elif(event.delta<0):
如果self.zoomcycle!=0:self.zoomcycle-=1
自裁剪(事件)
def作物(自身、事件):
if self.zimg_id:self.canvas.delete(self.zimg_id)
如果(自身动物循环)!=0:
x、 y=事件.x,事件.y
如果self.zoomcycle==1:
tmp=自身原始图像裁剪((x-45,y-30,x+45,y+30))
elif self.zoomcycle==2:
tmp=自原点作物(x-30,y-20,x+30,y+20))
elif self.zoomcycle==3:
tmp=自原点作物(x-15,y-10,x+15,y+10))
elif self.zoomcycle==4:
tmp=自身原始图像裁剪((x-6,y-4,x+6,y+4))
尺寸=300200
self.zimg=ImageTk.PhotoImage(tmp.resize(大小))
self.zimg_id=self.canvas.create_image(event.x,event.y,image=self.zimg)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
root=Tk()
根标题(“作物测试”)
App=LoadImage(根目录)
root.mainloop()
例如,基于平铺。就像谷歌地图一样
<
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()
from Tkinter import *
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 = "INSERT JPG FILE PATH"
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()