I have built a dialog with several buttons arranged in a stack and I
want the width of all buttons to be the same.
Moreover, this dialog can be displayed in many languages and I'm using
the msgcat package for handling the translations.
To set the same width for the buttons I'm using the msgcat::mcmax
command as advertised in the msgcat help page. I get the max length of
the translated strings that have to be show in the buttons. After
digging into the source code I found out that mcmax is just
calculating [string length $str] and returning the max of them.
This works apparently OK for languages using ascii characters. But now
I have a chinese translation and the width calculated by mcmax is no
longer the right one. The longest label gets truncated.
Here is a simplified example in wish (chinese strings noted in \uxxx
to avoid mangling by ascii readers):
#set str1 "尋找下一個 - Find N"
set str1 "\u5C0B\u627E\u4E0B\u4E00\u500B - Find N"
#set str2 "取代 - Replace"
set str2 "\u53D6\u4EE3 - Replace"
set len1 [string length $str1]
set len2 [string length $str2]
# emulate mcmax
if {$len1 < $len2} {set best $len1} else {set best $len2}
# display
button .b1 -text $str1 -anchor w -width $best
button .b2 -text $str2 -anchor w -width $best
pack .b1 .b2
The truncation shown on the first label is the problem.
On the other hand if I do not give the -width option to the button
command, then each button correctly shows untruncated labels.
Is this a bug of msgcat?
How can I handle this? This is on WinXPTcl 8.4.15 or 8.5.6b1 as well.
Thanks for any hint.
Francois
# don't fix the widht:
button .bb1 -text $str1 -anchor w
button .bb2 -text $str2 -anchor w
grid .b1 -column 1 -row 1
grid .b2 -column 1 -row 2
# make the buttons hug the left and right border
# of their grid column via "-sticky we"
grid .bb1 -column 2 -row 3 -sticky we
grid .bb2 -column 2 -row 4 -sticky we
grid columnconfigure . 1 -weight 1
grid columnconfigure . 2 -weight 1
#################
use grid as geometry manager
It will provide more happiness to the programmer.
beware : do not use grid and pack on the same master.
uwe
Thanks for replying. I have studied your code and indeed this works
for me too.
Nevertheless, I would say that the behavior I see is a bug.
The msgcat man page states that:
"Given several source strings, ::msgcat::mcmax returns the length of
the longest translated string. This is useful when designing localized
GUIs, which may require that all buttons, for example, be a fixed
width (which will be the width of the widest button)."
This is exactly what I'm trying to achieve. Using grid looks like a
workaround to me. You suggest using grid because the above statement
from msgcat is not always true.
My understanding is that mcmax should be fixed, or its man page
explain that the result returned by msgcat will be OK with -width only
under certain conditions (that need to be clarified).
Do you agree with my understanding?
Is it worth to file a bug report on this?
> beware : do not use grid and pack on the same master.
What would happen in such a case (ok, I'll try and check it myself)?
But what exactly do you mean by "master"? Is it a toplevel? I think I
can have a toplevel with two frames in it, one of them containing
packed widgets and the other one showing gridded widgets, is this not
true?
Thanks again,
Francois
> My understanding is that mcmax should be fixed, or its man page explain
> that the result returned by msgcat will be OK with -width only under
> certain conditions (that need to be clarified).
msgcat has imho no bug. the width in chars is correct.
try the following ( add at bottom of your example )
after 2000 .b1 configure -width 0
after 3000 .b2 configure -width $best
watch what happens after 2 seconds and after 3 seconds.
The width you configure for the button widget seems to translate
to a width in pixels to the current font and normal char width.
This gets reevaluated if you reconfigure the button.
( and it breaks your design target ;-(
>
> Do you agree with my understanding?
> Is it worth to file a bug report on this?
filing bugs is never a bad thing.
>
>
>> beware : do not use grid and pack on the same master.
>
>
> What would happen in such a case (ok, I'll try and check it myself)?
The geometry managers depart on a game of poker, nobody wins.
The gui job has been forgotten about .
(i.e. an endless loop fixing the geometry to two tastes )
>
> But what exactly do you mean by "master"? Is it a toplevel? I think I
the "master" is the grided widget. in your case the toplevel.
> can have a toplevel with two frames in it, one of them containing packed
> widgets and the other one showing gridded widgets, is this not true?
yes
uwe
Yes it is. This is the first thing I checked. mcmax returns the
correct number of chars.
At the same time, -width for buttons containing text is supposed to
receive a number measured in chars:
"Command-Line Name: -width
Specifies a desired width for the button. [...]; for text it is in
characters. If this option isn't specified, the button's desired width
is computed from the size of the image or bitmap or text being
displayed in it."
True, it does not really say what's happening when you specify this
option.
My assumption was that when you give a width in chars that is equal to
[string length $buttonlabel], then $buttonlabel will not get truncated.
Now I understand that when -width $x is given then the button width
will be $x characters, but even if $x == [string length $buttonlabel],
anything can happen wrt truncation.
Is this the reason why when I configure my button with -width [mcmax
...] I may see truncated text? This is still unclear to me.
If the above turns out to be true, what is the correct use of mcmax
then? Since I can't feed the result from mcmax to -width, what is the
use case for mcmax (especially, considering what is said in its man page)?
Francois
> If the above turns out to be true, what is the correct use of mcmax
> then? Since I can't feed the result from mcmax to -width, what is the
> use case for mcmax (especially, considering what is said in its man page)?
My guess is nobody anticipated that you would combine narrow latin
type chars with (wider) pictogramm style chars ;-)
I don't know which basic charset the button widget assumes for width calculation
with the content unknown. ( probably the fitting width for ascii ? )
uwe
::msgcat::mcmax is completely useless for the case described in the msgcat
manpage. Just discard that advice.
> I don't know which basic charset the button widget assumes for width
> calculation with the content unknown. ( probably the fitting width for
> ascii ? )
>
The widget reports its actual width depending on its content. If there is no
content, it use a default size.
Usually, you don't have to know the display width of a certain string. Just
let the geometry manager (grid preffered) do its work. If you want all the
buttons to be the same width, use the "-sticky ew" (east-west) option when
gridding a button. The button will horizontally fit into it's grid cell
entirely then.
Kind regards
Jan
No. Using grid is the far better approach. It takes the layout off from
your shoulders. You shouldn't worry about pixels and geometry
calculation, exactly this *is* the job of the geometry managers.
For completeness, it should be mentioned, that also pack can manage the
width of the buttons automatically. Just change the last line of your
example to:
pack .b1 .b2 -expand yes -fill x
With grid, it's just easier to manage the behaviour of a more
complicated layout, when the toplevel is resized by the user.
Christian
As others have said, the length of a string in characters isn't
tightly correlated to its width in pixels. As a quick workaround, here
is a custom function (oversimplifying) that counts Unicodes above
\u3000 as having a width of 2:
proc strlen2 str {
set res [string length $str]
foreach c [split $str ""] {
if {$c > "\u3000"} {incr res}
}
return $res
}
73 % strlen2 hello
5
28 % strlen2 (北京)
6
Probably a good heuristic.
Anyway, there is also a chance that no font is found to display those
characters and thus they get displayed as "\u3456\u4567..." literally.
Ok, so if that happens then the far-east glyphs are unlegible anyway,
but due to "\u3456" taking even more space to print than the real glyph
it means that the latin text following it will most likely be clipped
away at the end of the button.
Using grid and have the button determine it's size itself is most likely
the best way.
I've seen by far too many clipped texts in badly written GUIs (mostly
non-tcl apps), already.
PS: as a sidenote, as it appeared somewhere in this thread: would it
be feasible for geometry managers to recognize the presence of other
geometry managers for the same master, and either refuse instantly, or
switch to some kind of passive mode only propagating size-changes inwards?
The other thing is that it's really best to try to think in terms of
the font that you're using. The [font measure] command makes this
easier.
proc strlenf {font string} {
set width [font measure $font $string]
set width0 [font measure $font "0"]
return [expr {int(ceil(double($width)/$width0))}]
}
Testing, first with a fixed width font then a variable width one...
% strlenf {Courier 10} abcde
5
% strlenf {Times 10} aaaaaa
6
% strlenf {Times 10} mmmmmm
10
% strlenf {Times 10} iiiiii
4
Donal.
I have a patch for that, though I haven't had time to finish it.
I hope to get it in for one of the 8.6 betas.
I once had a grid/pack fight hang my entire desktop. Debugging that
with
a reboot in the loop was tedious enough to get started on that
patch ;-)
/Peter
> PS: as a sidenote, as it appeared somewhere in this thread: would it
> be feasible for geometry managers to recognize the presence of other
> geometry managers for the same master, and either refuse instantly, or
> switch to some kind of passive mode only propagating size-changes inwards?
Yes, that's feasible, it would just take a medium-sized
overhaul of the geometry management infrastructure.
--Joe English
Here a few remaining questions.
Jan Kandziora said on 20/12/2008 23:08:
> ::msgcat::mcmax is completely useless for the case described in the msgcat
> manpage. Just discard that advice.
Which I did.
Side note: Without being capable to find *any* use then to mcmax. At
least the use case described in its man page should be removed since
it's very misleading.
Anyway.
> Usually, you don't have to know the display width of a certain string. Just
> let the geometry manager (grid preffered) do its work.
This is now what I'm using in almost all dialogs of my application and
it works nicely.
My remaining problem is that I have a dialog containing a table (sort
of a matrix), with elements being labels, buttons, entries, checkboxes
and spinboxes.
The problem is with the spinbox. I want the width of this spinbox to
be just large enough to show any of its elements unclipped, without
resizing the cell whenever I change the spinbox active element.
My approach was to pass all possible items of the spinbox (which are
predefined) to mcmax, which gave what to feed in -width.
Now I don't want to use -width anymore, and want to replace pack by grid.
This new approach works only if the active item in the spinbox is the
widest (in pixels), otherwise the spinbox widget gets gridded with a
size corresponding to the size of the currently active item, and when
I switch this item to a widest item, this one gets clipped. Ouch.
How can I grid my spinboxes so that they have minimum width and so
that anything they can display will not get clipped?
I have thought about several options, such as:
- use grid columnconfigure -minsize, but what would be the size to
feed in there (without using mcmax)?
- save the currently selected item in the spinbox, search for the
longest ([string length ...]) item of the spinbox, select it, grid the
spinbox, select back the item initially selected. What a mess...
Other suggestions, perhaps?
Thanks a lot.
Francois
Hmmm, I see what you mean here.
Measure $string in pixels, arbitrarily decide that one char is the
width of the character "0" and divide to know how many such characters
the given $string is wide.
This can lead to unexpected results:
% strlenf {Times 10} 北京
5
% strlenf {Times 30} 北京
4
In this case I guess I should use a reference character different than
zero.
This remark points to a weakness of your proposal IMHO: the result
depends on the character set that $string is using. The choice of the
reference character is important and should be made by proc strlenf in
consistency with the characters that $string is composed of.
Further headaches...
Francois
> Side note: Without being capable to find *any* use then to mcmax. At
> least the use case described in its man page should be removed since
> it's very misleading.
I can think of at least one. If your data comes from or goes to or
defines a database with a fixed field size for one of these messages,
you want to make sure you've met the storage requirements.
Just think of msgcat::mcmax as returning storage requirements, not
screen requirements.
I think the reason Donal suggested zero as the reference character is
that is what is used by Tk internally. It's been years since I looked
at the sources so I could be wrong. Even if you get the "right"
reference character the answer won't be precise. If you have more
characters wider than the reference, or narrower, the answer will be
skewed.
The only truly precise solution is to use font measure on each string
(or use a fixed width font). Do that, then create the widget exactly
as many pixels as you know you need. You can create a text-based
widget a fixed number of pixels by packing it in a frame, turning
geometry propagation off in that frame, then setting the frame to the
desired size.
> How can I grid my spinboxes so that they have minimum width and so
> that anything they can display will not get clipped?
>
> I have thought about several options, such as:
>
> - use grid columnconfigure -minsize, but what would be the size to
> feed in there (without using mcmax)?
>
> - save the currently selected item in the spinbox, search for the
> longest ([string length ...]) item of the spinbox, select it, grid the
> spinbox, select back the item initially selected. What a mess...
>
That's the way to go. But string length won't help you. It has the same
problem as ::msgcat::mcmax. You have to use font measure with a font that
contains all characters in use.
% font measure mincho "??Jan???"
Kind regards
Jan
Actually, you should use the font that you're going to display the
characters in and let Tk's font fallback mechanism take care of the
details. Of course, which font you use to display might be affected by
which characters are being used...
Donal.
peter....@gmail.com <peter....@gmail.com> wrote:
> I have a patch for that, though I haven't had time to finish it.
> I hope to get it in for one of the 8.6 betas.
Joe English wrote in a sibling-post:
> Yes, that's feasible, it would just take a medium-sized
> overhaul of the geometry management infrastructure.
As a layman in this area I see two somewhat conflicting statements,
and both hadn't any discussion/comments following it so far.
How's the patch doing, Peter?
I cleaned up what I had and put it at:
https://sourceforge.net/tracker/index.php?func=detail&aid=2475855&group_id=12997&atid=312997
Looking at it now I can't remember why I didn't think it was
complete...
Feedback is welcome.
/Peter