1

The program I'm working on allows the user to enter a text and check if it is a palindrome. The input is ordered with order(), tested with testPalindrome(orderedText) and then the result is shown with showResult(Palindrome). My problem arises when it comes to saving a palindrome. The user is supposed to be able to save an input if it has been verified to be a palindrome. I thought it would make sense if the save button is disabled until an input has been tested true. So I put "saveButton.config(state = 'normal')" in showResult(Palindrome), but that doesn't work. The error says "global name 'saveButton' is not defined", and I can see why. However, I can't manage to get it right. I wonder if it is possible to solve this problem with some addition to this single line of text or if I have to make more thorough modifications. (I tried for instance to turn "saveButton" into a global variable such as "inputEntry" but for whatever reason I couldn't get that to work either.)

    import Tkinter

    root = Tkinter.Tk()
    infoLabel = Tkinter.Label(root, text = 'Enter a text and check if it is a palindrome.', height = 12, width = 64, bg = 'green')
    inputEntry = Tkinter.Entry(root, width = 64)

    def main():
        window()
        Tkinter.mainloop()

    def window():
        root.title('Palindrome detector')
        root.geometry('534x285+500+250')
        infoLabel.grid(row = 0, rowspan = 1, column = 0, columnspan = 4, pady = 8, padx = 8)
        inputEntry.insert(0, 'Enter text...')
        inputEntry.grid(row = 1, rowspan = 1, column = 0, columnspan = 4, padx = 4)
        verifyButton = Tkinter.Button(root, text = 'Verify', width = 7, command = order).grid(row = 2, column = 0, pady = 10)
        saveButton = Tkinter.Button(root, text = 'Save', state = 'disabled', width = 7, command = save).grid(row = 2, column = 3)

    def order():
        Text_input = inputEntry.get()
        orderedText = Text_input.replace(" ", "")
        orderedText = orderedText.lower()
        orderedText = filter(str.isalnum, orderedText)
        if any(char.isalpha() or char.isdigit() for char in orderedText):
            testPalindrome(orderedText)
        else:
            infoLabel.config(text = 'The input must contain at least one alphanumeric character.', bg = 'red')

    def testPalindrome(orderedText):
        Palindrome = True
        i = 0
        for char in orderedText:
            if orderedText[i] == orderedText[-(i+1)]:
                Palindrome = True
                i += 1
            else:
                Palindrome = False
                break
        showResult(Palindrome)

    def showResult(Palindrome):
        if Palindrome == True:
            infoLabel.config(text = 'Yes! "' + str(inputEntry.get()) + '" is a palindrome!', bg = 'green')
            saveButton.config(state = 'normal')
        else:
            infoLabel.config(text = 'No! "' + str(inputEntry.get()) + '" is not a palindrome!', bg = 'red')
            saveButton.config(state = 'disabled')

    def save():
        savePalindrome = inputEntry.get()
        palindromes_file = open("Palindromes.txt", "a")
        palindromes_file.write(savePalindrome + "\n")
        palindromes_file.close()
        infoLabel.config(text = '"' + str(savePalindrome) + '" has been saved to file.', bg = 'green')
        saveButton.config(state = 'disabled')

    if __name__ == '__main__':
        main()

Edit: After reading the answers I followed a textbook and tried to rewrite the whole thing using class. Surely there may be any number of defects as a result. My concern right now is "NameError: global name 'testPalindrome' is not defined". The error regards line 7 in order(self). I can't see why this line and the "bridge" between order(self) and testPalindrome(self) suddenly is a problem?

    import Tkinter
    import tkMessageBox

    root = Tkinter.Tk()

    class MyGUI:
        def __init__(self, master):
            self.master = master
            master.title('Palindrome detector')
            master.geometry('534x285+500+250')
            self.infoLabel = Tkinter.Label(self.master, text = 'Enter a text and check if it is a palindrome.', height = 12, width = 64, bg = 'green')
            self.inputEntry = Tkinter.Entry(self.master, width = 64)
            self.infoLabel.grid(row = 0, rowspan = 1, column = 0, columnspan = 4, pady = 8, padx = 8)
            self.inputEntry.insert(0, 'Enter text...')
            self.inputEntry.grid(row = 1, rowspan = 1, column = 0, columnspan = 4, padx = 4)
            self.verifyButton = Tkinter.Button(self.master, text = 'Verify', width = 7, command = self.order)
            self.verifyButton.grid(row = 2, column = 0, pady = 10)
            self.saveButton = Tkinter.Button(self.master, text = 'Save', state = 'disabled', width = 7, command = self.save)
            self.saveButton.grid(row = 2, column = 3)

        def order(self):
            self.Text_input = self.inputEntry.get()
            self.orderedText = self.Text_input.replace(" ", "")
            self.orderedText = self.orderedText.lower()
            self.orderedText = filter(str.isalnum, self.orderedText)
            if any(char.isalpha() or char.isdigit() for char in self.orderedText):
                testPalindrome(self)
            else:
                self.infoLabel.config(text = 'The input must contain at least one alphanumeric character.', bg = 'red')

        def testPalindrome(self):
            self.Palindrome = True
            i = 0
            for char in self.orderedText:
                if self.orderedText[i] == self.orderedText[-(i+1)]:
                    self.Palindrome = True
                    i += 1
                else:
                    self.Palindrome = False
                    break
            showResult(self)

        def showResult(self):
            if self.Palindrome == True:
                self.infoLabel.config(text = 'Yes! "' + str(self.inputEntry.get()) + '" is a palindrome!', bg = 'green')
                self.saveButton.config(state = 'normal')
            else:
                self.infoLabel.config(text = 'No! "' + str(self.inputEntry.get()) + '" is not a palindrome!', bg = 'red')
                self.saveButton.config(state = 'disabled')

        def save(self):
            self.savePalindrome = self.inputEntry.get()
            self.palindromes_file = open("Palindromes.txt", "a")
            self.palindromes_file.write(self.savePalindrome + "\n")
            self.palindromes_file.close()
            self.infoLabel.config(text = '"' + str(self.savePalindrome) + '" has been saved to file.', bg = 'green')
            self.saveButton.config(state = 'disabled')

    my_gui = MyGUI(root)
    Tkinter.mainloop()
VillaRava
  • 157
  • 2
  • 10
  • 2
    This doesn't completely answer your question, but you should not `grid` or `pack` or `place` a widget and assign it to a variable on the same line. See [Tkinter: AttributeError: NoneType object has no attribute get](http://stackoverflow.com/q/1101750/953482) for more information. – Kevin Jan 04 '16 at 20:57
  • btw: always put full error message instead of describe it. – furas Jan 04 '16 at 21:22

2 Answers2

2

You create saveButton in function so it is local variable and it is not avaliable in other functions. You can use global saveButton in window() to make this variable global.

def window():
    global saveButton

    # ....

    saveButton = Tkinter.Button(...)
    saveButton.grid(...)

--

As Kevin already said:

This line

 saveButton = Tkinter.Button(...).grid(...)

assigns None to saveButton because grid() always returns None

Use

 saveButton = Tkinter.Button(...)
 saveButton.grid(...)

or

 Tkinter.Button(...).grid(...)

if you don't need saveButton variable.

furas
  • 134,197
  • 12
  • 106
  • 148
  • That "global saveButton" worked, but I thought global variables were a bit frowned upon? Are there any other methods I could investigate? – VillaRava Jan 05 '16 at 10:57
  • you can use Object Oriented Programming (OOP) and create class with all functions and use `self.` instead of `global`. – furas Jan 05 '16 at 11:13
0

Just put the call in your verify function,since you want to automatically save every time when it is a Palindrome. And you can pass the value to the function instead of getting it a second time.

def testPalindrome(orderedText):
    ## change to False so an empty string will not 
    ## register as True
    Palindrome = False
    i = 0
    for char in orderedText:
        if orderedText[i] == orderedText[-(i+1)]:
            Palindrome = True
            i += 1
        else:
            Palindrome = False
            break
    showResult(Palindrome)
    if Palindrome:
        save(orderedText)

Next learn classes as it solves these problems. One of many tutorials on the web http://python-textbok.readthedocs.org/en/latest/Introduction_to_GUI_Programming.html To make the button global, put it in the global namespace (like inputEntry).

root = Tkinter.Tk()
infoLabel = Tkinter.Label(root, text = 'Enter a text and check if it is a palindrome.', height = 12, width = 64, bg = 'green')
inputEntry = Tkinter.Entry(root, width = 64)

saveButton = Tkinter.Button(root, text = 'Save', state = 'disabled', width = 7, command = save)
saveButton.grid(row = 2, column = 3)
  • Making the saveButton global in that way gives me "NameError: name 'save' is not defined". – VillaRava Jan 05 '16 at 10:59
  • The function has to be declared before it is used, so solving the problem is something you can figure out if you try different things. Once again, for future attempts, a class structure does not have these kinds of problems, so most people are ignoring this thread because you are creating your own, needless problems. –  Jan 06 '16 at 01:22