Tkinter: dibuja un rectángulo usando un ratón

Porfavor ayudame a resolver este problema.

Quiero permitir que el usuario dibuje un rectángulo aleatorio alrededor de una región específica de interés en una imagen con el mouse (haciendo clic con el botón derecho o izquierdo del mouse hasta que lo suelte).

Trato con imágenes grandes (imágenes más grandes que la resolución de mi pantalla, como esta), por lo que el usuario debe desplazarse por la ventana para poder ver la imagen completamente.

Aquí está el código que intenté solo para mostrar una imagen grande, pero no tengo idea de cómo permitir que el usuario dibuje usando su mouse un rectángulo sobre un objeto (por ejemplo, una persona en una imagen):

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() 

EDITAR 1:

  • El rectángulo no debe ser rellenado. Quiero decir, solo quiero dibujar sus 4 líneas (segmentos) pero debe estar vacío por dentro, quiero dibujar solo sus contornos en un ancho de píxel.

  • También quiero dibujar cuando el cursor se está moviendo (arrastrando) no después de soltar el botón.

  • Además, tenga en cuenta que el rectángulo para dibujar puede ser largo, me refiero a que la barra de desplazamiento vertical deberá moverse hacia abajo para poder delimitar todo el objeto de interés (digamos que es una persona)

Cualquier ayuda será muy apreciada.

Muchas gracias por adelantado

EDIT 2:

Siguiendo el enlace que me dieron arriba, codifiqué esto. Mi problema es que las barras de desplazamiento no aparecen . Puede ser que alguien me pueda decir por qué?

Tenga en cuenta que en este código, resolví el primer y el segundo problema resaltado en 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("", self.on_button_press) self.canvas.bind("", self.on_move_press) self.canvas.bind("", 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() 

Las barras de desplazamiento no se muestran porque las self.sbarv=Scrollbar(self, ...) en un marco ( self.sbarv=Scrollbar(self, ...) ) que no coloca en la ventana principal. Sin embargo, puede self.canvas = Canvas(master, ...) directamente el Canvas en la ventana principal ( self.canvas = Canvas(master, ...) ).

Lo que debes hacer es poner el canvas en self y luego empacar el marco en la ventana maestra usando

 app = ExampleApp(root) app.pack() 

Sin embargo, cuando se desplaza, el event.x y el event.y ya no representan la posición correcta en el canvas, por lo que debe usar

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

y

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

Entonces, entiendo que desea desplazar automáticamente el canvas cuando el mouse está arrastrando a uno de los bordes del canvas. Para hacerlo, debe comprobar si el mouse está en uno de los bordes del canvas y desplazarse en esa dirección si lo está. Puedes usar algo como:

 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') 

Entonces, todo lo implementado en tu código se convierte en:

 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("", self.on_button_press) self.canvas.bind("", self.on_move_press) self.canvas.bind("", 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()