所以,我的代码是一团糟,但我希望它能帮助你入门并获得基本的想法。
第一个想法是需要将<Motion>鼠标事件绑定到c画布上。
bind 方法有两个参数:一个事件,它说明什么时候做某事,一个函数,它说明做什么。
我选择定义一个redraw_chart 函数,根据鼠标的位置绘制饼图。
这个函数是在<Motion>事件上调用的,所以我绑定如下:
c.bind('<Motion>', lambda e: redraw_chart(e.x, e.y))
lambda函数只是一个匿名函数,它接收引发的事件,并将事件的两个坐标(即鼠标坐标)传递给redraw_chart。
redraw_chart 函数真的很笨:它根据收到的坐标绘制饼图:
def redraw_chart(x, y):
global redCode, blueCode, whiteCode
arc = get_arc(x, y)
if arc == "red":
c.itemconfig(redCode, fill="green")
c.itemconfig(redCode, fill="blue")
c.itemconfig(redCode, fill="white")
elif arc == "blue":
c.itemconfig(redCode, fill="red")
c.itemconfig(redCode, fill="green")
c.itemconfig(redCode, fill="white")
elif arc == "white":
c.itemconfig(redCode, fill="red")
c.itemconfig(redCode, fill="blue")
c.itemconfig(redCode, fill="green")
else:
c.itemconfig(redCode, fill="green")
c.itemconfig(redCode, fill="blue")
c.itemconfig(redCode, fill="white")
现在,redCode、blueCode 和 whiteCode 是什么?
它们是c.create_arc 方法创建的三个弧对象的地址。
它们对于修改弧线很有用,以避免创建新的弧线。
还有一件事需要定义:get_arc 函数。
get_arc 函数接受(x, y) 对,代表画布的一个点,并返回相应的弧:
def get_arc(x, y):
if is_in_arc(x, y, redArc[0], redArc[0]+redArc[1]):
return "red"
elif is_in_arc(x, y, blueArc[0], blueArc[0]+blueArc[1]):
return "blue"
elif is_in_arc(x, y, whiteArc[0], whiteArc[0]+whiteArc[1]):
return "white"
else:
return None
它依赖于is_in_arc 函数,该函数获取一个点、饼图的一部分,并判断该点是否位于该部分中。
def is_in_arc(x, y, angle0, angle1):
if (x-50)**2 + (y-50)**2 > 48**2:
return False
theta = - np.arctan2(y-50, x-50)
return angle0 <= frac(theta) <= angle1
来自numpy 的np.arctan2 函数返回与(x, y) 点对应的弧度角。
然后,fract 方法返回相应的度值。
我修改了它,因为我并没有真正理解你的:
def frac(n):
if n < 0:
n += 2*np.pi
return 360 * n / (2*np.pi)
这就是它的样子。您无法在屏幕截图中看到光标,但我向您保证,悬停时部分会变为绿色。
完整代码如下:
import tkinter as tk
import numpy as np
def frac(n):
if n < 0:
n += 2*np.pi
return 360 * n / (2*np.pi)
c = tk.Canvas(width=100, height=100)
c.pack()
redArc = (frac(0), frac(np.pi/3))
blueArc = (frac(np.pi/3), frac(4*np.pi/3))
whiteArc = (frac(5*np.pi/3), frac(np.pi/3))
redCode = c.create_arc((2,2,98,98), fill="red", start=redArc[0], extent=redArc[1])
blueCode = c.create_arc((2,2,98,98), fill="blue", start=blueArc[0], extent=blueArc[1])
whiteCode = c.create_arc((2,2,98,98), fill="white", start=whiteArc[0], extent=whiteArc[1])
def is_in_arc(x, y, angle0, angle1):
if (x-50)**2 + (y-50)**2 > 48**2:
return False
theta = - np.arctan2(y-50, x-50)
return angle0 <= frac(theta) <= angle1
def get_arc(x, y):
if is_in_arc(x, y, redArc[0], redArc[0]+redArc[1]):
return "red"
elif is_in_arc(x, y, blueArc[0], blueArc[0]+blueArc[1]):
return "blue"
elif is_in_arc(x, y, whiteArc[0], whiteArc[0]+whiteArc[1]):
return "white"
else:
return None
def redraw_chart(x, y):
global redCode, blueCode, whiteCode
arc = get_arc(x, y)
if arc == "red":
c.itemconfig(redCode, fill="green")
c.itemconfig(redCode, fill="blue")
c.itemconfig(redCode, fill="white")
elif arc == "blue":
c.itemconfig(redCode, fill="red")
c.itemconfig(redCode, fill="green")
c.itemconfig(redCode, fill="white")
elif arc == "white":
c.itemconfig(redCode, fill="red")
c.itemconfig(redCode, fill="blue")
c.itemconfig(redCode, fill="green")
else:
c.itemconfig(redCode, fill="green")
c.itemconfig(redCode, fill="blue")
c.itemconfig(redCode, fill="white")
c.bind('<Motion>', lambda e: redraw_chart(e.x, e.y))
c.mainloop()