Welcome, guest | Sign In | My Account | Store | Cart
"""XRC dialog runner.

This module facilitates creating and running simple dialogs that are defined
in XRC files. The XRC file must define OK and Cancel buttons with XRC IDs of
'ID_OK' and 'ID_CANCEL'. The feature to run a dialog will only set and return
values for controls that support GetValue() and SetValue().

Customizing this to support other types of controls is doable.

The basic principle of operation can be summarized with this sample code:

import wx
from wx import xrc
app = wx.PySimpleApp() # assuming you don't already have an app running
# Load the dialog from the resource file.
res = xrc.XmlResource('dialog.xrc')
dlg = res.LoadDialog(None, 'dlgLogin')
dlg.Fit()
# Configure OK & Cancel buttons for default behavior.
dlg.FindWindowByName('ID_OK'    ).SetId(wx.ID_OK)
dlg.FindWindowByName('ID_CANCEL').SetId(wx.ID_CANCEL)
# Run the dialog.
if dlg.ShowModal() == wx.ID_OK:
    print 'OK'
else:
    print 'Cancel'
# Extract results.
print '%r' % dlg.FindWindowByName('fldUser'    ).GetValue()
print '%r' % dlg.FindWindowByName('fldPassword').GetValue()

Once the dialog or frame is created, you are free to use Bind() calls to add
handlers to various controls:

def myHandler(evt):
    print 'click!'
dlg.FindWindowByName('myButton').Bind(wx.EVT_BUTTON, myHandler)
"""

import os
import wx
from wx import xrc


resourceCache = dict()


def insureWxApp():
    """Create an instance of PySimpleApp if there is no app already.
    This is required by wxPython before creating any GUI objects.
    """
    global _wxApp
    _wxApp = wx.GetApp()
    if not _wxApp:
        _wxApp = wx.PySimpleApp()
    return _wxApp


def loadXrc(filePath, reload=False):
    """Return an xrc.XmlResource instance."""
    filePath = os.path.abspath(filePath)
    if reload or (filePath not in resourceCache):
        result = xrc.XmlResource(filePath)
        resourceCache[filePath] = result
    else:
        result = resourceCache[filePath]
    return result


def escapeSuppressor(evt):
    """wx.EVT_CHAR_HOOK handler that suppresses processing ESC.
    By default, if you don't have a Cancel button, wx will trigger the
    OK button when you press ESC. Binding this to a dialog will deactivate
    the ESC key. We need this when there is no Cancel button.
    """
    evt.Skip(evt.GetKeyCode() != wx.WXK_ESCAPE)


def buildDialog(filePath, resourceName, mayCancel, **defaults):
    """Return a configured wx.Dialog.
    Assumes that the OK and Cancel buttons are named ID_OK & ID_CANCEL.
    """
    res = loadXrc(filePath)
    insureWxApp()
    dlg = res.LoadDialog(None, resourceName)
    assert isinstance(dlg, wx.Dialog)
    dlg.Fit()
    fetchWidget = dlg.FindWindowByName
    bOk     = dlg.FindWindowByName('ID_OK')
    bCancel = dlg.FindWindowByName('ID_CANCEL')
    bOk.SetId(wx.ID_OK)
    bCancel.SetId(wx.ID_CANCEL)
    if not mayCancel:
        bCancel.Disable()
        bCancel.Hide()
    for name, value in defaults.items():
        dlg.FindWindowByName(name).SetValue(value)
    if not mayCancel:
        dlg.Bind(wx.EVT_CHAR_HOOK, escapeSuppressor)
    return dlg


def runDialog(dlg, mayCancel, *itemNames):
    """Run the specified dialog and return the values of the named items.
    Return None if the user cancels.
    """
    while True:
        if dlg.ShowModal() == wx.ID_OK:
            result = tuple((dlg.FindWindowByName(name).GetValue()
                            for name in itemNames))
            break
        elif mayCancel:
            result = None
            break
        else:
            wx.Bell()
    return result


def useTemporaryDialog(filePath, resourceName, mayCancel,
                       *itemNames, **defaults):
    """Create a dialog, run it, capture the results, destroy the dialog,
    and return the results.
    Return None if the user cancels.
    """
    dlg = buildDialog(filePath, resourceName, mayCancel, **defaults)
    try:
        result = runDialog(dlg, mayCancel, *itemNames)
    finally:
        dlg.Destroy()
    return result


# --- Sample Dialog ---
def askLogin(defaultUser='', defaultPassword='', mayCancel=True):
    """Return None if user cancels; otherwise (user, password) as unicode."""
    result = useTemporaryDialog('dialog.xrc', 'dlgLogin', mayCancel,
                                'fldUser', 'fldPassword',
                                fldUser=defaultUser,
                                fldPassword=defaultPassword)
    return result


# --- Sample Usage ---
import sys
print askLogin('john_doe', '123', mayCancel=False)
print askLogin()


# --- Sample XRC File named dialog.xrc ---
# Open in XRCed (part of "wxPython Docs Demos & Tools") or wxGlade to edit.
"""
<?xml version="1.0" encoding="utf-8"?>
<resource>
  <object class="wxDialog" name="dlgLogin">
    <title>Login</title>
    <object class="wxBoxSizer">
      <orient>wxVERTICAL</orient>
      <object class="sizeritem">
        <object class="wxFlexGridSizer">
          <cols>2</cols>
          <rows>2</rows>
          <object class="sizeritem">
            <object class="wxStaticText">
              <label>User:</label>
            </object>
            <flag>wxALL|wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL</flag>
            <border>8</border>
          </object>
          <object class="sizeritem">
            <object class="wxTextCtrl" name="fldUser"/>
            <flag>wxALL|wxEXPAND</flag>
            <border>8</border>
            <minsize>200,20</minsize>
          </object>
          <object class="sizeritem">
            <object class="wxStaticText">
              <label>Password:</label>
            </object>
            <flag>wxALL|wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL</flag>
            <border>8</border>
          </object>
          <object class="sizeritem">
            <object class="wxTextCtrl" name="fldPassword">
              <style>wxTE_PASSWORD</style>
            </object>
            <flag>wxALL|wxEXPAND</flag>
            <border>8</border>
          </object>
          <growablecols>1</growablecols>
        </object>
        <flag>wxALL|wxEXPAND</flag>
        <border>8</border>
      </object>
      <object class="sizeritem">
        <object class="wxBoxSizer">
          <orient>wxHORIZONTAL</orient>
          <object class="sizeritem">
            <object class="wxButton" name="ID_CANCEL">
              <label>Cancel</label>
            </object>
            <flag>wxALL|wxALIGN_BOTTOM</flag>
            <border>8</border>
          </object>
          <object class="sizeritem">
            <object class="wxButton" name="ID_OK">
              <label>OK</label>
              <default>1</default>
            </object>
            <flag>wxALL|wxALIGN_BOTTOM</flag>
            <border>8</border>
          </object>
        </object>
        <option>1</option>
        <flag>wxBOTTOM|wxLEFT|wxRIGHT|wxALIGN_RIGHT</flag>
        <border>8</border>
      </object>
    </object>
    <centered>1</centered>
    <style>wxDEFAULT_DIALOG_STYLE|wxSTAY_ON_TOP|wxRESIZE_BORDER|wxDIALOG_MODAL|wxTAB_TRAVERSAL</style>
  </object>
</resource>"""

History