Mac: 7 compatibility Problems and Solutions experienced after porting a medium-sized app from Windows/Linux to Mac

Following is a list of wxPython problems discovered while porting a medium-sized application to the Mac (CrossMgr, www.sites.google.com/site/crossmgrsoftware‎).

The solutions/workarounds are also documented below.

None of the fixes were all that difficult, but the solutions took weeks to find, so I am posting this for the greater good.

Thanks to all those on the forum who helped me along the way.

If anyone has some better suggestions, let me know.

System Details:

OSX: Mavericks, 64-bit Intel

Python: 2.7.5

wxPython: 2.9.5.0

LogPrintStackStderr.py (1.38 KB)

···

****Problem:

On Windows and Linux, wx errors are shown in stderr with a stack trace.

By default, errors on the Mac are shown in a dialog, but no stack trace, making it impossible to find out where the error is occurring.

Solution:

Write a custom error logger. See attachment “LogPrintStackStderr.py” for a logger that writes to stderr (you can modify on your own to write to the screen, if that’s what you want).

You then need to tell wx to use your new handler as follows:

import wx

from LogPrintStackStderr import LogPrintStackStderr

if ‘WXMAC’ in wx.Platform: # More reliable than getting the right number of underscores are in WXMAC.

wx.Log.SetActiveTarget( LogPrintStackStderr() )


****Problem:

wx.Button does not draw properly with a larger font. The button keeps the standard height, but the text spills outside of the button’s borders on top and bottom.

This is especially problematic on touch-screen-friendly displays designed with larger buttons.

Solution:

Use wx.lib.buttons.ThemedGenButton instead. They do not match the Mac look-and-feel, but look better than plain GenButton. They will draw in the proper height with a larger font.

Example:

import wx.lib.buttons

btn = wx.lib.buttons.ThemedGenButton( parent, label=‘Big Font Button’ )

btn.SetFont( wx.FontFromPixelSize(wx.Size(0,64), wx.DEFAULT, wx.NORMAL, wx.NORMAL) )


****Problem:

A wx.TextCtrl does not adjust its height to accommodate a larger font. The wx.TextCtrl uses its standard height, even if the contents are illegible.

Solution:

Explicitly set the height to fontHeight*1.2 when you create the wx.TextCtrl.

import wx

fontSize = 64

font = wx.FontFromPixelSize(wx.Size(0,fontSize), wx.DEFAULT, wx.NORMAL, wx.NORMAL)

tc = wx.TextCtrl( parent, value=‘’, size=(-1, fontSize*1.2) )

tc.SetFont( font )


****Problem:

A wx.TextCtrl defaults to one line in height even when style=wx.TE_MULTILINE. On Windows and Linux, wx.TE_MULTILINE shows at least two lines.

Solution:

Set the height to be larger than 1 line. Example:

tc = wx.TextCtrl( parent, value=‘’, size=(-1,96), style=wx.TE_MULTILINE )


****Problem:

wx.DC.GetMultiLineTextExtent function returns garbage, causing havoc in custom drawing routines that use it.

Solution:

Define your own, then monkey-patch over the non-working standard function:

if ‘WXMAC’ in wx.Platform:

# wx.DC.GetMultiLineTextExtent does not work on the Mac.

# Replace it with our own function.

def GetMultiLineTextExtent( dc, text, font = None ):

	textWidth, textHeight, lineHeight = 0, 0, None

	for line in text.split('\n'):

		lineWidth, lineHeight = dc.GetFullTextExtent( line, font )[:2]

		textWidth = max( textWidth, lineWidth )

		textHeight += lineHeight

	if lineHeight is None:

		lineHeight = dc.GetFullTextExtent( '000Yy', font )[1]

	return textWidth, textHeight, lineHeight

wx.DC.GetMultiLineTextExtent = GetMultiLineTextExtent

****Problem:

wx.MessageDialog shows a rocket ship instead of the appropriate Warning, Error or Information icon.

Solution:

Use the wx.lib.agw.genericmessagedialog.GenericMessageDialog instead. It does not match the Mac dialog look and feel, but it does display informative icons.

To replace wx.MessageDialog everywhere without changing your code, apply the following monkey-patch:

import wx

import wx.lib.agw.genericmessagedialog

if ‘WXMAC’ in wx.Platform:

wx.MessageDialog = wx.lib.agw.genericmessagedialog.GenericMessageDialog

****Problem:

Advanced Splash Screen does not work.

Solution:

Use the standard wx.SplashScreen instead - it works fine. Be satisfied with a rectangular splash screen, without the fancy features of the advanced splash.

Example:

import wx

wx.SplashScreen( bitmap, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT, 2500, None )

Edward,

Thanks for writing this up, and sorry there were so many issues (some really should be fixed…)

Any chance we could get you to put this on the wiki?

http://wiki.wxpython.org/

-Chris

···

On Fri, Nov 22, 2013 at 7:23 AM, Edward Sitarski edward.sitarski@gmail.com wrote:

Following is a list of wxPython problems discovered while porting a medium-sized application to the Mac (CrossMgr, www.sites.google.com/site/crossmgrsoftware).

The solutions/workarounds are also documented below.

None of the fixes were all that difficult, but the solutions took weeks to find, so I am posting this for the greater good.

Thanks to all those on the forum who helped me along the way.

If anyone has some better suggestions, let me know.

System Details:

OSX: Mavericks, 64-bit Intel

Python: 2.7.5

wxPython: 2.9.5.0


****Problem:

On Windows and Linux, wx errors are shown in stderr with a stack trace.

By default, errors on the Mac are shown in a dialog, but no stack trace, making it impossible to find out where the error is occurring.

Solution:

Write a custom error logger. See attachment “LogPrintStackStderr.py” for a logger that writes to stderr (you can modify on your own to write to the screen, if that’s what you want).

You then need to tell wx to use your new handler as follows:

import wx

from LogPrintStackStderr import LogPrintStackStderr

if ‘WXMAC’ in wx.Platform: # More reliable than getting the right number of underscores are in WXMAC.

wx.Log.SetActiveTarget( LogPrintStackStderr() )


****Problem:

wx.Button does not draw properly with a larger font. The button keeps the standard height, but the text spills outside of the button’s borders on top and bottom.

This is especially problematic on touch-screen-friendly displays designed with larger buttons.

Solution:

Use wx.lib.buttons.ThemedGenButton instead. They do not match the Mac look-and-feel, but look better than plain GenButton. They will draw in the proper height with a larger font.

Example:

import wx.lib.buttons

btn = wx.lib.buttons.ThemedGenButton( parent, label=‘Big Font Button’ )

btn.SetFont( wx.FontFromPixelSize(wx.Size(0,64), wx.DEFAULT, wx.NORMAL, wx.NORMAL) )


****Problem:

A wx.TextCtrl does not adjust its height to accommodate a larger font. The wx.TextCtrl uses its standard height, even if the contents are illegible.

Solution:

Explicitly set the height to fontHeight*1.2 when you create the wx.TextCtrl.

import wx

fontSize = 64

font = wx.FontFromPixelSize(wx.Size(0,fontSize), wx.DEFAULT, wx.NORMAL, wx.NORMAL)

tc = wx.TextCtrl( parent, value=‘’, size=(-1, fontSize*1.2) )

tc.SetFont( font )


****Problem:

A wx.TextCtrl defaults to one line in height even when style=wx.TE_MULTILINE. On Windows and Linux, wx.TE_MULTILINE shows at least two lines.

Solution:

Set the height to be larger than 1 line. Example:

tc = wx.TextCtrl( parent, value=‘’, size=(-1,96), style=wx.TE_MULTILINE )


****Problem:

wx.DC.GetMultiLineTextExtent function returns garbage, causing havoc in custom drawing routines that use it.

Solution:

Define your own, then monkey-patch over the non-working standard function:

if ‘WXMAC’ in wx.Platform:

wx.DC.GetMultiLineTextExtent does not work on the Mac.

Replace it with our own function.

def GetMultiLineTextExtent( dc, text, font = None ):

  textWidth, textHeight, lineHeight = 0, 0, None
  for line in text.split('\n'):
  	lineWidth, lineHeight = dc.GetFullTextExtent( line, font )[:2]
  	textWidth = max( textWidth, lineWidth )
  	textHeight += lineHeight
  if lineHeight is None:
  	lineHeight = dc.GetFullTextExtent( '000Yy', font )[1]
  return textWidth, textHeight, lineHeight

wx.DC.GetMultiLineTextExtent = GetMultiLineTextExtent


****Problem:

wx.MessageDialog shows a rocket ship instead of the appropriate Warning, Error or Information icon.

Solution:

Use the wx.lib.agw.genericmessagedialog.GenericMessageDialog instead. It does not match the Mac dialog look and feel, but it does display informative icons.

To replace wx.MessageDialog everywhere without changing your code, apply the following monkey-patch:

import wx

import wx.lib.agw.genericmessagedialog

if ‘WXMAC’ in wx.Platform:

wx.MessageDialog = wx.lib.agw.genericmessagedialog.GenericMessageDialog


****Problem:

Advanced Splash Screen does not work.

Solution:

Use the standard wx.SplashScreen instead - it works fine. Be satisfied with a rectangular splash screen, without the fancy features of the advanced splash.

Example:

import wx

wx.SplashScreen( bitmap, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT, 2500, None )

You received this message because you are subscribed to the Google Groups “wxPython-users” group.

To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax

Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Edward Sitarski wrote:

Following is a list of wxPython problems discovered while porting a
medium-sized application to the Mac (CrossMgr,
www.sites.google.com/site/*crossmgrsoftware*).
The solutions/workarounds are also documented below.

None of the fixes were all that difficult, but the solutions took weeks
to find, so I am posting this for the greater good.

Thanks to all those on the forum who helped me along the way.
If anyone has some better suggestions, let me know.

System Details:

OSX: Mavericks, 64-bit Intel
Python: 2.7.5
wxPython: 2.9.5.0

***************************************************************
****Problem:
On Windows and Linux, wx errors are shown in stderr with a stack trace.
By default, errors on the Mac are shown in a dialog, but no stack trace,
making it impossible to find out where the error is occurring.

>>>Solution:
Write a custom error logger. See attachment "LogPrintStackStderr.py" for
a logger that writes to stderr (you can modify on your own to write to
the screen, if that's what you want).
You then need to tell wx to use your new handler as follows:

import wx
from LogPrintStackStderr import LogPrintStackStderr
...
if 'WXMAC' in wx.Platform:# More reliable than getting the right number
of underscores are in __WXMAC__.
wx.Log.SetActiveTarget( LogPrintStackStderr() )
...

ok

***************************************************************
****Problem:
wx.Button does not draw properly with a larger font. The button keeps
the standard height, but the text spills outside of the button's borders
on top and bottom.
This is especially problematic on touch-screen-friendly displays
designed with larger buttons.

>>>>Solution:
Use wx.lib.buttons.ThemedGenButton instead. They do not match the Mac
look-and-feel, but look better than plain GenButton. They will draw in
the proper height with a larger font.

Yep, the native buttons are designed by Apple with the "feature" that they will all be a certain size (based on the size variant which can be set in wx via the SetWindowVariant method.) Well, actually the widget can be larger than that, but the drawing of the button's borders, corners and interior by cocoa is always going to be one of those fixed sizes. So if you want something different then you must use a generic button.

***************************************************************
****Problem:
A wx.TextCtrl does not adjust its height to accommodate a larger font.
The wx.TextCtrl uses its standard height, even if the contents are
illegible.

>>>>Solution:
Explicitly set the height to fontHeight*1.2 when you create the
wx.TextCtrl.

Once again the size is dictated by the Apple HIG and wx tries to enforce that in the wxTextCtrl's DoGetBestSize method. However in this case I agree that the font size (if a font has been set) should be taken into account for the best size of this widget since unlike the native buttons it can be drawn at any size. Please add a "wxOSX-Cocoa" ticket for it at trac.wxwidgets.org requesting this change and we'll see what Stefan has to say.

***************************************************************
****Problem:
A wx.TextCtrl defaults to one line in height even when
style=wx.TE_MULTILINE. On Windows and Linux, wx.TE_MULTILINE shows at
least two lines.

>>>>Solution:
Set the height to be larger than 1 line. Example:

tc = wx.TextCtrl( parent, value='', size=(-1,96), style=wx.TE_MULTILINE )

Ditto the above. The DoGetBestSize doesn't look at the multi-line flag. It probably should.

***************************************************************
****Problem:
wx.DC.GetMultiLineTextExtent function returns garbage, causing havoc in
custom drawing routines that use it.

>>>>Solution:
Define your own, then monkey-patch over the non-working standard
function:

if 'WXMAC' in wx.Platform:
# wx.DC.GetMultiLineTextExtent does not work on the Mac.
# Replace it with our own function.
def GetMultiLineTextExtent( dc, text, font = None ):
textWidth, textHeight, lineHeight = 0, 0, None
for line in text.split('\n'):
lineWidth, lineHeight = dc.GetFullTextExtent( line, font )[:2]
textWidth = max( textWidth, lineWidth )
textHeight += lineHeight
if lineHeight is None:
lineHeight = dc.GetFullTextExtent( '000Yy', font )[1]
return textWidth, textHeight, lineHeight
wx.DC.GetMultiLineTextExtent = GetMultiLineTextExtent

Please define "garbage" and provide an example that shows the problem.

***************************************************************
****Problem:
wx.MessageDialog shows a rocket ship instead of the appropriate Warning,
Error or Information icon.

>>>>Solution:
Use the wx.lib.agw.genericmessagedialog.GenericMessageDialog instead. It
does not match the Mac dialog look and feel, but it does display
informative icons.
To replace wx.MessageDialog everywhere without changing your code, apply
the following monkey-patch:

import wx
import wx.lib.agw.genericmessagedialog
if 'WXMAC' in wx.Platform:
wx.MessageDialog = wx.lib.agw.genericmessagedialog.GenericMessageDialog

Yep. The HIG stipulates that the application's icon should be used on the message dialogs. Until you make an app bundle for your application with py2app then the icon used will be the one for the Python framework. Using the generic message dialog is fine, just be aware that from the typical Mac users's perspective that it will make it look even more like a non-native application.

***************************************************************
****Problem:
Advanced Splash Screen does not work.

>>>>Solution:
Use the standard wx.SplashScreen instead - it works fine. Be satisfied
with a rectangular splash screen, without the fancy features of the
advanced splash.
Example:

import wx
...
wx.SplashScreen( bitmap, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT,
2500, None )
...

I think I've fixed this one.

-- Robin Dunn
Software Craftsman