Good afternoon, all. I need some help with my program. I anticipate the program growing steadily larger as I add more subjects to the program, so I’m trying to avoid the use of if/else statements. To do so, I’ve tried using a dictionary that will call the function through a while loop. When doing so, I obtain an attribute issue ‘AttributeError: ‘QuestionWindow’ object has no attribute ‘QuestionWindow’’. I’ve tried different indentations, moving the function within the program, and changing some of the names, but I’m unable to get it to work. I’m still very new to Python and self learning, so I’m also posting the entire code to get feedback on how I might be able to make it more readable or possibly reduce the line count as well as possibly being given an explanation on how certain aspects why certain aspects should be changed. Any help will be greatly appreciated.
import tkinter as tk
from tkinter import *
from PIL import ImageTk,Image
import random
class QuestionWindow():
#This will open up the question window and allow the user to study
def aminoSelect(self):
AminoAcidList = [
["Alanine", "Ala", "A", 2.34, 9.69],
["Arginine", "Arg", "R", 2.17, 9.04, 12.48],
["Asparagine", "Asn", "N", 2.02, 8.84],
["Aspartic Acid", "Asp", "D", 2.09, 9.82, 3.71],
["Cysteine", "Cys", "C", 1.71, 10.78, 8.33],
["Glutamine", "Gln", "Q", 2.17, 9.13],
["Glutamic Acid", "Glu", "E", 2.19, 9.67, 4.15],
["Glycine", "Gly", "G", 2.34, 9.60],
["Histidine", "His", "H", 1.82, 9.17, 6.00],
["Isoleucine", "Ile", "I", 2.36, 9.68],
["Leucine", "Leu", "L", 2.36, 9.60],
["Lysine", "Lys", "K", 2.18, 8.95, 10.53],
["Methionine", "Met", "M", 2.28, 9.21],
["Phenylalanine", "Phe", "F", 2.20, 9.13],
["Proline", "Pro", "P", 1.99, 10.96],
["Serine", "Ser", "S", 2.21, 9.15],
["Threonine", "Thr", "T", 2.11, 9.62],
["Tryptophan", "Trp", "W", 2.38, 9.39],
["Tyrosine", "Tyr", "Y", 2.20, 9.11, 10.07],
["Valine", "Val", "V", 2.32, 9.62]
]
AminoAcid = random.choice(AminoAcidList)
return AminoAcid
#######################################################
def ChargeDetermination(self, pHVal, aminoChain):
pH = pHVal
Amino = []
Amino.append(aminoChain)
PosPka3 = ['R', 'K', 'H']
NegPka3 = ['D', 'E', 'C', 'Y']
self.TotalCharge = 0
X = 0
while X < len(Amino):
if Amino[X][2] in PosPka3:
if pH < Amino[X][5]:
self.TotalCharge += 1
elif pH == Amino[X][5]:
self.TotalCharge += .5
if Amino[X][2] in NegPka3:
if pH > Amino[X][5]:
self.TotalCharge -= 1
elif pH == Amino[X][5]:
self.TotalCharge -= .5
if pH < Amino[0][4]:
self.TotalCharge += 1
elif pH == Amino[0][4]:
self.TotalCharge += .5
if pH > Amino[-1][3]:
self.TotalCharge -= 1
elif pH == Amino[-1][3]:
self.TotalCharge -= .5
X = X+1
return self.TotalCharge
#######################################################
#This will ask for ID if ID questions are selected
def AminoAcidID(self):
self.Quest1Lbl = Label(self.QuestionWindow, text = "What is the full name of the amino acid?", wraplength = 300)
self.Quest1Lbl.pack()
self.Quest1Answer = Entry(self.QuestionWindow, width = 20, bg = "yellow")
self.Quest1Answer.pack()
self.Quest1Reveal = Label (self.QuestionWindow, text = " ", width = 20)
self.Quest1Reveal.pack()
self.Quest2Lbl = Label(self.QuestionWindow, text = "What is the three letter code of the amino acid?", wraplength = 300).pack()
self.Quest2Answer = Entry(self.QuestionWindow, width = 20, bg = "yellow")
self.Quest2Answer.pack()
self.Quest2Reveal = Label (self.QuestionWindow, text = " ", width = 20)
self.Quest2Reveal.pack()
self.Quest3Lbl = Label(self.QuestionWindow, text = "What is the one letter code of the amino acid?", wraplength = 300).pack()
self.Quest3Answer = Entry(self.QuestionWindow, width = 20, bg = "yellow")
self.Quest3Answer.pack()
self.Quest3Reveal = Label(self.QuestionWindow, text = " ", width = 20)
self.Quest3Reveal.pack()
#######################################################
def pKaQuestion(self):
self.pHVal = round(random.uniform(0, 14), 2)
self.aminoChain = self.AcidName
self.TotalCharge = self.ChargeDetermination(self.pHVal, self.aminoChain)
self.Quest4Lbl = Label(self.QuestionWindow, text = f"What is the amino acids charge at pH {self.pHVal: .2f}?")
self.Quest4Lbl.pack()
self.Quest4Answer = Entry(self.QuestionWindow, width = 20, bg = "yellow")
self.Quest4Answer.pack()
self.Quest4Reveal = Label (self.QuestionWindow, text = ' ')
self.Quest4Reveal.pack()
#######################################################
#This will show the questions of all question types
def CombinedWindow(self):
self.AminoAcidID()
self.pKaQuestion()
#######################################################
#This will validate the entered answer and show if they are correct
def CheckAnswer(self):
if self.IDCall == 1:
if len(self.Quest1Answer.get()) == 0 or len(self.Quest2Answer.get()) == 0 or len(self.Quest3Answer.get()) == 0:
self.ErrorMsgLbl.config(text = "Please Enter A Value Into All Fields", fg = "red")
else:
self.ShowAnswerBtn.config(state = ACTIVE)
self.NameInput = self.Quest1Answer.get()
self.Let3Input = self.Quest2Answer.get()
self.Let1Input = self.Quest3Answer.get()
if self.AcidName[0].upper() == self.NameInput.upper():
self.Quest1Answer.config(bg = "green")
else:
self.Quest1Answer.config(bg = "red")
if self.AcidName[1].upper() == self.Let3Input.upper():
self.Quest2Answer.config(bg = "green")
else:
self.Quest2Answer.config(bg = "red")
if self.AcidName[2].upper() == self.Let1Input.upper():
self.Quest3Answer.config(bg = "green")
else:
self.Quest3Answer.config(bg = "red")
if self.pKCall == 1:
if len(self.Quest4Answer.get()) == 0:
self.ErrorMsgLbl.config(text = "Please Enter A Value Into All Fields", fg = "red")
else:
self.ShowAnswerBtn.config(state = ACTIVE)
self.pKAnswerInt = int(self.Quest4Answer.get())
self.ErrorMsgLbl.config(text = '')
if self.pKAnswerInt == self.TotalCharge:
self.Quest4Answer.config(bg = "green")
else:
self.Quest4Answer.config(bg = "red")
#######################################################
#This defines which amino acid is used
def AminoChoice(self):
self.NewAcidName = self.aminoSelect()
self.NewAminoAcidPic = Image.open(f"{self.NewAcidName[0]}.png")
self.NewAminoAcidPicResize = self.NewAminoAcidPic.resize((200, 200))
self.NewActualImage = ImageTk.PhotoImage(self.NewAminoAcidPicResize)
self.QuestionBox.configure(image = self.NewActualImage)
self.QuestionBox.photo = self.NewActualImage
self.QuestionBox.config(text = self.NewAcidName[0])
self.AcidName = self.NewAcidName
self.ShowAnswerBtn.config(state = DISABLED)
self.ErrorMsgLbl.config(text = ' ')
if self.IDCall == 1:
self.Quest1Answer.config(bg = "yellow")
self.Quest1Answer.delete(0,END)
self.Quest1Reveal.config(text = ' ')
self.Quest2Answer.config(bg = "yellow")
self.Quest2Answer.delete(0,END)
self.Quest2Reveal.config(text = ' ')
self.Quest3Answer.config(bg = "yellow")
self.Quest3Answer.delete(0,END)
self.Quest3Reveal.config(text = ' ')
if self.pKCall == 1:
self.Quest4Answer.config(bg = "yellow")
self.Quest4Answer.delete(0, END)
self.NewpHVal = round(random.uniform(0, 14), 2)
self.TotalCharge = self.ChargeDetermination(self.NewpHVal, self.AcidName)
self.Quest4Lbl.config(text = f"What is the amino acids charge at pH {self.NewpHVal: .2f}?")
self.Quest4Reveal.config(text = ' ')
#######################################################
#This will show the answer when pressed
def RevealAnswer(self):
if self.IDCall == 1:
self.Quest1Reveal.config(text = self.AcidName[0])
self.Quest2Reveal.config(text = self.AcidName[1])
self.Quest3Reveal.config(text = self.AcidName[2])
if self.pKCall == 1:
self.TotalChargeString = str(self.TotalCharge)
self.Quest4Reveal.config(text = self.TotalChargeString)
#######################################################
def openQuestionWindow(self):
self.QuestionWindow = Toplevel(root)
self.QuestionWindow.title('Question Window')
self.IDCall = 0
self.pKCall = 0
self.AcidName = self.aminoSelect()
self.AminoAcidPic = Image.open(f"{self.AcidName[0]}.png")
self.AminoAcidPicResize = self.AminoAcidPic.resize((200, 200))
self.ActualImage = ImageTk.PhotoImage(self.AminoAcidPicResize)
self.QuestionBox = Label(self.QuestionWindow, image = self.ActualImage)
self.QuestionBox.pack()
self.QuestionBox.config(image = self.ActualImage)
self.ErrorMsgLbl = Label(self.QuestionWindow, text = ' ')
self.ErrorMsgLbl.pack()
PossibleFuncList = ['AminoID', 'GenpKa', 'ActpKa']
CallList = AddtoFunList()
for Call in PossibleFuncList:
for PossibleFuncList in CallList:
CallList[PossibleFuncList]()
if (SelName_V.get() == 1) & ((SelGenPk_V.get() == 1) or (SelActPk_V.get() == 1)):
self.IDCall += 1
self.pKCall += 1
self.CombinedWindow()
## elif (SelName_V.get() == 1) & ((SelGenPk_V.get() == 0) or (SelActPk_V.get() == 0)):
## self.IDCall +=1
## self.AminoAcidID()
elif (SelName_V.get() == 0) & ((SelGenPk_V.get() == 1) or (SelActPk_V.get() == 1)):
self.pKCall += 1
self.pKaQuestion()
self.CheckAnswerBtn = Button(self.QuestionWindow, text = "Check Answers", command = self.CheckAnswer).pack(padx = 5, pady = 5,side = RIGHT)
self.NewAminoBtn = Button(self.QuestionWindow, text = "Change Amino", command = self.AminoChoice).pack(pady = 5, padx = 5, side = RIGHT)
self.CloseBtn = Button(self.QuestionWindow, text = "Close", command = lambda: self.QuestionWindow.destroy()).pack(padx = 5, pady = 5, side = LEFT)
self.ShowAnswerBtn = Button (self.QuestionWindow, text = "Show Answers", command = self.RevealAnswer)
self.ShowAnswerBtn.pack(pady = 5, padx = 5, side = LEFT)
self.ShowAnswerBtn.config(state = DISABLED)
#This will act as the main, interactable window
def AddtoFunList():
FunList = {}
AminoID = {'AminoID' : QuestionWindow().AminoAcidID}
GenpKa = {'GenpKa' : QuestionWindow().pKaQuestion}
if SelName_V.get() == 1:
FunList.update(AminoID)
elif SelName_V.get() == 0 and 'AminoID' in FunList:
FunList.pop(AminoID)
if SelGenPk_V.get() == 1:
FunList.append(GenPk)
elif SelGenPk_V.get() == 0 and 'GenPk' in FunList:
FunList.remove('GenPk')
if SelActPk_V.get() == 1:
FunList.append('ActPk')
elif SelActPk_V.get() == 0 and 'ActPk' in FunList:
FunList.remove('ActPk')
return FunList
def SwitchState():
UpdatedFunList = AddtoFunList()
print(UpdatedFunList)
if UpdatedFunList == {}:
QuestionOpenbtn.config(state = DISABLED)
else:
QuestionOpenbtn.config(state = NORMAL)
if SelGenPk_V.get() == 1:
SelActPk.config(state = DISABLED)
elif SelActPk_V.get() == 1:
SelGenPk.config(state = DISABLED)
else:
SelActPk.config(state = NORMAL)
SelGenPk.config(state = NORMAL)
root = tk.Tk()
root.title("Biochemistry Start Window")
Introlbl = Label(root, text = "Welcome to the amino acid study aid. Down below you can choose what you'd like to focus on.", wraplength = 300, font = 16).pack(padx = 5, pady = 5)
CheckFrame = tk.Frame(root, relief = 'ridge', highlightbackground = 'black', highlightthickness = 2)
CheckFrame.pack(padx = 20, pady = 20)
SelName_V = IntVar()
SelGenPk_V = IntVar()
SelActPk_V = IntVar()
SelName = Checkbutton(CheckFrame, text = "Amino Acid Identification", command = SwitchState, variable = SelName_V, onvalue = 1, offvalue = 0)
SelName.pack(padx = 5, pady = 5)
SelGenPk = Checkbutton(CheckFrame, text = "Amino Charge, Rounded Values", command = SwitchState, variable = SelGenPk_V, onvalue = 1, offvalue = 0)
SelGenPk.pack(padx = 5, pady = 5)
SelActPk = Checkbutton(CheckFrame, text = "Amino Charge, Actual Values", command = SwitchState, variable = SelActPk_V, onvalue = 1, offvalue = 0)
SelActPk.pack(padx = 5, pady = 5)
QuestionOpenbtn = Button(root, text = "Show Selection Questions", command = QuestionWindow().openQuestionWindow)
QuestionOpenbtn.pack(padx = 5, pady = 5)
CloseWindowBtn = Button(root, text = "Close", command = lambda: root.destroy()).pack(padx = 5, pady =5)
QuestionOpenbtn.config(state = DISABLED)
NoteLbl = Label(root, text = "Note: Rounded values will have the C-Termini and N-Termini pKas rounded to 2.2 and 9.8 respectively.\n", wraplength = 300).pack(padx = 5, pady = 5)
root.mainloop()