I want to change the canvas size after I have added some widgets to it
Example:
from Tkinter import *
master = Tk()
w = Canvas(master, width=100, height=100)
w.config(bg='white')
w.create_oval(90,90,110,110, width=0, fill = "ivory3")
w = Canvas(master, width=200, height=200)
w.pack()
mainloop()
But it seem that when I re-declare the canvas size, the objects get removed.
Is it possible to update the canvas after I have created some objects on it?
What you are looking for is the configure option, as is documentered here. Basically, something like this should help, in place of creating a new canvas:
w.config(width=200, height=200)
For reference, the reason why everything got deleted off of the Canvas is because you created a brand new Canvas, with a different size and the same name. If you are going to change properties of an existing object, you must change the existing object, and not overwrite it. Basically you overwrite something if you declare it equal to something else (w=Canvas(...)).
You can easily resize to canvas with the .config() command like so:
w.config(width=x height=y)
The x and y represent integers (whole numbers). You can also add on some other customisation attributes like bg (background) to further customise the canvas.
Also, you have created a brand new canvas at the end there so that is why your attributes disappeared. You can fix that by deleting the last 2 lines of code starting with w =.
Related
If I were to make a button or label that won't be called or re-configed anywhere else in the code should I make it a variable or just create that instance? Just instantiating it makes it more readable IMO and uses less memory (not that I have to think about it using python), but I see most people creating a instance variable. Which one is the "better" way of writing it (assuming I don't need to call it later)?
Button(root, text="Button").grid(row=0, column=0)
or
self.my_button = Button(root, text="Button")
self.my_button.grid(row=0, column=0)
I always assign widgets to variables. They aren't always instance variables; sometimes they are local variables. I'll use instance or global variables if I ever need to reference the widget outside of the function where it is defined.
In my opinion, tkinter code is much easier to understand when widgets are created and laid out in separate blocks and grouped together logically. When all of the layout code is in a block it's much easier to visualize the layout.
In other words, instead of this:
root = Tk()
Canvas(root, ...).grid(...)
toolbar = Frame(root,...).grid(...)
Label(toolbar,...).pack(...)
Button(toolbar, ...).pack(...)
... I prefer this:
toolbar = Frame(root, ...)
canvas = Canvas(root, ...)
app_icon = Label(toolbar,...)
save_button = Button(toolbar, ...)
toolbar.grid(...)
canvas.grid(...)
app_icon.pack(...)
save_button.pack(...)
I think it becomes much more clear that the toolbar and canvas are laid out together in the window, and that the label and icon are part of the toolbar.
I just wrote a class in Tkinter, that allows me to make some widgets draggable. This really works great as long as the master of the widget is the root window. But if I, for example, have a Frame with a Lable in it, I can drag the Frame but the Lable just disappears. This is cause the class places the label in relation to the main window. So for example, if the Frame has the size 100x100 and is at the position 500, 500 at the main window, and I drag the Label(0, 0) 1px to the right, it will be placed at 501, 500 instead of 1, 0 cause the class thinks the master is the root window.
So now I thought to just use the master's position to subtract it from the Label position (501-500, 500-500 > 1, 0) There's just one problem. This:
f = Frame(root, width=100, height=100, bg='grey')
f.place(x=500, y=500)
l = Label(f, text='Drag me!)
l.place(x=0, y=0)
master = l.master
print(master)
returns me not one master but two. Even if there's just one print statement, it gives me this:
>>.
>>.!frame
If I put a sleep statement in between the declaration and the print it just takes longer. But if I check the types it's not a list, this are two objects.
Can anybody explain this? I just need a way to get the master of the Label to get it's position!
Tkinter widgets exist in a tree structure, and there must be an instance of Tk at the root. If you don't create one, it will be created for you the first time you create a widget.
In your output, "." represents the root window that was presumably created for you, and ".!frame" represents the frame.
Though, given the fact that you explicitly pass root to Frame(), it looks like your code is explicitly creating the root window somewhere.
I found the problem. It's not a problem with the code. I just didn't noticed I also müde the Frame dragable. And cause the Frames parent is root I got it.
I just came across a strange behavior of Tkinter when debugging my program. If a Frame object is created before a Canvas object and later inserted into that Canvas, it can't be displayed. However if the creation order is inverted (firstly Canvas and then Frame), contents in the Frame is displayed correctly.
For example, the following code works well:
from Tkinter import *
app = Frame()
canvas = Canvas(app)
frame = Frame(app)
Label(frame, text = 'aaaa').pack()
Label(frame, text = 'bbbb').pack()
canvas.create_window(0, 0, anchor = NW, window = frame)
canvas.grid()
app.grid()
app.mainloop()
But if the initialization order is inverted, like:
frame = Frame(app)
canvas = Canvas(app)
you get nothing but a blank window.
Is this a intentionally designed behavior (If so, why?), or I just found a bug in Tkinter?
It is a feature. Widgets have a stacking order that defaults to the order that they were created. You can adjust this stacking order with the lift and lower methods.
For example, you can create the frame first and then the canvas, so that the canvas has a higher stacking order. As you observe, you don't see the frame because it is behind the canvas. To make it visible, you can lift it:
frame.lift(canvas)
Doing so will give the same visual effect as if you had created the canvas first.
This technique can be useful to hide and show widgets. For example, you can create a notebook-like widget by stacking several frames on top of each other, and then using lift to bring the one you want to be visible to the top of the order.
I would like to put an entry and a text label one under the other and with the same width.
Here is my code :
from tkinter import *
root = Tk()
title = StringVar()
title_entry = Entry(root, textvariable=title, width=30)
title_entry.pack()
content_text = Text(root, width=30)
content_text.pack()
root.mainloop()
But my 2 widgets don't have the same width. Any idea to solve it ?
The widgets are different sizes probably because they have different default fonts. If they have the same fonts and the same widths, they should have the same natural width. However, the actual width can be affected by how they are placed in the window, and there are often good reasons to use different fonts for these widgets.
The simplest solution in your case is to have each widget fill the container in the x axis. This makes sure that, regardless of their natural width, they will expand to fill the window from edge to edge:
title.pack(fill="x")
content_text.pack(fill="x")
If these are your only two widgets you'll want to go a step further and specify additional options to get proper resize behavior:
title.pack(fill="x")
content_text.pack(fill="both", expand=True)
The width for the Text and Entry widgets is set by the amount of characters. I think, possibly the default font sizes are different for Text and Entry. You may have to set the font sizes in your argument??
You simply need to add a single argument to your .pack attribute. Just put this in:
.pack (fill=X)
Put this in and both widgets will stretch all the width of your window.
I am experimenting with Tkinter, as I was trying to figure out is there a way to set the tkinter's window size without using canvas. I came upon this how to set frame size question on SO's Question & Answer. So I went ahead and test it by writing a very small program to display a text label. But I found out it is "missing", or disappear when I use frame.pack_propagate(0)
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root, width=400, height=400)
# Does not work at the moment, textBox is missing
# frame.pack_propagate(0)
frame.pack()
textBox = tk.Label(frame, text="(x,y): ")
textBox.pack()
root.mainloop()
So my question is, can you explain why my textBox (Label) is not appearing when I use the frame.pack_propagate(0) instead of frame.pack() method? And secondly, is there a way to set the window size without using a canvas? I want to know because I am writing a series of small programs to teach my friend about tkinter, before introducing canvas to him. It would be nice if the window size are all the same across my tkinter samples. And I am just wondering as well (curious). Thank you very much.
I am using python 3.2.2 on MAC OS 10.5.8.
pack_propagate only sets a flag, it doesn't cause the frame to be placed in the widget. It is not a substitute for calling pack.
In other words you must do this:
# put the frame in its parent
frame.pack()
# tell frame not to let its children control its size
frame.pack_propagate(0)
# put the textbox in the frame
textBox.pack()
To answer your second question: Yeah, there is a way.
tkinters Tk do have the Tk.geometry function. When you just call it without arguments, you will get the current geometry in form of 'widthxheight+x+y', so for example (on Windows 10) '200x200+26+26' when you create your first Tk window. Using that format you can resize the Tk by, e.g., writing: root.geometry('400x500+60+60') to set the width to 400, the height to 500 and place it at the coordinates (60|60).
This works for Tk alswell as for Toplevel. But Toplevel also takes the arguments height and width when initialized or configured. If you want them to keep their size when packing something inside just use root.pack_propagate(False) on them.
By the way there is something similar for the grid manager: root.grid_propagate(False)