【问题标题】:Tkinter: mini canvas windows as a sliderTkinter:作为滑块的迷你画布窗口
【发布时间】:2021-05-16 04:00:20
【问题描述】:

我是 Tkinter 的新手。我想用一个光标和一个小窗口来控制图像的移动:

enter image description here

我尝试使用此代码,但结果并不是我真正想要的。

import tkinter as tk
from io import BytesIO
import requests
from PIL import Image , ImageTk


def full_dimensions(imag_fs):
    top = tk.Toplevel(root)
    img = tk.Label(top, image=imag_fs)
    img.pack()


def get_image():
    _url = 'https://i.imgur.com/4m7AHVu.gif'
    _img = requests.get(_url)
    if _img.status_code == 200:
        _content = BytesIO(_img.content)
    else:
        _content = 'error.gif'
    print('image loaded')
    return _content


root = tk.Tk()

_content =  get_image()   
_x = Image.open(_content)
imag_fs = ImageTk.PhotoImage(_x)
_x.thumbnail((100, 100), Image.ANTIALIAS)

imag = ImageTk.PhotoImage(_x)
img = tk.Button(root, image=imag, command=lambda: full_dimensions(imag_fs))
img.grid(column=3, row=1)

root.mainloop()

我测试了一个窗口,但是当我导入图像时我无法控制

import tkinter as tk

main_window = tk.Tk()


def check_hand_enter():
    canvas.config(cursor="hand1")


def check_hand_leave():
    canvas.config(cursor="")


canvas = tk.Canvas(width=200, height=200)
tag_name = "polygon"

canvas.create_polygon((25, 25), (25, 100), (125, 100), (125, 25), outline='black', fill="", tag=tag_name)

canvas.tag_bind(tag_name, "<Enter>", lambda event: check_hand_enter())
canvas.tag_bind(tag_name, "<Leave>", lambda event: check_hand_leave())

canvas.pack()
main_window.mainloop()

【问题讨论】:

  • 我没有看到任何尝试在图像中滚动的尝试。不清楚问题是什么,除了您在提出问题之前没有尝试解决问题。
  • 所以问题是我编写了代码来执行此操作,或者更像是代码示例,但我想先看看你对此的实际尝试,或者至少是一个详细的伪代码
  • 我试过了,但我做不到,因为我是初学者。我需要看看你的代码,这对我有很大帮助
  • 你到底尝试了什么?你能展示你的尝试吗?你刚才说你试过了,那么你到底哪里没有成功?至少展示你的尝试。另外,如果您可以编写一个非常详细的伪代码来说明它是如何工作的,我也许也可以向您展示代码,因为我需要您尝试不只是免费给您代码,否则它不会有太大帮助,或者我可以解释一下东西,但是...我想看看你做了什么
  • 感谢您的回答。我放了一段可复现的代码,满足stackoverflow的要求。但我更新了我的代码

标签: python tkinter canvas


【解决方案1】:

这就是我的做法(一个简单的例子):

# 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 就不必使用了,而且我会说字典更好,因为其他原因,就像所有必要的变量都是在一个地方,例如 xy 变量名不会被全局采用)。

然后设置将注册鼠标点击的第一个函数。这样做是为了获得鼠标相对于可移动方块的位置(如果方块是独立窗口或某物)并且可以移动,但在此之前,该函数会检查鼠标是否在正方形内。如果不移动被禁用,例如,如果您要在正方形外部单击并开始移动,则不会这样做。

然后定义移动函数。在那里检索相对 x 和 y 坐标,但首先它检查它是否可以移动,如果不能移动,它会停止该函数的执行。然后进行更多的计算并检查边界(同样,这是非常硬编码的,应该更改为更动态的函数,以便它可以根据图片检测到这一点)。然后是移动部分,它只是将导航框和实际图片都移动到相应的位置(这有点硬编码,但基本上如果你要将比例保持在较小框的 1/10,那么这个特定部分将与不同的图像)。

然后是基本的Tk() 启动,然后是.mainloop()

在中间只需打开图片并将其设置为 2 个变量并调整将转到导航框的那个。

然后创建主画布,其中将显示主图像。

然后将该图像添加到画布并保留 id 引用,以便以后可以移动该图像。

创建较小的画布,不要打包它或其他任何东西,而是将实例作为窗口添加到主画布,使其位于其顶部(如您问题中的照片) .它再次以硬编码的方式放置。

然后添加导航图片到导航画布中,并添加将要移动的小框。

然后绑定函数nav_canvas中的鼠标活动。

如果您有任何问题,请提出。 请注意如果您使用自己的图像,则不需要try/except,它会始终随程序一起提供。之所以存在,是因为您可能没有具有确切名称的确切尺寸的确切图片,因此出于测试目的,这只是暂时的。

编辑:将代码放在继承自 Frameclass 中,以便可以将其作为小部件放置。

【讨论】:

  • 非常感谢。是否可以同时移动两者?移动画布,移动迷你窗口?因为 Canvas 被冻结了
  • 同时是什么意思?它们确实同时移动,只要您在nav_canvas 中移动白色矩形,它就会立即移动图像。也不需要移动画布,只需移动其中的对象即可。
  • 这里有点困惑。您是否希望能够在没有任何迷你窗口的情况下仅通过鼠标移动图像?因为你展示的图片有一个小窗口?还是只是应该显示用户在图像上的位置?那么基本上反转功能
  • 添加了代码来做我认为你的意思。 (答案最后)
  • @ZacCherbourg 我编辑了代码。现在该类可以用作小部件,因此您将其放入您的 GUI 应该没有问题
猜你喜欢
  • 2017-04-08
  • 1970-01-01
  • 2013-01-03
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-15
相关资源
最近更新 更多