2014-09-11 79 views
4

這裏是我想要的內容的描述: 在tkinter畫布中繪製幾何對象(這裏是矩形)的集合,並且可以使用鼠標瀏覽此畫布。點擊並拖動畫布,滾動放大並縮小。用鼠標移動和縮放tkinter畫布

使用這個話題,我找到的點擊和拖動部分: Move a tkinter canvas with Mouse 用鼠標

我設法寫點東西滾動放大。 移動和縮放都可以單獨使用。

問題: 如果我移動然後放大,縮放的焦點不再是光標的位置。

有什麼建議嗎?

下面一段代碼來測試

[編輯:現在應該適用於Linux和Windows]

import Tkinter as tk 
import random 

class Example(tk.Frame): 
    def __init__(self, root): 
     tk.Frame.__init__(self, root) 
     self.canvas = tk.Canvas(self, width=400, height=400, background="bisque") 
     self.xsb = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview) 
     self.ysb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) 
     self.canvas.configure(yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set) 
     self.canvas.configure(scrollregion=(0,0,1000,1000)) 

     self.xsb.grid(row=1, column=0, sticky="ew") 
     self.ysb.grid(row=0, column=1, sticky="ns") 
     self.canvas.grid(row=0, column=0, sticky="nsew") 
     self.grid_rowconfigure(0, weight=1) 
     self.grid_columnconfigure(0, weight=1) 

     #Plot some rectangles 
     for n in range(50): 
      x0 = random.randint(0, 900) 
      y0 = random.randint(50, 900) 
      x1 = x0 + random.randint(50, 100) 
      y1 = y0 + random.randint(50,100) 
      color = ("red", "orange", "yellow", "green", "blue")[random.randint(0,4)] 
      self.canvas.create_rectangle(x0,y0,x1,y1, outline="black", fill=color, activefill="black", tags=n) 
     self.canvas.create_text(50,10, anchor="nw", text="Click and drag to move the canvas\nScroll to zoom.") 

     # This is what enables using the mouse: 
     self.canvas.bind("<ButtonPress-1>", self.move_start) 
     self.canvas.bind("<B1-Motion>", self.move_move) 
     #linux scroll 
     self.canvas.bind("<Button-4>", self.zoomerP) 
     self.canvas.bind("<Button-5>", self.zoomerM) 
     #windows scroll 
     self.canvas.bind("<MouseWheel>",self.zoomer) 

    #move 
    def move_start(self, event): 
     self.canvas.scan_mark(event.x, event.y) 
    def move_move(self, event): 
     self.canvas.scan_dragto(event.x, event.y, gain=1) 

    #windows zoom 
    def zoomer(self,event): 
     if (event.delta > 0): 
      self.canvas.scale("all", event.x, event.y, 1.1, 1.1) 
     elif (event.delta < 0): 
      self.canvas.scale("all", event.x, event.y, 0.9, 0.9) 
     self.canvas.configure(scrollregion = self.canvas.bbox("all")) 

    #linux zoom 
    def zoomerP(self,event): 
     self.canvas.scale("all", event.x, event.y, 1.1, 1.1) 
     self.canvas.configure(scrollregion = self.canvas.bbox("all")) 
    def zoomerM(self,event): 
     self.canvas.scale("all", event.x, event.y, 0.9, 0.9) 
     self.canvas.configure(scrollregion = self.canvas.bbox("all")) 

if __name__ == "__main__": 
    root = tk.Tk() 
    Example(root).pack(fill="both", expand=True) 
    root.mainloop() 
+0

當我運行的代碼我不能縮放在Windows上即時通訊。 – W1ll1amvl 2014-09-11 19:37:32

+0

我爲Windows兼容性添加了一些行。不知道它是否會起作用... – 2014-09-12 12:57:42

+0

不管它仍然不起作用?它是否始終專注於相同的位置,即始終是右側的一小部分,還是隨機的? – W1ll1amvl 2014-09-13 01:57:23

回答

4

鼠標事件被報道在 '屏幕座標'。當你有一個滾動的 畫布時,你經常需要將這些數字轉換成'canvas(即scrollregion) 座標'。

例如。對於您的變焦對焦:

true_x = canvas.canvasx(event.x) 
true_y = canvas.canvasy(event.y) 
1

這是一個簡化的縮放示例。您應該使用更先進的技術,以便不用大型縮放大型圖像填充內存。

不要忘記在腳本的末尾放置一個指向圖像的路徑。

P.S.對於高級放大示例look here

# -*- coding: utf-8 -*- 
# WARNING: This is a simplified zoom example. 
# You should use more advanced techniques to not cram the memory 
# with a huge resized image for the large zooms. 
import random 
import tkinter as tk 
from tkinter import ttk 
from PIL import Image, ImageTk 

class AutoScrollbar(ttk.Scrollbar): 
    ''' A scrollbar that hides itself if it's not needed. 
     Works only if you use the grid geometry manager ''' 
    def set(self, lo, hi): 
     if float(lo) <= 0.0 and float(hi) >= 1.0: 
      self.grid_remove() 
     else: 
      self.grid() 
     ttk.Scrollbar.set(self, lo, hi) 

    def pack(self, **kw): 
     raise tk.TclError('Cannot use pack with this widget') 

    def place(self, **kw): 
     raise tk.TclError('Cannot use place with this widget') 

class Zoom(ttk.Frame): 
    ''' Simple zoom with mouse wheel ''' 
    def __init__(self, mainframe, path): 
     ''' Initialize the main Frame ''' 
     ttk.Frame.__init__(self, master=mainframe) 
     self.master.title('Simple zoom with mouse wheel') 
     # Vertical and horizontal scrollbars for canvas 
     vbar = AutoScrollbar(self.master, orient='vertical') 
     hbar = AutoScrollbar(self.master, orient='horizontal') 
     vbar.grid(row=0, column=1, sticky='ns') 
     hbar.grid(row=1, column=0, sticky='we') 
     # Open image 
     self.image = Image.open(path) 
     # Create canvas and put image on it 
     self.canvas = tk.Canvas(self.master, highlightthickness=0, 
           xscrollcommand=hbar.set, yscrollcommand=vbar.set) 
     self.canvas.grid(row=0, column=0, sticky='nswe') 
     vbar.configure(command=self.canvas.yview) # bind scrollbars to the canvas 
     hbar.configure(command=self.canvas.xview) 
     # Make the canvas expandable 
     self.master.rowconfigure(0, weight=1) 
     self.master.columnconfigure(0, weight=1) 
     # Bind events to the Canvas 
     self.canvas.bind('<ButtonPress-1>', self.move_from) 
     self.canvas.bind('<B1-Motion>',  self.move_to) 
     self.canvas.bind('<MouseWheel>', self.wheel) # with Windows and MacOS, but not Linux 
     self.canvas.bind('<Button-5>', self.wheel) # only with Linux, wheel scroll down 
     self.canvas.bind('<Button-4>', self.wheel) # only with Linux, wheel scroll up 
     # Show image and plot some random test rectangles on the canvas 
     self.imscale = 1.0 
     self.imageid = None 
     self.delta = 0.75 
     width, height = self.image.size 
     minsize, maxsize = 5, 20 
     for n in range(10): 
      x0 = random.randint(0, width - maxsize) 
      y0 = random.randint(0, height - maxsize) 
      x1 = x0 + random.randint(minsize, maxsize) 
      y1 = y0 + random.randint(minsize, maxsize) 
      color = ('red', 'orange', 'yellow', 'green', 'blue')[random.randint(0, 4)] 
      self.canvas.create_rectangle(x0, y0, x1, y1, outline='black', fill=color, 
             activefill='black', tags=n) 
     # Text is used to set proper coordinates to the image. You can make it invisible. 
     self.text = self.canvas.create_text(0, 0, anchor='nw', text='Scroll to zoom') 
     self.show_image() 
     self.canvas.configure(scrollregion=self.canvas.bbox('all')) 

    def move_from(self, event): 
     ''' Remember previous coordinates for scrolling with the mouse ''' 
     self.canvas.scan_mark(event.x, event.y) 

    def move_to(self, event): 
     ''' Drag (move) canvas to the new position ''' 
     self.canvas.scan_dragto(event.x, event.y, gain=1) 

    def wheel(self, event): 
     ''' Zoom with mouse wheel ''' 
     scale = 1.0 
     # Respond to Linux (event.num) or Windows (event.delta) wheel event 
     if event.num == 5 or event.delta == -120: 
      scale  *= self.delta 
      self.imscale *= self.delta 
     if event.num == 4 or event.delta == 120: 
      scale  /= self.delta 
      self.imscale /= self.delta 
     # Rescale all canvas objects 
     x = self.canvas.canvasx(event.x) 
     y = self.canvas.canvasy(event.y) 
     self.canvas.scale('all', x, y, scale, scale) 
     self.show_image() 
     self.canvas.configure(scrollregion=self.canvas.bbox('all')) 

    def show_image(self): 
     ''' Show image on the Canvas ''' 
     if self.imageid: 
      self.canvas.delete(self.imageid) 
      self.imageid = None 
      self.canvas.imagetk = None # delete previous image from the canvas 
     width, height = self.image.size 
     new_size = int(self.imscale * width), int(self.imscale * height) 
     imagetk = ImageTk.PhotoImage(self.image.resize(new_size)) 
     # Use self.text object to set proper coordinates 
     self.imageid = self.canvas.create_image(self.canvas.coords(self.text), 
               anchor='nw', image=imagetk) 
     self.canvas.lower(self.imageid) # set it into background 
     self.canvas.imagetk = imagetk # keep an extra reference to prevent garbage-collection 

path = 'doge2.jpg' # place path to your image here 
root = tk.Tk() 
app = Zoom(root, path=path) 
root.mainloop()