Home > Enterprise >  Is there a better way to organize my program?
Is there a better way to organize my program?

Time:10-26

I've written a study program in an attempt to teach myself Python and help my classmates. I originally learned C , but that had been a while back. I feel like my code is a mess and I've tried organizing things into classes, but can't seem to get the program to work afterwards. Unfortunately, I'm incredibly rusty and know I've made mistakes, but the program itself seems to work. I'd appreciate any guidance. Here's the code I've developed so far:

import tkinter as tk
from tkinter import *
from PIL import ImageTk,Image
import random

#Well, this is my third attempt at getting this program to work how I want
#No idea why, but everything was working fine until I tried to fix another error
#so hopefully doing this stepwise will help me figure out why it's being a bastitch

#This is the core of the program. It's a list of the twenty amino acids, with
#a list containing the name, three letter code, one letter code, and the pKas
#of the C-terminal and N-terminal respecitvely. A third number is the pKa of the
#R group. More information can be added as needed. Each amino acid also has a
#number assigned to pull the acid from the RNG.
def aminoAcids(SelectionNo):

    Alanine = ["Alanine", "Ala", "A", 2.2, 9.8]
    Arginine = ["Arginine", "Arg", "R", 2.2, 9.8, 12.48]
    Asparagine = ["Asparagine", "Asn", "N", 2.2, 9.8]
    AsparticAcid = ["Aspartic Acid", "Asp", "D", 2.2, 9.8, 3.71]
    Cysteine = ["Cysteine", "Cys", "C", 2.2, 9.8, 8.33]
    Glutamine = ["Glutamine", "Gln", "Q", 2.2, 9.8]
    GlutamicAcid = ["Glutamic Acid", "Glu", "E", 2.2, 9.8, 4.15]
    Glycine = ["Glycine", "Gly", "G", 2.2, 9.8]
    Histidine = ["Histidine", "His", "H", 2.2, 9.8, 6.00]
    Isoleucine = ["Isoleucine", "Ile", "I", 2.2, 9.8]
    Leucine = ["Leucine", "Leu", "L", 2.2, 9.8]
    Lysine = ["Lysine", "Lys", "K", 2.2, 9.8, 10.53]
    Methionine = ["Methionine", "Met", "M", 2.2, 9.8]
    Phenylalanine = ["Phenylalanine", "Phe", "F", 2.2, 9.8]
    Proline = ["Proline", "Pro", "P", 2.2, 9.8]
    Serine = ["Serine", "Ser", "S", 2.2, 9.8]
    Threonine = ["Threonine", "Thr", "T", 2.2, 9.8]
    Tryptophan = ["Tryptophan", "Trp", "W", 2.2, 9.8]
    Tyrosine = ["Tyrosine", "Tyr", "Y", 2.2, 9.8, 10.07]
    Valine = ["Valine", "Val", "V", 2.2, 9.8]

    if SelectionNo == 1:
        AminoAcid = Alanine

    elif SelectionNo == 2:
        AminoAcid = Arginine

    elif SelectionNo == 3:
        AminoAcid = Asparagine

    elif SelectionNo == 4:
        AminoAcid = AsparticAcid

    elif SelectionNo == 5:
        AminoAcid = Cysteine

    elif SelectionNo == 6:
        AminoAcid = Glutamine

    elif SelectionNo == 7:
        AminoAcid = GlutamicAcid

    elif SelectionNo == 8:
        AminoAcid = Glycine

    elif SelectionNo == 9:
        AminoAcid = Histidine

    elif SelectionNo == 10:
        AminoAcid = Isoleucine

    elif SelectionNo == 11:
        AminoAcid = Leucine

    elif SelectionNo == 12:
        AminoAcid = Lysine

    elif SelectionNo == 13:
        AminoAcid = Methionine

    elif SelectionNo == 14:
        AminoAcid = Phenylalanine

    elif SelectionNo == 15:
        AminoAcid = Proline

    elif SelectionNo == 16:
        AminoAcid = Serine

    elif SelectionNo == 17:
        AminoAcid = Threonine

    elif SelectionNo == 18:
        AminoAcid = Tryptophan

    elif SelectionNo == 19:
        AminoAcid = Tyrosine

    elif SelectionNo == 20:
        AminoAcid = Valine

    return AminoAcid



#######################################################

#This will open up the question window and allow the user to study
def openQuestionWindow():
    
    global QuestionWindow
    QuestionWindow = Toplevel(root)
    QuestionWindow.title('Second Window')

    global IDCall
    global pKCall
    
    IDCall = 0
    pKCall = 0

    global ErrorMsgLbl

    global AcidName

    global ImageLbl

    global ShowAnswerBtn
    AcidName = aminoAcids(SelectionNo = random.randint(1,20))

    AminoAcidPic = Image.open(f"{AcidName[0]}.png")

    AminoAcidPicResize = AminoAcidPic.resize((200, 200))

    ActualImage = ImageTk.PhotoImage(AminoAcidPicResize)

    ImageLbl = Label(QuestionWindow, image = ActualImage)

    ImageLbl.config(image = ActualImage)

    #ImageLbl.grid(column = 1, columnspan = 2, row = 0)
    ImageLbl.pack()
    
    ErrorMsgLbl = Label(QuestionWindow, text = ' ')
    ErrorMsgLbl.pack()

    if (SelName_V.get() == 1) & (SelPk_V.get() == 1):
        IDCall  = 1
        pKCall  = 1
        CombinedWindow()
        
    elif (SelName_V.get() == 1) & (SelPk_V.get() == 0):
        IDCall  =1
        AminoAcidID()

    elif (SelName_V.get() == 0) & (SelPk_V.get() == 1):
        pKCall  = 1
        pKaQuestion()


    CheckAnswerBtn = Button(QuestionWindow, text = "Check Answers", command = CheckAnswer)
    CheckAnswerBtn.pack(padx = 5, pady = 5,side = RIGHT)

    NewAminoBtn = Button(QuestionWindow, text = "Change Amino", command = AminoChoice)
    NewAminoBtn.pack(pady = 5, padx = 5,  side = RIGHT)

    CloseBtn = Button(QuestionWindow, text = "Close", command = lambda: QuestionWindow.destroy())
    CloseBtn.pack(padx = 5, pady = 5, side = LEFT)

    ShowAnswerBtn = Button (QuestionWindow, text = "Show Answers", command = RevealAnswer) 
    ShowAnswerBtn.pack(pady = 5, padx = 5, side = LEFT)
    ShowAnswerBtn.config(state = DISABLED)

    QuestionWindow.mainloop()


#######################################################

#This defines which amino acid is used
def AminoChoice():

    global AcidName
    
    NewAcidName = aminoAcids(SelectionNo = random.randint(1,20))

    NewAminoAcidPic = Image.open(f"{NewAcidName[0]}.png")

    NewAminoAcidPicResize = NewAminoAcidPic.resize((200, 200))

    NewActualImage = ImageTk.PhotoImage(NewAminoAcidPicResize)

    ImageLbl.configure(image = NewActualImage)

    ImageLbl.photo = NewActualImage

    AcidName = NewAcidName

    ShowAnswerBtn.config(state = DISABLED)

    ErrorMsgLbl.config(text = ' ')

    if IDCall == 1:
        NameAnswer.config(bg = "yellow")
        NameAnswer.delete(0,END)
        Let3Answer.config(bg = "yellow")
        Let3Answer.delete(0,END)
        Let1Answer.config(bg = "yellow")
        Let1Answer.delete(0,END)
        NameReveal.config(text = ' ')
        Let3Reveal.config(text = ' ')
        Let1Reveal.config(text = ' ')

    if pKCall == 1:
        pKAnswer.config(bg = "yellow")
        pKAnswer.delete(0, END)
        NewpHVal = round(random.uniform(0, 14), 2)
        TotatlCharge = tk.IntVar()
        TotalCharge.set(0)
        if AcidName[3] < NewpHVal:
            TotalCharge.set(TotalCharge.get() - 1)
        elif AcidName[3] == NewpHVal:
            TotalCharge.set(TotalCharge.get() - .5)

        if AcidName[4] > NewpHVal:
            TotalCharge.set(TotalCharge.get()   1)
        elif AcidName[4] == NewpHVal:
            TotalCharge.set(TotalCharge.get()   .5)
        pKQuestionLbl.config(text = f"What is the amino acids charge at pH {NewpHVal: .2f}?")
        pKReveal.config(textvariable = ' ')


#######################################################

#This will show the questions of all question types
def CombinedWindow():

    AminoAcidID()

    pKaQuestion()
    
    
#######################################################

#This will ask for ID if ID questions are selected
def AminoAcidID():

    global NameAnswer
    global NameReveal
    global Let3Answer
    global Let3Reveal
    global Let1Answer
    global Let1Reveal
    
    NameQuest = Label(QuestionWindow, text = "What is the full name of the amino acid?", wraplength = 300)
    NameAnswer = Entry(QuestionWindow, width = 20, bg = "yellow")
    NameReveal = Label (QuestionWindow, text = " ", width = 20)

    Let3Quest = Label(QuestionWindow, text = "What is the three letter code of the amino acid?", wraplength = 300)
    Let3Answer = Entry(QuestionWindow, width = 20, bg = "yellow")
    Let3Reveal = Label (QuestionWindow, text = " ", width = 20)

    Let1Quest = Label(QuestionWindow, text = "What is the one letter code of the amino acid?", wraplength = 300)
    Let1Answer = Entry(QuestionWindow, width = 20, bg = "yellow")
    Let1Reveal = Label (QuestionWindow, text = " ", width = 20)

    NameQuest.pack()
    NameAnswer.pack()
    NameReveal.pack()

    Let3Quest.pack()
    Let3Answer.pack()
    Let3Reveal.pack()

    Let1Quest.pack()
    Let1Answer.pack()
    Let1Reveal.pack()


#######################################################


def pKaQuestion():

    pHVal = round(random.uniform(0, 14), 2)

    global TotalCharge
    global pKAnswer
    global pKReveal
    global pKQuestionLbl
    
    TotalCharge = tk.IntVar()
    TotalCharge.set(0)
    
    if AcidName[3] < pHVal:
        TotalCharge.set(TotalCharge.get() - 1)
    elif AcidName[3] == pHVal:
        TotalCharge.set(TotalCharge.get() - .5)

    if AcidName[4] > pHVal:
        TotalCharge.set(TotalCharge.get()   1)
    elif AcidName[4] == pHVal:
        TotalCharge.set(TotalCharge.get()   .5)

    pKQuestionLbl = Label(QuestionWindow, text = f"What is the amino acids charge at pH {pHVal: .2f}?")
    pKQuestionLbl.pack()

    pKAnswer = Entry(QuestionWindow, width = 20, bg = "yellow")
    pKAnswer.pack()

    pKReveal = Label (QuestionWindow, text = ' ')
    pKReveal.pack()

######################################################################


#This will validate the entered answer and show if they are correct
def CheckAnswer():

    if IDCall == 1:

        CorrectName = AcidName[0]
        Correct3Let = AcidName[1]
        CorrectLet = AcidName[2]

        NameInput = NameAnswer.get()
        Let3Input = Let3Answer.get()
        Let1Input = Let1Answer.get()

        if len(NameInput.get() == 0) or len(Let3Input.get() == 0) or len(Let1Input.get() == 0):
           ErrorMsgLbl.config(text = "Please Enter A Value Into All Fields", fg = "red")                                                            
        else:
            ShowAnswerBtn.config(state = ACTIVE)
            if CorrectName.upper() == NameInput.upper():
                NameAnswer.config(bg = "green")
            else:
                NameAnswer.config(bg = "red")

            if Correct3Let.upper() == Let3Input.upper():
                Let3Answer.config(bg = "green")
            else:
                Let3Answer.config(bg = "red")

            if CorrectLet.upper() == Let1Input.upper():
                Let1Answer.config(bg = "green")
            else:
                Let1Answer.config(bg = "red")

    if pKCall == 1:

        ActualCharge = TotalCharge.get()
        if len(pKAnswer.get()) == 0:
            ErrorMsgLbl.config(text = "Please Enter A Value Into All Fields", fg = "red")        

        else:
            ShowAnswerBtn.config(state = ACTIVE)
            pKAnswerInt = int(pKAnswer.get())
            ErrorMsgLbl.config(text = ' ')
            if pKAnswerInt == ActualCharge:
                pKAnswer.config(bg = "green")
            else:
                pKAnswer.config(bg = "red")


#######################################################


#This will show the answer when pressed
def RevealAnswer():
    
    if IDCall == 1:
        NameReveal.config(text = AcidName[0])
        Let3Reveal.config(text = AcidName[1])
        Let1Reveal.config(text = AcidName[2])

    if pKCall == 1:
        pKReveal.config(textvariable = TotalCharge)

    
#######################################################

#This will act as the main, interactable window
root = tk.Tk()

root.title("Biochemistry Start Window")
root.geometry('400x200')
#root.columnconfigure( 1, weight = 1 )
#root.rowconfigure( 1, weight = 0 )

#This function will ensure that at least one item is selected
def SwitchState():

    if SelName_V.get() == 0 and SelPk_V.get() == 0:
        QuestionOpenbtn.config(state = DISABLED)
    else:
        QuestionOpenbtn.config(state = NORMAL)

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)
#Introlbl.grid(column = 1, row = 0 )
Introlbl.pack()

SelName_V = IntVar()
SelPk_V = IntVar()
SelName = Checkbutton(root, text = "Amino Acid Identification", command = SwitchState, wraplength = 100, variable = SelName_V, onvalue = 1, offvalue = 0)
SelPk = Checkbutton(root, text = "Amino Charge", command = SwitchState, height = 2, width = 10, variable = SelPk_V, onvalue = 1, offvalue = 0)

#SelName.grid(column = 1, row = 1)
SelName.pack()
#SelPk.grid(column = 1, row = 2)
SelPk.pack()

QuestionOpenbtn = Button(root, text = "Show Selection Questions", command = openQuestionWindow)

#QuestionOpenbtn.grid(column = 1, row = 3)
QuestionOpenbtn.pack()

CloseWindowBtn = Button(root, text = "Close", command = lambda: root.destroy())

#CloseWindowBtn.grid(column = 1, row = 4)
CloseWindowBtn.pack()
QuestionOpenbtn.config(state = DISABLED)




root.mainloop()

I'm very unfamiliar with classes and had a hard time setting everything up and had issues with being unable to interact between the two.

CodePudding user response:

Yes. You seriously need to learn about python lists and dictionaries. Your code could be about three times shorted.

Also, not all data needs to be part of the code. Loading the amino acid names and properties from a text file would have separated concerns of logic and data.

And, you know C , so you should know better than to use globals. You have no use case for them, so properly design your function interfaces and get rid of those you use to exchange data. However, declaring globals within a function body at the start probably means you do not understand what that means - so please revisit the official python.org introduction on writing basic python.

CodePudding user response:

one small suggestion I can give you is to use 'switch' condition instead of this ifelse (because it's ugly seeing it too much). now if you want a quick fix to hide those variables in the beginning of the code, you can wrap them with "if(true){all variables goes here}". then you can 'expand/collapse' it using your editor. Also you can wrap it with comment to know what is wrapped. I know this is 'unprofessional' but I personally use sometimes I thought I throw it here ..

  • Related