user8254375 user8254375 - 1 month ago 7
Python Question

My matplotlib bar-chart duplicates every time a function is run, how can I refresh my bar-chart?

My bar chart that I created by looking around the internet here and the bar chart seems to stack the same result on top of a previous plot from a function that is run instead of changing values while taking data from a database.

The code below utilizes classes and consists of a page I created in tkinter alongside the use of SQLite3 and a function:

class ViewTrackData(tkinter.Frame):
def __init__(self, parent, controller):
tkinter.Frame.__init__(self, parent)
# label1 = tkinter.Label(self, text="This is the page where track data will be shown.")
# label1.grid(row=50, column=70, sticky="nsew")

self.athleteData = ""
self.athlete = ""
self.position = 1
self.times = {}
self.roundFigUp = ""
self.roundFigDown = ""
self.x = []
self.y = []
self.text = ""


self.trackName = tkinter.Spinbox(self, values=trackTables, width=50, justify="center", font=entry_font,
foreground="#000000", background="#FFFFFF")
self.trackName.place(relx=0.15, rely=0.1)

self.eventYear = tkinter.Entry(self, width=50, justify="center", font=entry_font, foreground="#000000",
background="#FFFFFF")
self.eventYear.insert(0, "The year which the event took place.")
self.eventYear.place(relx=0.15, rely=0.41)

self.yearGroup = tkinter.Spinbox(self, from_=7, to=11, width=50, justify="center", font=entry_font,
foreground="#000000", background="#FFFFFF")
self.yearGroup.place(relx=0.15, rely=0.70)

self.filterButton = tkinter.Button(self, text="filter", bg="#383A39", fg="#AB97BD", width=15, height=3,
command=self.show_graph)
self.filterButton.place(relx=0.24, rely=0.82)

self.infoLabel = tkinter.Label(self, height=25, width=40, wraplength=300, anchor="center", relief="groove", font=font_use)
self.infoLabel.place(relx=0.52, rely=0.1)

def show_graph(self):
self.infoLabel.configure(text="")
c.execute("SELECT * FROM " + self.trackName.get() + " WHERE Year_Of_Event = ? AND Athlete_Year_Group = ? "
"ORDER BY ATHLETE_TIME ASC", (self.eventYear.get(), self.yearGroup.get()))
self.athleteData = c.fetchall()
if self.athleteData is None:
self.infoLabel.configure(text="There are no records in this year for this year group.")
else:
for self.item in self.athleteData:
self.athlete = self.item
self.times.update({self.athlete[1] : self.position}) # adds data about the name of the athlete as the key and the position which a athlete placed as a value.
self.position += 1 # increments the position value
self.x.append(self.times[self.athlete[1]]) # adds the position in which an athlete placed as an argument to the x axis
self.truncatedTime = str(self.athlete[3]).replace((self.athlete[3])[5:],"") # turns the value for the seconds an athlete has run under into a string and slices off " seconds"
print(self.truncatedTime)
if int((self.truncatedTime)[3]) >= 5: # if statement which rounds up or down depending on the first value after the decimal point.
self.roundFigUp = math.ceil(float(self.truncatedTime))
self.y.append(int(self.roundFigUp))
else:
self.roundFigDown = math.floor(float(self.truncatedTime))
self.y.append(int(self.roundFigDown))
self.text += ("representing number " + str(self.times[self.athlete[1]]) + " is " + str(self.athlete[1])
+ ", ")
self.infoLabel.configure(text=self.text)
print(self.athlete)

plt.bar(self.x, self.y, label="Athlete time bars", color="green")

plt.xlabel("Athlete Positions")
plt.ylabel("Athlete Time")
plt.title("Bar chart, representing times of athletes.")
plt.legend()
plt.show()


I have tried to search for an answer but I can't seem to be able implement everything I see into my program.

I apologize beforehand if this question seems noob-ish to you.

There is also one last thing i would like to request.

Could you explain to me how I should go about using plt.xticks? In order to change the labels from numbers to the actual names of the athletes. Which is denoted from self.athlete[1].

Answer Source

Every time show_graph is called more data is appended to self.x and self.y:

self.x.append(self.times[self.athlete[1]]) 
...
if int((self.truncatedTime)[3]) >= 5: 
    self.roundFigUp = math.ceil(float(self.truncatedTime))
    self.y.append(int(self.roundFigUp))
else:
    self.roundFigDown = math.floor(float(self.truncatedTime))
    self.y.append(int(self.roundFigDown))

The fix is to re-initialize self.x and self.y at the beginning of show_graph:

def show_graph(self):
    self.x = []
    self.y = []
    ...

The initialization of self.x and self.y that happens within the __init__ method only happens once, when the ViewTrackData Frame was instantiated.


PS. You may also be interested in seeing this example which shows how to embed a matplotlib figure inside a Tk app.