Python Forum

Full Version: Tkinter won't run my simple function
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hey guys, I'm trying to make a magic 8 ball interface using Tkinter. I'm fairly new to coding, and I guess you could say this is my first "personal" project without having to use tutorials or read any books. I'm trying to have this code print a random response when you press the button, but I get these callbacks.

Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Harrison\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 1921, in __call__
return self.func(*args)
File "C:\Users\Harrison\PycharmProjects\8ball\8ball.py", line 37, in <lambda>
shake_button = tk.Button(root, textvariable=shake_text, command=lambda:shake())
File "C:\Users\Harrison\PycharmProjects\8ball\8ball.py", line 30, in shake
text_box.insert(1.0, responses.EightBall())
File "C:\Users\Harrison\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 3772, in insert
self.tk.call((self._w, 'insert', index, chars) + args)
_tkinter.TclError: wrong # args: should be ".!text insert index chars ?tagList chars tagList ...?"

Here is my code.

import tkinter as tk
from PIL import Image, ImageTk
from tkinter.filedialog import askopenfile

import responses

root = tk.Tk(className="Magic 8 Ball")



canvas = tk.Canvas(root, width=800, height=500)
canvas.grid(columnspan=3, rowspan=3)

#logo
logo = Image.open("image.png")
logo = ImageTk.PhotoImage(logo)
logo_label = tk.Label(image=logo)
logo_label.image = logo
logo_label.grid(column=1, row=0)

#instructions
instructions = tk.Label(root, text="Please type in your question. Then 'Shake' to see your fate.")
instructions.grid(columnspan=3, column=0, rowspan=5, row=1)



def shake():

	text_box = tk.Text(root, height=10, width=50, padx=15, pady=15)
	text_box.insert(1.0, responses.EightBall()) <-------------------- THIS IS WHERE MY ISSUE IS
	text_box.tag_configure("center", justify="center")
	text_box.tag_add("center", 1.0, "end")
	text_box.grid(column=1, row=3)

#shake button
shake_text = tk.StringVar()
shake_button = tk.Button(root, textvariable=shake_text, command=lambda:shake())

shake_text.set("Shake")
shake_button.grid(column=1, row=2)


root.mainloop()
the responses.py isn't necessary, it's just a simple random generator. but I run into the trouble at the spot that I highlighted (LINE 30). I can have the program print a statement, but I can't have it run my function of randomly printing a response.

Thanks for your time and help.
 import random

"""A simple 8 ball program. Will develop a GUI to further my knowledge in the future."""

responses = ["It is certain.",
             "It is decidedly so.",
             "Without a doubt",
             "Yes - definitely",
             "You may rely on it.",
             "As I see it, yes.",
             "Most likely.",
             "Outlook good.",
             "Yes.",
             "Signs point to yes.",
             "Reply hazy, try again.",
             "Ask again later.",
             "Better not tell you now.",
             "Cannot predict now.",
             "Concentrate and ask again.",
             "Don't count on it.",
             "My reply is no.",
             "My sources say no.",
             "Outlook not so good.",
             "Very doubtful."]

def EightBall():
	print(random.choice(responses))
Here's the responses code actually. I've tried removing the print statement and that didn't work either. I can get the response on the terminal but can't display it in the GUI.
Here is something for you to work with.

import tkinter as tk
from random import choice
def eightball(box):
    box.delete('1.0', tk.END)
    responses = ["It is certain.",
             "It is decidedly so.",
             "Without a doubt",
             "Yes - definitely",
             "You may rely on it.",
             "As I see it, yes.",
             "Most likely.",
             "Outlook good.",
             "Yes.",
             "Signs point to yes.",
             "Reply hazy, try again.",
             "Ask again later.",
             "Better not tell you now.",
             "Cannot predict now.",
             "Concentrate and ask again.",
             "Don't count on it.",
             "My reply is no.",
             "My sources say no.",
             "Outlook not so good.",
             "Very doubtful."]
    box.insert(tk.END, choice(responses))

root = tk.Tk()
root['padx'] = 8
root['pady'] = 5
label = tk.Label(root, text='Type a question then, shake')
label.grid(column=0, row=0, sticky='new')

string = tk.StringVar()
box = tk.Text(root)
box.grid(column=0, row=1, sticky='new')

btn = tk.Button(root, text='Shake')
btn['command'] = lambda: eightball(box)
btn.grid(column=0, row=2, sticky='new')
root.mainloop()
Are you passing the arguments to insert in the right order? The error message it telling you something about the arguments is wrong (the TclError in the traceback). Did you check the docs for insert?

Also, a small thing: on line 37, you don't need the lambda. A lambda is just a function literal (in the same way as "blue" is a string literal) and all you're doing there is creating a function that takes no arguments and calls shake. shake is itself a function that takes no arguments, so you can just pass it as the command instead of wrapping it in another function (i.e. command=shake).
(May-02-2022, 04:42 AM)menator01 Wrote: [ -> ]Here is something for you to work with.

import tkinter as tk
from random import choice
def eightball(box):
    box.delete('1.0', tk.END)
    responses = ["It is certain.",
             "It is decidedly so.",
             "Without a doubt",
             "Yes - definitely",
             "You may rely on it.",
             "As I see it, yes.",
             "Most likely.",
             "Outlook good.",
             "Yes.",
             "Signs point to yes.",
             "Reply hazy, try again.",
             "Ask again later.",
             "Better not tell you now.",
             "Cannot predict now.",
             "Concentrate and ask again.",
             "Don't count on it.",
             "My reply is no.",
             "My sources say no.",
             "Outlook not so good.",
             "Very doubtful."]
    box.insert(tk.END, choice(responses))

root = tk.Tk()
root['padx'] = 8
root['pady'] = 5
label = tk.Label(root, text='Type a question then, shake')
label.grid(column=0, row=0, sticky='new')

string = tk.StringVar()
box = tk.Text(root)
box.grid(column=0, row=1, sticky='new')

btn = tk.Button(root, text='Shake')
btn['command'] = lambda: eightball(box)
btn.grid(column=0, row=2, sticky='new')
root.mainloop()

Thank you so much! I'll dissect this and figure out why this works and my other one doesn't. I appreciate it.
(May-02-2022, 05:22 AM)ndc85430 Wrote: [ -> ]Are you passing the arguments to insert in the right order? The error message it telling you something about the arguments is wrong (the TclError in the traceback). Did you check the docs for insert?

Also, a small thing: on line 37, you don't need the lambda. A lambda is just a function literal (in the same way as "blue" is a string literal) and all you're doing there is creating a function that takes no arguments and calls shake. shake is itself a function that takes no arguments, so you can just pass it as the command instead of wrapping it in another function (i.e. command=shake).

Thank you for your help! I'm not too sure if it was the order of anything, I'll have to toy around with it. The lambda thing I was still confused about, but I think I understand now. Thank you!
Lamda expressions are confusing. A lambda expression creates an anonymous function. You wrote this:
shake_button = tk.Button(root, textvariable=shake_text, command=lambda:shake())
Python sees this:
def unnamed_function():
    shake()
shake_button = tk.Button(root, textvariable=shake_text, command=unnamed_function)
This offers no benefit over using "command=shake". So why are there lambda expressions?

Lambda expressions can be used for many reasons, but the main reason they are used in GUI code (I think) is to bind an event to a function call with arguments.

You get your code working and you realize that it is shake should not create a Text widget. It should append text to an existing widget. You rewrite your code to do this.
def shake(textVar):
    textVar.set(responses.EightBall())
 
text_box = tk.Text(root, height=10, width=50, padx=15, pady=15)
text_box.tag_configure("center", justify="center")
text_box.tag_add("center", 1.0, "end")
text_box.grid(column=1, row=3)
 
#shake button
shake_text = tk.StringVar()
shake_button = tk.Button(root, textvariable=shake_text, command=lambda:shake(shake_text))
This code passes the Text widget as an argument to the shake() function. Now the lambda expression is doing this:
def unnamed_function():
    shake(shake_text)

shake_button = tk.Button(root, textvariable=shake_text, command=unnamed_function)