这就是我的做法(一个简单的例子):
# import all necessary modules and classes
from tkinter import Tk, Canvas, Frame
from PIL import Image, ImageTk
import requests
# checking if the file exists, if it doesn't exist download it, if can't download it, exit the program
try:
open('space.jpg')
except FileNotFoundError:
url = 'https://images5.alphacoders.com/866/866360.jpg'
img = requests.get(url)
if img.status_code == 200:
with open('space.jpg', 'wb') as file:
file.write(img.content)
print('File not found. Downloaded the necessary file.')
else:
print('File not found. Could not download the necessary file.')
exit()
class MovableImage(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
# dictionary for storing information about movement
self.start_coords = {'x': 0, 'y': 0, 'move': False}
# dictionary for storing information about movement
self.start_coords_main = {'x': 0, 'y': 0, 'move': False}
# loads the image
self.image = Image.open('space.jpg')
# sets the images to their corresponding variables so that they can be referenced later
# resizes the smaller image to fit the navigation window
self.main_image = ImageTk.PhotoImage(self.image)
self.nav_image = ImageTk.PhotoImage(self.image.resize((200, 100), Image.ANTIALIAS))
# creates the canvas to store the bigger image on
self.main_canvas = Canvas(self, width=700, height=500, highlightthickness=0)
self.main_canvas.pack()
# puts image on canvas
self.main_image_id = self.main_canvas.create_image((0, 0), image=self.main_image, anchor='nw', tags='main_image')
# creates the smaller canvas that will be used for navigation
self.nav_canvas = Canvas(self.main_canvas, width=200, height=100, highlightthickness=0)
# adds the smaller canvas as a window to the main_canvas
self.main_canvas.create_window((500, 400), window=self.nav_canvas, anchor='nw', tags='nav_canvas')
# adds the resized image to nav_canvas
self.nav_canvas.create_image((0, 0), image=self.nav_image, anchor='nw')
# creates a rectangle to indicate the current view of the image
self.nav_box = self.nav_canvas.create_rectangle((0, 0, 70, 50), outline='white')
# binds functions
self.main_canvas.bind('<Button-1>', self.set_start_coords_main)
self.main_canvas.bind('<B1-Motion>', self.move_coords_main)
# binds functions
self.nav_canvas.bind('<Button-1>', self.set_start_coords)
self.nav_canvas.bind('<B1-Motion>', self.move_coords)
# function that sets the starting coords so that they can be referenced later, also sets whether the box can be moved at all
def set_start_coords(self, event):
x1, y1, x2, y2 = self.nav_canvas.coords(self.nav_box)
if x1 < event.x < x2 and y1 < event.y < y2:
self.start_coords['x'] = event.x - x1
self.start_coords['y'] = event.y - y1
self.start_coords['move'] = True
else:
self.start_coords['move'] = False
# the moving part, this takes reference from the starting coords and uses them for calculation
# basic border checks and then the actual moving
def move_coords(self, event):
if not self.start_coords['move']:
return
dx = self.start_coords['x']
dy = self.start_coords['y']
x = event.x - dx
y = event.y - dy
if x < 0:
x = 0
elif x + 70 > 200:
x = 130
if y < 0:
y = 0
elif y + 50 > 100:
y = 50
self.nav_canvas.coords(self.nav_box, x, y, x + 70, y + 50)
self.main_canvas.coords(self.main_image_id, -x * 10, -y * 10)
# function that sets the starting coords so that they can be referenced later, also sets whether the box can be moved at all
def set_start_coords_main(self, event):
x1, y1, x2, y2 = self.main_canvas.bbox('main_image')
if x1 < event.x < x2 and y1 < event.y < y2:
self.start_coords_main['x'] = event.x - x1
self.start_coords_main['y'] = event.y - y1
self.start_coords_main['move'] = True
else:
self.start_coords_main['move'] = False
# the moving part, this takes reference from the starting coords and uses them for calculation
# basic border checks and then the actual moving
def move_coords_main(self, event):
if not self.start_coords_main['move']:
return
dx = self.start_coords_main['x']
dy = self.start_coords_main['y']
x = event.x - dx
y = event.y - dy
if x < -1300:
x = -1300
elif x > 0:
x = 0
if y < -500:
y = -500
elif y > 0:
y = 0
self.nav_canvas.coords(self.nav_box, -x / 10, -y / 10, -x / 10 + 70, -y / 10 + 50)
self.main_canvas.coords(self.main_image_id, x, y)
# basic Tk() instance and afterwards the root.mainloop()
root = Tk()
MovableImage(root).pack()
# mainloop
root.mainloop()
几件事要提一下:这是一个硬编码示例,仅适用于分辨率为 2000x1000 像素的图像,其他图像可能无法正确显示或调整大小看起来不太好。对于这个问题,您将不得不处理自己或询问其他问题,了解您在尝试调整此问题时遇到的问题。因此,如果可以将任何图像放在那里并且它会起作用,那就太好了。
关于代码,很简单:
导入模块
检查文件(可能有更好的方法,但这也可以),然后如果文件不存在,只需下载它,如果无法完成,则退出程序.
然后设置一些引用(字典,这样global 就不必使用了,而且我会说字典更好,因为其他原因,就像所有必要的变量都是在一个地方,例如 x 和 y 变量名不会被全局采用)。
然后设置将注册鼠标点击的第一个函数。这样做是为了获得鼠标相对于可移动方块的位置(如果方块是独立窗口或某物)并且可以移动,但在此之前,该函数会检查鼠标是否在正方形内。如果不移动被禁用,例如,如果您要在正方形外部单击并开始移动,则不会这样做。
然后定义移动函数。在那里检索相对 x 和 y 坐标,但首先它检查它是否可以移动,如果不能移动,它会停止该函数的执行。然后进行更多的计算并检查边界(同样,这是非常硬编码的,应该更改为更动态的函数,以便它可以根据图片检测到这一点)。然后是移动部分,它只是将导航框和实际图片都移动到相应的位置(这有点硬编码,但基本上如果你要将比例保持在较小框的 1/10,那么这个特定部分将与不同的图像)。
然后是基本的Tk() 启动,然后是.mainloop()。
在中间只需打开图片并将其设置为 2 个变量并调整将转到导航框的那个。
然后创建主画布,其中将显示主图像。
然后将该图像添加到画布并保留 id 引用,以便以后可以移动该图像。
创建较小的画布,不要打包它或其他任何东西,而是将实例作为窗口添加到主画布,使其位于其顶部(如您问题中的照片) .它再次以硬编码的方式放置。
然后添加导航图片到导航画布中,并添加将要移动的小框。
然后绑定函数到nav_canvas中的鼠标活动。
如果您有任何问题,请提出。 请注意如果您使用自己的图像,则不需要try/except,它会始终随程序一起提供。之所以存在,是因为您可能没有具有确切名称的确切尺寸的确切图片,因此出于测试目的,这只是暂时的。
编辑:将代码放在继承自 Frame 的 class 中,以便可以将其作为小部件放置。