Thanks Alan and Peter,

I've gone through and made a bunch of changes based on all your advice.
Thank you...everything seems to be fully working and I think I have made
the code more efficient using your advice...hope I got everything. (full
code below)


I've just added an example menu to highlight the concept for teachers or
students to refer to.


2 questions:

1.) Centre feature

When I use txt.insert I need to use the feature Peter found...

center = txt.tag_config("center", justify="center")
txt.insert(0.0, result, "center")

but in the Enter label section...I could just use it straight away..without
defining it with the tag...

answerbox = Entry(root, bg="grey", font=("arial", 24, "bold"), justify
="center")
answerbox.grid(row=15, columnspan=2, padx=0, pady=0, sticky=EW)

Can you explain why?

2.)  When inserting the wording re: 'You have made mistake' or 'Great job'
etc...I found I had to break it up into separate lines...as I was getting
syntax errors.

txt.insert(0.0, result, "center")
txt.insert(3.0, wordScore, "center")
txt.insert(8.1, score, "center")


Is there a way to simply combine all these commands with one txt.insert and
using commas?

3.) Is there anything else in this code that looks like a bit of a
glaring...'Coding Rookie' mistake? Or is simply inefficient?

Thanks again for all your help. I've found things are starting to make a
lot more sense...and googling things now let me add other features. eg. The
rounding function here:

def viewPercent():
    percentCalc = score/total*100
    rounded = round(percentCalc, 2)
    percentViewLab["text"] = rounded


Thank you Peter and Allan..and others here who have helped on the journey.


from tkinter import *

import random

# GLOBAL VARIABLES
# Created with a starting value.
answer = 0
score = 0
wrong = 0
mistakes = 0
total = 0


def makeproblem():

    # access global answer...this is only required IF you need to update
the variable within the function.
    # this happens below when answer is changed to number1 * number2
    global answer


    # erases all text in current text box.
    txt.delete(0.0, 'end')

    sentence = "Here is your problem "

    # example of generating a random number.
    number1 = random.randint(2,12)
    number2 = random.randint(2,12)

    answer = number1 * number2

    # creates a justification command called centre. (Not too sure why this
is needed here...but not on the answer box elsewhere!)
    center = txt.tag_config("center", justify="center")

    txt.insert(0.0, sentence, "center")
    txt.insert(2.2, number1, "center")
    txt.insert(3.3, " x ", "center")
    txt.insert(4.4, number2, "center")

def checkanswer():
    txt.delete(0.0, 'end')

    # as each variable changes outside of function...we need to make these
global.
    global score
    global mistakes
    global total

    # checks for what is written in answerbox using the 'get'
feature...makes it an integer.
    response = int(answerbox.get())
    wordScore = "Your score is now "

    if response == answer:
        score += 1
        total += 1
        result = "Great Job! "
        root.update()
        viewSC()
        viewPercent()
        viewTotal()


    else :
        total += 1
        result = "Sorry...you made a mistake. \n "
        # the \n above adds a line break.
        mistakes += 1
        viewMistakes()
        viewTotal()
        viewPercent()

    center = txt.tag_config("center", justify="center")
    txt.insert(0.0, result, "center")
    txt.insert(3.0, wordScore, "center")
    txt.insert(8.1, score, "center")
   # txt.insert(1,1, "Score is")
    #txt.insert(3,3, score)


def about():
    txt.delete(0.0, 'end')

    instructions = "Here is how you play the game. Press generate
problem..and then enter your answer. Your score will be displayed below."

    txt.insert(0.0,instructions)



root = Tk()
root.geometry("640x700+0+0")
root.title("Times Tables Game")

# MENU SECTION
# These are included as an example menu structure...in many cases they
don't do much...but do feature instructions and a quit feature.

# create a toplevel menu
menubar = Menu(root)

# Just an example of printing  hello to console for use in a menu item.
def hello():
    print ("hello!")

# display the menu
root.config(menu=menubar)

# create a pulldown menu, and adds it to the menu bar
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Open", command=makeproblem)
filemenu.add_command(label="Save", command=makeproblem)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=root.quit)

menubar.add_cascade(label="File", menu=filemenu)

# create more pulldown menus
editmenu = Menu(menubar, tearoff=0)
editmenu.add_command(label="Cut", command=checkanswer)
editmenu.add_command(label="Copy", command=checkanswer)
editmenu.add_command(label="Paste", command=checkanswer)
menubar.add_cascade(label="Edit", menu=editmenu)

helpmenu = Menu(menubar, tearoff=0)
helpmenu.add_command(label="About", command=about)
menubar.add_command(label="Hello", command=hello)
menubar.add_cascade(label="Help", menu=helpmenu)
menubar.add_command(label="Quit", command=root.quit)


# Plain text labels at top of window.
timeslabel = Label(root, text="Times Tables Practice", fg="white",
bg="blue", font=("arial", 36, "bold"))
timeslabel.grid(columnspan=12, sticky='ew')
instruction = Label(root, text="Please click on the button to generate a
problem", fg="blue", bg="white", font=("arial", 16, "bold"))
instruction.grid(row=2, columnspan=20)

# Makes an entry box with the variable of 'answerbox'
answerbox = Entry(root, bg="grey", font=("arial", 24, "bold"), justify
="center")
answerbox.grid(row=15, columnspan=2, padx=0, pady=0, sticky=EW)

# Makes a button that generate the Times Tables problem
btn = Button(root, text="GENERATE PROBLEM", bg="blue", fg="white",
command=makeproblem)
btn.grid(row=11, columnspan=2, sticky=EW)

# Makes a button that checks the answer
btn = Button(root, text="CHECK ANSWER", bg="darkblue", fg="white",
command=checkanswer)
btn.grid(row=13, columnspan=2, sticky=EW)

#TEXT BOX AREA
#This important command creates the text box called 'txt'. This is used for
all the text output.
txt = Text(root, width=35, height=8, wrap=WORD, font=("arial", 20, "bold"))
txt.grid(row=12, columnspan=2, sticky=EW )


#Adds a blankline below answer box...but leaves top alignment alone. You
could use PAD...but that command does both sides. There may be another way
to achieve this.
blankline3 = Label(root, text = "", bg = "white")
blankline3.grid(row=17, sticky='ew')

# SCORING LABELS AND RESULTS SECTION
# Places the labels and the results beside each other in column 0 and
column 1.
# Note: Each 'View' label grabs the starting score from the declarations at
the top. eg. score = 0
scorelab = Label(root, text="Score", bg="green", fg="white", font=("arial",
20, "bold"))
scorelab.grid(row=18, column=0, sticky=E)

scoreViewLab = Label(root, text=score, fg="black", bg="grey",
font=("arial", 20, "bold"))
scoreViewLab.grid(row=18, column=1, sticky=W)

totalLab = Label(root, text="Out of", bg="purple", fg="white",
font=("arial", 20, "bold"))
totalLab.grid(row=19, column=0, sticky=E)

totalViewLab = Label(root, text=total, fg="black", bg="grey",
font=("arial", 20, "bold"))
totalViewLab.grid(row=19, column=1, sticky=W)

mistakeslab = Label(root, text="Mistakes", bg="red", fg="white",
font=("arial", 20, "bold"))
mistakeslab.grid(row=20, column=0, sticky=E)

mistakesViewLab = Label(root, text=mistakes, fg="black", bg="grey",
font=("arial", 20, "bold"))
mistakesViewLab.grid(row=20, column=1, sticky=W)

percentlab = Label(root, text="Percentage", bg="aqua", fg="white",
font=("arial", 20, "bold"))
percentlab.grid(row=21, column=0, sticky=E)


percentViewLab = Label(root, text=0, fg="black", bg="grey", font=("arial",
20, "bold"))
percentViewLab.grid(row=21, column=1, sticky=W)


#SCORE UPDATE SECTION.
# Each of these functions allows the 4 scoring labels to be updated as
answers are deemed correct or incorrect with the if/else statement.
# The viewPercent function includes the calculation to update the
percentView label...shows an example of a calculation within a label.

def viewSC():
    scoreViewLab["text"] = score

def viewTotal():
    totalViewLab["text"] = total

def viewMistakes():
    mistakesViewLab["text"] = mistakes

def viewPercent():
    # This calculates the percentage and then rounds it to 2 decimal
places..before placing the Percent result on the percentView Label.
    percentCalc = score/total*100
    rounded = round(percentCalc, 2)
    percentViewLab["text"] = rounded

# This keeps the program running.
root.mainloop()

















from tkinter import *

import random

# GLOBAL VARIABLES
# Created with a starting value.
answer = 0
score = 0
wrong = 0
mistakes = 0
total = 0


def makeproblem():

    # access global answer...this is only required IF you need to update
the variable within the function.
    # this happens below when answer is changed to number1 * number2
    global answer


    # erases all text in current text box.
    txt.delete(0.0, 'end')

    sentence = "Here is your problem "

    # example of generating a random number.
    number1 = random.randint(2,12)
    number2 = random.randint(2,12)

    answer = number1 * number2

    # creates a justification command called centre. (Not too sure why this
is needed here...but not on the answer box elsewhere!)
    center = txt.tag_config("center", justify="center")

    txt.insert(0.0, sentence, "center")
    txt.insert(2.2, number1, "center")
    txt.insert(3.3, " x ", "center")
    txt.insert(4.4, number2, "center")

def checkanswer():
    txt.delete(0.0, 'end')

    # as each variable changes outside of function...we need to make these
global.
    global score
    global mistakes
    global total

    # checks for what is written in answerbox using the 'get'
feature...makes it an integer.
    response = int(answerbox.get())
    wordScore = "Your score is now "

    if response == answer:
        score += 1
        total += 1
        result = "Great Job! "
        root.update()
        viewSC()
        viewPercent()
        viewTotal()


    else :
        total += 1
        result = "Sorry...you made a mistake. \n "
        # the \n above adds a line break.
        mistakes += 1
        viewMistakes()
        viewTotal()
        viewPercent()

    center = txt.tag_config("center", justify="center")
    txt.insert(0.0, result, "center")
    txt.insert(3.0, wordScore, "center")
    txt.insert(8.1, score, "center")
   # txt.insert(1,1, "Score is")
    #txt.insert(3,3, score)


def about():
    txt.delete(0.0, 'end')

    instructions = "Here is how you play the game. Press generate
problem..and then enter your answer. Your score will be displayed below."

    txt.insert(0.0,instructions)



root = Tk()
root.geometry("640x700+0+0")
root.title("Times Tables Game")

# MENU SECTION
# These are included as an example menu structure...in many cases they
don't do much...but do feature instructions and a quit feature.

# create a toplevel menu
menubar = Menu(root)

# Just an example of printing  hello to console for use in a menu item.
def hello():
    print ("hello!")

# display the menu
root.config(menu=menubar)

# create a pulldown menu, and adds it to the menu bar
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Open", command=makeproblem)
filemenu.add_command(label="Save", command=makeproblem)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=root.quit)

menubar.add_cascade(label="File", menu=filemenu)

# create more pulldown menus
editmenu = Menu(menubar, tearoff=0)
editmenu.add_command(label="Cut", command=checkanswer)
editmenu.add_command(label="Copy", command=checkanswer)
editmenu.add_command(label="Paste", command=checkanswer)
menubar.add_cascade(label="Edit", menu=editmenu)

helpmenu = Menu(menubar, tearoff=0)
helpmenu.add_command(label="About", command=about)
menubar.add_command(label="Hello", command=hello)
menubar.add_cascade(label="Help", menu=helpmenu)
menubar.add_command(label="Quit", command=root.quit)


# Plain text labels at top of window.
timeslabel = Label(root, text="Times Tables Practice", fg="white",
bg="blue", font=("arial", 36, "bold"))
timeslabel.grid(columnspan=12, sticky='ew')
instruction = Label(root, text="Please click on the button to generate a
problem", fg="blue", bg="white", font=("arial", 16, "bold"))
instruction.grid(row=2, columnspan=20)

# Makes an entry box with the variable of 'answerbox'
answerbox = Entry(root, bg="grey", font=("arial", 24, "bold"), justify
="center")
answerbox.grid(row=15, columnspan=2, padx=0, pady=0, sticky=EW)

# Makes a button that generate the Times Tables problem
btn = Button(root, text="GENERATE PROBLEM", bg="blue", fg="white",
command=makeproblem)
btn.grid(row=11, columnspan=2, sticky=EW)

# Makes a button that checks the answer
btn = Button(root, text="CHECK ANSWER", bg="darkblue", fg="white",
command=checkanswer)
btn.grid(row=13, columnspan=2, sticky=EW)

#TEXT BOX AREA
#This important command creates the text box called 'txt'. This is used for
all the text output.
txt = Text(root, width=35, height=8, wrap=WORD, font=("arial", 20, "bold"))
txt.grid(row=12, columnspan=2, sticky=EW )


#Adds a blankline below answer box...but leaves top alignment alone. You
could use PAD...but that command does both sides. There may be another way
to achieve this.
blankline3 = Label(root, text = "", bg = "white")
blankline3.grid(row=17, sticky='ew')

# SCORING LABELS AND RESULTS SECTION
# Places the labels and the results beside each other in column 0 and
column 1.
# Note: Each 'View' label grabs the starting score from the declarations at
the top. eg. score = 0
scorelab = Label(root, text="Score", bg="green", fg="white", font=("arial",
20, "bold"))
scorelab.grid(row=18, column=0, sticky=E)

scoreViewLab = Label(root, text=score, fg="black", bg="grey",
font=("arial", 20, "bold"))
scoreViewLab.grid(row=18, column=1, sticky=W)

totalLab = Label(root, text="Out of", bg="purple", fg="white",
font=("arial", 20, "bold"))
totalLab.grid(row=19, column=0, sticky=E)

totalViewLab = Label(root, text=total, fg="black", bg="grey",
font=("arial", 20, "bold"))
totalViewLab.grid(row=19, column=1, sticky=W)

mistakeslab = Label(root, text="Mistakes", bg="red", fg="white",
font=("arial", 20, "bold"))
mistakeslab.grid(row=20, column=0, sticky=E)

mistakesViewLab = Label(root, text=mistakes, fg="black", bg="grey",
font=("arial", 20, "bold"))
mistakesViewLab.grid(row=20, column=1, sticky=W)

percentlab = Label(root, text="Percentage", bg="aqua", fg="white",
font=("arial", 20, "bold"))
percentlab.grid(row=21, column=0, sticky=E)


percentViewLab = Label(root, text=0, fg="black", bg="grey", font=("arial",
20, "bold"))
percentViewLab.grid(row=21, column=1, sticky=W)


#SCORE UPDATE SECTION.
# Each of these functions allows the 4 scoring labels to be updated as
answers are deemed correct or incorrect with the if/else statement.
# The viewPercent function includes the calculation to update the
percentView label...shows an example of a calculation within a label.

def viewSC():
    scoreViewLab["text"] = score

def viewTotal():
    totalViewLab["text"] = total

def viewMistakes():
    mistakesViewLab["text"] = mistakes

def viewPercent():
    percentViewLab["text"] = score/total*100

# This keeps the program running.
root.mainloop()



Matthew Polack | Teacher


[image: Emailbanner3.png]

Trinity Drive  |  PO Box 822

Horsham Victoria 3402

p. 03 5382 2529   m. 0402456854

e. matthew.pol...@htlc.vic.edu.au

w. www.htlc.vic.edu.au

On Thu, Aug 16, 2018 at 7:11 PM, Alan Gauld via Tutor <tutor@python.org>
wrote:

> On 16/08/18 08:18, Matthew Polack wrote:
>
> > The last remaining issue is the program does not calculate the percentage
> > right when you make mistakes...it just keeps giving a result of 100%.
>
> > def percentCheck():
> >     global percentScore
> >     global score
> >     global mistakes
> >     global total
>
> While you do need some globals in your program you only
> need to declare the ones you are changing. In this case
> you only need to declare percentScore as global.
>
> >     percentScore = float(score/total) * 100
>
> To debug this I would add a print line such as
>
> print(score, total)
>
> Since if you always get 100 it suggests that score
> and total are the same.
>
> One problem here is that you are converting to float
> after doing the division. That's usually the wrong
> thing to do. In fact I notice you do that a lot of
> conversions on expressions. Its much safer to do the
> conversions on the individual values then perform
> the computation after the conversion.
>
> In fact in this case (and most of the other similar
> cases) you should not need the float conversion since
> you already converted the values when you read them
> from the GUI. You should aim to convert the data
> into the correct type as soon as possible then you
> will not need to use conversions anywhere else.
>
> Some more observations below...
>
> >     viewPercent()
> >
> >
> > def makeproblem():
> >     # deletes anything in text box from location 00 to the end
> >     global answer # access global answer
> >
> >     txt.delete(0.0, 'end')
> >
> >     sentence = "Here is your problem "
> >
> >     number1 = random.randint(2,12)
> >     number2 = random.randint(2,12)
> >
> >     answer = number1 * number2
> >
> >     txt.insert(0.0, sentence)
> >     txt.insert(2.2, number1)
> >     txt.insert(3.3, " x ")
> >     txt.insert(4.4, number2)
> >
> > def checkanswer():
> >     # deletes anything in text box from location 00 to the end
> >     txt.delete(0.0, 'end')
> >
> >     global answer
> >     global score
> >     global wrong
> >     global mistakes
> >     global percentScore
> >     global total
> >
> >     # checks for what is written in answerbox
> >     response = int(answerbox.get())
> >     wordScore = "Your score is now "
> >
> >     if response == answer:
> >         score = int(score + 1)
> >         total = int(total + 1)
> ....
> >     else :
> >         score= int(score + 1)
> >         total = int(total + 1)
>
> Notice that regardless of what the answer is
> you update both score and total. And again you
> don't need the int() conversions, the variables
> are already integers.
>
> > root = Tk()
> > root.geometry("640x700+0+0")
> > root.title("Times Tables")
> >
> >
> > #HOW CAN I CENTRE THE TEXT???
> > txt = Text(root, width=35, height=8, wrap=WORD, font=("arial", 24,
> "bold"))
> > txt.grid(row=12, columnspan=2, sticky=EW )
>
> AS per Peter's link you need to create a tag and configure
> it with the font and justification you want. Then when you
> insert the text you add the tag name.
>
> eg:
>
> center = txt.tag_config("center", justify="center")
> txt.insert(1.0, "Hello world", "center")
>
>
> > blankline3 = Label(root, text = "", bg = "white")
> > blankline3.grid(row=17, sticky='ew')
>
> I'm not sure what this is for. If you want some space
> just add some padding to the label below.
>
> > scorelab = Label(root, text="Score", bg="green", fg="white",
> font=("arial",
> > 20, "bold"))
> > scorelab.grid(row=18, column=0, sticky=E)
>
> Note that you create a Label here, but in ViewSC below you create
> another label which causes this one to be deleted. This is not
> necessary, instead just change the text of this label in viewSC.
>
> scorelab['text'] = "New text here"
>
> > totalLab = Label(root, text="Out of", bg="purple", fg="white",
> > font=("arial", 20, "bold"))
> > totalLab.grid(row=19, column=0, sticky=E)
>
> Same applies to the other viewXXX functions, just update these
> labels, don't build and destroy new label widgets each time.
> That's a lot of wasted work and time for your PC.
>
> > def viewSC():
> >     global score
>
> You don't change score so no need for global here.
> You only need global when changing the data.
>
> >     scoreViewLab = Label(root, text=score, fg="black", bg="grey",
> > font=("arial", 20, "bold"))
> >     scoreViewLab.grid(row=18, column=1, sticky=W)
>
> I just noticed this is in a different grid location,
> but the principle still applies create it once at the
> start of the GUI then update it in the view functions.
>
> > def viewTotal():
> >     global total
> >     #scoreViewLab=Label(root, scoreView, textvariable=scoreView)
> >     totalViewLab = Label(root, text=total, fg="black", bg="grey",
> > font=("arial", 20, "bold"))
> >     totalViewLab.grid(row=19, column=1, sticky=W)
> >
> > def viewWrong():
> >     global wrong
> >     global mistakes
> >
> >     #scoreViewLab=Label(root, scoreView, textvariable=scoreView)
> >     wrongViewLab = Label(root, text=mistakes, fg="black", bg="grey",
> > font=("arial", 20, "bold"))
> >     wrongViewLab.grid(row=20, column=1, sticky=W)
>
> You declare wrong as global but never use it.
> Should this function not be called viewMistakes?
>
> > def viewPercent():
> >     global percentScore
> >     global total
> >     global score
> >     #scoreViewLab=Label(root, scoreView, textvariable=scoreView)
> >     percentViewLab = Label(root, text=percentScore, fg="black",
> bg="grey",
> > font=("arial", 20, "bold"))
> >     percentViewLab.grid(row=21, column=1, sticky=W)
>
> > viewSC()
> > viewWrong()
> > viewPercent()
> > viewTotal()
> >
> > root.mainloop(
> While your code sort of works its doing an awful
> lot of work that it doesn't need to do. You need
> to stop and think through why you use each global
> and type conversions and only keep the ones you
> really need.
>
> Similarly you should only need to create the
> widgets once when you build the GUI. After that
> its just a matter of updating the content.
>
>
> --
> Alan G
> Author of the Learn to Program web site
> http://www.alan-g.me.uk/
> http://www.amazon.com/author/alan_gauld
> Follow my photo-blog on Flickr at:
> http://www.flickr.com/photos/alangauldphotos
>
>
> _______________________________________________
> Tutor maillist  -  Tutor@python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor
>

-- 
**Disclaimer: *Whilst every attempt has been made to ensure that material 
contained in this email is free from computer viruses or other defects, the 
attached files are provided, and may only be used, on the basis that the 
user assumes all responsibility for use of the material transmitted. This 
email is intended only for the use of the individual or entity named above 
and may contain information that is confidential and privileged. If you are 
not the intended recipient, please note that any dissemination, 
distribution or copying of this email is strictly prohibited. If you have 
received this email in error, please notify us immediately by return email 
or telephone +61 3 5382 2529** and destroy the original message.*
_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Reply via email to