Tkinter:使用鼠标绘制矩形

Posted

技术标签:

【中文标题】Tkinter:使用鼠标绘制矩形【英文标题】:Tkinter: Draw rectangle using a mouse 【发布时间】:2015-06-29 15:18:53 【问题描述】:

请帮我解决这个问题。

我想让用户使用鼠标在图片中的特定感兴趣区域周围绘制一个随机矩形(通过单击鼠标的右键或左键直到他释放它)。

我处理的是大图像(大于我屏幕分辨率的图像,比如这个one),所以用户需要滚动窗口才能完全看到图片。

这是我试图显示一张大图片的代码,但我不知道如何允许用户使用鼠标在对象上绘制一个矩形(比如图片中的一个人):

from Tkinter import *
import Image,ImageTk

root=Tk()
canv=Canvas(root,relief=SUNKEN)

sbarv=Scrollbar(root,orient=VERTICAL)
sbarh=Scrollbar(root,orien=HORIZONTAL)

sbarv.config(command=canv.yview)
sbarh.config(command=canv.xview)

canv.config(yscrollcommand=sbarv.set)
canv.config(xscrollcommand=sbarh.set)

canv.grid(row=0,column=0,sticky=N+S+E+W)
sbarv.grid(row=0,column=1,sticky=N+S)

sbarh.grid(row=1,column=0,sticky=E+W)


im=Image.open("image.jpg")
width,height=im.size
canv.config(scrollregion=(0,0,width,height))
im2=ImageTk.PhotoImage(im)
imgtag=canv.create_image(0,0,anchor="nw",image=im2)

root.mainloop()

编辑 1:

不得填充矩形。我的意思是我只想画它的 4 条线(线段),但里面必须是空的,我只想画一个像素宽度的轮廓。

我还想在鼠标移动(拖动)时进行绘制,而不是在按钮释放后进行绘制。

另外,请注意,要绘制的矩形可能很长,我的意思是垂直滚动条需要向下移动才能界定整个感兴趣的对象(假设它是一个人)

我们将不胜感激。

非常感谢您

编辑 2:

按照上面给我的链接,我对此进行了编码。我的问题是 滚动条没有出现。有人能告诉我为什么吗?

请注意,在这段代码中,我解决了 EDIT 1 中突出显示的第一个和第二个问题:

import PIL.Image
import Image
import ImageTk
from Tkinter import *    


class ExampleApp(Frame):
    def __init__(self,master):
        Frame.__init__(self,master=None)
        self.x = self.y = 0
        self.canvas = Canvas(master,  cursor="cross")

        self.sbarv=Scrollbar(self,orient=VERTICAL)
        self.sbarh=Scrollbar(self,orient=HORIZONTAL)
        self.sbarv.config(command=self.canvas.yview)
        self.sbarh.config(command=self.canvas.xview)

        self.canvas.config(yscrollcommand=self.sbarv.set)
        self.canvas.config(xscrollcommand=self.sbarh.set)

        self.canvas.grid(row=0,column=0,sticky=N+S+E+W)
        self.sbarv.grid(row=0,column=1,stick=N+S)
        self.sbarh.grid(row=1,column=0,sticky=E+W)

        self.canvas.bind("<ButtonPress-1>", self.on_button_press)
        self.canvas.bind("<B1-Motion>", self.on_move_press)
        self.canvas.bind("<ButtonRelease-1>", self.on_button_release)

        self.rect = None

        self.start_x = None
        self.start_y = None


        self.im = PIL.Image.open("logo.png")
        self.wazil,self.lard=self.im.size
        self.canvas.config(scrollregion=(0,0,self.wazil,self.lard))
        self.tk_im = ImageTk.PhotoImage(self.im)
        self.canvas.create_image(0,0,anchor="nw",image=self.tk_im)   


    def on_button_press(self, event):
        # save mouse drag start position
        self.start_x = event.x
        self.start_y = event.y

        # create rectangle if not yet exist
        #if not self.rect:
        self.rect = self.canvas.create_rectangle(self.x, self.y, 1, 1, fill="")

    def on_move_press(self, event):
        curX, curY = (event.x, event.y)

        # expand rectangle as you drag the mouse
        self.canvas.coords(self.rect, self.start_x, self.start_y, curX, curY)    


    def on_button_release(self, event):
        pass    

if __name__ == "__main__":
    root=Tk()
    app = ExampleApp(root)
    root.mainloop()

【问题讨论】:

这应该会有所帮助:Drawing rectangle using mouse events in Tkinter @CraigBurgler 非常感谢你,我会看看我能用那个链接中的答案做些什么。 @CraigBurgler 如果需要,您可以查看我的第二次编辑 【参考方案1】:

滚动条不显示,因为您将grid它们放入了一个未放入父窗口的框架 (self.sbarv=Scrollbar(self, ...))。不过,您可以直接将 Canvas 网格化到父窗口中 (self.canvas = Canvas(master, ...))。

你应该做的也是把画布放在self中,然后使用

将框架打包到主窗口中
app = ExampleApp(root)
app.pack()

但是,滚动时,event.xevent.y 不再代表画布上的正确位置,因此您应该使用

self.start_x = self.canvas.canvasx(event.x)
self.start_y = self.canvas.canvasy(event.y)

curX = self.canvas.canvasx(event.x)
curY = self.canvas.canvasy(event.y)

那么,我知道您想在鼠标拖动到画布的其中一个边框时自动滚动画布?为此,您需要检查鼠标是否位于画布的边缘之一,如果是则朝该方向滚动。你可以使用类似的东西:

w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
if event.x > 0.9*w:
    self.canvas.xview_scroll(1, 'units') 
elif event.x < 0.1*w:
    self.canvas.xview_scroll(-1, 'units')
if event.y > 0.9*h:
    self.canvas.yview_scroll(1, 'units') 
elif event.y < 0.1*h:
    self.canvas.yview_scroll(-1, 'units')

因此,您的代码中实现的所有内容都变为:

import PIL.Image
import Image
import ImageTk
from Tkinter import *    


class ExampleApp(Frame):
    def __init__(self,master):
        Frame.__init__(self,master=None)
        self.x = self.y = 0
        self.canvas = Canvas(self,  cursor="cross")

        self.sbarv=Scrollbar(self,orient=VERTICAL)
        self.sbarh=Scrollbar(self,orient=HORIZONTAL)
        self.sbarv.config(command=self.canvas.yview)
        self.sbarh.config(command=self.canvas.xview)

        self.canvas.config(yscrollcommand=self.sbarv.set)
        self.canvas.config(xscrollcommand=self.sbarh.set)

        self.canvas.grid(row=0,column=0,sticky=N+S+E+W)
        self.sbarv.grid(row=0,column=1,stick=N+S)
        self.sbarh.grid(row=1,column=0,sticky=E+W)

        self.canvas.bind("<ButtonPress-1>", self.on_button_press)
        self.canvas.bind("<B1-Motion>", self.on_move_press)
        self.canvas.bind("<ButtonRelease-1>", self.on_button_release)

        self.rect = None

        self.start_x = None
        self.start_y = None

        self.im = PIL.Image.open("logo.png")
        self.wazil,self.lard=self.im.size
        self.canvas.config(scrollregion=(0,0,self.wazil,self.lard))
        self.tk_im = ImageTk.PhotoImage(self.im)
        self.canvas.create_image(0,0,anchor="nw",image=self.tk_im)   


    def on_button_press(self, event):
        # save mouse drag start position
        self.start_x = self.canvas.canvasx(event.x)
        self.start_y = self.canvas.canvasy(event.y)

        # create rectangle if not yet exist
        if not self.rect:
            self.rect = self.canvas.create_rectangle(self.x, self.y, 1, 1, outline='red')

    def on_move_press(self, event):
        curX = self.canvas.canvasx(event.x)
        curY = self.canvas.canvasy(event.y)

        w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
        if event.x > 0.9*w:
            self.canvas.xview_scroll(1, 'units') 
        elif event.x < 0.1*w:
            self.canvas.xview_scroll(-1, 'units')
        if event.y > 0.9*h:
            self.canvas.yview_scroll(1, 'units') 
        elif event.y < 0.1*h:
            self.canvas.yview_scroll(-1, 'units')

        # expand rectangle as you drag the mouse
        self.canvas.coords(self.rect, self.start_x, self.start_y, curX, curY)    

    def on_button_release(self, event):
        pass    

if __name__ == "__main__":
    root=Tk()
    app = ExampleApp(root)
    app.pack()
    root.mainloop()

【讨论】:

先生,我不知道该如何感谢您。在问这里之前,我在 4 天内一直在努力解决这个问题。非常感谢您提供详细而令人信服的解释。 你甚至解决了我遇到但没有提到的一个问题:绘制一个新的矩形必须删除前一个。再次感谢您。 您几乎已经自己解决了这个问题,但它被注释掉了。 if not self.rect: 行确保只创建一次矩形,然后更改该矩形的位置。 很遗憾,我不能对你的答案投票 1000 次

以上是关于Tkinter:使用鼠标绘制矩形的主要内容,如果未能解决你的问题,请参考以下文章

如何清除 Tkinter 画布?

使用鼠标在 python tkinter 画布上绘制并获取指向列表的点?

Tkinter:如何为画布矩形的轮廓着色?

matplotlib 在 tkinter 画布中的缩放功能

GUI的最终选择 Tkinter:Canvas组件

Tkinter 控件详细介绍