When I draw a shape using:
canvas.create_rectangle(10, 10, 50, 50, color="green")
Does Tkinter keep track of the fact that it was created?
In a simple game I'm making, my code has one Frame create a bunch of rectangles, and then draw a big black rectangle to clear the screen, and then draw another set of updated rectangles, and so on.
Am I creating thousands of rectangle objects in memory?
I know you can assign the code above to a variable, but if I don't do that and just draw directly to the canvas, does it stay in memory, or does it just draw the pixels, like in the HTML5 canvas?
Every canvas item is an object that Tkinter keeps track of. If you are clearing the screen by just drawing a black rectangle, then you effectively have created a memory leak -- eventually your program will crash due to the millions of items that have been drawn.
To clear a canvas, use the delete method. Give it the special parameter "all" to delete all items on the canvas (the string "all"" is a special tag that represents all items on the canvas):
canvas.delete("all")
If you want to delete only certain items on the canvas (such as foreground objects, while leaving the background objects on the display) you can assign tags to each item. Then, instead of "all", you could supply the name of a tag.
If you're creating a game, you probably don't need to delete and recreate items. For example, if you have an object that is moving across the screen, you can use the move or coords method to move the item.
Items drawn to the canvas are persistent. create_rectangle returns an item id that you need to keep track of. If you don't remove old items your program will eventually slow down.
From Fredrik Lundh's An Introduction to Tkinter:
Note that items added to the canvas are kept until you remove them. If
you want to change the drawing, you can either use methods like
coords, itemconfig, and move to modify the items, or use delete to
remove them.
Yes, I believe you are creating thousands of objects. If you're looking for an easy way to delete a bunch of them at once, use canvas tags described here. This lets you perform the same operation (such as deletion) on a large number of objects.
Related
I am working with a simulation of objects (circles) that live in a 2d world (the objects move and can interect with eachother, they colide).
To view the simulation I am using the tkinter module.
All the variables that define each state of the simulation are computed using a recursive function that I have outside the tkinter implementation (that way i can run the simulation without having to waste time drawing).
My question is: What do you think would be better for drawing the simulation states into the tkinter canvas?: to delete all previous state elements and redraw all new state elements for each new state, or to find a way to be able to update the position of the elements using tkinter canvas method move for each new state.
It is better to update elements rather than create new ones. You cannot create unlimited items on a canvas, and the more items you've created (even if you later destroy them), the slower the canvas will be. While the canvas can handle a few thousand items, once you start getting into the tens of thousands you will start to have problems.
I have a window and I'm rendering an image onto it. Every frame the position changes, so the problem is that (obviously) it doesn't disappear from the last frame. I want to clear the window every frame. I can't find any clear methods for the window object (GraphWin) anywhere. And I can't use undraw() because then I'd have to do it for everything on the screen.
First thing to consider is if you're simply moving graphic objects between frames, then call their move() methods rather than erase and redraw them. If you must clear the screen, then I suggest:
Before dropping down to the Tkinter level, I'd consider using Zelle Graphics' own underpinnings. The reason is that Zelle Graphics keeps it's own parallel records of objects and if you delete them from the Tkinter level, you could get the two out of sync. Here's my suggestion:
def clear(win):
for item in win.items[:]:
item.undraw()
win.update()
However, undrawing items is slow, likely slower than your desired frame rate. So, you'll want to turn off auto flush:
win = GraphWin(..., autoflush=False)
and then call:
update()
whenever you have something to present to your user -- this will speed up the graphics since it won't show all the intermediate steps.
The above advice does not apply to things drawn with the win.plot() method, however. Plotting is implemented at a lower level than other Zelle graphics so you do need to drop down to Tkinter to clear the plot. See How to undraw plot with Zelle graphics?
for an example.
From reading the docs (http://effbot.org/tkinterbook/canvas.htm#reference), there doesn't appear to me to be a way to do this. Just wanted to make sure I hadn't made a mistake. (I.e. one would have to internally store the coordinates of various items in a program if one wanted to do boundary checking, for example. [e.g. Check, before moving an oval, that it will not bump into a wall, represented by a line on the current canvas, the coordinate information of which is also stored.])
Use the coords method:
coords = the_canvas.coords(item_id)
Is there a way to group elements in tkinter under single ID?
For example, if I want an 'S' with a line striking through it. Is there a way to store both the line and the character under the same id?
Alternatively, is there a way to create a costume (simple) shapes for tkinter?
Edit:
I wish to do this on a canvas widget
Tkinter Canvas objects do allow you to create "simple" objects (anything you can draw using canvas items. Then you can group your objects together using tags.
If you are specifically asking for text with an overstrike, you can create a custom font that has the overstrike attribute set.
In a more general sense, you cannot have one id shared between two objects on the canvas, but you can have two objects share the same tag, and tags can be used just about anywhere an id can be used (ie: for changing colors, coordinates, deleting, etc)
I'm tinkering around with tkinter, and have used it to make some dynamic graphics based on a dataset.
I started of with an arbitrary sized canvas of 1000x1000 pixels, and now I have my images made, I wondered if there is was anyway to crop the canvas around the unoccupied parts of the edges.
I think of a couple of ways to achieve this, one would be to trim each edge until an object is hit, however, I think this wouldn't work because as far as I can tell the objects are directly addressed onto the canvas location, so any changes to the canvas in the top RHS would just result in objects moving in concert, another would be to (somehow) group all the objects into a single named object, get the borders and a somehow redraw the lot on a newly sized canvas.
I wondered if anyone had any ideas or done this before?
The bbox method of the canvas gives you the bounding box (opposite corners) of an object or objects on the canvas. So, my_canvas.bbox("all") will return you a rectangle that encompasses all of the items on the canvas. According to the official Tk documentation, this method "may overestimate the actual bounding box by a few pixels"
I'm not sure exactly what you mean by "crop" in the context of this question, but since you know the x/y of the upper left corner of the objects, you can use the move method to move all objects by -x1/-y1 pixels to move everything to the upper left corner.