Tkinter bug with Python 3.3.3 - python-3.x

I have created a mad libs program and the window shows up as just an empty GUI and after I shut it down the tkinter code comes up saying that tk has no attribute. Here is my Program.
from tkinter import *
class Application(Frame):
'''GUI that takes a user's input in order to create a story'''
def initializeFrame(self, master):
'''Initializes the frame'''
#superclass constructor
super(Application, self).initializeFrame(master)
self.grid()
self.createWidgets()
def createWidgets(self):
'''Creates some widgets that get the information of the story and display it'''
#instruction label
Label(self,
text = "Enter story information"
).grid(row = 0, column = 0, columnspan = 2, sticky = W)
Label(self,
text = "Person: "
).grid(row = 1, column = 0, sticky = W)
self.personEntry = Entry(self)
self.PersonEntry.grid(row = 1, column = 1, sticky = W)
Label(self,
text = "Plural Noun:"
).grid(row = 2, column = 0, sticky = W)
self.nounEntry = Entry(self)
self.nounEntry.grid(row = 2, column = 1, sticky = W)
Label(self,
text = "Verb:"
).grid(row = 3, column = 0, sticky = W)
self.verbEntry = Entry(self)
self.verbEntry.grid(row = 3, column = 1, sticky = W)
Label(self,
text = "Adjective:"
).grid(row = 4, column = 0, sticky = W)
self.isItchy = BooleanVar()
Checkbutton(self,
text = "itchy",
variable = self.isItchy
).grid(row = 4, column = 1, sticky = W)
self.isJoyous = BooleanVar()
Checkbutton(self,
text = "joyous",
variable = self.isJoyous
).grid(row = 4, column = 2, sticky = W)
self.isElectric = BooleanVar()
Checkbutton(self,
text = "electric",
variable = self.isElectric
).grid(row = 4, column = 3, sticky = W)
Label(self,
text = "Body Part:"
).grid(row = 5, column = 0, sticky = W)
self.bodyPart = StringVar()
self.bodyPart.set(None)
bodyParts = ["gluteus maximus", "platella", "Cranium"]
column = 1
for part in bodyParts:
Radiobutton(self,
text = part,
variable = self.bodyPart,
value = part
).grid(row = 5, column = column, sticky = W)
column += 1
Button(self,
text = "Click for the story",
command = self,
).grid(row = 6, column = 0, sticky = W)
self.storyTxt = Text(self, width = 75, height = 10, wrap = WORD)
self.storyTxt.grid(row = 7, column = 0, columnspan = 4)
def tellStory(self):
'''Prints the new story based on the users inputs'''
person = self.personEntry.get()
noun = self.nounEntry.get()
verb = self.verbEntry.get()
adjectives = ""
if self.isItchy.get():
adjectives +='itchy, '
if self.isJoyous.get():
adjectives += 'itchy, '
if self.isElectric.get():
adjectives += 'electric, '
bodyPart = self.bodyPart.get()
#the story
story = "The famous explorer"
story += person
story = "had nearly given up on a life-long quest to find The Lost City of"
story += noun.title()
story = "when one day, the"
story += noun
story += "found"
story += person + "."
story += "A strong"
story += adjectives
story += "peculiar feeling overwhelmed the explorer. "
story += "After all this time, the quest was finally over. A tear came to "
story += person + "'s"
story += bodyPart + ". "
story += "And then, the "
story += noun
story += "promptly devoured "
story += person + "."
story += "The moral of this story? Be careful what you "
story += verb
story += "for. "
self.storyTxt.delete(0.0, END)
self.storyTxt.insert(0.0, story)
root = Tk()
root.title("Mad Libs")
app = Application(root)
root.mainloop()
Application(Frame)
createWidgets(self)
And here is the part of the tkinter program that has an error
class BaseWidget(Misc):
"""Internal class."""
def _setup(self, master, cnf):
"""Internal function. Sets up information about children."""
if _support_default_root:
global _default_root
if not master:
if not _default_root:
_default_root = Tk()
master = _default_root
self.master = master
self.tk = master.tk
name = None
if 'name' in cnf:
name = cnf['name']
del cnf['name']
if not name:
name = repr(id(self))
self._name = name
if master._w=='.':
self._w = '.' + name
else:
self._w = master._w + '.' + name
self.children = {}
if self._name in self.master.children:
self.master.children[self._name].destroy()
self.master.children[self._name] = self
def __init__(self, master, widgetName, cnf={}, kw={}, extra=()):
"""Construct a widget with the parent widget MASTER, a name WIDGETNAME
and appropriate options."""
if kw:
cnf = _cnfmerge((cnf, kw))
self.widgetName = widgetName
BaseWidget._setup(self, master, cnf)
if self._tclCommands is None:
self._tclCommands = []
classes = [(k, v) for k, v in cnf.items() if isinstance(k, type)]
for k, v in classes:
del cnf[k]
self.tk.call(
(widgetName, self._w) + extra + self._options(cnf))
for k, v in classes:
k.configure(self, v)
def destroy(self):
"""Destroy this and all descendants widgets."""
for c in list(self.children.values()): c.destroy()
self.tk.call('destroy', self._w)
if self._name in self.master.children:
del self.master.children[self._name]
Misc.destroy(self)
def _do(self, name, args=()):
# XXX Obsolete -- better use self.tk.call directly!
return self.tk.call((self._w, name) + args)

This is not a bug in tkinter, it is a bug in your code.
root.mainloop() needs to be the last line of code executed in your program. It will not return ultil the main window is destroyed. You have code after that which tries to create more windows. Since the main window has been destroyed, you get the error that you report.
Your window is likely coming up blank because you call self.createWidgets but that class has no such method. Perhaps this is a typo in your code, since you do define a function with that name, but outside the scope of the class.

Related

Problem with dynamically changing which command a button calls in tkinter

In this simple calculator GUI, I'm creating a frame template using classes. The frame has 2 labels, 2 entry boxes, and a button. I'd like the button to run a specific command depending on the function_call variable passed when initializing - but this doesn't work. The two_points function should be called for the first object, and one_point should be called for the second. How do I dynamically change which command is called based on which object I'm using? Thank you for taking the time to read this.
from tkinter import *
root = Tk()
root.title("Simple Slope Calculator")
class Slope_Calc:
# Variable info that changes within the frame
def __init__(self, master, num_1, num_2, frame_name, label_1_name, label_2_name, function_call):
self.num_1 = int(num_1)
self.num_2 = int(num_2)
self.frame_name = frame_name
self.label_1_name = label_1_name
self.label_2_name = label_2_name
self.function_call = function_call
# Frame template
self.frame_1 = LabelFrame(master, text = self.frame_name, padx = 5, pady = 5)
self.frame_1.grid(row = self.num_1, column = self.num_2, padx = 10, pady = 10)
self.label_1 = Label(self.frame_1, text = self.label_1_name)
self.label_1.grid(row = 0, column = 0)
self.entry_1 = Entry(self.frame_1)
self.entry_1.grid(row = 0, column = 1)
self.label_2 = Label(self.frame_1, text = self.label_2_name)
self.label_2.grid(row = 1, column = 0)
self.entry_2 = Entry(self.frame_1)
self.entry_2.grid(row = 1, column = 1)
self.calc_button = Button(self.frame_1, text = "Calculate", command = self.function_call) # This is what doesn't work
self.calc_button.grid(row = 1, column = 2, padx = 5)
# Strips string of spaces and parentheses
# Returns a list of relevant ordered pair
def strip_string(self, entry_num):
ordered_pair = entry_num.get().split(", ")
ordered_pair[0] = ordered_pair[0].replace("(", "")
ordered_pair[1] = ordered_pair[1].replace(")", "")
return(ordered_pair)
# Calculates slope based on one point and y-intercept
def one_point(self):
pair_1 = self.strip_string(self.entry_1)
b = int(self.entry_2.get())
m = (int(pair_1[1]) - b)/(float(pair_1[1]))
label_3 = Label(self.frame_1, text = "SLOPE-INTERCEPT EQUATION: y = " + str(m) + "x + " + str(b))
label_3.grid(row = 2, column = 0, columnspan = 2)
# Calculates slope based on two points given
def two_points(self):
pair_1 = self.strip_string(self.entry_1)
pair_2 = self.strip_string(self.entry_2)
m = (int(pair_2[1]) - int(pair_1[1]))/float(int(pair_2[0]) - int(pair_1[0]))
b = (int(pair_1[1])) - (m*int(pair_1[0]))
label_3 = Label(self.frame_1, text = "SLOPE-INTERCEPT EQUATION: y = " + str(m) + "x + " + str(b))
label_3.grid(row = 2, column = 0, columnspan = 2)
# Calling each object
two_p = Slope_Calc(root, 0, 0, "Two Points", "First Ordered Pair", "Second Ordered Pair", "two_points")
one_p = Slope_Calc(root, 0, 1, "One Point and Y-Intercept", "Ordered Pair", "Y-intercept", "one_point")
root.mainloop()
The command keyword argument of the Button constructor is supposed to be a function.
Here you give it instead a string which is the name of the method of self that should be called. So you must first get this method using setattr to be able to call it. This should do it:
def call():
method = getattr(self, self.function_call)
method()
self.calc_button = Button(
self.frame_1,
text = "Calculate",
command = call)
You then have an error in strip_string but that's another story.

Changing a variable with a button (and a function) in Python Tkinter

I have the following code (it's partly in Dutch but I don't think that'll be an issue):
from tkinter import *
root = Tk()
woorden_en = ["mouse", "armadillo", "caterpillar", "buffalo", "dragonfly", "eel", "monkey", "lark", "manatee", "squid"]
woorden_nl = ["muis", "gordeldier", "rups", "buffel", "libelle", "paling", "aap", "leeuwerik", "zeekoe", "inktvis"]
nummer = IntVar()
nlWoord = StringVar()
enWoord = StringVar()
goedfout = StringVar()
def vorige():
nummer -= 1
def volgende():
nummer += 1
def controleer():
print("Correct!")
secondGrid = Frame(root)
secondGrid.grid(row = 2, column = 1, columnspan = 2)
labelVertaling = Label(root, text="vertaling")
textVertaling = Entry(root, width=30, textvariable = nlWoord)
runVorige = Button(secondGrid, text="vorige", command = vorige)
runVolgende = Button(secondGrid, text="volgende", command = volgende)
runControleer = Button(secondGrid, text="controleer", command = controleer)
labelWoord = Label(root, text="woord")
labelWoordEn = Label(root, textvariable = enWoord)
labelNo = Label(root, textvariable = nummer)
Correct = Label(root, textvariable = goedfout)
Correct.grid(row = 2, column = 0)
labelNo.grid(row = 0, column = 0)
labelWoord.grid(row = 0, column = 1)
labelWoordEn.grid(row = 1, column = 1)
labelVertaling.grid(row = 0, column = 2)
textVertaling.grid(row = 1, column = 2)
runVorige.grid(row = 0, column = 0, sticky = "W")
runVolgende.grid(row = 0, column = 1, sticky = "W")
runControleer.grid(row = 0, column = 2, sticky = "W")
nummer.set(1)
enWoord.set(woorden_en[0])
root.mainloop()
The start value of 'nummer' is 1, as set in the 3rd to last line. This value needs to be changed with either -1 or +1 when clicking buttons 'vorige' (previous) or 'volgende' (next). The current code in the functions give me erros. Apparently I need to use set/get functions, but I cannot find out how to make it work. Any input or help would be appreciated.
Just change your function to:
def vorige():
nummer_val = nummer.get()
nummer_val -= 1
nummer.set(nummer_val)
def volgende():
nummer_val = nummer.get()
nummer_val += 1
nummer.set(nummer_val)
This is because nummer is an IntVar() and you have to get the value of the variable using get() method. After that you have to assign it to a variable and then reduce/increase its value by 1. The first error you were getting was because the nummer was not globalized inside the function, but that was not the approach you should have taken, anyway this should fix your errors.

Is there any way to create entries in a for loop with separate variables (for accessing later)?

Just working on a personal project and was wondering if it was possible to create entries in a for loop that have their own variables so they could be accessed later. You can do something similar with radiobuttons:
from tkinter import *
mydict = ["a":1, "b":2]
i = -1
var = IntVar()
for key in mydict.keys():
i += 1
Radiobutton(self, text = key, variable = var, value = i).grid(column = 0, sticky = W)
This will create a radiobutton for each key in a dictionary but they all use the same variable.
Aka something that will turn this:
Label(self, text = "Label 1").grid(column = 0, sticky = W)
Entry(self, textvariable = entryOne).grid(column = 1, sticky = W)
Label(self, text = "").grid()
Label(self, text = "Label 2").grid(column = 0, sticky = W)
Entry(self, textvariable = entryTwo).grid(column = 1, sticky = W)
Label(self, text = "").grid()
into a for loop so it could be used for x times.

How to change background color in ttk python

I've made a simple gui age converter app but i want to change its bg color to black.
The problem is in the ttk frame.i don't know how to configure its bg color.
I have tried different methods but that didn't work.
i would be grateful if you guys could help.
here is the code
from tkinter import *
from tkinter import ttk
from PIL import Image
def calculate(*args):
try:
age_sec = int(age.get())
age_sec = age_sec * 12 * 365 * 24 * 60 * 60
age_seconds.set(age_sec)
except:
age_seconds.set('Either the field is empty or \n value is not numeric.')
root = Tk()
root.title("Converter")
root.configure(background="black")
mainframe = ttk.Frame(root, padding = "6 6 12 12")
mainframe.grid(column = 0, row = 0, sticky = (N, W, E, S))
mainframe.columnconfigure(0, weight = 1)
mainframe.rowconfigure(0, weight = 1)
#mainframe['borderwidth'] = 2
#mainframe['relief'] = 'groove'
age = StringVar()
age_seconds = StringVar()
ttk.Label(mainframe, foreground = "#4D4E4F", text = "Enter your Age: ").grid(column = 1, row = 1, sticky = E)
age_entry = ttk.Entry(mainframe, width = 30, textvariable = age)
age_entry.grid(column = 2, row = 1, sticky = (W, E))
ttk.Label(mainframe, foreground = "#4D4E4F", text = "your age in seconds is ").grid(column = 1, row = 2, sticky = (E))
ttk.Label(mainframe, textvariable = age_seconds, background = "lightyellow", foreground = "#727475", width = 30).grid(column = 2, row = 2, sticky = W)
#Mouse Events...\\
butt_image = PhotoImage(file = 'images.gif')
ttk.Button(mainframe, compound = TOP, text = "Hit Now", image =butt_image, cursor = "hand2", width= 30, command = calculate).grid(column = 2, row = 4, sticky = W)
l2 = ttk.Label(mainframe,foreground = "#4D4E4F", text = "Mouse Events: ").grid(column = 1, row = 3, sticky = E)
l = ttk.Label(mainframe,background = "lightyellow", foreground = "#727475", text = 'Measurement is starting...', width = 30)
l.grid(column = 2, row = 3, sticky = W)
l.bind('<Enter>', lambda e: l.configure(text = 'Moved Mouse Inside'))
l.bind('<Leave>', lambda e: l.configure(text = 'Mouse Moved Out'))
l.bind('<1>', lambda e: l.configure(text = 'left Mouse clicked'))
l.bind('<Double-1>', lambda e: l.configure(text = 'Double clicked'))
l.bind('<B1-Motion>', lambda e: l.configure(text = 'Left button drag to %d, %d' %(e.x, e.y)))
image = PhotoImage(file = 'waves.gif')
ttk.Label(mainframe, compound = CENTER, text = "Image text", font = ('roman', 9, 'normal'), foreground ='green', image = image).grid(column = 3, row = 1, sticky = (N, E))
#if '__name__' == '__main__':
for child in mainframe.winfo_children(): child.grid_configure(padx = 15, pady = 15)
age_entry.focus()
root.bind('<Return>', calculate)
root.mainloop()
A ttk.Frame does not have a background / bg option. To change it you need to use style.
See ttk.Frame Info
If you don't really need to use a ttk.Frame you can just switch to a tkinter.Frame

updating tkinter window for text background changes

Trying to show a green or red background in the text field of the answer to the simple addition quizzer.
Currently in PyCHarm complains that:
Entry.grid_configure(background = "red")
TypeError: grid_configure() missing 1 required positional argument: 'self'
0
I can't seem to figure this out. Any help is appreciated.
Here's the code so far:
from tkinter import *
import random
class MainGUI:
def __init__(self):
window = Tk() # Create the window
window.title("Addition Quizzer") # Set the title
#window.width(len(window.title()))
self.number1 = random.randint(0, 9)
self.number2 = random.randint(0, 9)
Label(window, text = "+").grid(row = 2, column = 1, sticky = E)
Label(window, text = "Answer").grid(row = 3, column = 1, sticky = W)
self.firstNumber = StringVar()
Label(window, text = self.number1, justify = RIGHT).grid(row = 1, column = 2)
self.secondNumber = StringVar()
Label(window, text = self.number2, justify = RIGHT).grid(row = 2, column = 2)
self.entry = StringVar()
Entry(window, textvariable = self.entry, justify = CENTER, width = 4, background = "grey").grid(row = 3, column = 2)
Button(window, text = "Answer:", command = self.computeAnswer).grid(row = 4, column = 1, sticky = E)
self.result = StringVar()
Label(window, textvariable = self.result).grid(row = 4, column = 2)
window.mainloop() # Create the event loop
def computeAnswer(self):
self.result.set(format(self.number1 + self.number2))
if self.entry == self.result:
self.displayCorrect()
else:
self.displayIncorrect()
def displayCorrect(self):
# self.correctAnswer = "Correct"
# Label(self.window, text = self.correctAnswer, background = "green", justify = RIGHT).grid(row = 5, column = 2)
Entry.grid_configure(background = "green")
def displayIncorrect(self):
# self.incorrectAnswer = "Incorrect"
# Label(self.window, text = self.incorrectAnswer, background = "red", justify = RIGHT).grid(row = 5, column = 2)
Entry.grid_configure(background = "red")
MainGUI()
If you had read and followed this in the Help Center material, you would have reduced your code to the following, which still gets the same error message.
from tkinter import *
Entry.grid_configure()
The message refers to the fact that Python instance methods require an instance. This is usually done by calling the method on an instance instead of the class. Otherwise, an instance must be given as the first argument. Consider
mylist = []
mylist.append(1)
list.append(mylist, 2)
print(mylist)
# [1, 2]
You need to save a reference to your Entry box. Change
Entry(window, ..., background = "grey").grid(...)
to
self.entry = Entry(window, ..., background = "grey").grid(...)
I do not know if calling .grid_configure(background=color will do what you want.
This will, I am sure.
self.entry['background'] = 'red'

Resources