Question

My application has a module called gui_constants that contains the definitions of all of the fonts and colors used by my application. For example, gui_constants.py might contain a line like

heading_font = wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, face='LucidaGrande')

and another file might say

heading_label = wx.StaticText(self, label='Lorem Ipsum')
heading_label.SetFont(gui_constants.heading_font)

The problem I’m having is that my gui_constants module is being imported before the wx.App is created, which leads to an exception:

Traceback (most recent call last):
  File "./main.py", line 3, in <module>
    from new_gui_app import NewGuiApp
  File ".../application/new_gui_app.py", line 3, in <module>
    from views import MainWindow
  File ".../application/views/__init__.py", line 1, in <module>
    from banner import Banner
  File ".../application/views/banner.py", line 3, in <module>
    import gui_constants
  File ".../application/gui_constants.py", line 63, in <module>
    heading_font = wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, face='LucidaGrande')
  File "/usr/local/lib/python2.7/site-packages/wx-2.9.5-osx_cocoa/wx/_gdi.py", line 2156, in __init__
    _gdi_.Font_swiginit(self,_gdi_.new_Font(*args, **kwargs))
wx._core.PyNoAppError: The wx.App object must be created first!

Can anyone suggest a way to keep these “constant” declarations factored out into a single module, while also preventing them from being initialized before the wx.App is created?

Was it helpful?

Solution 2

The approach I ended up using was to add an initialize method to my gui_constants module:

def initialize():
    global heading_font
    heading_font = wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, face='LucidaGrande')

This approach requires that the user call this method before they use any of the constants defined in gui_constants, but after the App has been created. (Calling initialize before the App is ready results in a PyNoAppError, as in my question, and calling it too late gives an AttributeError when someone tries to access one of the not-yet-defined fonts.) The timing is not as tricky as I thought it would be, though; I call initialize at the beginning of my App subclass’s OnInit method and that seems to work fine.

There were three other options mentioned (by Mike Driscoll and by VZ):

  1. Store the font information in a tuple and pass this tuple to a create_font function each time a font is needed. For my taste this is too heavy; all users of the constants module need to know about the font-creation function, which should be a private implementation detail. It also adds a small amount of syntactic overhead all over the place.

  2. Use eval. The dangers of eval are well-known, and my personal policy is not to use it unless there’s no other way.

  3. Store the fonts as FontInfo objects and convert them to Font objects later. This could be done by passing the FontInfo to some constructor each time we want to use that font, as in option (1), or by constructing the Font objects en masse, as I’m doing in my eventual solution. Using the former approach has the same objections I mentioned for item (1). Using the latter approach is basically what I’m doing, but with an extra layer of indirection.

OTHER TIPS

You have a couple of options. One really easy way to do it is to change the constants into tuples instead of wx.Font objects:

heading_font = (12, wx.SWISS, wx.NORMAL, wx.BOLD, 'LucidaGrande')

Then when you import it, you can do something like this:

#----------------------------------------------------------------------
def create_font(data):
    """"""
    size, family, style, weight, face = data
    font = wx.Font(size, family, style, weight, face=face)
    return font


########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)

        print type(gui_constants.heading_font)
        print create_font(gui_constants.heading_font)

An alternative you could try is using Python's eval. If you did it this way, you would want to store your constants as strings, like this:

heading_font = "wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, face='LucidaGrande')"

Then in your wxPython code, you'd do something like this:

########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)

        print type(eval(gui_constants.heading_font))

Note that eval has a small danger in that if the user is allowed to modify the gui_constants file, they could insert malicious code. If they cannot modify it, then there isn't much of a danger at all.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top