I've started programming using Python two months ago. My only prior programming experience was VBA. I'm completely self taught. I wrote the code below for a project I'm creating as a way to learn the language.
I was hoping someone experienced with Python could look over my code quickly and let me know which places could be written better. It all works but I feel(especially with the variables) there must be best practices I am missing.
Any feedback would be greatly appreciated.
The code is meant to load a window with input boxes where you can save/load player information into a database file. It's will be used for a Dungeons and Dragons game. It does what its supposed to.
I'm more concerned with the code vs functionality. Is there a more elegant way to get the same results? Specifically within the def makeVar(self)
section.
from tkinter import * #import tkinter
from tkinter import messagebox as mb
import SQLclass
SQL = SQLclass.Database("Players.db") #connect db
window = Tk() #make variable for tkinter
#overwrite the X button to exit script
def xbutt():
exit()
window.protocol('WM_DELETE_WINDOW', xbutt)
class PlayerWindow:
"""
Player input window. Used to add groups with player stats to players database.
"""
def __init__(self, top):
self.makeVar()
self.layoutWindow()
self.buttons()
self.populateOM()
self.getvalues()
def layoutWindow(self):
window.title("Player Entry Form")
#numbers
Label(window,text="1").grid(row=1,column=0)
Label(window,text="2").grid(row=2,column=0)
Label(window,text="3").grid(row=3,column=0)
Label(window,text="4").grid(row=4,column=0)
Label(window,text="5").grid(row=5,column=0)
Label(window,text="6").grid(row=6,column=0)
Label(window,text="7").grid(row=7,column=0)
Label(window,text="8").grid(row=8,column=0)
#Player Names
Label(window,text="Player Name").grid(row=0,column=1, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][1]).grid(row=1,column=1, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][1]).grid(row=2,column=1, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][1]).grid(row=3,column=1, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][1]).grid(row=4,column=1, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][1]).grid(row=5,column=1, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][1]).grid(row=6,column=1, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][1]).grid(row=7,column=1, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][1]).grid(row=8,column=1, padx=5, pady=2)
#Character Names
Label(window,text="Character Name").grid(row=0,column=3, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][2]).grid(row=1,column=3, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][2]).grid(row=2,column=3, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][2]).grid(row=3,column=3, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][2]).grid(row=4,column=3, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][2]).grid(row=5,column=3, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][2]).grid(row=6,column=3, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][2]).grid(row=7,column=3, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][2]).grid(row=8,column=3, padx=5, pady=2)
#Class Names
Label(window,text="Class Name").grid(row=0,column=4, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][3], width=12).grid(row=1,column=4, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][3], width=12).grid(row=2,column=4, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][3], width=12).grid(row=3,column=4, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][3], width=12).grid(row=4,column=4, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][3], width=12).grid(row=5,column=4, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][3], width=12).grid(row=6,column=4, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][3], width=12).grid(row=7,column=4, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][3], width=12).grid(row=8,column=4, padx=5, pady=2)
#Level
Label(window,text="Level").grid(row=0,column=5, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][4], width=3).grid(row=1,column=5, padx=5, pady=4)
Entry(window, textvariable=self.PlayerValues[1][4], width=3).grid(row=2,column=5, padx=5, pady=4)
Entry(window, textvariable=self.PlayerValues[2][4], width=3).grid(row=3,column=5, padx=5, pady=4)
Entry(window, textvariable=self.PlayerValues[3][4], width=3).grid(row=4,column=5, padx=5, pady=4)
Entry(window, textvariable=self.PlayerValues[4][4], width=3).grid(row=5,column=5, padx=5, pady=4)
Entry(window, textvariable=self.PlayerValues[5][4], width=3).grid(row=6,column=5, padx=5, pady=4)
Entry(window, textvariable=self.PlayerValues[6][4], width=3).grid(row=7,column=5, padx=5, pady=4)
Entry(window, textvariable=self.PlayerValues[7][4], width=3).grid(row=8,column=5, padx=5, pady=4)
#HP
Label(window,text="HP").grid(row=0,column=6, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][5], width=3).grid(row=1,column=6, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][5], width=3).grid(row=2,column=6, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][5], width=3).grid(row=3,column=6, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][5], width=3).grid(row=4,column=6, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][5], width=3).grid(row=5,column=6, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][5], width=3).grid(row=6,column=6, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][5], width=3).grid(row=7,column=6, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][5], width=3).grid(row=8,column=6, padx=5, pady=2)
#Strength Names
Label(window,text="STR").grid(row=0,column=7, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][6], width=3).grid(row=1,column=7, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][6], width=3).grid(row=2,column=7, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][6], width=3).grid(row=3,column=7, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][6], width=3).grid(row=4,column=7, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][6], width=3).grid(row=5,column=7, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][6], width=3).grid(row=6,column=7, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][6], width=3).grid(row=7,column=7, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][6], width=3).grid(row=8,column=7, padx=5, pady=2)
#Dexterity Names
Label(window,text="DEX").grid(row=0,column=8, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][7], width=3).grid(row=1,column=8, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][7], width=3).grid(row=2,column=8, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][7], width=3).grid(row=3,column=8, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][7], width=3).grid(row=4,column=8, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][7], width=3).grid(row=5,column=8, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][7], width=3).grid(row=6,column=8, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][7], width=3).grid(row=7,column=8, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][7], width=3).grid(row=8,column=8, padx=5, pady=2)
#Constitution
Label(window,text="CON").grid(row=0,column=9, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][8], width=3).grid(row=1,column=9, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][8], width=3).grid(row=2,column=9, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][8], width=3).grid(row=3,column=9, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][8], width=3).grid(row=4,column=9, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][8], width=3).grid(row=5,column=9, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][8], width=3).grid(row=6,column=9, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][8], width=3).grid(row=7,column=9, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][8], width=3).grid(row=8,column=9, padx=5, pady=2)
#Intelligence
Label(window,text="INT").grid(row=0,column=10, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][9], width=3).grid(row=1,column=10, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][9], width=3).grid(row=2,column=10, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][9], width=3).grid(row=3,column=10, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][9], width=3).grid(row=4,column=10, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][9], width=3).grid(row=5,column=10, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][9], width=3).grid(row=6,column=10, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][9], width=3).grid(row=7,column=10, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][9], width=3).grid(row=8,column=10, padx=5, pady=2)
#Wisdom
Label(window,text="WIS").grid(row=0,column=11, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][10], width=3).grid(row=1,column=11, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][10], width=3).grid(row=2,column=11, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][10], width=3).grid(row=3,column=11, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][10], width=3).grid(row=4,column=11, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][10], width=3).grid(row=5,column=11, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][10], width=3).grid(row=6,column=11, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][10], width=3).grid(row=7,column=11, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][10], width=3).grid(row=8,column=11, padx=5, pady=2)
#Charisma
Label(window,text="CHA").grid(row=0,column=12, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[0][11], width=3).grid(row=1,column=12, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[1][11], width=3).grid(row=2,column=12, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[2][11], width=3).grid(row=3,column=12, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[3][11], width=3).grid(row=4,column=12, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[4][11], width=3).grid(row=5,column=12, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[5][11], width=3).grid(row=6,column=12, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[6][11], width=3).grid(row=7,column=12, padx=5, pady=2)
Entry(window, textvariable=self.PlayerValues[7][11], width=3).grid(row=8,column=12, padx=5, pady=2)
def makeVar(self):
self.Player1 = [StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ]
self.Player2 = [StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ]
self.Player3 = [StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ]
self.Player4 = [StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ]
self.Player5 = [StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ]
self.Player6 = [StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ]
self.Player7 = [StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ]
self.Player8 = [StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ,StringVar() ]
self.output1 = ["","","","","","","","","","","",""]
self.output2 = ["","","","","","","","","","","",""]
self.output3 = ["","","","","","","","","","","",""]
self.output4 = ["","","","","","","","","","","",""]
self.output5 = ["","","","","","","","","","","",""]
self.output6 = ["","","","","","","","","","","",""]
self.output7 = ["","","","","","","","","","","",""]
self.output8 = ["","","","","","","","","","","",""]
self.check1 = ["","","","","","","","","","","",""]
self.check2 = ["","","","","","","","","","","",""]
self.check3 = ["","","","","","","","","","","",""]
self.check4 = ["","","","","","","","","","","",""]
self.check5 = ["","","","","","","","","","","",""]
self.check6 = ["","","","","","","","","","","",""]
self.check7 = ["","","","","","","","","","","",""]
self.check8 = ["","","","","","","","","","","",""]
self.PlayerValues = [self.Player1,self.Player2,self.Player3,self.Player4,self.Player5,self.Player6,self.Player7,self.Player8]
def populateOM(self):
global existingGroups
existingGroups = []
existingGroups = SQL.fetchSQL('SELECT DISTINCT "Group Name" from players') #get group names
existingGroups = [a_tuple[0] for a_tuple in existingGroups] #remove tuples from list
existingGroups.append("New Group")
self.groupmenu['menu'].delete(0, 'end')
for choice in existingGroups:
self.groupmenu['menu'].add_command(label=choice, command=lambda value=choice: self.optV.set(value))
def buttons(self):
#pulldown
self.optV = StringVar()
self.optV.trace("w", self.OptionMenu_Change) #put a trace on the variable(pulldown) to call if anything changes
self.optV.set("New Group")
self.groupmenu = OptionMenu(window, self.optV , "")
self.groupmenu.grid(row=10,column=1, columnspan=2,padx=5, pady=2)
self.optN = self.optV.get()
#Buttons
Button(window, text="Save and Close", command=self.SaveNClose, width=15).grid(row=10,column=3,padx=5, pady=2)#load button to points to SaveNClose function
Button(window, text="Delete Group", command=self.DeleteGroup, width=15).grid(row=10,column=4,padx=5, pady=2,columnspan=2)#load button to points to SaveNClose function
Button(window, text="Clear", command=self.clearvalues, width=15).grid(row=10,column=6,padx=5, pady=2,columnspan=2)#load button to points to SaveNClose function
def getvalues(self):
self.optN = self.optV.get()
self.OutputValues = [self.output1,self.output2,self.output3,self.output4,self.output5,self.output6,self.output7,self.output8]
for r in range(0,7):
for c in range(1,11): #11 is how many columns in the DB table minus the group name
self.OutputValues[r][c] = self.PlayerValues[r][c].get()
def checkvalues(self):
self.optN = self.optV.get()
self.CheckedValues = [self.check1,self.check2,self.check3,self.check4,self.check5,self.check6,self.check7,self.check8]
for r in range(0,7):
for c in range(1,11):
self.CheckedValues[r][c] = self.PlayerValues[r][c].get()
def LoadData(self):
self.clearvalues()
self.existingGroups = SQL.fetchSQL(f'SELECT * from players where "Group Name" = "{self.optV.get()}"')
for r in range(0,len(self.existingGroups)):
for c in range(1,11):
self.PlayerValues[r][c].set(self.existingGroups[r][c])
def OptionMenu_Change(self, *args):
self.checkvalues()
if self.optV.get() != "New Group":
if self.OutputValues == self.CheckedValues: #check for changes made since loaded
self.LoadData()
else:
x= mb.askyesno("Load Group?", f"Do you want to load {self.optV.get()}? All unsaved changes will be lost.")
if x == True:
self.LoadData()
self.getvalues()
def DeleteGroup(self):
if self.optV.get() != "New Group":
x= mb.askyesno("Delete Group?", f"Delete the group {self.optV.get()} ?")
if x == True:
SQL.SendSQL(f'DELETE FROM Players WHERE "Group Name" = "{self.optV.get()}";')
self.optV.set("New Group")
self.clearvalues()
self.populateOM()
def clearvalues(self):
for r in range(0,7):
for c in range(1,11):
self.PlayerValues[r][c].set("")
def SaveNClose(self):
self.getvalues()
window.destroy()
pWindow=PlayerWindow(window) #make PlayerWindow an object(variable)
window.mainloop() #keep window open until its forced closed
class InputWindow:
def __init__(self, title, question):
self.window2 = Tk()
self.window2.title(title)
self.q = StringVar()
Label(self.window2,text=question, wraplength=250).grid(row=0,column=1, columnspan=2) #question
Entry(self.window2, textvariable=self.q, width =50).grid(row=1,column=1, padx=5, pady=2)
Button(self.window2, text="Save", command=self.SaveNClose, width=10).grid(row=2,column=1,padx=5, pady=2)
self.window2.mainloop()
def SaveNClose(self):
self.answer = self.q.get()
self.window2.destroy()
if pWindow.optN == "New Group":
if pWindow.OutputValues[0][1] != "":
inpWindow=InputWindow("Group Name", "What would you like to name this group?")
groupname = inpWindow.answer
else:
groupname = pWindow.optN
#make sql value statements
theValues = ""
for x in range(0,7):
if x > 0:
if pWindow.OutputValues[x][1] != "":
theValues = theValues + f",('{groupname}', '{pWindow.OutputValues[x][1]}','{pWindow.OutputValues[x][2]}','{pWindow.OutputValues[x][3]}','{pWindow.OutputValues[x][4]}','{pWindow.OutputValues[x][5]}','{pWindow.OutputValues[x][6]}','{pWindow.OutputValues[x][7]}','{pWindow.OutputValues[x][8]}','{pWindow.OutputValues[x][9]}','{pWindow.OutputValues[x][10]}','{pWindow.OutputValues[x][11]}')"
else:
if pWindow.OutputValues[x][1] != "":
theValues = theValues + f"('{groupname}', '{pWindow.OutputValues[x][1]}','{pWindow.OutputValues[x][2]}','{pWindow.OutputValues[x][3]}','{pWindow.OutputValues[x][4]}','{pWindow.OutputValues[x][5]}','{pWindow.OutputValues[x][6]}','{pWindow.OutputValues[x][7]}','{pWindow.OutputValues[x][8]}','{pWindow.OutputValues[x][9]}','{pWindow.OutputValues[x][10]}','{pWindow.OutputValues[x][11]}')"
#check if the players table exists yet and if not make it
SQL.SendSQL("CREATE TABLE IF NOT EXISTS players ('Group Name','Player Name', 'Character Name', 'Class','Level', 'HP','STR', 'DEX', 'CON', 'INT', 'WIS', 'CHA')")
#add group to players db
if theValues != "":
SQL.SendSQL(f"INSERT INTO players VALUES{theValues}")