简体   繁体   中英

change color of a pie chart when hovering mouse over it

I have just created a pie chart by Tkinter in Python as follows:

def frac(n): 
    return 360. * n / 500

import Tkinter
c = Tkinter.Canvas(width=100, height=100); c.pack()
c.create_arc((2,2,98,98), fill="red", start=frac(0), extent = 
frac(100))
c.create_arc((2,2,98,98), fill="blue", start=frac(100), extent = frac(400))
c.create_arc((2,2,98,98), fill="white", start=frac(400), extent = frac(100), width=0)
c.mainloop()

This is the result:

在此输入图像描述

Now, I want to change the color of each slice when hovering the mouse over it. How can I do that? Many thanks

So, my code is a mess, but I hope it will help you get started and get the basic ideas.

The first idea is that you need to bind the <Motion> mouse event to the c canvas. The bind method takes two arguments: an event, which says when to do something, and a function, which says what to do. I chose to define a redraw_chart function, that draws the pie according to the position of the mouse. This function is what will be called on a <Motion> event, so I bind as follows:

c.bind('<Motion>', lambda e: redraw_chart(e.x, e.y))

The lambda function is just an anonymous function, that receives the event raised, and passes the two coordinates of the event (that is, the coordinates of the mouse) to the redraw_chart .

The redraw_chart function is really dumb: it draws the pie according to the coordinates it received:

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

Now, what are redCode , blueCode and whiteCode ? They are the addresses of the three arc objects created by the c.create_arc method. They are useful to modify the arcs, so as to avoid creating new ones. There is still one thing left to define: the get_arc function.

The get_arc function takes a (x, y) couple, representing a point of the canvas, and returns the corresponding arc:

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

It relies on the is_in_arc function, that takes a point, a portion of the pie, and tells if the point lies in the portion.

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

The np.arctan2 function from numpy returns the angle in radians corresponding to the (x, y) point. Then, the fract method returns the corresponding value in degrees. I modified it, because I did not really understand yours:

def frac(n):
    if n < 0:
        n += 2*np.pi
    return 360 * n / (2*np.pi)

So here is what it looks like. You cannot see the cursor one the screenshot, but I guarantee you that the parts turn green when hovered.

饼图徘徊

Here is the complete code:

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

You can use the bind method to, well, bind an event and redraw the chart, like this:

def on_enter(event):
    c.create_arc((2,2,98,98), fill="orange", start=frac(100), extent = frac(400))
(...)
c.bind('<Enter>', on_enter)

See this answer for an example of how to embed the whole thing in a class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM