Draw rectangle on mouse click [Python]

旧街凉风 提交于 2020-01-03 03:45:05

问题


def xaxis(event):
   x1, y1 = (event.x - 1), (event.y - 1)

def yaxis(event):
   x2, y2 = (event.x + 1), (event.y + 1)

def create(event):
   w.create_rectangle(x1,y1,x2,y2,fill='Black')

w = Canvas(root, width=canvas_width, height=canvas_height)
w.config(cursor='cross')
w.pack(expand=YES, fill=BOTH)
w.bind("<Button-1>", xaxis)
w.bind("<ButtonRelease-1>", yaxis)
w.bind("<ButtonRelease-1>", create)

The shell says

Exception in Tkinter callback Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1410, in call return self.func(*args) File "/Users/Leo/Desktop/draw.py", line 22, in create w.create_rectangle(x1,y1,x2,y2,fill='Black') NameError: global name 'x1' is not defined

and it think the create function is not able to get the coordinates of the other functions...

I did it this way because I need the coordinates later!

I hope you can help me.. ;-) Thanks!


回答1:


Solving the "global name 'x1' is not defined" problem

A good rule of thumb when debugging is to assume the error message is telling the truth. In this case it is saying that there is no global variable named "x1". So ask youself "why?". Either you aren't creating an "x1" variable at all, or you're creating it in such a way that it's not global.

In your case, when you define x1, y1, x2 and y2, you are creating them as local variables. This is python's default behavior when creating variables. The simplest solution is to declare them as global:

def xaxis(event):
   global x1, y1
   x1, y1 = (event.x - 1), (event.y - 1)

def yaxis(event):
    global x2, y2
    x2, y2 = (event.x + 1), (event.y + 1)

An unrelated second problem

You have another problem in your code with how you do your bindings. Consider this code snippet:

w.bind("<ButtonRelease-1>", yaxis)
w.bind("<ButtonRelease-1>", create)

You aren't creating two bindings, you are creating one, and then overwriting it with another. You don't need two bindings, however. You can call your yaxis function from within the create function.

Using an object oriented approach

However, global variables aren't usually the best solution to a problem. If you switched to using an object-oriented approach then you can store the coordinates as attributes of an object. Here's a complete, working example:

import Tkinter as tk

class ExampleApp(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.x = self.y = 0
        self.canvas = tk.Canvas(self, width=400, height=400, cursor="cross")
        self.canvas.pack(side="top", fill="both", expand=True)
        self.canvas.bind("<ButtonPress-1>", self.on_button_press)
        self.canvas.bind("<ButtonRelease-1>", self.on_button_release)

    def on_button_press(self, event):
        self.x = event.x
        self.y = event.y

    def on_button_release(self, event):
        x0,y0 = (self.x, self.y)
        x1,y1 = (event.x, event.y)

        self.canvas.create_rectangle(x0,y0,x1,y1, fill="black")

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

Drawing as you drag the mouse

If you want to draw the rectangle as you drag the cursor, you can alter the program to create the rectangle on the button press. If you either give the object a unique tag or save the canvas id, you can set up a mouse motion event to adjust the coordinates of the current rectangle using the coords method of the canvas object. I'll leave that as an exercise for the reader since it isn't directly related to the question that was asked.




回答2:


You have only set x1 etc. locally in their respective functions (e.g. xaxis), and so they can't be used in create. You could rewrite these so they return the desired results:

def xaxis(event):
   return (event.x - 1), (event.y - 1) # x1, y1

def yaxis(event):
   return (event.x + 1), (event.y + 1) # x2, y2

def create(event):
   x1, y1 = xaxis(event)
   x2, y2 = yaxis(event)
   w.create_rectangle(x1,y1,x2,y2,fill='Black')



回答3:


You are storing results of x1, x2 ... in local variables. These values are lost when you exit the event handler function. You must use global vars or a dedicated object for managing event handlers. I would prefer the 2nd solution.

Another issue is that you have two functions bound with Button-Release1. My solution is not to bound yaxis and to call it from create. A cleaner approach can be found :)

See example below. It should work:

from Tkinter import *

root=Tk()

class Handler:

    def __init__(self, w):
        self.w = w
        w.bind("<Button-1>", self.xaxis)
        #w.bind("<ButtonRelease-1>", self.yaxis)
        w.bind("<ButtonRelease-1>", self.create)


    def xaxis(self, event):
        self.x1, self.y1 = (event.x - 1), (event.y - 1)

    def yaxis(self, event):
        self.x2, self.y2 = (event.x + 1), (event.y + 1)

    def create(self, event):
        self.yaxis(event)
        self.w.create_rectangle(self.x1,self.y1,self.x2,self.y2,fill='Black')

w = Canvas(root, width=200, height=200)
w.config(cursor='cross')
w.pack(expand=YES, fill=BOTH)

Handler(w)


root.mainloop()


来源:https://stackoverflow.com/questions/12837575/draw-rectangle-on-mouse-click-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!