Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #12725 > unrolled thread
| Started by | Bart Kastermans <bkasterm@gmail.com> |
|---|---|
| First post | 2011-09-03 16:15 -0600 |
| Last post | 2011-09-04 14:15 -0700 |
| Articles | 12 — 2 participants |
Back to article view | Back to comp.lang.python
Tkinter label height to fit content Bart Kastermans <bkasterm@gmail.com> - 2011-09-03 16:15 -0600
Re: Tkinter label height to fit content rantingrick <rantingrick@gmail.com> - 2011-09-03 18:15 -0700
Re: Tkinter label height to fit content Bart Kastermans <bkasterm@gmail.com> - 2011-09-04 13:39 -0600
Re: Tkinter label height to fit content rantingrick <rantingrick@gmail.com> - 2011-09-04 14:10 -0700
Re: Tkinter label height to fit content Bart Kastermans <bkasterm@gmail.com> - 2011-09-06 14:15 -0600
Re: Tkinter label height to fit content rantingrick <rantingrick@gmail.com> - 2011-09-06 14:23 -0700
Re: Tkinter label height to fit content rantingrick <rantingrick@gmail.com> - 2011-09-06 14:37 -0700
Re: Tkinter label height to fit content Bart Kastermans <bkasterm@gmail.com> - 2011-09-06 16:00 -0600
Re: Tkinter label height to fit content rantingrick <rantingrick@gmail.com> - 2011-09-06 15:40 -0700
Re: Tkinter label height to fit content rantingrick <rantingrick@gmail.com> - 2011-09-06 15:43 -0700
Re: Tkinter label height to fit content Bart Kastermans <bkasterm@gmail.com> - 2011-09-06 18:33 -0600
Re: Tkinter label height to fit content rantingrick <rantingrick@gmail.com> - 2011-09-04 14:15 -0700
| From | Bart Kastermans <bkasterm@gmail.com> |
|---|---|
| Date | 2011-09-03 16:15 -0600 |
| Subject | Tkinter label height to fit content |
| Message-ID | <87mxelgtmx.fsf@gmail.com> |
I have a label into which I am going to put content of different sizes. I would like to know how high I need to make the label so that I can size the window so it can stay the same for the different content sizes. I have a strategy, but it seems more complicated then it should be. I want to set a label to a given width and wraplength: l = Label(root) l['width'] = 30 l['wraplength'] = 244 l['text'] = "testing this" Now I want to query the label to find how many lines are used. l['height'] stays at 0, so the best I have been able to come up with is to use l.winfo_height() and convert the height given in pixels to the number of lines used. Nothing in dir(l) seems to give me the information directly, but this strategy is fragile to font changes and other changes. Any suggestions?
[toc] | [next] | [standalone]
| From | rantingrick <rantingrick@gmail.com> |
|---|---|
| Date | 2011-09-03 18:15 -0700 |
| Message-ID | <69ce4e11-5ef4-4b81-b66b-87d1017b1ec3@s7g2000yqk.googlegroups.com> |
| In reply to | #12725 |
On Sep 3, 5:15 pm, Bart Kastermans <bkast...@gmail.com> wrote: > Any suggestions? Yeah, have you considered using the "linespace()" method of tk.Font objects to calculate the height? Although i must say it "feels" as if your doing something you should not need to do, however i cannot be sure without knowing more about this GUI. Sounds a lot like trying to put socks on a rooster. http://infohost.nmt.edu/tcc/help/pubs/tkinter/std-attrs.html#fonts
[toc] | [prev] | [next] | [standalone]
| From | Bart Kastermans <bkasterm@gmail.com> |
|---|---|
| Date | 2011-09-04 13:39 -0600 |
| Message-ID | <87obz0w0ze.fsf@gmail.com> |
| In reply to | #12729 |
rantingrick <rantingrick@gmail.com> writes: > On Sep 3, 5:15 pm, Bart Kastermans <bkast...@gmail.com> wrote: > >> Any suggestions? > > Yeah, have you considered using the "linespace()" method of tk.Font > objects to calculate the height? Although i must say it "feels" as if > your doing something you should not need to do, however i cannot be > sure without knowing more about this GUI. Sounds a lot like trying to > put socks on a rooster. > > http://infohost.nmt.edu/tcc/help/pubs/tkinter/std-attrs.html#fonts Thx. That function should allow for a bit of robustness. I get bits of information over RSS, these are of varying length. I want to show 10 at a time, and scroll through them. Now when I scroll the window grows and shrinks depending on their size, I want to right from the start make it high enough to contain even the biggest that will have to be shown. So the question is determining the height parameter for the labels ahead of time. My strategy has been to put all in labels and then try to get the information from the label of how high it needs to be made at a certain width.
[toc] | [prev] | [next] | [standalone]
| From | rantingrick <rantingrick@gmail.com> |
|---|---|
| Date | 2011-09-04 14:10 -0700 |
| Message-ID | <8d7cf6e4-5794-4f57-88ae-b0d0dcfd68a6@d25g2000yqh.googlegroups.com> |
| In reply to | #12748 |
On Sep 4, 2:39 pm, Bart Kastermans <bkast...@gmail.com> wrote: > I get bits of information over RSS, these are of varying length. I > want to show 10 at a time, and scroll through them. Now when I > scroll the window grows and shrinks depending on their size, I want > to right from the start make it high enough to contain even the > biggest that will have to be shown. So the question is determining > the height parameter for the labels ahead of time. My strategy has > been to put all in labels and then try to get the information from > the label of how high it needs to be made at a certain width. I see. However i might opt instead for a text widget with columns of wrapped text. You could use the textwrap.py module to help (although you'll have to work around it's shortcomings for paragraphs and such). In any event it's difficult to offer good advice without seeing the code directly.
[toc] | [prev] | [next] | [standalone]
| From | Bart Kastermans <bkasterm@gmail.com> |
|---|---|
| Date | 2011-09-06 14:15 -0600 |
| Message-ID | <87obyx4ed4.fsf@gmail.com> |
| In reply to | #12751 |
I build on the suggestion by rantingrick, but took it in a bit
different direction.
I now have working code that performs reasonable. The reason for
the class lines (as opposed to just a function) is b/c font.measure
appears not that fast. So I want to remember between different
calls to lines.count where the cutoff was, and then start looking
from there. This one step of "optimization" was enough to make it
run reasonable on my system.
There are one important thing skipped, Tkinter.Label takes whitespace
into account. This code does not yet. Also I just hacked this
together as an example solution to work further from.
import Tkinter as Tk
import tkFont
import random
import sys
def genstr (j):
rno = random.randint(4,50)
ret_val = str(j) + ":"
for i in range (0, rno):
ret_val += "hello" + str(i)
return ret_val
def gendata (lh):
ret_val = []
for i in range(0,lh):
ret_val.append (genstr (i))
return ret_val
data = gendata (100)
root = Tk.Tk()
font = tkFont.Font(family='times', size=13)
class lines:
def __init__ (self):
self.lastct = 1 # remember where the cutoff was last work from there
def count (self, text, cutoff = 400):
global font
no_lines = 1
start_idx = 0
idx = self.lastct
while True:
if idx > len (text):
idx = len (text)
# shrink from guessed value
while font.measure (text[start_idx:idx - 1]) > cutoff:
if idx <= start_idx:
print "error"
sys.exit ()
else:
idx -= 1
self.lastct = idx - start_idx # adjust since was too big
# increase from guessed value (note: if first shrunk then done)
while (idx < len (text)
and font.measure (text[start_idx:idx]) < cutoff):
idx += 1
self.lastct = idx - start_idx # adjust since was too small
# next line has been determined
print "*" + text[start_idx:idx-1] + "*"
if idx == len(text) and font.measure (text[start_idx:]) < cutoff:
return no_lines
elif idx == len(text):
return no_lines + 1
else:
no_lines += 1
start_idx = idx - 1
idx = start_idx + self.lastct
lin = lines()
# for testing speed compute for all data
for i in range(0,len(data)):
lin.count(data[i], 450)
# show only first 10
for i in range(0,min(len(data),10)):
l = Tk.Label(root)
l.pack()
l['text'] = data[i]
print i
no = lin.count (data[i], 450)
print "computed lines", no
l['width'] = 50
l['justify'] = Tk.LEFT
l['anchor'] = 'w'
l['wraplength'] = 450
l['padx']=10
l['pady'] = 5
l['height'] = no
l['font'] = font
if i % 2 == 0:
l['background'] = 'grey80'
else:
l['background'] = 'grey70'
root.mainloop()
[toc] | [prev] | [next] | [standalone]
| From | rantingrick <rantingrick@gmail.com> |
|---|---|
| Date | 2011-09-06 14:23 -0700 |
| Message-ID | <8777abed-e2b1-469e-96e5-662c535220be@y21g2000yqy.googlegroups.com> |
| In reply to | #12848 |
Hmm, i can replace all that code with this...
#
# Easy_as.py
#
import Tkinter as tk
from ScrolledText import ScrolledText
import tkFont
import random
# Create some puesdo data.
data = [
'{0}.{1}'.format(x, 'blah'*random.randint(4, 50))
for x in range(100)
]
##print data
# Create the main window and a scrolled text widget.
root = tk.Tk()
font = tkFont.Font(family='times', size=13)
textbox = ScrolledText(
root,
width=60,
height=20,
font=('Times', 10),
wrap=tk.WORD,
)
textbox.pack(
fill=tk.BOTH,
expand=True,
padx=5,
pady=5,
)
textbox.insert(1.0, '\n\n'.join(data))
# Start the event loop.
root.mainloop()
#
# End
#
[toc] | [prev] | [next] | [standalone]
| From | rantingrick <rantingrick@gmail.com> |
|---|---|
| Date | 2011-09-06 14:37 -0700 |
| Message-ID | <d7f1b720-941c-4c29-92cc-94c1daa3e512@t3g2000vbe.googlegroups.com> |
| In reply to | #12851 |
Or if you prefer the alternating background approach...
##################
# Easy_as.py
##################
import Tkinter as tk
from ScrolledText import ScrolledText
import tkFont
import random
END = 'end'
INSERT = 'insert'
#
# Create some puesdo data.
data = [
'{0}.{1}'.format(x, 'blah'*random.randint(4, 50))
for x in range(100)
]
##print data
#
# Create the main window and a scrolled text widget.
root = tk.Tk()
font = tkFont.Font(family='times', size=13)
textbox = ScrolledText(
root,
width=60,
height=20,
font=('Times', 10),
wrap=tk.WORD,
)
textbox.pack(
fill=tk.BOTH,
expand=True,
padx=5,
pady=5,
)
#
# Add a tag to the very end of the widget and
# configure the tag only once!
textbox.tag_add('one', END)
textbox.tag_config('one', background='gray')
#
# Iterate over the lines stuffing them into the textbox.
idata = iter(data)
sidx = 1.0
while True:
try:
textbox.insert(END, idata.next()+"\n")
textbox.tag_add('one', sidx, INSERT)
textbox.insert(END, idata.next()+"\n")
print sidx, textbox.index(END)
sidx = textbox.index(INSERT)
except StopIteration:
break
#
# Start the event loop.
root.mainloop()
##################
# End
##################
[toc] | [prev] | [next] | [standalone]
| From | Bart Kastermans <bkasterm@gmail.com> |
|---|---|
| Date | 2011-09-06 16:00 -0600 |
| Message-ID | <87k49l49gs.fsf@gmail.com> |
| In reply to | #12851 |
rantingrick <rantingrick@gmail.com> writes:
> Hmm, i can replace all that code with this...
Because I stupidly forgot to repeat the original problem I had, and my
code doesn't show it (and doesn't show the correct use of the function I
wrote). The code shows that I now know how to compute the number of
lines and item will have; in the actual program I am developing I will
take the max of these numbers and make all items that height.
This means the code I should have shown is as follows (here I first
compute the maximum height needed for any item, and then show all items
using this height). Also in the actual program there will be scrolling
options to change the item shown by the different labels.
import Tkinter as Tk
import tkFont
import random
import sys
def genstr (j):
rno = random.randint(4,50)
ret_val = str(j) + ":"
for i in range (0, rno):
ret_val += "hello" + str(i)
return ret_val
def gendata (lh):
ret_val = []
for i in range(0,lh):
ret_val.append (genstr (i))
return ret_val
data = gendata (100)
root = Tk.Tk()
font = tkFont.Font(family='times', size=13)
class lines:
def __init__ (self):
self.lastct = 1 # remember where the cutoff was last work from there
def count (self, text, cutoff = 400):
global font
no_lines = 1
start_idx = 0
idx = self.lastct
while True:
if idx > len (text):
idx = len (text)
# shrink from guessed value
while font.measure (text[start_idx:idx - 1]) > cutoff:
if idx <= start_idx:
print "error"
sys.exit ()
else:
idx -= 1
self.lastct = idx - start_idx # adjust since was too big
# increase from guessed value (note: if first shrunk then done)
while (idx < len (text)
and font.measure (text[start_idx:idx]) < cutoff):
idx += 1
self.lastct = idx - start_idx # adjust since was too small
# next line has been determined
print "*" + text[start_idx:idx-1] + "*"
if idx == len(text) and font.measure (text[start_idx:]) < cutoff:
return no_lines
elif idx == len(text):
return no_lines + 1
else:
no_lines += 1
start_idx = idx - 1
idx = start_idx + self.lastct
lin = lines()
max_ht = 0
for i in range(0,len(data)):
ct = lin.count(data[i], 450)
if ct > max_ht:
max_ht = ct
for i in range(0,min(len(data),5)):
l = Tk.Label(root)
l.pack()
l['text'] = data[i]
l['width'] = 50
l['justify'] = Tk.LEFT
l['anchor'] = 'w'
l['wraplength'] = 450
l['padx']=10
l['pady'] = 5
l['height'] = max_ht
l['font'] = font
if i % 2 == 0:
l['background'] = 'grey80'
else:
l['background'] = 'grey70'
root.mainloop()
>
> #
> # Easy_as.py
> #
> import Tkinter as tk
> from ScrolledText import ScrolledText
> import tkFont
> import random
> # Create some puesdo data.
> data = [
> '{0}.{1}'.format(x, 'blah'*random.randint(4, 50))
> for x in range(100)
> ]
> ##print data
> # Create the main window and a scrolled text widget.
> root = tk.Tk()
> font = tkFont.Font(family='times', size=13)
> textbox = ScrolledText(
> root,
> width=60,
> height=20,
> font=('Times', 10),
> wrap=tk.WORD,
> )
> textbox.pack(
> fill=tk.BOTH,
> expand=True,
> padx=5,
> pady=5,
> )
> textbox.insert(1.0, '\n\n'.join(data))
> # Start the event loop.
> root.mainloop()
> #
> # End
> #
[toc] | [prev] | [next] | [standalone]
| From | rantingrick <rantingrick@gmail.com> |
|---|---|
| Date | 2011-09-06 15:40 -0700 |
| Message-ID | <af467c29-d1c5-4a42-b1ec-91e0afba6819@d25g2000yqh.googlegroups.com> |
| In reply to | #12856 |
On Sep 6, 5:00 pm, Bart Kastermans <bkast...@gmail.com> wrote: > rantingrick <rantingr...@gmail.com> writes: > > Hmm, i can replace all that code with this... > > Because I stupidly forgot to repeat the original problem I had, and my > code doesn't show it (and doesn't show the correct use of the function I > wrote). Oh NOW i see! This new code you posted is like night and day compared to the original </sarcasm> :o) Anyway, i did not read the code to find the difference because i want to know why you insist on using multiple labels for this when a scrolled text will suffice. Are you trying to create some sort of textual "animation frames" that you can flip through on demand? If not i would use the scrolled text (and even the scrolled text can "feel" like animation frames whist scrolling). Take your input data and replace ALL single newlines with null strings (thereby preserving paragraphs) and let the textbox control the line wrapping. The benefits are enormous using my way; your way is limited and requires re-inventing the wheel. Tell me WHY the textbox approach is not a viable solution and THEN i'll listen to you. Until then; you can lead a horse to water...
[toc] | [prev] | [next] | [standalone]
| From | rantingrick <rantingrick@gmail.com> |
|---|---|
| Date | 2011-09-06 15:43 -0700 |
| Message-ID | <700374dd-49ff-4f0a-8c22-198bff6b7b42@k15g2000yqd.googlegroups.com> |
| In reply to | #12859 |
On Sep 6, 5:40 pm, rantingrick <rantingr...@gmail.com> wrote: > On Sep 6, 5:00 pm, Bart Kastermans <bkast...@gmail.com> wrote: > Take your input data and replace ALL single newlines with null strings CORRECTION: Take your input data and replace ALL single newlines with A SINGLE SPACE
[toc] | [prev] | [next] | [standalone]
| From | Bart Kastermans <bkasterm@gmail.com> |
|---|---|
| Date | 2011-09-06 18:33 -0600 |
| Message-ID | <8762l5jin7.fsf@gmail.com> |
| In reply to | #12859 |
rantingrick <rantingrick@gmail.com> writes:
> On Sep 6, 5:00 pm, Bart Kastermans <bkast...@gmail.com> wrote:
>> rantingrick <rantingr...@gmail.com> writes:
>> > Hmm, i can replace all that code with this...
>>
>> Because I stupidly forgot to repeat the original problem I had, and my
>> code doesn't show it (and doesn't show the correct use of the function I
>> wrote).
>
> Oh NOW i see! This new code you posted is like night and day compared
> to the original </sarcasm> :o)
Quite, I should know better (I deal with students thinking what is in
their mind should be clear to me all the time):
##############################################
# run through all the data we have, compute the maximum height
max_ht = 0
for i in range(0,len(data)):
# lin.count is the line counting function
ct = lin.count(data[i], 450)
if ct > max_ht:
max_ht = ct
for i in range(0,min(len(data),5)):
# l is the Tkinter.Label with all formatting applied and data[i]
# set for text
l['height'] = max_ht # use this maximum height for all Labels.
###############################################
Thinking on this some more, the first computation should surely be
max_ht = max (map (lambda x: lin.count(x, 450), data))
The second one could then be
labels = map (label_prepare_pack, data[:5])
where label_prepare_pack prepares (sets all attributes and data), packs,
and returns the new label. The first (for max_ht) is a clear
improvement to me, the second (for labels) uses too many side-effects to
be very appealing to me.
> Anyway, i did not read the code to find the difference because i want
> to know why you insist on using multiple labels for this when a
> scrolled text will suffice. Are you trying to create some sort of
> textual "animation frames" that you can flip through on demand?
I don't follow precisely, but I am indeed looking to flip through all of
data while showing only 5 or 10 at a time. The problem I wanted to
solve was that my window kept changing size while doing this.
> If not
> i would use the scrolled text (and even the scrolled text can "feel"
> like animation frames whist scrolling).
>
> Take your input data and replace ALL single newlines with null strings
> (thereby preserving paragraphs) and let the textbox control the line
> wrapping. The benefits are enormous using my way; your way is limited
> and requires re-inventing the wheel. Tell me WHY the textbox approach
> is not a viable solution and THEN i'll listen to you.
The reason I thought this, was that I didn't realize I could bind
actions to tags (to get different actions for different bits of text).
Now that I do know this I could use code like my lin.count to get the
maximum height still (to get a constant location for the i-th item as
the items are changed; more importantly to get a constant height for the
whole list of items), and then use tags to bind the corresponding
actions.
> Until then; you can lead a horse to water...
I certainly appreciate your trying. I might not see the fresh stream
yet, but I do see liquid (possibly a shallow muddy pool, but big
progress from the dry sandy dunes before). I will keep both approaches
in mind as I further develop.
Again, thanks for the help, greatly appreciated.
[toc] | [prev] | [next] | [standalone]
| From | rantingrick <rantingrick@gmail.com> |
|---|---|
| Date | 2011-09-04 14:15 -0700 |
| Message-ID | <d9f70fa4-da45-445c-b976-824c92ea8de0@s12g2000yqm.googlegroups.com> |
| In reply to | #12748 |
On Sep 4, 2:39 pm, Bart Kastermans <bkast...@gmail.com> wrote: > Thx. That function should allow for a bit of robustness. Correction. The function is actually "tkFont.metrics(arg)" which takes "linespace" as an optional argument.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web