Discussion:
[Tkinter-discuss] When to use a Canvas vs. a Frame from a container perspective?
python
2010-12-17 17:52:19 UTC
Permalink
This question is related to using Canvases and Frames as
containers and does not consider the drawing capabilities of the
Canvas widget.

Canvas and Frames are both containers. My understanding is that
both of these containers provide identical layout behaviors via
their pack, grid, and place methods (is this really true?). In
other words, I should be able to replace a Frame with a Canvas
and have identical behavior, correct?
From a container perspective, would it be correct to describe a
Canvas as an enhanced Frame with the following capabilities?

- the ability to support virtual sizes greater than the visible
display area
- the scrollbar, scroll and viewport features associated with
having a larger virtual display area

One of my points of confusion with Canvas containers is figuring
out what techniques to use for the layout of traditional widgets.
Can I use regular pack and grid placement always, only when a
canvas does not have scrolling, or never? When should one use the
create_window() technique for widget placement?

Thank you,
Malcolm
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/tkinter-discuss/attachments/20101217/b312629c/attachment.html>
Michael O'Donnell
2010-12-17 18:14:27 UTC
Permalink
Hi Malcom,

I have never heard of packing widgets within a canvas. Just use create_window,
so there is no automatic placement, you need to handle all placement.

Note with the following, packing a label within a canvas:

from Tkinter import *
root = Tk()
c=Canvas(root, bg="red", width=400, height=400)
c.pack()
d=Label(c, text="Hello")
d.pack()
root.mainloop()

The Label widget in fact REPLACES the canvas in the display
rather than being packed within it. I don't know why (try commenting out
the d.pack() line) and see the difference.)

Mick
This question is related to using Canvases and Frames as containers and does
not consider the drawing capabilities of the Canvas widget.
Canvas and Frames are both containers. My understanding is that both of
these containers provide identical layout behaviors via their pack, grid,
and place methods (is this really true?). In other words, I should be able
to replace a Frame with a Canvas and have identical behavior, correct?
From a container perspective, would it be correct to describe a Canvas as an
enhanced Frame with the following capabilities?
- the ability to support virtual sizes greater than the visible display area
- the scrollbar, scroll and viewport features associated with having a
larger virtual display area
One of my points of confusion with Canvas containers is figuring out what
techniques to use for the layout of traditional widgets. Can I use regular
pack and grid placement always, only when a canvas does not have scrolling,
or never? When should one use the create_window() technique for widget
placement?
Thank you,
Malcolm
_______________________________________________
Tkinter-discuss mailing list
Tkinter-discuss at python.org
http://mail.python.org/mailman/listinfo/tkinter-discuss
Michael Lange
2010-12-17 20:53:08 UTC
Permalink
Hi,

Thus spoketh "Michael O'Donnell" <michael.odonnell at uam.es>
unto us on Fri, 17 Dec 2010 19:14:27 +0100:

(...)
Post by Michael O'Donnell
The Label widget in fact REPLACES the canvas in the display
rather than being packed within it. I don't know why (try commenting out
the d.pack() line) and see the difference.)
I changed your example a bit, and so we can see that actually the canvas
is resized:

from Tkinter import *
root = Tk()
root.geometry('500x500')
c=Canvas(root, bg="red", width=400, height=400, relief='solid', bd=4)
c.pack()
d=Label(c, text="Hello", relief='sunken', bd=2)
d.pack()
c.update_idletasks()
print c.winfo_height(), c.winfo_width()
root.mainloop()

You can avoid this by ading a c.pack_propagate(0) before packing the
label. However I agree, it's better to use create_window() to avoid
pitfalls like this.

Regards

Michael



.-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-.

To live is always desirable.
-- Eleen the Capellan, "Friday's Child", stardate 3498.9
python
2010-12-17 21:07:48 UTC
Permalink
Michael,
However I agree, it's better to use create_window() to avoid pitfalls like this.
1. So the proper way to use Canvases as containers for traditional
widgets is to manually handle layout and place all widgets via the
create_window() method.

2. The only reason to use a Canvas as a container for traditional
widgets is when you need a scrollable container.

Are those points accurate?

Thanks,
Malcolm
Michael O'Donnell
2010-12-18 17:47:15 UTC
Permalink
Hi Malcom,

I use both Canvas and Text for scrollable containers.

I use the Canvas when I want pixel accurate placement.

I use a Text widget for more lazy placement (one can place
items after each other on a row, and start a new row with a "\n").
One can make spreadsheets by placing rows of entry widgets of
equal width.

However, with hundreds of widgets in a text widget, I find performance
suffers.

Mick
Post by python
Michael,
However I agree, it's better to use create_window() to avoid pitfalls like this.
1. So the proper way to use Canvases as containers for traditional
widgets is to manually handle layout and place all widgets via the
create_window() method.
2. The only reason to use a Canvas as a container for traditional
widgets is when you need a scrollable container.
Are those points accurate?
Thanks,
Malcolm
_______________________________________________
Tkinter-discuss mailing list
Tkinter-discuss at python.org
http://mail.python.org/mailman/listinfo/tkinter-discuss
python
2010-12-19 17:35:27 UTC
Permalink
Mick,
Post by Michael O'Donnell
I use both Canvas and Text for scrollable containers.
I use the Canvas when I want pixel accurate placement.
I use a Text widget for more lazy placement (one can place items after each other on a row, and start a new row with a "\n"). One can make spreadsheets by placing rows of entry widgets of equal width. However, with hundreds of widgets in a text widget, I find performance suffers.
I love the idea of creating scrollable containers using Text widgets.
I'll give this technique a try (with your caution about performance for
large number of widgets).

Thanks,
Malcolm
Michael O'Donnell
2010-12-19 18:56:33 UTC
Permalink
As an example of how to use a Text widget as a container,
below is a simple spreadsheet made using Entry widgets organised
into a Text widget. The user can edit cells, and later save the values of
the cells as a tab delimited file by pressing the "Save" button.

from Tkinter import *

class ScrolledText(Frame):

def __init__(self, master, **keywords):

# Set some defaults
if not keywords.has_key("width"): keywords["width"]=24
if not keywords.has_key("bg"): keywords["bg"]="white"
if not keywords.has_key("relief"): keywords["relief"]="sunken"

Frame.__init__(self, master)
self.config(bg=keywords["bg"])

# Scrollbars
scrollbar = Scrollbar(self, orient=VERTICAL)

# Create the Text wgt
self.text=Text(self, yscrollcommand=scrollbar.set, **keywords)
scrollbar.config(command=self.text.yview)

scrollbar.pack(side=RIGHT, fill=Y)
self.text.pack(side=LEFT, fill=BOTH, expand=True)
self.scroll=scrollbar

def saveSpreadsheet(w):
out=open("speadsheet.txt", "w")
for row in w.rows:
out.write("\t".join([col.get() for col in row])+"\n")
out.close()

tk = Tk()
stw = ScrolledText(tk, width=100, height=60, wrap="none")
stw.pack(side=BOTTOM, expand=TRUE, fill=BOTH)
Button(tk, text="Save Spreadsheet", command=lambda w=stw:
saveSpreadsheet(w)).pack(side=TOP)

stw.rows=[]
for row in range(60):
cols=[]
for col in range(20):
x=Entry(stw.text, width=10)
x.row=row
x.col=col
cols.append(x)
stw.text.window_create(END, window=x)
stw.text.insert(END, "\n")
stw.rows.append(cols)

tk.mainloop()
Post by python
Mick,
Post by Michael O'Donnell
I use both Canvas and Text for scrollable containers.
I use the Canvas when I want pixel accurate placement.
I use a Text widget for more lazy placement (one can place items after each other on a row, and start a new row with a "\n"). One can make spreadsheets by placing rows of entry widgets of equal width. However, with hundreds of widgets in a text widget, I find performance suffers.
I love the idea of creating scrollable containers using Text widgets.
I'll give this technique a try (with your caution about performance for
large number of widgets).
Thanks,
Malcolm
python
2010-12-19 23:27:50 UTC
Permalink
Mick,
Post by Michael O'Donnell
As an example of how to use a Text widget as a container,
below is a simple spreadsheet made using Entry widgets organised
into a Text widget. The user can edit cells, and later save the values of
the cells as a tab delimited file by pressing the "Save" button.
Cool! Thank you for sharing that example.

Malcolm
python
2010-12-20 16:41:11 UTC
Permalink
Cameron,
.
While many valuable applications have been coded through the years using exactly this cell-in-Text technique, and the code above nicely demonstrates the promised scrolling, those focused on spreadsheet-like constructs will want to know about <URL: http://tkinter.unpythonic.net/wiki/TkTable >.
What is your opinion on using the new ttk Treeview control to implement
multi-cell tables and grids?

My understanding is that one can configure the Treeview control as a
single level, multi-column list/grid and then float a borderless
Toplevel window with an input widget over the current Treeview "cell" to
allow for input? Disclaimer: I haven't tried this yet.

Malcolm
Michael Lange
2010-12-20 17:24:55 UTC
Permalink
Hi,

Thus spoketh python at bdurham.com
Post by python
Cameron,
.
While many valuable applications have been coded through the years
using exactly this cell-in-Text technique, and the code above nicely
demonstrates the promised scrolling, those focused on
http://tkinter.unpythonic.net/wiki/TkTable >.
What is your opinion on using the new ttk Treeview control to implement
multi-cell tables and grids?
My understanding is that one can configure the Treeview control as a
single level, multi-column list/grid and then float a borderless
Toplevel window with an input widget over the current Treeview "cell" to
allow for input? Disclaimer: I haven't tried this yet.
Tktreectrl has a built-in feature that does exactly what you describe.
However to make it really functional it requires some extra tweaking by
the programmer and it is also relativeley slow. Obviously it is designed
for occasional use , e.g. explorer-alike file renaming in a directory
listing.

To add this from scratch for the ttk.Treeview and building a spreadsheet
from it will probably be quite a lot of work for a relatively poor result.
For a very simple spreadsheet widget that comes with the standard library
the Tix.Grid seems to be the widget of choice. If this is not capable
enough I believe tktable is the best bet.

Regards

Michael


.-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-.

Insults are effective only where emotion is present.
-- Spock, "Who Mourns for Adonais?" stardate 3468.1

Cameron Laird
2010-12-20 16:22:50 UTC
Permalink
On Sun, Dec 19, 2010 at 07:56:33PM +0100, Michael O'Donnell wrote:
.
.
.
Post by Michael O'Donnell
As an example of how to use a Text widget as a container,
below is a simple spreadsheet made using Entry widgets organised
into a Text widget. The user can edit cells, and later save the values of
the cells as a tab delimited file by pressing the "Save" button.
from Tkinter import *
# Set some defaults
if not keywords.has_key("width"): keywords["width"]=24
if not keywords.has_key("bg"): keywords["bg"]="white"
if not keywords.has_key("relief"): keywords["relief"]="sunken"
Frame.__init__(self, master)
self.config(bg=keywords["bg"])
# Scrollbars
scrollbar = Scrollbar(self, orient=VERTICAL)
# Create the Text wgt
self.text=Text(self, yscrollcommand=scrollbar.set, **keywords)
scrollbar.config(command=self.text.yview)
scrollbar.pack(side=RIGHT, fill=Y)
self.text.pack(side=LEFT, fill=BOTH, expand=True)
self.scroll=scrollbar
out=open("speadsheet.txt", "w")
out.write("\t".join([col.get() for col in row])+"\n")
out.close()
tk = Tk()
stw = ScrolledText(tk, width=100, height=60, wrap="none")
stw.pack(side=BOTTOM, expand=TRUE, fill=BOTH)
saveSpreadsheet(w)).pack(side=TOP)
stw.rows=[]
cols=[]
x=Entry(stw.text, width=10)
x.row=row
x.col=col
cols.append(x)
stw.text.window_create(END, window=x)
stw.text.insert(END, "\n")
stw.rows.append(cols)
tk.mainloop()
.
.
.
While many valuable applications have been coded through the
years using exactly this cell-in-Text technique, and the code
above nicely demonstrates the promised scrolling, those
focused on spreadsheet-like constructs will want to know about
<URL: http://tkinter.unpythonic.net/wiki/TkTable >.
python
2010-12-17 20:57:29 UTC
Permalink
Hi Mick,
Post by Michael O'Donnell
I have never heard of packing widgets within a canvas.
Here's the link that gave me that impression:
http://stackoverflow.com/questions/4080413/python-tkinter-place-a-widget-in-a-canvas-widget

<quote>
You can easily add widgets to a canvas just like you do any other
container, using pack or grid or place. when you do this, the items will
not scroll when you scroll the canvas because they aren't actually part
of the canvas.

The other choice is to create window objects on the canvas. You do this
with the create_window method of the canvas. The advantage is, this
window becomes part of the canvas and will scroll along with any other
objects on the canvas. The downside is, your only option is absolute
placement and you have to explicitly control the size of the widgets.
</quote>

BUT: In reviewing the documentation on this page, I see that there are
no pack and grid methods for the Canvas widget:
http://effbot.org/tkinterbook/canvas.htm
Post by Michael O'Donnell
Just use create_window, so there is no automatic placement, you need to handle all placement.
OK. This makes sense. Does the following advice make sense:

The only time one should use a Canvas for a general purpose widget
container is when they need a container that scrolls.

To use a scrollable container one must be prepared to manually position
all widgets using the create_window() method call.
Post by Michael O'Donnell
Note with the following, packing a label within a canvas ... the Label widget in fact REPLACES the canvas in the display rather than being packed within it. I don't know why (try commenting out the d.pack() line) and see the difference).
Interesting example. And I think this makes sense given the fact that
Canvas's don't support pack and grid layouts.

Thanks for your thoughts,

Malcolm
Continue reading on narkive:
Loading...