-1

I am having difficulties with global variables.

My code:

from Tkinter import *
import Tkinter as tk
import tkMessageBox
import time
import re
import string
from random import randint
class prgrm():

    def start():
        global bs
        global bsrly
        global bsrly2
        global turns
        global rec
        global exp
        global word
        global x
        global w
        global guess
        print "Hangman v1.7 - by Josh & Paul"
        bsrly2 = False
        bsrly = False
        notlie = True
        turns = 8
        rec = ''
        exp = '^[a-z]+$'
        textfile = open('dictionary.txt', 'r')
        words = ['j']
        n = randint(0, len(words)-1)
        word = words[n]
        x = 0
        w = list(word)
        guess = ''
        bs = ''
        for letter in word:
            if letter in guess:
                bs += letter + ' '
            else:
                bs += '_ '

        bs = bs.upper()
        first()


    def MainProgram():
        global ui
        global guess
        global turns
        global rec
        global bs
        global bsrly
        global bsrly2
        bs = ''
        inp = ui.get().strip()
        inp = inp.lower()
        ui.delete(0, END)
        if bsrly2 == True:
            root.quit()

        if inp == "":
            tkMessageBox.showerror("Incorrect Entry", "Error: Please enter a letter")

        elif inp in guess:
            tkMessageBox.showerror("Incorrect Entry", "Error: You have already tried that letter")

        elif not re.match(exp, inp):
            tkMessageBox.showerror("Incorrect Entry", "Error: Please enter a letter")




        else:
            if inp not in word:
                turns -= 1

            if turns == 7:
                img.configure(image=image0)
            if turns == 6:
                img.configure(image=image1)
            if turns == 5:
                img.configure(image=image2)
            if turns == 4:
                img.configure(image=image3)
            if turns == 3:
                img.configure(image=image4)
            if turns == 2:
                img.configure(image=image5)
            if turns == 1:
                img.configure(image=image6)

            guess += ' ' + inp
            if turns == 0:
                img.configure(image=image7)
                bsrly2 = True

            if inp not in word:
                upd.configure(text= "Wrong, try again")
                rec += ' ' + inp
            if inp in word:
                upd.configure(text= "Thats correct!")

            guess2 = rec.upper()
            fb2.configure(text = "Wrong letters:" + guess2)

            wait = 0
            left = 0
            for letter in word:
                if letter in guess:
                    bs += letter + " "
                else:
                    bs += '_ '
                    left += 1

            bs = bs.upper()
            if left == 0:
                bsrly = True
            feedback.configure(text=bs)
            bs = ''
            if bsrly2 == True:
                root
                upd.configure(text="You lose, the word was " + word)

            check()
    def check():
        if bsrly == True:
            root.destroy()
            root2 = Tk()
            root2.wm_iconbitmap('hmn.ico')
            root2.title("You Win!")
            youwin = tk.PhotoImage(master=root2, file="YouWin.gif")
            winer = Label(master=root2, image=youwin)
            winer.image = youwin
            winer.grid(row=0, rowspan=20)
            wanaquit = Label(master=root2, text="Play Again?")
            wanaquit.grid(row=21)
            pbuton = Button(master=root2, text="Yes", command=start)
            pbuton.grid(row=22)
            root2.mainloop()
    global val      
    def val(i):
        if int(i) > 0:
          return False
        return True

    def first():
        global bs
        root = Tk()
        root.wm_iconbitmap('hmn.ico')
        root.title("Hangman v1.7")
        vcmd = (root.register(val), '%i')
        image = tk.PhotoImage(file="image.gif")
        image0 = tk.PhotoImage(file="image0.gif")
        image1 = tk.PhotoImage(file="image1.gif")
        image2 = tk.PhotoImage(file="image2.gif")
        image3 = tk.PhotoImage(file="image3.gif")
        image4 = tk.PhotoImage(file="image4.gif")
        image5 = tk.PhotoImage(file="image5.gif")
        image6 = tk.PhotoImage(file="image6.gif")
        image7 = tk.PhotoImage(file="image7.gif")
        content = tk.Frame(root, bg='black')
        namelbl = tk.Label(content, text="Enter a letter:", bg="black", fg="green")
        feedback = tk.Label(content, text=bs, bg="black", fg="green")
        rb = tk.Checkbutton(content, text="Music", bg="black", fg="green")
        slave = tk.Label(content, text="", bg="black", fg="green")
        slave2 = tk.Label(content, text="", bg="black", fg="green")
        upd = tk.Label(content, text="", bg="black", fg="green")
        fb2 = tk.Label(content, text="Used letters:", bg="black", fg="green")
        ui = tk.Entry(content, validate="key", validatecommand=vcmd)
        ui["width"] = 2
        img = tk.Label(master=content, image=image, bg="black")
        ok = tk.Button(content, text="Okay", bg="black", fg="green", command=MainProgram)
        ui.focus()
        ui.bind('<Return>', (lambda e: MainProgram()))
        content.grid(column=0, row=0)
        img.grid(column=0, row=0, columnspan=4)
        feedback.grid(column=0, row=1)
        fb2.grid(column=0, row=2)
        slave.grid(row=3)
        slave2.grid(row=5)
        upd.grid(row=4, columnspan=4)
        namelbl.grid(column=0, row=6)
        ui.grid(column=1, row=6, sticky=W)
        ok.grid(column=1, row=6)
        rb.grid(row=7)
        root.mainloop()
    first()

start()

I am going crazy because it keeps on saying things like "the global variable 'bs' is not defined' etc. What sould i do? please and thankyous :)

Sam
  • 86,580
  • 20
  • 181
  • 179
  • 3
    My recommendation that is only slightly related to the question: don't use global variables. Package what you need into classes and pass around instantiations of those classes. Using global variables will only cause you headaches. – San Jacinto Mar 27 '11 at 02:49
  • How do i do that?, sorry Im really horrible at scripting -.- – ThatAussieScripter Mar 27 '11 at 02:51
  • I see you rolled out a class. So do not use global vars, use instance members. i.e. `self.bs = sth` and etc. – pajton Mar 27 '11 at 02:51
  • so if i write self.whatever in one def, how do i use it in another def? – ThatAussieScripter Mar 27 '11 at 02:59
  • self.whatever is accessible in any of your methods (defs) within that class (but you have to pass self as the first argument to that method in order to have self available). Please see my answer for an example. – Mark Nenadov Mar 27 '11 at 03:09

4 Answers4

3

Best approach that will fix your issues: Eliminate the global variables and pass them as arguments either to the method at hand, or, to a previously called method (such as the constructor) and set them within the class.

So instead of:

class Foo:
    def test():
        global a
        global b
        global c

..etc..

Do:

class Foo:
    a = 0
    b = 0
    c = 0

    def test(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

..etc..

Or perhaps with a constructor:

class Foo:
    a = 0
    b = 0
    c = 0

    def __init__(self,a, b, c): 
        self.a = a
        self.b = b
        self.c = c

    def test(self):

..etc..

Basically what you are accomplishing is splitting up your code into components (classes). Rather than having a class with entangled alliances elsewhere (ie. global variables), you want to turn each class into a component that inherently has (or receives through a method) everything it needs.

Its like the difference between a box with twenty cables coming into it in unpredictable ways vs. a box that has a well-defined procedure for accepting the information that comes over the cables and clearly specifies a few well-defined and testable routes into its internals. You want the classes / objects you create to have as few points of contact with the outside world as possible in order to make it predictable, and easy to debug and extend. Think of global variables as piercings in the armor of your code and the cause of many unpleasant surprises.

Reliance on too many global variables can lead to problems and should be avoided in many if not most if not all cases.

Mark Nenadov
  • 6,421
  • 5
  • 24
  • 32
2

First you should not use global variables unless you really have to. Which is not the case here. They are evil :). Check Mark Nenadov's answer.

But for you information. You need to declare the variable outside the function scope, and then use the global keyword (if you need to modifiy the content) in you functions. See this thread Using global variables in a function other than the one that created them

Community
  • 1
  • 1
t00ny
  • 1,598
  • 1
  • 11
  • 8
1

Edit: obviously I was not clear enough in explaining my motivation;

While you could define enough global variables to make your solution work, it remains very bad practice. The following code shows how to pass variables back and forth properly, maintaining encapsulation, making troubleshooting and maintenance and reuse much less of a headache.

You should be able to create a Tkinter Hangman by inheriting from Hangman and overriding the input and output methods.

import random

def canonicalString(s):
    return s.strip().upper()

class Words(object):
    @classmethod
    def fromFile(cls, fname):
        "Create a Words object from a file (containing no more than one word per line)"
        with open(fname) as inf:
            return cls([word for word in (line.strip() for line in inf) if word])

    def __init__(self, words):
        super(Words,self).__init__()
        self.words = [canonicalString(word) for word in words]

    def randWord(self):
        return random.choice(self.words)

class Hangman(object):
    def __init__(self, maxTurns=8):
        super(Hangman,self).__init__()
        self.maxTurns = maxTurns

    def run(self, target):
        self.sayWelcome()
        target  = canonicalString(target)
        guesses = ''
        turn    = 1
        solved  = False
        while turn <= self.maxTurns and not solved:
            while True:
                self.sayState(turn, target, guesses)
                nextGuess = self.getGuess(guesses)
                if nextGuess in target:
                    self.sayGoodGuess(nextGuess)
                    guesses += nextGuess
                    solved = all(ch in guesses for ch in target)
                    if solved:
                        break
                else:
                    self.sayBadGuess(nextGuess)
                    break
            turn += 1
        self.sayState(turn, target, guesses)
        self.saySolved(turn, target, guesses) if solved else self.sayFailed(turn, target, guesses)

    def sayWelcome(self):
        print("Hangman v1.7 - by Hugh & Josh & Paul")

    def sayState(self, turn, target, guesses):
        print("\nTurn #{0}".format(turn))
        print('\n'.join([
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "____________",
            "",
            "",
            "",
            "     O",
            "     +",
            "    /|\ ",
            "     +",
            "    / \ ",
            "",
            "____________",
            "",
            "",
            "     O",
            "     +",
            "    /|\ ",
            "     +     |",
            "    / \    |",
            "           |",
            "           |",
            "___________|",
            "",
            "           |",
            "     O     |",
            "     +     |",
            "    /|\    |",
            "     +     |",
            "    / \    |",
            "           |",
            "           |",
            "___________|",
            "         ----",
            "           |",
            "     O     |",
            "     +     |",
            "    /|\    |",
            "     +     |",
            "    / \    |",
            "           |",
            "           |",
            "___________|",
            "        -----",
            "         \ |",
            "     O    \|",
            "     +     |",
            "    /|\    |",
            "     +     |",
            "    / \    |",
            "           |",
            "           |",
            "___________|",
            "    ---------",
            "         \ |",
            "     O    \|",
            "     +     |",
            "    /|\    |",
            "     +     |",
            "    / \    |",
            "           |",
            "           |",
            "___________|",
            "    ---------",
            "     |   \ |",
            "     @    \|",
            "   --+--   |",
            "     |     |",
            "     +     |",
            "    / \    |",
            "           |",
            "           |",
            "___________|",
            "    ---------",
            "     |   \ |",
            "     |    \|",
            "     |     |",
            "     &     |",
            "     %     |",
            "    /|\    |",
            "     +     |",
            "    / \    |",
            "___________|"        
        ][(turn-1)*10: turn*10]))
        print(' "{0}"'.format(''.join(ch if ch in guesses else '_' for ch in target)))

    def getGuess(self, guesses):
        while True:
            ch = canonicalString(raw_input("Please enter a letter: "))

            if len(ch) != 1:
                print("  One character only!")
            elif not ch.isalpha():
                print("  Must be a letter!")
            elif ch in guesses:
                print("  You already picked it!")
            else:
                return ch

    def sayGoodGuess(self, guess):
        print("Yep, that's one!")

    def sayBadGuess(self, guess):
        print("Say yer prayers, you lown-down side-winder!")

    def saySolved(self, turn, target, guesses):
        print("Ah, shucks - looks like we got the wrong man. Cut 'im loose!")

    def sayFailed(self, turn, target, guesses):
        print("Thet-there's one polecat won't trouble us no more! (Your last word on Earth was {0})".format(target))

def main():
    print("Loading dict... ")
    words = Words.fromFile("dictionary.txt")
    print("done.")

    game = Hangman()
    while True:
        game.run(words.randWord())

        tryagain = raw_input("Try another round? (Y/n)").strip().lower()
        if 'yes'.startswith(tryagain):
            print("Okey-doke...")
        else:
            print("G'bye, pardner!")
            break

if __name__=="__main__":
    main()
Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99
  • How does this answer the question? – Bryan Oakley Mar 27 '11 at 17:58
  • @Brian Oakley: By showing a moderately complex example which passes variables properly - without using global variables - from which a Tkinter solution can be easily derived, without giving the whole Tkinter-based solution. – Hugh Bothwell Mar 27 '11 at 20:08
  • Your answer is better now that you added that extra paragraph. Just posting code without an explanation or inline comments isn't very helpful to someone just learning how to program IMO. Explaining how to use the code makes it a considerably better answer. – Bryan Oakley Mar 27 '11 at 21:18
0

You call first() before you call start() so the variable "bs" is indeed not there yet. Maybe you meant to call first() after start() not sure, though... Maybe change feedback = tk.Label(content, text=bs, bg="black", fg="green") to feedback = tk.Label(content, text="?", bg="black", fg="green") since you update it later anyhow. Or add a bs="?" at the very BEGINNING of the file?