I have a little applet that I wrote in Python and Tkinter, and I was wondering what a good way would be to make it public, or open source or whatever.I think it's quite fun.  You can add players, add singles games, add doubles games, and then it gives you a table of recent games and ratings with a bunch of stats (games played, scores, streaks etc).You need to run the Python code, rather than just run an executable.What would be a good way to go about releasing this into the wild?  It would probably be a handy example of Tkinter code (which would be nice), and might even be useful for someone trying to implement an elo system somewhere.  And I'm sure that some interesting improvements could be made.I've stripped out some of the doubles features to make it fit in the limit (although I'm sure a good coder could do it with cleverer class structures).The full code is here, which includes a little code to add random games (you need to add players first).import datetimeimport picklefrom operator import attrgetter #https://docs.python.org/2/howto/sorting.htmlimport refrom tkinter import *from tkinter import ttkimport tkinter.font as tkFont   #import itertoolsimport bisectimport matplotlibmatplotlib.use(TkAgg)from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg# implement the default mpl key bindingsfrom matplotlib.backend_bases import key_press_handlerfrom matplotlib.figure import Figure#To do list# - make it an executable for both ubuntu and windowsclass AutocompleteCombobox(ttk.Combobox):  #https://mail.python.org/pipermail/tkinter-discuss/2012-January/003041.html    def set_completion_list(self, completion_list, menu):        Use our completion list as our drop down selection menu, arrows move through menu.        self._completion_list = completion_list  #sorted(completion_list, key=str.lower) # Work with a sorted list        self._hits = []        self._hit_index = 0        self.position = 0        self.bind('<KeyRelease>', self.handle_keyrelease)        self['values'] = self._completion_list  # Setup our popup menu        self.menu = menu    def autocomplete(self, delta=0):        autocomplete the Combobox, delta may be 0/1/-1 to cycle through possible hits        if delta: # need to delete selection otherwise we would fix the current position            self.delete(self.position, END)        else: # set position to end so selection starts where textentry ended            self.position = len(self.get())        # collect hits        _hits = []        for element in self._completion_list:            if element.lower().startswith(self.get().lower()): # Match case insensitively                _hits.append(element)        # if we have a new hit list, keep this in mind        if _hits != self._hits:            self._hit_index = 0            self._hits=_hits        # only allow cycling if we are in a known hit list        if _hits == self._hits and self._hits:            self._hit_index = (self._hit_index + delta) % len(self._hits)        # now finally perform the auto completion        if self._hits:            self.delete(0,END)            self.insert(0,self._hits[self._hit_index])            self.select_range(self.position,END)    def handle_keyrelease(self, event):        event handler for the keyrelease event on this widget        if event.keysym == BackSpace:            self.delete(self.index(INSERT), END)            self.position = self.index(END)        if event.keysym == Left:            if self.position < self.index(END): # delete the selection                self.delete(self.position, END)            else:                self.position = self.position-1 # delete one character                self.delete(self.position, END)        if event.keysym == Right:            self.position = self.index(END) # go to end (no selection)        if len(event.keysym) == 1:            self.autocomplete()            self.menu.selected()        # No need for up/down, we'll jump to the popup        # list at the position of the autocompletionclass Player:  def __init__(self,name,singles=1,rating=1200):    self.name = name    self.singles=singles  #1=singles,0=doubles,2=pairs    self.initial_rating = rating    self.rating = rating    self.active = False    self.games_played = 0    self.wins = 0    self.losses = 0    self.draws = 0    self.points_for = 0    self.points_against = 0    self.streak = 0  #positive is winning/negative is losing    self.max_winning_streak = 0    self.max_losing_streak = 0    self.max_rating = rating    self.min_rating = rating    self.last_game = datetime.datetime.now()  def __str__(self):    return %s,%d,%s%(self.name,self.rating,self.singles)  def __repr__(self):    return Player('%s',%d,%s)%(self.name,self.rating,self.singles)  def reset(self):    self.rating = self.initial_rating    self.active = False    self.games_played = 0    self.wins = 0    self.losses = 0    self.draws = 0    self.points_for = 0    self.points_against = 0    self.streak = 0  #positive is winning/negative is losing    self.max_winning_streak = 0    self.max_losing_streak = 0    self.max_rating = self.initial_rating    self.min_rating = self.initial_ratingclass Game:  def __init__(self,player1,player2,score1,score2,date):    self.player1 = player1    self.player2 = player2    self.score1 = score1    self.score2 = score2    self.date = date    self.play()  def __repr__(self):    return Game(%s,%s,%d,%d,%s)%(self.player1,self.player2,self.score1,self.score2,self.date)  def __str__(self):    return %s:\t%s:%d\t%s:%d)%(self.date,self.player1,self.score1,self.player2,self.score2)  def play(self):    #datetime    self.player1.last_game = self.date    self.player2.last_game = self.date    #points    self.player1.points_for+=self.score1    self.player1.points_against+=self.score2    self.player2.points_for += self.score2    self.player2.points_against+=self.score1    #games    self.player1.games_played+=1    self.player2.games_played+=1    #wins & streaks    if self.score1>self.score2:      self.player1.wins+=1      self.player2.losses+=1      self.player1.streak = max([1,self.player1.streak+1])      self.player2.streak = min([-1,self.player2.streak-1])      self.player1.max_winning_streak = max([self.player1.max_winning_streak, self.player1.streak])      self.player2.max_losing_streak  = max([self.player2.max_losing_streak, -self.player2.streak])    elif self.score1==self.score2:      self.player1.draws+=1      self.player2.draws+=1      self.player1.streak=0      self.player2.streak=0    else:      self.player1.losses+=1      self.player2.wins+=1      self.player2.streak = max([1,self.player2.streak+1])      self.player1.streak = min([-1,self.player1.streak-1])      self.player2.max_winning_streak = max([self.player2.max_winning_streak, self.player2.streak])      self.player1.max_losing_streak  = max([self.player1.max_losing_streak, -self.player1.streak])    #rating    expected_score = 1.0/(1+10**((self.player2.rating-self.player1.rating)/400.0))    max_score = max([self.score1,self.score2])    actual_score = (max_score+self.score1-self.score2)/(2.0*max_score)    rating_change = int(round(32*(actual_score-expected_score),0))    self.player1.rating += rating_change    self.player2.rating -= rating_change    self.rating_change = rating_change    #max/min rating    self.player1.max_rating = max([self.player1.max_rating,self.player1.rating])    self.player1.min_rating = min([self.player1.min_rating,self.player1.rating])    self.player2.max_rating = max([self.player2.max_rating,self.player2.rating])    self.player2.min_rating = min([self.player2.min_rating,self.player2.rating])class DoublesGame:  def __init__(self,t1p1,t1p2,t2p1,t2p2,score1,score2,date):    self.t1p1 = t1p1    self.t1p2 = t1p2    self.t2p1 = t2p1    self.t2p2 = t2p2    self.score1 = score1    self.score2 = score2    self.date = date    self.play()  def __repr__(self):    return DoublesGame(%s,%s,%s,%s,%d,%d,%s)%(self.t1p1,self.t1p2,self.t2p1,self.t2p2,self.score1,self.score2,self.date)  def __str__(self):    return %s:\t%s/%s:%d\t%s/%s:%d)%(self.date,self.t1p1,self.t1p2,self.score1,self.t2p1,self.t2p2,self.score2)  def play(self):    #Find/make pairs (stripped out for codereview)    passdef saveGames(fileName=foos.pkl):  myF = open(fileName,wb)  pickle.dump((players,games,doubles_games,settings),myF)  myF.close()def loadGames(fileName=foos.pkl):  global players  global games  global doubles_games  global settings  myF = open(fileName,'rb')  a = pickle.load(myF)  myF.close()  players,games,doubles_games,b = a  settings.update(b)  def makeGraph(singles=1):  if singles==2: singles=0  d = dict()  keys = [Date]  for p in players:    if p.singles==singles:      keys.append(p.name)      d[p.name]=[1200]  if singles==1:    d[Date]=[games[0].date - datetime.timedelta(days=1)]    for g in games:      d[Date].append(g.date)      for p in players:        if p.singles==singles:          d[p.name].append(d[p.name][-1])      d[g.player1.name][-1] = d[g.player1.name][-1] + g.rating_change      d[g.player2.name][-1] = d[g.player2.name][-1] - g.rating_change  else:    d[Date]=[doubles_games[0].date - datetime.timedelta(days=1)]    for g in doubles_games:      d[Date].append(g.date)      for p in players:        if p.singles==singles:          d[p.name].append(d[p.name][-1])      d[g.t1p1.name][-1] = d[g.t1p1.name][-1] + g.rating_change      d[g.t1p2.name][-1] = d[g.t1p2.name][-1] + g.rating_change      d[g.t2p1.name][-1] = d[g.t2p1.name][-1] - g.rating_change      d[g.t2p2.name][-1] = d[g.t2p2.name][-1] - g.rating_change  ls = [--,-,:]  cs = [g,b,r,k]  annot2=[]  for k in keys[1:]:    annot2.append(d[k][-1]-2)  annot=[]  sortedKeys=[]  for a,k in zip(annot2,keys[1:]):    i=bisect.bisect(annot,a)    annot.insert(i,a)    sortedKeys.insert(i,k)  annot.reverse()  sortedKeys.reverse()  #print(annot,sortedKeys)  cont=True  #print(Optimizing annotations...,)  while cont:    for i in range(len(annot)-1):      if annot[i] - annot[i+1]<=5:        annot[i] = annot[i]+0.5        annot[i+1] = annot[i+1]-0.5        cont = False    cont = not cont  #print(done!)  l=len(d[k])  f = Figure(figsize=(12,8))  pt = f.add_subplot(111)  for i,k in enumerate(sortedKeys):    if k==Date: continue    pt.plot(range(l),d[k],cs[sortedKeys.index(k)%len(cs)]+ls[sortedKeys.index(k)%len(ls)],label = k)    pt.annotate(k,((l-1)*1.01,annot[i]),color=cs[sortedKeys.index(k)%len(cs)])  pt.set_ylabel(Rating)  pt.set_xlabel(Games)  pt.set_xlim((-l*0.2,l*1.12))  pt.set_title(Ratings at +datetime.datetime.now().strftime(%Y-%m-%d))  pt.legend(loc=upper left)  xr,xl=[0],[d[Date][0].strftime(%d-%b)]  for i,dt in enumerate(d[Date]):    if dt != xl[-1]:      if i-xr[-1]>l*0.05:        xr.append(i)        xl.append(dt.strftime(%d-%b))  if xr[-1]<0.95*l:    xr.append(l)    xl.append(dt.strftime(%d-%b))  else:    xr[-1]=l    xl[-1]=d[Date][-1].strftime(%d-%b)  pt.set_xticks(xr)  pt.set_xticklabels(xl,rotation=90)  return fclass AddPlayer:  def __init__(self,mainmenu):    self.root = Toplevel(mainmenu.root)    self.root.title(Add new player)    mainframe = ttk.Frame(self.root,padding=3 3 12 12)    mainframe.grid(column=0,row=0, sticky=(N,W,E,S))    mainframe.columnconfigure(0,weight=1)    mainframe.rowconfigure(0,weight=1)    self.name = StringVar()    self.warning = StringVar()    self.name_entry = ttk.Entry(mainframe, width=12, textvariable=self.name)    self.name_entry.grid(column=2,row=1,sticky=(W,E))    ttk.Label(mainframe,textvariable=self.warning).grid(column=1,row=2,columnspan=2,sticky=(W,E))    ttk.Button(mainframe,text=Add Player, command=self.addPlayer).grid(column=3,row=1,sticky=W)    self.root.bind('<Return>',self.addPlayer)    ttk.Button(mainframe,text=Close,command=self.quit).grid(column=3,row=2,sticky=W)    self.root.bind('<Escape>',self.quit)    ttk.Label(mainframe, text=Player's name:).grid(column=1,row=1,sticky=W)    for child in mainframe.winfo_children(): child.grid_configure(padx=5,pady=5)    self.root.focus()    self.name_entry.focus()    self.root.mainloop()  def quit(self,*args):    self.root.destroy()  def addPlayer(self,*args):    newname = self.name.get()    newname = newname.strip()    newname = re.sub(r'[ ]',_,newname)    newname = re.sub(r'[^a-zA-Z0-9_]', '', newname)    if newname == :      self.warning.set(Name must be alphanumeric!)      return    for p in players:      if p.name.lower()==newname.lower():        self.warning.set(Name %s already used!%newname)        self.name.set()        self.name_entry.focus()        return    newname = newname[0].upper()+newname[1:].lower()    players.append(Player(newname,True))    players.append(Player(newname,False))    saveGames()    self.warning.set(Player %s added!%newname)    self.name.set()    self.name_entry.focus()class AddGame:  def __init__(self,mainmenu):    self.mainmenu = mainmenu    self.root = Toplevel(mainmenu.root)    self.root.title(Add singles game)    mainframe = ttk.Frame(self.root,padding=3 3 12 12)    mainframe.grid(column=0,row=0, sticky=(N,W,E,S))    mainframe.columnconfigure(0,weight=1)    mainframe.rowconfigure(0,weight=1)    self.score1 = StringVar()    self.score2 = StringVar()    self.warning = StringVar()    self.score1.set('0')    self.score2.set('0')    self.ps=[]    for p in players:      if p.singles==True:        self.ps.append(p.name)    self.ps.sort()    self.ps1 = self.ps[:]    self.ps2 = self.ps[:]    self.player1_combo = AutocompleteCombobox(mainframe, width=12, font=tkFont.Font(size=28))    self.player1_combo.grid(column=2,row=2,sticky=(W,E))    self.player1_combo.bind(<<ComboboxSelected>>, self.selected)    self.player2_combo = AutocompleteCombobox(mainframe, width=12, font=tkFont.Font(size=28))    self.player2_combo.grid(column=2,row=4,sticky=(W,E))    self.player2_combo.bind(<<ComboboxSelected>>, self.selected)    self.selected()    self.score1_spin = Spinbox(mainframe, width=5, from_=0, to=settings['maxScore'], textvariable = self.score1, font=tkFont.Font(size=28))    self.score1_spin.grid(column=3,row=2,sticky=(W))    self.score2_spin = Spinbox(mainframe, width=5, from_=0, to=settings['maxScore'], textvariable = self.score2, font=tkFont.Font(size=28))    self.score2_spin.grid(column=3,row=4,sticky=(W))    ttk.Label(mainframe,textvariable=self.warning).grid(column=1,row=5,columnspan=4,sticky=(W,E))    ttk.Button(mainframe,text=Add Game, command=self.addGame).grid(column=4,row=2,sticky=W)    self.root.bind('<Return>',self.addGame)    ttk.Button(mainframe,text=Close,command=self.quit).grid(column=4,row=3,sticky=W)    self.root.bind('<Escape>',self.quit)    ttk.Label(mainframe, text=Player 1).grid(column=1,row=2,sticky=W)    ttk.Label(mainframe, text=versus).grid(column=2,row=3,sticky=E)    ttk.Label(mainframe, text=Player 2).grid(column=1,row=4,sticky=W)    ttk.Label(mainframe, text=Players:).grid(column=2,row=1,sticky=W)    ttk.Label(mainframe, text=Score:).grid(column=3,row=1,sticky=W)    for child in mainframe.winfo_children(): child.grid_configure(padx=5,pady=5)    self.root.focus()    self.player1_combo.focus()    self.root.mainloop()  def selected(self,*args):    self.ps1 = self.ps[:]    self.ps2 = self.ps[:]    self.player1 = self.player1_combo.get()    self.player2 = self.player2_combo.get()    cboVals = [self.player1,self.player2]    i=0    for cboList in [self.ps1,self.ps2]:      cboVals2 = cboVals[:]      cboVals2.pop(i)      i+=1      for cboVal in cboVals2:        try:          cboList.remove(cboVal)        except:          pass    self.player1_combo.set_completion_list(self.ps1,self)    self.player2_combo.set_completion_list(self.ps2,self)  def quit(self,*args):    self.root.destroy()  def addGame(self,*args):    p1=0    p2=0    for p in players:      if p.name == self.player1_combo.get() and p.singles==True:        p1 = p      if p.name == self.player2_combo.get() and p.singles==True:        p2 = p    if p1==0:      self.warning.set(Select player 1!)      self.player1_combo.focus()      return    if p2==0:      self.warning.set(Select player 2!)      self.player2_combo.focus()      return    if p1==p2:      self.warning.set(Must have different players!)      return    if int(self.score1.get())<settings['minMaxScore'] and int(self.score2.get())<settings['minMaxScore']:      self.warning.set(One of the players must score at least %d!%settings['minMaxScore'])      self.score1_spin.focus()      return    if (not settings['allowDraw']) and int(self.score1.get())==int(self.score2.get()):      self.warning.set(Draws are not allowed (change in settings))      return    games.append(Game(p1,p2,int(self.score1.get()),int(self.score2.get()),datetime.datetime.now()))    saveGames()    self.warning.set(Game added!)    self.score1.set(0)    self.score2.set(0)    self.mainmenu.update()class AddDoublesGame:  def __init__(self,mainmenu):    #stripped out for code review    passclass EditGame:  def __init__(self,mainmenu,game):    self.mainmenu = mainmenu    self.game = game    self.root = Toplevel(mainmenu.root)    self.root.title(Edit singles game)    mainframe = ttk.Frame(self.root,padding=3 3 12 12)    mainframe.grid(column=0,row=0, sticky=(N,W,E,S))    mainframe.columnconfigure(0,weight=1)    mainframe.rowconfigure(0,weight=1)    self.score1 = StringVar()    self.score2 = StringVar()    self.warning = StringVar()    self.score1.set(game.score1)    self.score2.set(game.score2)    self.ps=[]    for p in players:      if p.singles==True:        self.ps.append(p.name)    self.ps.sort()    self.ps1 = self.ps[:]    self.ps2 = self.ps[:]    self.player1_combo = AutocompleteCombobox(mainframe, width=12, font=tkFont.Font(size=28))    self.player1_combo.grid(column=2,row=2,sticky=(W,E))    self.player1_combo.bind(<<ComboboxSelected>>, self.selected)    self.player2_combo = AutocompleteCombobox(mainframe, width=12, font=tkFont.Font(size=28))    self.player2_combo.grid(column=2,row=4,sticky=(W,E))    self.player2_combo.bind(<<ComboboxSelected>>, self.selected)    self.selected()    self.player1_combo.set(game.player1.name)    self.player2_combo.set(game.player2.name)    self.score1_spin = Spinbox(mainframe, width=5, from_=0, to=settings['maxScore'], textvariable = self.score1, font=tkFont.Font(size=28))    self.score1_spin.grid(column=3,row=2,sticky=(W))    self.score2_spin = Spinbox(mainframe, width=5, from_=0, to=settings['maxScore'], textvariable = self.score2, font=tkFont.Font(size=28))    self.score2_spin.grid(column=3,row=4,sticky=(W))    ttk.Label(mainframe,textvariable=self.warning).grid(column=1,row=5,columnspan=4,sticky=(W,E))    ttk.Button(mainframe,text=Edit Game, command=self.addGame).grid(column=4,row=2,sticky=W)    self.root.bind('<Return>',self.addGame)    ttk.Button(mainframe,text=Cancel,command=self.quit).grid(column=4,row=3,sticky=W)    self.root.bind('<Escape>',self.quit)    ttk.Label(mainframe, text=Player 1).grid(column=1,row=2,sticky=W)    ttk.Label(mainframe, text=versus).grid(column=2,row=3,sticky=E)    ttk.Label(mainframe, text=Player 2).grid(column=1,row=4,sticky=W)    ttk.Label(mainframe, text=Players:).grid(column=2,row=1,sticky=W)    ttk.Label(mainframe, text=Score:).grid(column=3,row=1,sticky=W)    for child in mainframe.winfo_children(): child.grid_configure(padx=5,pady=5)    self.root.focus()    self.player1_combo.focus()    self.root.mainloop()  def selected(self,*args):    self.ps1 = self.ps[:]    self.ps2 = self.ps[:]    self.player1 = self.player1_combo.get()    self.player2 = self.player2_combo.get()    cboVals = [self.player1,self.player2]    i=0    for cboList in [self.ps1,self.ps2]:      cboVals2 = cboVals[:]      cboVals2.pop(i)      i+=1      for cboVal in cboVals2:        try:          cboList.remove(cboVal)        except:          pass    self.player1_combo.set_completion_list(self.ps1,self)    self.player2_combo.set_completion_list(self.ps2,self)     def quit(self,*args):    self.root.destroy()  def addGame(self,*args):    p1=0    p2=0    for p in players:      if p.name == self.player1_combo.get() and p.singles==True:        p1 = p      if p.name == self.player2_combo.get() and p.singles==True:        p2 = p    if p1==0:      self.warning.set(Select player 1!)      self.player1_combo.focus()      return    if p2==0:      self.warning.set(Select player 2!)      self.player2_combo.focus()      return    if p1==p2:      self.warning.set(Must have different players!)      return    if int(self.score1.get())<settings['minMaxScore'] and int(self.score2.get())<settings['minMaxScore']:      self.warning.set(One of the players must score at least %d!%settings['minMaxScore'])      self.score1_spin.focus()      return    if (not settings['allowDraw']) and int(self.score1.get())==int(self.score2.get()):      self.warning.set(Draws are not allowed (change in settings))      return    self.game.player1 = p1    self.game.player2 = p2    self.game.score1 = int(self.score1.get())    self.game.score2 = int(self.score2.get())    recalculate(True)    saveGames()    self.mainmenu.update()    self.root.destroy()class EditDoublesGame:  def __init__(self,mainmenu,game):    #stripped out for code review    passclass PlayerStats:  def __init__(self,mainmenu):    self.root = Toplevel(mainmenu.root)    self.root.title(Player Stats)    self.root.columnconfigure(0,weight=1)    self.root.rowconfigure(0,weight=1)    self.mainframe = ttk.Frame(self.root,padding=3 3 12 12)    self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S))    self.mainframe.columnconfigure(3,weight=1)    self.mainframe.rowconfigure(2,weight=1)    self.gameType = StringVar()    self.gameType.set(1)    self.tree_headers=[Opponent,Rating taken,Games,Wins,Losses,Goals by,Goals on,                 Av goals by,Av goals on,Streak,Max win streak,Max lose streak,               5-0,5-1,5-2,5-3,5-4,4-5,3-5,2-5,1-5,0-5]    self.gameType_frame = ttk.LabelFrame(self.mainframe,text=Game type)  #,padding=3 3 12 12)    self.gameType_frame.grid(column=2,row=1,sticky=(W,N))    self.gameType_frame['borderwidth']=2    self.gameType_frame['relief']='solid'    self.display_frame = ttk.Frame(self.mainframe,padding=3 3 12 12)    self.display_frame.grid(column=1,row=2, columnspan = 3,sticky=(W,N,E,S))    self.display_frame['borderwidth']=2    self.display_frame['relief']='sunken'    self.display_frame.columnconfigure(0,weight=1)    self.display_frame.rowconfigure(0,weight=1)    self.player_combo = AutocompleteCombobox(self.mainframe, width=12, font=tkFont.Font(size=28))    self.player_combo.grid(column=1,row=1,sticky=(W,E))    self.player_combo.bind(<<ComboboxSelected>>, self.selected)    self.singles_radio = ttk.Radiobutton(self.gameType_frame,text=Singles,variable=self.gameType,value=1,command=self.selected)    self.singles_radio.grid(column=1,row=1,sticky=(W,N))    self.doubles_radio = ttk.Radiobutton(self.gameType_frame,text=Doubles,variable=self.gameType,value=0,command=self.selected)    self.doubles_radio.grid(column=2,row=1,sticky=(W,N))    self.pairs_radio = ttk.Radiobutton(self.gameType_frame,text=Pairs,variable=self.gameType,value=2,command=self.selected)    self.pairs_radio.grid(column=3,row=1,sticky=(W,N))    for child in self.mainframe.winfo_children(): child.grid_configure(padx=10,pady=10)    self.selected()    self.player_combo.focus()    self.root.mainloop()  def selected(self,*args):    player = 0    self.ps=[]    for p in players:      if p.singles==int(self.gameType.get()):        self.ps.append(p.name)        if p.name == self.player_combo.get():          player = p    if self.player_combo.get() not in self.ps:      self.player_combo.set()    self.ps.sort()    self.player_combo.set_completion_list(self.ps,self)    #update treeview    if player != 0:      self.tree_list = dict()      if int(self.gameType.get())==1:    #######  Singles        for g in games:          if g.player1==player:            opponent = g.player2            p1=1          elif g.player2==player:            opponent = g.player1            p1=-1          else:            continue          if opponent.name not in self.tree_list.keys():            self.tree_list[opponent.name] = [opponent.name,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]          self.tree_list[opponent.name][1] += p1*g.rating_change  #rating taken          self.tree_list[opponent.name][2] += 1           #games          if p1*g.score1 > p1*g.score2:   #player wins            self.tree_list[opponent.name][3] += 1  #wins            self.tree_list[opponent.name][9] = max([1,self.tree_list[opponent.name][9]+1])  #streak            self.tree_list[opponent.name][10] = max([self.tree_list[opponent.name][10],self.tree_list[opponent.name][9]]) #max win streak          elif p1*g.score2 > p1*g.score1:   #player loses            self.tree_list[opponent.name][4] += 1  #losses            self.tree_list[opponent.name][9] = min([-1,self.tree_list[opponent.name][9]-1])  #streak            self.tree_list[opponent.name][11] = max([self.tree_list[opponent.name][10],-self.tree_list[opponent.name][9]]) #max lose streak          else:               #draw            self.tree_list[opponent.name][9] =0  #streak          if p1==1:            self.tree_list[opponent.name][5] += g.score1  #goals by            self.tree_list[opponent.name][6] += g.score2  #goals on            if g.score1==5:              self.tree_list[opponent.name][12+g.score2] += 1     #game count            else:              self.tree_list[opponent.name][21-g.score1] +=1      #game count          else:            self.tree_list[opponent.name][5] += g.score2  #goals by            self.tree_list[opponent.name][6] += g.score1  #goals on            if g.score2==5:              self.tree_list[opponent.name][12+g.score1] += 1     #game count            else:              self.tree_list[opponent.name][21-g.score2] +=1      #game count        for k in self.tree_list.keys():          self.tree_list[k][7] = %.3f%(1.0*self.tree_list[k][5]/self.tree_list[k][2])          self.tree_list[k][8] = %.3f%(1.0*self.tree_list[k][6]/self.tree_list[k][2])          self.tree_list[k] = [str(i) for i in self.tree_list[k]]      elif int(self.gameType.get())==2:    #######  Pairs        for g in doubles_games:          if g.team1==player:            opponent = g.team2            p1=1          elif g.team2==player:            opponent = g.team1            p1=-1          else:            continue          if opponent.name not in self.tree_list.keys():            self.tree_list[opponent.name] = [opponent.name,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]          self.tree_list[opponent.name][1] += p1*g.rating_change2  #rating taken          self.tree_list[opponent.name][2] += 1           #games          if p1*g.score1 > p1*g.score2:   #player wins            self.tree_list[opponent.name][3] += 1  #wins            self.tree_list[opponent.name][9] = max([1,self.tree_list[opponent.name][9]+1])  #streak            self.tree_list[opponent.name][10] = max([self.tree_list[opponent.name][10],self.tree_list[opponent.name][9]]) #max win streak          elif p1*g.score2 > p1*g.score1:   #player loses            self.tree_list[opponent.name][4] += 1  #losses            self.tree_list[opponent.name][9] = min([-1,self.tree_list[opponent.name][9]-1])  #streak            self.tree_list[opponent.name][11] = max([self.tree_list[opponent.name][10],-self.tree_list[opponent.name][9]]) #max lose streak          else:               #draw            self.tree_list[opponent.name][9] =0  #streak          if p1==1:            self.tree_list[opponent.name][5] += g.score1  #goals by            self.tree_list[opponent.name][6] += g.score2  #goals on            if g.score1==5:              self.tree_list[opponent.name][12+g.score2] += 1     #game count            else:              self.tree_list[opponent.name][21-g.score1] +=1      #game count          else:            self.tree_list[opponent.name][5] += g.score2  #goals by            self.tree_list[opponent.name][6] += g.score1  #goals on            if g.score2==5:              self.tree_list[opponent.name][12+g.score1] += 1     #game count            else:              self.tree_list[opponent.name][21-g.score2] +=1      #game count        for k in self.tree_list.keys():          self.tree_list[k][7] = %.3f%(1.0*self.tree_list[k][5]/self.tree_list[k][2])          self.tree_list[k][8] = %.3f%(1.0*self.tree_list[k][6]/self.tree_list[k][2])          self.tree_list[k] = [str(i) for i in self.tree_list[k]]      elif int(self.gameType.get())==0:  ########   Doubles        for g in doubles_games:          if g.t1p1==player or g.t1p2==player:            opponent1 = g.t2p1            opponent2 = g.t2p2            p1=1          elif g.t2p1==player or g.t2p2==player:            opponent1 = g.t1p1            opponent2 = g.t1p2            p1=-1          else:            continue          if opponent1.name not in self.tree_list: self.tree_list[opponent1.name] = [opponent1.name,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]          if opponent2.name not in self.tree_list: self.tree_list[opponent2.name] = [opponent2.name,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]          #Opponent 1          self.tree_list[opponent1.name][1] += p1*g.rating_change  #rating taken          self.tree_list[opponent1.name][2] += 1           #games          if p1*g.score1 > p1*g.score2:   #player wins            self.tree_list[opponent1.name][3] += 1  #wins            self.tree_list[opponent1.name][9] = max([1,self.tree_list[opponent1.name][9]+1])  #streak            self.tree_list[opponent1.name][10] = max([self.tree_list[opponent1.name][10],self.tree_list[opponent1.name][9]]) #max win streak          elif p1*g.score2 > p1*g.score1:   #player loses            self.tree_list[opponent1.name][4] += 1  #losses            self.tree_list[opponent1.name][9] = min([-1,self.tree_list[opponent1.name][9]-1])  #streak            self.tree_list[opponent1.name][11] = max([self.tree_list[opponent1.name][10],-self.tree_list[opponent1.name][9]]) #max lose streak          else:               #draw            self.tree_list[opponent1.name][9] =0  #streak          if p1==1:            self.tree_list[opponent1.name][5] += g.score1  #goals by            self.tree_list[opponent1.name][6] += g.score2  #goals on            if g.score1==5:              self.tree_list[opponent1.name][12+g.score2] += 1     #game count            else:              self.tree_list[opponent1.name][21-g.score1] +=1      #game count          else:            self.tree_list[opponent1.name][5] += g.score2  #goals by            self.tree_list[opponent1.name][6] += g.score1  #goals on            if g.score2==5:              self.tree_list[opponent1.name][12+g.score1] += 1     #game count            else:              self.tree_list[opponent1.name][21-g.score2] +=1      #game count          #Opponent 2          self.tree_list[opponent2.name][1] += p1*g.rating_change  #rating taken          self.tree_list[opponent2.name][2] += 1           #games          if p1*g.score1 > p1*g.score2:   #player wins            self.tree_list[opponent2.name][3] += 1  #wins            self.tree_list[opponent2.name][9] = max([1,self.tree_list[opponent2.name][9]+1])  #streak            self.tree_list[opponent2.name][10] = max([self.tree_list[opponent2.name][10],self.tree_list[opponent2.name][9]]) #max win streak          elif p1*g.score2 > p1*g.score1:   #player loses            self.tree_list[opponent2.name][4] += 1  #losses            self.tree_list[opponent2.name][9] = min([-1,self.tree_list[opponent2.name][9]-1])  #streak            self.tree_list[opponent2.name][11] = max([self.tree_list[opponent2.name][10],-self.tree_list[opponent2.name][9]]) #max lose streak          else:               #draw            self.tree_list[opponent2.name][9] =0  #streak          if p1==1:            self.tree_list[opponent2.name][5] += g.score1  #goals by            self.tree_list[opponent2.name][6] += g.score2  #goals on            if g.score1==5:              self.tree_list[opponent2.name][12+g.score2] += 1     #game count            else:              self.tree_list[opponent2.name][21-g.score1] +=1      #game count          else:            self.tree_list[opponent2.name][5] += g.score2  #goals by            self.tree_list[opponent2.name][6] += g.score1  #goals on            if g.score2==5:              self.tree_list[opponent2.name][12+g.score1] += 1     #game count            else:              self.tree_list[opponent2.name][21-g.score2] +=1      #game count        for k in self.tree_list.keys():          self.tree_list[k][7] = %.3f%(1.0*self.tree_list[k][5]/self.tree_list[k][2])          self.tree_list[k][8] = %.3f%(1.0*self.tree_list[k][6]/self.tree_list[k][2])          self.tree_list[k] = [str(i) for i in self.tree_list[k]]      #build tree      try:        self.vsb.destroy()      except:        pass      try:        self.hsb.destroy()      except:        pass      try:        self.tree.destroy()      except:        pass      self.tree = ttk.Treeview(self.display_frame,columns=self.tree_headers, show=headings)      self.vsb = ttk.Scrollbar(self.display_frame,orient=vertical, command=self.tree.yview)      self.hsb = ttk.Scrollbar(self.display_frame,orient=horizontal, command=self.tree.xview)      self.tree.configure(yscrollcommand=self.vsb.set)      self.tree.configure(xscrollcommand=self.hsb.set)      self.tree.grid(column=0, row=0, sticky=(N,S,E,W))  #, in_=self.display_frame)      self.vsb.grid(column=1,row=0, sticky=(N,S))  #, in_=self.display_frame)      self.hsb.grid(column=0,row=1, sticky=(W,E))  #, in_=self.display_frame)      self.tree.columnconfigure(0,weight=1)      self.tree.rowconfigure(0,weight=1)      for col in self.tree_headers:        self.tree.heading(col, text=col.title() , command=lambda c=col: sortby(self.tree, c, 0))        #adjust the column's width to the header string        self.tree.column(col,width=tkFont.Font().measure(col.title()),anchor=CENTER)      for k in self.tree_list:        item = self.tree_list[k]        self.tree.insert(, end, values=item)        #adjust column's width if necessary to fit each value        for ix, val in enumerate(item):          col_w = tkFont.Font().measure(val)          if self.tree.column(self.tree_headers[ix],width=None)<col_w:            self.tree.column(self.tree_headers[ix], width=col_w)      sortby(self.tree, Rating taken, 1)class Settings:  def __init__(self,mainmenu):    self.root = Toplevel(mainmenu.root)    self.root.title(Settings)    mainframe = ttk.Frame(self.root,padding=3 3 12 12)    mainframe.grid(column=0,row=0, sticky=(N,W,E,S))    mainframe.columnconfigure(0,weight=1)    mainframe.rowconfigure(0,weight=1)    self.title = StringVar()    self.recentGame = StringVar()    self.gamesNeeded = StringVar()    self.maxScore = StringVar()    self.minMaxScore = StringVar()    self.allowDraw = StringVar()    self.title.set(settings['title'])    self.recentGame.set(settings['recentGame'])    self.gamesNeeded.set(settings['gamesNeeded'])    self.maxScore.set(settings['maxScore'])    self.minMaxScore.set(settings['minMaxScore'])    self.allowDraw.set(settings['allowDraw'])    ttk.Label(mainframe,text=Settings).grid(column=1,row=1,sticky=(W))    ttk.Label(mainframe,text=Values).grid(column=2,row=1,sticky=(W))    ttk.Label(mainframe,text=Days until player inactive).grid(column=1,row=2,sticky=(W))    ttk.Label(mainframe,text=Games needed for player to be made active).grid(column=1,row=3,sticky=(W))    ttk.Label(mainframe,text=Maximum allowed score).grid(column=1,row=4,sticky=(W))    ttk.Label(mainframe,text=Minimum maximum score).grid(column=1,row=5,sticky=(W))    ttk.Label(mainframe,text=Allow draws).grid(column=1,row=6,sticky=(W))    ttk.Label(mainframe,text=Title (restart to take effect)).grid(column=1,row=7,sticky=(W))    self.recentGame_spin = Spinbox(mainframe, width=5, from_=0, to=1000, textvariable = self.recentGame)    self.recentGame_spin.grid(column=2,row=2,sticky=(W))    self.gamesNeeded_spin = Spinbox(mainframe, width=5, from_=0, to=1000, textvariable = self.gamesNeeded)    self.gamesNeeded_spin.grid(column=2,row=3,sticky=(W))    self.maxScore_spin = Spinbox(mainframe, width=5, from_=0, to=1000, textvariable = self.maxScore,command=self.newMaxScore)    self.maxScore_spin.grid(column=2,row=4,sticky=(W))    self.minMaxScore_spin = Spinbox(mainframe, width=5, from_=0, to=int(self.maxScore.get()), textvariable = self.minMaxScore)    self.minMaxScore_spin.grid(column=2,row=5,sticky=(W))    self.allowDraw_check = ttk.Checkbutton(mainframe,variable=self.allowDraw,onvalue=True,offvalue=False)    self.allowDraw_check.grid(column=2,row=6,sticky=(W))    self.title_entry = ttk.Entry(mainframe, width=12, textvariable = self.title)    self.title_entry.grid(column=2,row=7,sticky=(W,E))    ttk.Button(mainframe,text=Save settings, command=self.update).grid(column=1,row=8,sticky=(W,E))    self.root.bind('<Return>',self.update)    ttk.Button(mainframe,text=Close,command=self.quit).grid(column=2,row=8,sticky=(W,E))    self.root.bind('<Escape>',self.quit)    for child in mainframe.winfo_children(): child.grid_configure(padx=5,pady=5)    self.root.focus()    self.root.mainloop()  def newMaxScore(self,*args):    self.minMaxScore_spin['to'] = int(self.maxScore.get())  def quit(self,*args):    self.root.destroy()  def update(self,*args):    global settings    settings['title'] = self.title.get()    settings['recentGame'] = int(self.recentGame.get())    settings['gamesNeeded'] = int(self.gamesNeeded.get())    settings['maxScore'] = int(self.maxScore.get())    settings['minMaxScore'] = int(self.minMaxScore.get())    settings['allowDraw'] = int(self.allowDraw.get())    saveGames()    self.root.destroy()def sortby(tree, col, descending):  sort tree contents when a column header is clicked on  # grab values to sort  try:    data = [(int(tree.set(child, col)), child) for child in tree.get_children('')]  except:    data = [(tree.set(child,col),child) for child in tree.get_children('')]  data.sort(reverse=descending)  for ix, item in enumerate(data):    tree.move(item[1], '', ix)  # switch the heading so it will sort in the opposite direction  tree.heading(col, command=lambda col=col: sortby(tree, col, int(not descending)))class MainMenu:  def __init__(self):    self.root = Tk()    self.root.title(settings[title])    self.root.columnconfigure(0,weight=1)    self.root.rowconfigure(0,weight=1)    self.mainframe = ttk.Frame(self.root,padding=3 3 12 12)    self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S))    self.mainframe.columnconfigure(4,weight=1)    self.mainframe.rowconfigure(2,weight=1)    self.gameType = StringVar()  #1 = 'singles' or 0 = 'doubles' or 2 = 'pairs'    self.show = StringVar()    #'ranking' or 'games' or 'graph'    self.tree_headers = []    self.tree_list = []    self.gameType.set(1)    self.show.set('ranking')    self.button_frame = ttk.Frame(self.mainframe,padding=3 3 12 12)    self.button_frame.grid(column=1,row=1,rowspan=2,sticky=(W,N))    self.gameType_frame = ttk.LabelFrame(self.mainframe,text=Game type)  #,padding=3 3 12 12)    self.gameType_frame.grid(column=2,row=1,sticky=(W,N))    self.gameType_frame['borderwidth']=2    self.gameType_frame['relief']='solid'    self.show_frame = ttk.LabelFrame(self.mainframe,text=Game info)   #,padding=3 3 12 12)    self.show_frame.grid(column=3,row=1,sticky=(W,N))    self.show_frame['borderwidth']=2    self.show_frame['relief']='solid'    self.display_frame = ttk.Frame(self.mainframe,padding=3 3 12 12)    self.display_frame.grid(column=2,row=2,rowspan = 2, columnspan = 3,sticky=(W,N,E,S))    self.display_frame['borderwidth']=2    self.display_frame['relief']='sunken'    self.display_frame.columnconfigure(0,weight=1)    self.display_frame.rowconfigure(0,weight=1)    ttk.Button(self.button_frame,text=Add Singles Game,command=self.addGame).grid(column=1,row=1,sticky=(W,E))    ttk.Button(self.button_frame,text=Add Doubles Game,command=self.addDoublesGame).grid(column=1,row=2,sticky=(W,E))    ttk.Button(self.button_frame,text=Add Player,command=self.addPlayer).grid(column=1,row=3,sticky=(W,E))    ttk.Button(self.button_frame,text=Player stats,command=self.playerStats).grid(column=1,row=4,sticky=(W,E))    ttk.Button(self.button_frame,text=Settings,command=self.settings).grid(column=1,row=5,sticky=(W,E))    ttk.Button(self.button_frame,text=Exit,command=self.quit).grid(column=1,row=6,sticky=(W,E))    self.singles_radio = ttk.Radiobutton(self.gameType_frame,text=Singles,variable=self.gameType,value=1,command=self.update)    self.singles_radio.grid(column=1,row=1,sticky=(W,N))    self.doubles_radio = ttk.Radiobutton(self.gameType_frame,text=Doubles,variable=self.gameType,value=0,command=self.update)    self.doubles_radio.grid(column=2,row=1,sticky=(W,N))    self.pairs_radio = ttk.Radiobutton(self.gameType_frame,text=Pairs,variable=self.gameType,value=2,command=self.update)    self.pairs_radio.grid(column=3,row=1,sticky=(W,N))    self.ranking_radio = ttk.Radiobutton(self.show_frame,text=Ranking,variable=self.show,value=ranking,command=self.update)    self.ranking_radio.grid(column=1,row=1,sticky=(W,N))    self.games_radio = ttk.Radiobutton(self.show_frame,text=Recent games,variable=self.show,value=games,command=self.update)    self.games_radio.grid(column=2,row=1,sticky=(W,N))    self.graph_radio = ttk.Radiobutton(self.show_frame,text=Spagetti graph,variable=self.show,value=graph,command=self.update)    self.graph_radio.grid(column=3,row=1,sticky=(W,N))    self.update()    for child in self.mainframe.winfo_children(): child.grid_configure(padx=10,pady=10)    for child in self.button_frame.winfo_children(): child.grid_configure(padx=7,pady=7)    self.root.focus()    self.root.mainloop()  def update(self,*args):    ################   RANKING    #################    if self.show.get()==ranking:      #Establish active players      t = datetime.datetime.now()      for p in players:        if p.games_played>=settings['gamesNeeded'] and (t-p.last_game).days < settings['recentGame']:          p.active = True        else:          p.active = False      #Sort players      players.sort(key=attrgetter('name'))      players.sort(key=attrgetter('rating'),reverse=True)      players.sort(key=attrgetter('singles'))      players.sort(key=attrgetter('active'),reverse=True)      #Heading      self.tree_headers=[Rank,Name,Ratings,Games,Wins,Losses,Goals by,Goals on,Av goals by,Av goals on,Max rating,Min rating,                 Streak,Max win streak,Max lose streak]      self.tree_list = []      rank = 0      rankCount = 0      lastrating = 100000      for p in players:        if p.active==False: continue        if p.singles==int(self.gameType.get()):          rankCount +=1          if p.rating != lastrating:            rank = rankCount          lastrating = p.rating          row  = [%d%rank]          row.append(p.name)          row.append(%d%p.rating)          row.append(%d%(p.games_played))          row.append(%d%p.wins)          row.append(%d%p.losses)          row.append(%d%p.points_for)          row.append(%d%p.points_against)          row.append(%.3f%(1.0*p.points_for/p.games_played))          row.append(%.3f%(1.0*p.points_against/p.games_played))          row.append(%d%p.max_rating)          row.append(%d%p.min_rating)          row.append(%d%p.streak)          row.append(%d%p.max_winning_streak)          row.append(%d%p.max_losing_streak)          self.tree_list.append(row)  #may need tuple(row)      #build tree      try:        self.vsb.destroy()      except:        pass      try:        self.hsb.destroy()      except:        pass      try:        self.tree.destroy()      except:        pass      try:        self.toolbar.destroy()      except:        pass      try:        self.canvas.get_tk_widget().destroy()      except:        pass      self.tree = ttk.Treeview(columns=self.tree_headers, show=headings)      self.vsb = ttk.Scrollbar(orient=vertical, command=self.tree.yview)      self.hsb = ttk.Scrollbar(orient=horizontal, command=self.tree.xview)      self.tree.configure(yscrollcommand=self.vsb.set)      self.tree.configure(xscrollcommand=self.hsb.set)      self.tree.grid(column=0, row=0, sticky=(N,S,E,W), in_=self.display_frame)      self.vsb.grid(column=1,row=0, sticky=(N,S), in_=self.display_frame)      self.hsb.grid(column=0,row=1, sticky=(W,E), in_=self.display_frame)      self.tree.columnconfigure(0,weight=1)      self.tree.rowconfigure(0,weight=1)      for col in self.tree_headers:        self.tree.heading(col, text=col.title() , command=lambda c=col: sortby(self.tree, c, 0))        #adjust the column's width to the header string        self.tree.column(col,width=tkFont.Font().measure(col.title()),anchor=CENTER)      for item in self.tree_list:        self.tree.insert(, end, values=item)        #adjust column's width if necessary to fit each value        for ix, val in enumerate(item):          col_w = tkFont.Font().measure(val)          if self.tree.column(self.tree_headers[ix],width=None)<col_w:            self.tree.column(self.tree_headers[ix], width=col_w)    #####################   GAMES  #######################    elif self.show.get()==games:      if int(self.gameType.get())==1:        #Singles        self.tree_headers = [Number,Date,Player 1,Score 1,Player 2,Score 2]        self.tree_list = []        num=min([40,len(games)])        i = len(games)-1        for i in range(len(games)-1,len(games)-num-1,-1):          g = games[i]          row  = [%d%(i+1)]          row.append(%s%g.date.date())          if g.score1>=g.score2:            row.append(%s%g.player1.name)            row.append(%d%g.score1)          row.append(%s%g.player2.name)          row.append(%d%g.score2)          if g.score1<g.score2:            row.append(%s%g.player1.name)            row.append(%d%g.score1)          self.tree_list.append(row)          i+=1      else:        #Doubles        self.tree_headers = [Number,Date,Team 1/Player 1,Team 1/Player 2,Score 1,Team 2/Player 1,Team 2/Player 2,Score 2]        self.tree_list = []        num=min([40,len(doubles_games)])        i = len(doubles_games)-1        for i in range(len(doubles_games)-1,len(doubles_games)-num-1,-1):          g = doubles_games[i]          row  = [%d%(i+1)]          row.append(%s%g.date.date())          if g.score1>=g.score2:            row.append(%s%g.t1p1.name)            row.append(%s%g.t1p2.name)            row.append(%d%g.score1)          row.append(%s%g.t2p1.name)          row.append(%s%g.t2p2.name)          row.append(%d%g.score2)          if g.score1<g.score2:            row.append(%s%g.t1p1.name)            row.append(%s%g.t1p2.name)            row.append(%d%g.score1)          self.tree_list.append(row)          i+=1      #build tree      try:        self.vsb.destroy()      except:        pass      try:        self.hsb.destroy()      except:        pass      try:        self.tree.destroy()      except:        pass      try:        self.toolbar.destroy()      except:        pass      try:        self.canvas.get_tk_widget().destroy()      except:        pass      self.tree = ttk.Treeview(columns=self.tree_headers, show=headings)      self.vsb = ttk.Scrollbar(orient=vertical, command=self.tree.yview)      self.hsb = ttk.Scrollbar(orient=horizontal, command=self.tree.xview)      self.tree.configure(yscrollcommand=self.vsb.set)      self.tree.configure(xscrollcommand=self.hsb.set)      self.tree.grid(column=0, row=0, sticky=(N,S,E,W), in_=self.display_frame)      self.vsb.grid(column=1,row=0, sticky=(N,S), in_=self.display_frame)      self.hsb.grid(column=0,row=1, sticky=(W,E), in_=self.display_frame)      self.tree.columnconfigure(0,weight=1)      self.tree.rowconfigure(0,weight=1)      for col in self.tree_headers:        self.tree.heading(col, text=col.title() , command=lambda c=col: sortby(self.tree, c, 0))        #adjust the column's width to the header string        self.tree.column(col,width=tkFont.Font().measure(col.title()),anchor=CENTER)      for item in self.tree_list:        self.tree.insert(, end, values=item, tags=(game,))        #adjust column's width if necessary to fit each value        for ix, val in enumerate(item):          col_w = tkFont.Font().measure(val)          if self.tree.column(self.tree_headers[ix],width=None)<col_w:            self.tree.column(self.tree_headers[ix], width=col_w)      self.tree.bind(<Double-1>,self.editGame)    ###############   GRAPH   ###################3    elif self.show.get()==graph:      f = makeGraph(int(self.gameType.get()))      try:        self.vsb.destroy()      except:        pass      try:        self.hsb.destroy()      except:        pass      try:        self.tree.destroy()      except:        pass      try:        self.toolbar.destroy()      except:        pass      try:        self.canvas.get_tk_widget().destroy()      except:        pass      self.canvas = FigureCanvasTkAgg(f,master = self.display_frame)      self.canvas.show()      self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)      self.toolbar = NavigationToolbar2TkAgg( self.canvas, self.display_frame )      self.toolbar.update()      self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1)  def editGame(self,*args):    if self.show.get()==games:      try:        gameNum = int(self.tree.set(self.tree.focus(),0))-1      except:        return      if int(self.gameType.get())==1:        EditGame(self,games[gameNum])      else:        EditDoublesGame(self,doubles_games[gameNum])  def addGame(self,*args):    AddGame(self)  def addDoublesGame(self,*args):    AddDoublesGame(self)  def addPlayer(self,*args):    AddPlayer(self)  def playerStats(self,*args):    PlayerStats(self)  def settings(self,*args):    Settings(self)  def quit(self,*args):    saveGames()    self.root.destroy()def recalculate(singles=1):  if singles==1:    for p in players:      if p.singles==1: p.reset()    for g in games:      g.play()  else:    for p in players:      if p.singles==0 or p.singles==2: p.reset()    for g in doubles_games:      g.play()  saveGames()players,games,doubles_games = [],[],[]settings=dict()settings[title] = Foosball Trackertry:  loadGames()except:  settings['recentGame'] = 31  #days of inactivity which will remove a player from the ranking  settings['gamesNeeded'] = 20 #games needed to have an ranking  settings['maxScore'] = 5  settings['minMaxScore'] = 5  settings['allowDraw'] = Falseif __name__==__main__: MainMenu()
Elo Rating Tracking Applet - Release into GNU?
python;python 3.x;tkinter