A question about tkinter update

09 May, 2018

Feng writes:

I bought a chinese version of your book. when i tried the paddle ball at this step: 'Bounce (example 4) - ball moving up and down' at the first part of the code, you add a line tk.update(). I cannot see why this is necessary, since there are no balls moving at all. Then, i comment it with a '#'.

Now, it comes with a problem, i failed to see the ball move down, it seems stuck at the top line of the canvas. I tried to print the variable 'pos', and it shows as follows:
------------------(some part of the outcome)
[255.0, 1.0, 270.0, 16.0]
[255.0, 0.0, 270.0, 15.0]
[255.0, -1.0, 270.0, 14.0]
how did this happen, why the first 'tk.update()' cannot be omitted?

The first call to tk.update() isn't strictly necessary - but what it does do is to force a "wait" until all the Tk user interface events have been processed.

When you call pack to tell your Tk components to resize and display themselves in the right place, you're actually sending a message to a whole separate process (this is the Tk event loop). Let's imagine it took 1 minute for each component to set itself up, and you had 10 components in the window. If you called pack it wouldn't take 10 minutes to complete that function call -- it would actually complete almost instantly. Behind the scenes however, the components are busy re-arranging themselves to fit in the window (and in this imaginary scenario, that process would then take 10 minutes to complete).

Try running this code (create a file and run it in IDLE):

from tkinter import *
tk = Tk()
canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
print(canvas.winfo_height())

You'll get 1. That's because the canvas hasn't finished rearranging itself by the time you call winfo_height. Now try typing the first 4 lines of the code into the IDLE shell -- wait a few seconds then enter the last line:

print(canvas.winfo_height())

Now you'll get 400 (i.e. 400 pixels high). Because you've given the canvas time to complete.

In summary, pack is saying "ok Tk components, re-arrange yourselves on the screen" and update is saying: "ok Tk, I want to wait for that to complete". Which is why, if you remove the update call, inside the __init__ function of the Ball class, it calculates a height of 1, and the code no longer works correctly.


Problem with paddle-ball animation

19 Feb, 2018

Hayden B writes:

I am trying to code the bouncing red ball in Chapter 13 but the animation is not working. I have checked the code, and it makes sense, but I am getting these errors when I run:

RESTART: C:\Users\rf\AppData\Local\Programs\Python\Python36-32\Files .py\paddleball.py Traceback (most recent call last):   File "C:\Users\rf\AppData\Local\Programs\Python\Python36-32\Files .py\paddleball.py", line 33, in <module>     ball.draw()   File "C:\Users\rf\AppData\Local\Programs\Python\Python36-32\Files .py\paddleball.py", line 15, in draw     self.canvas.move(self.id, self.x, self.y)   File "C:\Users\rf\AppData\Local\Programs\Python\Python36-32\lib\tkinter_init_.py", line 2585, in move     self.tk.call((self.w, 'move') + args)_ _tkinter.TclError: invalid command name ".!canvas"

...and the red ball gets "stuck" at the top "jiggling" but not bouncing or returning down as it is supposed to. I cannot figure out what the problem is. Can you help?

Your code is almost right, there's just one thing you've mistyped. Here's a snippet from my code:

canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
tk.update()

And here's your code:

canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
tk.update

Check the difference in the last line. Why do the missing brackets cause such a problem? The reason is that Python can't calculate the height of the canvas (400 pixels) until you call pack() and then update(). You are calling tk.update which is effectively just dumping information about the function, rather than calling the function itself. Here's what happens if I try that code:

>>> tk.update
<bound method Misc.update of <tkinter.Tk object .>>

And, because you aren't actually calling the update function, this line...

        self.canvas_height = self.canvas.winfo_height()

...actually results in canvas_height being 1 pixel, rather than 400. As a consequence, your ball then gets stuck jiggling at the top of the screen because both these if-statements end up being true:

        if pos[1] <= 0:
             self.y = 1
        if pos[3] >= self.canvas_height:
             self.y = -1

(so pos[1] ends up being less than or equal to 0, and pos[3] ends up being >= to the canvas_height)

By the way, you can ignore the error message -- that has nothing to do with your code. When you close the game window, your program is still trying to call the move function and so you get an error message because the canvas no longer exists (effectively).

Hope that helps.


Tcl Error

11 May, 2017

丁明 writes (edited for spelling):

In your book <<python for kids>> chapter 13, when I run the program, the python shell has a error I can't understand. my python version 3.6.0. Thank you for your reply!!

error.png

I'm not sure if the Chinese edition has captured the text on page 199 of the English version of the book:

You may see error messages written to the shell when you close the game window. This is because when you close the window, the code is breaking out of the while loop, and Python is complaining about it.

The is exactly the error you are seeing when you run that code and then close the window. You are stopping (interrupting) Python while it is running the code, and an error message is displayed as a consequence.

Basically, you can ignore the message, but if you want to know how to get rid of it for good, one option is to replace the loop at the bottom:

while True:
    ball.draw()
    tk.update_idletasks()
    tk.update()
    time.sleep(0.01)

...with this...

try:
    while True:
        ball.draw()
        tk.update_idletasks()
        tk.update()
        time.sleep(0.01)
except TclError:
    pass

You can find more about error handling here: https://wiki.python.org/moin/HandlingExceptions


askcolor

18 Jan, 2017

Isai writes:

I am having trouble with this code in page 176:

from tkinter import * colorchooser.askcolor()

When I load it into IDLE, I get a blank canvas, and I can't close it, I have to restart the shell.

I have installed the proper ActiveTcl version (8.5), and no longer get the message giving me errors on startup.

When I run the previous code I was playing with (page 174), the canvas pops up and it all works again. Weirdly enough, the color picker pops up unprompted! I noticed that it does not bring up any buttons that appear in your book: [OK] [Cancel].

That's a bit of a gap in the book, because it doesn't make it clear that you need to create an instance of the top-level Tk widget first, before running that particular example (using tk = Tk()). So the first time you run this function: colorchooser.askcolor(), it's hanging, waiting for something that isn't there yet. Then you run the code on the previous page (which does create the Tk object), and that's when you suddenly get a popup dialog with the colorchooser in it.

So the code should really look like this:

from tkinter import *
from tkinter import colorchooser
tk = Tk()
tk.update()
colorchooser.askcolor()

When you run that, you should then get the colorchooser dialog immediately.

Update #1: One step I missed, is a call to tk.update(), which forces tk to update the screen. Once that's executed, the chooser will appear. The example is updated above.

Update #2: On Windows you need to explicitly import colorchooser for the code to work properly (example updated again).


Text position in tkinter

08 Dec, 2016

Drew M writes:

I have a question I am sure I don't know :) In tkinter I am creating a game and can't figure out how to set dimensions for text. I need it on the middle. My canvas is 1000 by 1000 so I divided in half. Which is of course 500. Now again, my question is how to put it there.

Adding text to a canvas is straightforward (similar to adding any other item, such as a line, or a rectangle). You say "middle" and not "center", so I assume you don't want the text directly in the center of the canvas? I've defined a smaller canvas here, but the principle is the same:

from tkinter import *
tk = Tk()
canvas = Canvas(tk, width=100, height=100, bd=0, highlightthickness=0)
canvas.create_text(50, 50, text='text')
canvas.pack()
tk.update()

text position center

So if you don't want the center, I'm guessing your problem is that this code...

from tkinter import *
tk = Tk()
canvas = Canvas(tk, width=100, height=100, bd=0, highlightthickness=0)
canvas.create_text(50, 0, text='text')
canvas.pack()
tk.update()

...(with coordinates of 50, 0) results in partially obscured text:

text position middle

The answer, is to use the named parameter anchor in the create_text function:

canvas.create_text(50, 0, text='text', anchor=N)

The anchor parameter controls the positioning of an item in terms of its coordinates. The default value is CENTER (so using this puts the center of the text at the coordinates (50, 0) in the earlier example), but you can also use NW, N, NE (effectively top-left, top-middle, top-right), W, E (left and right), and SW, S, SE (bottom-left, bottom-middle, bottom-right). So N puts the top and middle point of the text at the coordinates (50, 0), like so:

text position N

I hope that helps.


A parameter called evt

26 Jan, 2016

Dudley B writes:

I'm a Business Analyst trying to learn how to code. Luckily there's Python, a suitable language for absolute beginners like me. Thank God also for your book, Python for Kids. It's a great book which explains Python fundamentals in a fun and easy way. In Chapter 14 of the book, page 208, there is a parameter called "evt":

def turn_left(self, evt):     self.x = -2 def turn_right(self, evt):     self.x = 2

I can't figure out what this parameter is for, and why it's there since there is no value passed on to it. Please help me understand why "evt" is needed for those functions.

The evt parameter isn't brilliantly explained in that chapter, now that I look back at it. If you go back to the section titled "Making an object React to something" in Chapter 12, there's a description there of event bindings. The function described takes a parameter called event. After you've "told" tkinter about the function (that's the binding part), when an event occurs (such as the mouse being moved, or keyboard being pressed) it calls that function with an object containing more information (such as what sort of event it was).

The parameter evt on page 208, is simply a shortened name for the parameter event mentioned in the earlier chapter. In fact, the name isn't important at all -- you could even call it bob, or aardvark, or anything else you like.

By the way, another way of thinking about the tkinter event binding is that it's like a contract between two people. Let's say between a surfer, and an old man living on a cliff above the sea. The surfer says to the old man, "send me a message and let me know how high the surf is, whenever it changes". The function is that contract, and the message about the surf is the parameter value.

Hopefully that helps.


Tkinter mainloop issue on Raspberry Pi

26 Aug, 2015

Martin writes:

First of all thank you for writing "Python for Kids". It's a great book and offers "instant gratification". My 8-year-old is already hacking his way through the pages on his brand new Raspberry Pi.

He (we) ran into a bit of trouble when we started playing with Tkinter.

Your initial examples don't ever call mainloop() or update(). Turns out, on the Raspi, in IDLE, those examples don't work as expected - nothing appears on screen. Until you run mainloop() that is. raspberry pi screenshot

If you run a pure python3 interpreter inside a shell window, your examples work. [They will also not work on Windows inside a Python 2.7.10 IDLE.]

Could you throw any light on why things work without calling mainloop() on some setups but not on others?

Turns out that trying to reproduce your problem - without having a RPi to hand - was a lot more difficult than I expected. Even after getting Raspbian going with QEMU, I couldn't get a menu to display so that I could run IDLE (solution: create an empty file, and from the file's context menu you can then access the Terminal). However, when I ran IDLE, it only worked in "no subprocess mode" (see the first chapter of the book). If I run without specifying -n, I get the following error:

qemu error (small image)

Only after going through the above, did I remember that there was an issue with IDLE & tkinter on Python 3.2 (when running without -n). And sure enough, when running with 3.2 on my PC, I get the exact same problem (it doesn't occur with later versions of Python).

Best thing you can do, is to run IDLE with the -n flag - the turtle/tkinter code you run will be more stable as a consequence (and there's no need to call mainloop).

(PS. as for Python 2.7.x, many of the code samples won't work - the book is only intended to be used with Python 3.x)


Restart when running tkinter

13 Jun, 2014

Craig S writes:

My son is having a problem running the random rectangle program on page 174 of Python for Kids. He is using version 2.7.7, so the Tkinter module must be capitalized (just so you don't suggest to capitalize it). Every time he tries to run the module, it comes up with a new window saying, "RESTART". He tried to copy and paste your code, but came up with the same results. This is the code he's written. from Tkinter import * import random tk = Tk() canvas = Canvas(tk, width=400,height=400) def random_rectangle(width, height):     ... random_rectangle(400, 400)

This is what comes up when he runs it. Python 2.7.7 (default, Jun 1 2014, 14:17:13) on win32 Type "copyright", "credits" or "license()" for more information. >>> =============== RESTART ===============

Please share some advice. Thanks.

A couple of thoughts:

  1. He's missing the pack function after the 4th line of that code (canvas.pack())
  2. You're probably not running Idle in "No Subprocess" mode (see Chapter 1 of the book for setup instructions). If that doesn't work for you, for some reason, then best thing to do is use a text editor, save the code and then run it from the command prompt by typing python yourscript.py (see http://windows.microsoft.com/en-us/windows/command-prompt-faq for instructions on running the command prompt if you're not aware how to do that)
  3. You may hit other weird issues if you're not using Python3, since all the code in the book was written and tested with 3.x. Much of it is probably fine, but no guarantees there...

Python on the iPad?

10 Mar, 2014

Beth S writes:

I teach gifted elementary-aged students in Albuquerque, NM. A few of my students and I have been working through your book. We have limited desktops in our classroom, but we also have access to several iPads. In researching the various apps available, I’ve realized that most (if not all?) do not include the _tkinter module. To be honest, I’m rather new to programming myself!

Do you have a recommendation for an iPad app that would be appropriate for students using your book?

Thank you for your time and your ability to clearly explain the basics of Python!

I'm afraid I don't know of any way to use an iPad for Python, other than by jail-breaking the device (and even then I have no idea whether tkinter would work anyway - I think unlikely).

The only thought I had was whether you could use the desktop PCs in your class to run multiple VMs (using something like VirtualBox) and then connect to those using a Remote Desktop App for the iPad. Not straightforward to set up, but it might accomplish what you want (assuming the desktops are reasonably powerful of course).

Sorry I can't be much help there.

Update: since sending my initial reply, I came across this discussion, which explains why you're not likely to see tkinter on the iPad anytime soon.


Tkinter problems on the Raspberry Pi

09 Jul, 2013

Ben writes:

I am 12 and I am reading your book, Python for Kids. I have recently confronted a problem with the module tkinter. I have recently got a rasberry pi to program on python. I am pretty sure that tkinter is already installed on python, but when I run something like:

def hello():     print('hello there') from tkinter import * tk = Tk() btn = Button(tk, text='click me', command=hello) btn.pack()

There is nothing that pops up! I am frustrated and need help! Please help, me and my father are new to linux by the way and don't know what to do.

Ben is using Debian Linux on his Raspberry Pi. After a bit of to-ing and fro-ing, we eventually found that tkinter was definitely installed, but it looks like (according to this stackoverflow question that his father found) the tkinter window doesn't appear unless you use: tk.mainloop().

So if you're trying the code on page 178 (for example) on a Pi, you might need to add one line to get it to work successfully:

###
>>> from tkinter import *
>>> tk = Tk()
>>> canvas = Canvas(tk, width=400, height=400)
>>> canvas.pack()
>>> canvas.create_arc(10, 10, 200, 80, extent=45, style=ARC)
>>> canvas.create_arc(10, 80, 200, 160, extent=90, style=ARC)
>>> canvas.create_arc(10, 160, 200, 240, extent=135, style=ARC)
>>> canvas.create_arc(10, 240, 200, 320, extent=180, style=ARC)
>>> canvas.create_arc(10, 320, 200, 400, extent=359, style=ARC)###
>>> tk.mainloop()