0

I'm taking part in Code in Place 2021 and for my final project I developed a Madlibs generator using Python and Tkinter, and the code is functional and works the way I want it to, but obviously it's pretty long and convoluted. I was hoping some of you guys could offer some suggestions on how to make my code more concise and get rid of any unncessary lines!

I pasted all of the code below:

from tkinter import *

# initialize window
root = Tk()
canvas1 = Canvas(root, width=500, height=750)
canvas1.pack()
root.title('Mad Libs Generator')

# creates input fields for user to enter words
label1 = Label(root, text= 'Final Project Mad Libs Generator \n Enter your words below!' , font = ('helvetica', 20))
canvas1.create_window(250, 40, window=label1)

label2 = Label(root, text='Adverb:', font = ('helvetica', 15))
canvas1.create_window(150, 100, window=label2)

entry1 = Entry(root)
canvas1.create_window(300, 100, window=entry1)

label2 = Label(root, text='Noun:', font = ('helvetica', 15))
canvas1.create_window(150, 130, window=label2)

entry2 = Entry(root)
canvas1.create_window(300, 130, window=entry2)

label2 = Label(root, text='Liquid:', font = ('helvetica', 15))
canvas1.create_window(150, 160, window=label2)

entry3 = Entry(root)
canvas1.create_window(300, 160, window=entry3)

label2 = Label(root, text='Verb:', font = ('helvetica', 15))
canvas1.create_window(150, 190, window=label2)

entry5 = Entry(root)
canvas1.create_window(300, 190, window=entry5)

label2 = Label(root, text='Number:', font = ('helvetica', 15))
canvas1.create_window(150, 220, window=label2)

entry6 = Entry(root)
canvas1.create_window(300, 220, window=entry6)

label2 = Label(root, text='Noun(Plural):', font = ('helvetica', 15))
canvas1.create_window(150, 250, window=label2)

entry7 = Entry(root)
canvas1.create_window(300, 250, window=entry7)

label2 = Label(root, text='Verb:', font = ('helvetica', 15))
canvas1.create_window(150, 280, window=label2)

entry8 = Entry(root)
canvas1.create_window(300, 280, window=entry8)

label2 = Label(root, text='Adjective:', font = ('helvetica', 15))
canvas1.create_window(150, 310, window=label2)

entry9 = Entry(root)
canvas1.create_window(300, 310, window=entry9)

label2 = Label(root, text='Noun:', font = ('helvetica', 15))
canvas1.create_window(150, 340, window=label2)

entry10 = Entry(root)
canvas1.create_window(300, 340, window=entry10)

label2 = Label(root, text='Noun(Plural):', font = ('helvetica', 15))
canvas1.create_window(150, 370, window=label2)

entry11 = Entry(root)
canvas1.create_window(300, 370, window=entry11)

label2 = Label(root, text='Illness:', font = ('helvetica', 15))
canvas1.create_window(150, 400, window=label2)

entry12 = Entry(root)
canvas1.create_window(300, 400, window=entry12)

label2 = Label(root, text='Occupation:', font = ('helvetica', 15))
canvas1.create_window(150, 430, window=label2)

entry14 = Entry(root)
canvas1.create_window(300, 430, window=entry14)

label2 = Label(root, text='Body Part(Plural):', font = ('helvetica', 15))
canvas1.create_window(150, 460, window=label2)

entry15 = Entry(root)
canvas1.create_window(300, 460, window=entry15)

label2 = Label(root, text='Body Part:', font = ('helvetica', 15))
canvas1.create_window(150, 490, window=label2)

entry16 = Entry(root)
canvas1.create_window(300, 490, window=entry16)

# function that retrieves words and transfers them into the story
def generateMadLib():
    adverb=entry1.get()
    noun=entry2.get()
    liquid=entry3.get()
    verb=entry5.get()
    number=entry6.get()
    nounp=entry7.get()
    verb2=entry8.get()
    adjective=entry9.get()
    noun2=entry10.get()
    nounp2=entry11.get()
    illness=entry12.get()
    occupation=entry14.get()
    bodypartp=entry15.get()
    bodypart=entry16.get()

    label3 = Label(root, text= 'Here is your Mad Lib!', font = 'helvetica')
    canvas1.create_window(250, 580, window=label3)

    label4 = Label(root, text = ('In order to wash your face ' +adverb+ ' , you must wet your ' +noun+ ' in warm ' +liquid+'. \n Then, '+verb+' it across your face '+number+' times. This will wash off any remaining '+nounp+'. \n When you are done you should '+verb2+' the cloth in '+adjective+' water to clean it. \nYou should also wash your face with a '+noun2+' to keep it smooth and shiny. \n This will keep also keep away '+nounp2+'. Don`t worry. \n It is normal to experience '+illness+' the first time you try this. \n Consult your '+occupation+' if you break out in '+bodypartp+'. This works well on your '+bodypart+' too!'), font=('helvetica', 9))
    canvas1.create_window(250, 650, window=label4)

# button that calls the function to generate the completed story
button1 = Button(text='Generate  Lib!', command=generateMadLib, bg='red', fg='white', font=('helvetica', 9, 'bold'))
canvas1.create_window(250, 525, window=button1)

root.mainloop() 
DeviousLab
  • 17
  • 6
  • Make a class and wirte functions for repeating processes? https://stackoverflow.com/a/67755162/12216053 – Aru Jun 05 '21 at 16:43
  • Why are you putting all of the widgets inside a `Canvas`? Just use the `.grid` manager. – TheLizzard Jun 05 '21 at 16:50
  • You don't need to hold on to your references once they've been passed into a window or other container, so you don't need individual names. Because your labels and entries only differ by the text location, you can create functions to fill in all the rest. Even better, you can create a list of entries with the different data, and process them in a loop. – RufusVS Jun 05 '21 at 16:53
  • @RufusVS Holding a reference to **all** tkinter widgets is good practise. – TheLizzard Jun 05 '21 at 16:55
  • @TheLizzard I haven't done a lot of tkinter projects - I'm a wxPython user normally. I learned to depend on my containers to hold my references (unless I access them externally). this makes initialization easier when I have a bunch of buttons or labels, I just use a temp reference of `btn` or `lbl` as I build it, then drop it in. Makes cut 'n paste easier! – RufusVS Jun 05 '21 at 22:35
  • @RufusVS The problem is that in `tkinter` some people don't hold a reference to a widget and struggle to get data out of the widget. [This](https://stackoverflow.com/questions/1101750/tkinter-attributeerror-nonetype-object-has-no-attribute-attribute-name) problem is very common. I see questions like that every day. That is why I tell everyone to keep a reference to the widget. Also I haven't used `wxPython` – TheLizzard Jun 06 '21 at 09:53

1 Answers1

1

You can reduce your code to half if you use for loop and list.

Here is an example, you can modify the below code according to your need:

from tkinter import *

# initialize window
root = Tk()
root.title('Mad Libs Generator')

# creates input fields for user to enter words
label1 = Label(root, text='Final Project Mad Libs Generator \n Enter your words below!', font=('helvetica', 20))
label1.pack()

frame = Frame(root)
frame.pack()

lst = ['Adverb', 'Noun', 'Liquid', 'Verb', 'Number', 'Noun(Plural)',
       'Verb', 'Adjective', 'Noun', 'Verb', 'Adjective', 'Noun(Plural)', 'Illness',
       'Occupation', 'Body part(plural)', 'Body part']

entries = []

for row, x in enumerate(lst):
    Label(frame, text=f'{x}:').grid(row=row, column=0)
    ent = Entry(frame)
    ent.grid(row=row, column=1)
    entries.append(ent)


# function that retrieves words and transfers them into the story
def generateMadLib():
    story = 'In order to wash your face {0}, you must wet your {1} in warm {2}.\n Then, {3}'\
           'it across your face {4} times. This will wash off any remaining {5}.\n When you'\
           'are done you should {6} the cloth in {7} water to clean it. \nYou should also wash'\
           'your face with a {8} to keep it smooth and shiny.\n This will keep also keep away'\
           '{9}. Don`t worry. \n It is normal to experience {10} the first time you try this.\n'\
           'Consult your {11} if you break out in {12}. ' \
           'This works well on your {13} too! '.format(*(x.get() for x in entries))

    label3 = Label(root, text='Here is your Mad Lib!', font='helvetica')
    label3.pack()

    label4 = Label(root, text=story)
    label4.pack()

# button that calls the function to generate the completed story
button1 = Button(root, text='Generate  Lib!', command=generateMadLib, bg='red', fg='white',
                 font=('helvetica', 9, 'bold'))
button1.pack()

root.mainloop()
TheLizzard
  • 7,248
  • 2
  • 11
  • 31
Art
  • 2,836
  • 4
  • 17
  • 34
  • Sometimes StackOverflow IS a code writing service... But the code here is pretty sweet. Are the indexes actually needed in the replacement fields of the story text, though? – RufusVS Jun 05 '21 at 22:40
  • @RufusVS No, they are not needed in this case. I did it to keep a count of replacement fields in the story besides, OP will find it useful if he/she wants to change the order in which they are replaced. – Art Jun 06 '21 at 01:44