problems wrapping text in custom control

I wrote a custom button class that can have both a main label and a longer caption, plus an optional image. This works exactly as I want on Mac, but I’ve never been able to get text wrapping right on Linux. I typically set the maximum width explicitly, but still need to calculate the expected height after the caption is split into separate lines using wx.lib.wordwrap. If I do this, I get no text wrapping at all:

dc = wx.GraphicsContext.CreateMeasuringContext()

dc.SetFont(self._label2_font)

lines = wx.lib.wordwrap.wordwrap(self._label2,

width=txt_w,

dc=dc)

If I use a MemoryDC instead, I get wrapping, but the lines that it calculates are much shorter than necessary, and the control often ends up being too tall (as shown in the attached image). I assume this means that the character sizes and/or spacings are not the same everywhere, but none of the workarounds I’ve tried have helped (e.g. telling wordwrap to use a larger width). I can’t simply cut the height by some factor, because whether or not this happens depends on the specific caption.

I’ve attached the entire control source including built-in runnable code - it’s a bit of a mess, but everything that’s causing problems is in DoGetBestSize. Is there a reliable and genuinely cross-platform way to do this, or at least something that will work on Linux?

thanks,

Nat

button.jpg

metallicbutton.py (26 KB)

what’s your setup in linux? see my attached screenshot, In short, I didn’t experience the same problem as you, make sure you have all the up-to-date versions of stuff.

One suggestion that is unrelated to your question is that you should get the default colors from the system, that way it looks correct with all themes, like my dark one has dark button backgrounds, and white text, now, you got the white text, but you just need to adjust the button color, Other wise nice button!

Screenshot.png

···


Hi, I will kill all ads in google gmail.
They will all be dead and gone for all my emails to you. HA HA bye bye ads I just massacred you!!!

what’s your setup in linux? see my attached screenshot, In short, I didn’t experience the same problem as you, make sure you have all the up-to-date versions of stuff.

Ugh, sorry, shouldn’t have sent my query as I was preparing to flee the office. Python 2.7.1, wxPython 2.8.11.0, on top of GTK+ 2.10.11, and that particular screenshot was taken on Fedora 8 but it’s just as bad on Ubuntu. Which versions were you using? I notice that the text columns are still too narrow on your screen capture, but it’s definitely an improvement.

One suggestion that is unrelated to your question is that you should get the default colors from the system, that way it looks correct with all themes, like my dark one has dark button backgrounds, and white text, now, you got the white text, but you just need to adjust the button color,

The goal for this button was to emulate the appearance of the “themed generic” buttons I get on Mac, which are flat with sharp corners and sort of shiny with a smooth gray gradient. Since our distribution includes complete wxPython and GTK installations, I also bundle a theme I picked out, and force it to use that. (Although it’s not ideal - still too brown.) This is probably cheating, but I waste enough time on cross-platform issues already, and I’m not a big fan of theming in general.

-Nat

···

On Tue, Feb 1, 2011 at 3:55 AM, Micah Nordland mpnordland@gmail.com wrote:

At least part of the problem is that you are not consistent in DoGetBestSize and __DrawCaption. You're not setting the font in the latter for the dc passed to wordwrap, and if I'm reading the code correctly you are passing a different width to wordwrap as well.

I don't have time to investigate further, but here are some tips that may help you out a bit.

* I'm not sure if a MemoryDC without a bitmap selected into it will give correct sizes when measuring text. On at least one platform that DC will be invalid and nothing can be done with it. You can use a wx.ClientDC for measuring instead.

* You might want to try using wx.DC.GetMultiLineTextExtent instead of measuring each line yourself and adding in your own line spacing.

* The wx.DC.DrawLabel method will draw multi-line text inside a rectangle, saving you a bunch of work in your draw methods. I don't think it word wraps the text, so you will still need to do the wordwrap yourself.

* There is also the wx.DC.DrawImageLabel which can draw both an image and multi-line text, although I don't think that it will work in your case since you have both the main label and the caption next to the image.

* When calling SetInitialSize give it the size passed to __init__. Then it will set the min size and also set the physical size based on that and fill in with the calculated best size.

* You should avoid using self._size everywhere since the actual size can change. Instead use GetMinSize when measuring and GetSize when drawing, (adjusted by the image size and margin sizes of course.) If the min width hasn't been set when DoGetBestSize is called then you can just default to a reasonable width when doing the wordwrap for measuring. If the current width is greater than the min size then you may or may not want to use that instead, depending on which approach works best in practice.

* Since a number of factors can affect the best size you may not want to cache the best size and just let it call DoGetBestSize each time.

···

On 1/31/11 5:17 PM, Nat Echols wrote:

I wrote a custom button class that can have both a main label and a
longer caption, plus an optional image. This works exactly as I want on
Mac, but I've never been able to get text wrapping right on Linux. I
typically set the maximum width explicitly, but still need to calculate
the expected height after the caption is split into separate lines using
wx.lib.wordwrap. If I do this, I get no text wrapping at all:

   dc = wx.GraphicsContext.CreateMeasuringContext()
   dc.SetFont(self._label2_font)
   lines = wx.lib.wordwrap.wordwrap(self._label2,
     width=txt_w,
     dc=dc)

If I use a MemoryDC instead, I get wrapping, but the lines that it
calculates are much shorter than necessary, and the control often ends
up being too tall (as shown in the attached image). I assume this means
that the character sizes and/or spacings are not the same everywhere,
but none of the workarounds I've tried have helped (e.g. telling
wordwrap to use a larger width). I can't simply cut the height by some
factor, because whether or not this happens depends on the specific caption.

I've attached the entire control source including built-in runnable code
- it's a bit of a mess, but everything that's causing problems is in
DoGetBestSize. Is there a reliable and genuinely cross-platform way to
do this, or at least something that will work on Linux?

--
Robin Dunn
Software Craftsman