[wxPython] Anyone care so comment on this code?-- HTMLDialog.py

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[wxPython] Anyone care so comment on this code?-- HTMLDialog.py

Chris Fama
Hello,

I am just putting the finishing touches on a module to display
errors--either correctly requested by a script or arbitrary
interpreter errors--these needs arose in the course of a commercial
(contract) project, but were not developed in the course of such,
hence I can release the code under the GPL. This is the first part.

I wanted a dialog to display arbitrary HTML, as well as being able to
have more than just the usual "OK" and "Cancel" buttons... I also
wanted to make nice "About" dialogs with images, for instance, very
easily...  could I "release" (the term is perhaps rather
over-dignifying the code!  :8-) it for comment?  With a warning that
I've only at a Micros*ft system for development, so I'd be REALLY
interested to hear about how it goes on Linux systems -- please don't
e-mail me just to tell me that, though!  Followups to this list are
fine, though, up to a point, of course... :8-)

I'll include the module after my .signature...

I haven't managed to get the correct behavior when one clicks on a
link, as explained in the documentation string of the module, and I'm
rather keen to finish the job for which I wrote this, but that's
really a problem with wxPython (or rather with the wxWindow
documentation)... also, my expertise [sic] with HTML leaves something
to be desired, so don't expect to be blown away by the defaults!  :8-)

Thank you in advance for your time, and I really hope that it's
useful!  Looking forward to your comments...

Chris

--
Chris Fama <mailto:[hidden email]>      
Brisbane, Australia    


Here it is, then.

< HTMLDialog.py >
"""
HTMLDialog.py: By Chris Fama. A [Very] hacked version of Robin Dunn's
About.py.  [ usual GPL rave: ] This code is released under the
auspices of the GNU Public License.  If you do not have a copy of this
document please obtain one from, e.g., the League for Programming
Freedom (or has the name changed?... )... blah.

This is a simple wrapper around HTMLWindow for dialogs displaying
arbitrary HTML code, providing an extremely easy mechanism for the
creation/display/use/whatever of control buttons... other controls on
the dialog can, of course, be created using the standard <WXP> tags of
the wxHTML sub-library package, which (brilliantly!) enables one to
almost "program in HTML"...

The class HTMLDialog can be derived from if you want different
behavior (e.g., if you want a left-click on a link to actually launch
a browser rather than simply copy the link to the clipboard [PLEASE
NOTE THAT THE CODE DOES NOT EVEN DO THAT AT THE MOMENT], if you want
different defaults, and particularly if you want some controls to have
callbacks...).  See Errors.py for an example of the latter.

use:
    dlg = HTMLDialog(parent,OPTIONS)
[or
    dlg = HTMLDialog(parent)
    ...........
    dlg.SetContents(OPTIONS)
 ]
    ......
    dlg.ShowModal() **OR** dlg.Show()
    ..........>
    dlg.Destroy () # iff modal...........
   
    OPTIONS (with defaults) from
                 name="",
                 version=None,
                 buttons=OKButton(makedefault=1),
                 text="",
                 versionfmt='<p><h2>Version %.2f</h2>',
                 namefmt="<h1>%s</h1>",
                 caption=None,
                 betweenbuttons="",
                 size=None,
                 defaultsize=(420,380),
                 image=None,
                 imagedir="",
                 imageurl="",
                 clickinstructions="Click on a link/image to copy it "
                                   "to the clipboard.",
                 pythonpowered=1,
                 pythonpowereddir="",
                 link_regexp=re.compile("</a>",re.IGNORECASE),
                 pythonpoweredfile="PythonPowered.gif",
                 bgcolor=None,
                 fgcolor=None,
                 backgroundforWindows="ActiveBorder",
                 foregroundforWindows="WindowText",
                 boxcolor='#458154'

These defaults are meant to be particularly useful for a "Help -> About"
type dialog.

"text" is the HTML to go after the name

"buttons" is a sequence of -
    ("label",*"id") specifications for buttons, or
    "HTML code".

e.g.,
    HTMLDialog(.......,
               buttons=OKButton(makedefault=1) + \
                       CancelButton () +\
                       ApplyButton () +\
                       [" or be <i>brave</i> and "] +\
                       Button("Exit now",`exitID`),
               .........)
[this is equivalent to
    HTMLDialog(.......,
               buttons=[("OK",`wxID_OK`,1),
                        ("Cancel",`wxID_CANCEL),
                        ("Apply",`wxID_APPLY`),
                        " or be <i>brave</i> and ",
                        ("Exit now",`exitID`)],
               .........)
 .

"image", if specified, is the filename of an image file to be displayed
to the left of the name; if "pythonpowered" tests true but is not a
string, the "Python Powered" logo is displayed at the beginning of the
text.  (If it is a string, it is taken to be the name of an image
file to be displayed instead of the default "PythonPowered.gif"
("pythonpowereddir" [default ""] is the name of a directory for this
file--note that os.path.join() is used for this, so the correct
directory separator will be used, so should not be included e.g. at
the end of "pythonpowereddir".  "imagedir" acts similarly for the
4image filename "image" .)
***       wxImage_AddHandler(wxGIFHandler())
    is called if needed; other handlers MAY be necessary. ***

"clickinstructions" is a string to be displayed just before the
buttons, if there is actually a link, either specified directly in
"text" (as a ...</a> link, i.e., match for default of "link_regexp"),
or indirectly via either "pythonpowered" or "image").  NOTE THAT
CLICKING ON LINKS DOES NOT WORK AT THE MOMENT as wxPython does not
appear to wrap the appropriate functions [although the "drag and drop
AND CLIPBOARD overview" claims to be immediately applicable to
clipboard functions, it does not say how, and this is far from clear,
to me at least].  I shall have to correct this a future version,
although I imagine this is rather easily done by any programmer.

If "bgcolor"is unspecified or None, and we are running on a Windows
system, the Registry entry "backgroundforWindows" in the registry key
"HKEY_CURRENT_USER\\Control Panel\Colors" is consulted for the colour
for the buttonsbackground of the dialog. (Repeat that sentence with
"bgcolor" replaced with "fgcolor", and "foregroundforWindows"
replacing "backgroundforWindows".)

"boxcolor" is used for the interior colour
of the box containing the name and possibly either or both of the
version number or "image" image file.  Both of these, if specified,
should be HTML colour specifications [i.e., '#RRGGBB', where RR is the
hexadecimal value of the red component of the colour, etc.--IS THIS
RIGHT?  IT'S YEARS SINCE I'VE REALLY USED HTML...].

If "size" is unspecified or None, the size of the dialog is chosen so
as to be as small as possible without needing a vertical scrollbar,
while having the same aspect ratio as the display (see wxDisplaySize);
if this is not possible, then "defaultsize" is used.

"""

__version__ = 1.0
#)__debug__ = 0

from   wxPython.wx       import *
from   types             import *
from   wxPython.html     import *
import wxPython.lib.wxpTag
import os,string,re

def Button(label,id,makedefault=0):
    return [(label,id,makedefault)]

def OKButton(makedefault=1):
    return Button("OK",`wxID_OK`,makedefault)

def CancelButton(makedefault=0):
    return Button("Cancel",`wxID_CANCEL`,makedefault)

def ApplyButton(makedefault=0):
    return Button("Apply",`wxID_APPLY`,makedefault)

class HTMLDialog(wxDialog):
    def __init__(self,
                 parent,
                 size=None,
                 defaultsize=(420, 380),
                 name="",
                 caption=None,
                 **kwargs):

        if caption is None:
            caption = name
        kwargs['name'] = name

        if size is None:
            size = defaultsize
        else:
            kwargs['size'] = size
        kwargs['defaultsize'] = defaultsize

        wxDialog.__init__(self, parent, -1, caption,
                          size=size)

        self.html = wxHtmlWindow(self, -1)
        apply(self.SetContents,(),kwargs)

        self.SetAutoLayout(true)
        lc = wxLayoutConstraints()
        lc.top.SameAs(self, wxTop, 5)
        lc.left.SameAs(self, wxLeft, 5)
        lc.bottom.SameAs(self, wxBottom, 5)
        lc.right.SameAs(self, wxRight, 5)
        self.html.SetConstraints(lc)
        self.Layout ()

        self.SetFocus ()

        # from wxWindows docs: This function (wxWindow::OnCharHook) is
        # only relevant to top-level windows (frames and dialogs), and
        # under Windows only. Under GTK the normal EVT_CHAR_ event has
        # the functionality, i.e. you can intercepts it and if you
        # don't call wxEvent::Skip the window won't get the event.

        if sys.platform not in ["windows",'nt']:
            EVT_CHAR_HOOK(self,self.OnCharHook)
        else:
            EVT_CHAR(self,self.OnCharHook)

    DefaultHTML = '''\
        <html>
        <body%s%s>
        <center><table bgcolor="%s" width="100%%" cellspacing="0"
        cellpadding="0" border="1">
        <tr>
        %s<td align="center">%s<p></td>
        </tr>
        </table>
        <p><hline>%s
        </center>
        %s<p><hline>
        %s<p><hline>
        <center>
        %s
        </center>
        </body>
        </html>'''

    ImageHTML='''\
        <td align="center">
            <a href="%s">
                      <img src="%s" align=top width=%d
                           height=%d alt="%s" border=0>
                      </a>
        </td>'''

    def SetContents(self,
                    name="",
                    version=None,
                    buttons=OKButton(makedefault=1),
                    text="",
                    versionfmt='<p><h2>Version %.2f</h2>',
                    namefmt="<h1>%s</h1>",
                    betweenbuttons="",
                    clickinstructions="Click on a link/image to copy it "
                    "to the clipboard.",
                    link_regexp=re.compile("</a>",re.IGNORECASE),
                    defaultsize=(420, 380),
                    image=None,
                    imagedir="",
                    imageurl="",
                    pythonpowered=1,
                    size=None,
                    pythonpowereddir="",
                    pythonpoweredfile="PythonPowered.gif",
                    fgcolor=None,
                    bgcolor=None,
                    foregroundforWindows="WindowText",
                    backgroundforWindows="ActiveBorder",
                    boxcolor="#458154"):
        if buttons is None:
            buttons = OKButton ()
        name = namefmt % name
        if version is not None:
            name = name + versionfmt % version
        if image:
            if not width:
                width = 100
            if not height:
                height = width
            imstr1 = self.ImageHTML % (imageurl,
                                       os.path.join(imagedir,
                                                    image),
                                       width,
                                       height,
                                       "['%s']" % image)
        else:
            imstr1 = ""
        if pythonpowered or (image and string.lower(image[-4:]) == '.gif'):
            wxImage_AddHandler(wxGIFHandler())
        if pythonpowered:
            if type(pythonpowered) == type(''):
                pythonpoweredfile = pythonpowered
            imstr2 = (self.ImageHTML
                      + " Powered by Python %s, which is %s.<p>"
                      % (sys.version,sys.copyright)) \
                     % ("http://www.python.org/",
                        os.path.join(pythonpowereddir,
                                      pythonpoweredfile),
                        55,
                        22,
                        "[Python Powered]")
        else:
            imstr2 = ""
        if pythonpowered or image or link_regexp.search(text):
            ci = clickinstructions
        else:
            ci = ""
 
        if fgcolor is not None:
            colourstring1 = ' fgcolor="%s"' % fgcolor
        else:
            try:
                try:
                    if __debug__: sys.__stderr__.write (notyet)
                except NameError:
                    colourstring1 = ' fgcolor='\
                                    + HTMLWindowsColour(foregroundforWindows)
            except ImportError:
                colourstring1 = ""

        if bgcolor is not None:
            colourstring2 = 'bgcolor="%s"' % bgcolor
        else:
            try:
                try:
                    if __debug__: sys.__stderr__.write (notyet)
                except NameError:
                    colourstring2 = ' bgcolor='\
                                    + HTMLWindowsColour(backgroundforWindows)
            except ImportError:
                colourstring2 = ""

        buttonstring = HTMLforButtons(buttons,
                                      betweenbuttons=betweenbuttons)
        self.theHTMLpage = (self.DefaultHTML
                            % (colourstring1,
                               colourstring2,
                               boxcolor,
                               imstr1,
                               name,
                               imstr2,
                               text,
                               ci,
                               buttonstring))
##        if __debug__: sys.__stderr__.write("The HTML for this dialog is:\n"
##                                           + self.theHTMLpage + '\n')
        self.html.SetPage(self.theHTMLpage)

        if size is None:
            # now try to `shrink' dialog, only so far as no scrollbar nec.,,
            # mantaining aspect ratio of display.
            w = 200
            ds = wxDisplaySize ()
            c = self.html.GetInternalRepresentation ()
            while w < ds[0]:
                c.Layout(w)
                if c.GetHeight() < (w+20) * ds[1]/ds[0] - 50:
                    self.html.SetSize (wxSize (w,int ((w) * ds[1]/ds[0])))
                    self.SetSize (wxSize (w+20,int ((w+20) * ds[1]/ds[0])))
                    break
                w = w + 20
            else:
                s0elf.SetSize(wxSize(defaultsize))
                self.html.SetSize (wxSize (map(operator.subtract,
                                               defaultsize,
                                               (10,10))))
        else:
            self.SetSize(wxSize(size))
            self.html.SetSize (wxSize (map(operator.sub,
                                           size,
                                           (10,10))))
        self.Layout()

        self.CentreOnParent(wxBOTH)

    def OnCharHook(self,event):
        if event.KeyCode() == WXK_RETURN:
            di = self.html.GetDefaultItem()
            if __debug__:
                sys.__stderr__.write ("self.html.GetDefaultItem() = %s\n"
                                      % (di,))
                di.ProcessEvent(
                wxCommandEvent(wxEVT_COMMAND_BUTTON_CLICKED,
                               di.GetId ()))
            event.Skip ()

        elif event.KeyCode() == WXK_ESCAPE:
            if self.html.FindWindowById(wxID_CANCEL):
                self.html.FindWindowById(wxID_CANCEL).ProcessEvent(
                    wxCommandEvent(wxEVT_COMMAND_BUTTON_CLICKED,
                                   wxID_CANCEL))
            else:
                if self.IsModal() == FALSE:
                    self.Show(FALSE)
                else:
                    self.EndModal(wxID_CANCEL)
            #event.Skip ()

    def OnLinkClicked(self,link):
        if __debug__: sys.__stderr__.write ("link: %s\n not yet, sorry!\n"
                                            % link)
        # COMPLETE...

def _tupn(tup,n):
    if type(tup) == TupleType:
        try:
            return tup[n]
        except IndexError:
            return None
    else:
        if n == 0:
            return tup
        else:
            return None
           
DefaultButStr = '''\
<wxp class="%swxButton" module="%s">
    <param name="label" value="%s">
    <param name="id"    value="%s">
</wxp>'''

def HTMLforSingleButton(label,id=None,makedefault=0):
    if id is None:
        return label
    else:
        if makedefault == 1:
            defaultstring1 = "Default_"
            defaultstring2 = "HTMLDialog"
        else:
            defaultstring1 = ""
            defaultstring2 = ""
        return DefaultButStr % (defaultstring1,defaultstring2,label,id)

class Default_wxButton(wxButton):
    def __init__(self,*args,**kwargs):
##        size = kwargs['size']
##        if __debug__: sys.__stderr__.write ("size: %s args: %s kwargs: %s\n"
##                                            % (size,args,kwargs))
        apply(wxButton.__init__, (self,) + args,kwargs)
        self.SetDefault ()

def HTMLforButtons (buttons,betweenbuttons=""):
    def rn(n):
        return lambda tup,n=n: _tupn(tup,n)
##    if __debug__: sys.__stderr__.write ("map(rn(0),buttons) = " + `map(rn(0),buttons)`+'\n')
##    if __debug__: sys.__stderr__.write ("map(rn(1),buttons) = " + `map(rn(1),buttons)`+'\n')
##    if __debug__: sys.__stderr__.write ("map(rn(2),buttons) = " + `map(rn(2),buttons)`+'\n')
    return string.join (map (HTMLforSingleButton,
                             map(rn(0),buttons),
                             map(rn(1),buttons),
                             map(rn(2),buttons)),
                        betweenbuttons)

def HTMLWindowsColour(colourname):
    """Return the value of colourname in the section of the Windows
    registry, as an HTML colour specification ('"#RRGGBB"')."""
    import win32api,win32con,pywintypes
    try:
       key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER,
                           "Control Panel\\Colors",
                           0,
                           win32con.KEY_QUERY_VALUE)
       (s,junk) = win32api.RegQueryValueEx(key,colourname)
       key.Close()
    except win32api.error,edata:
        if __debug__:
            sys.__stderr__.write ('win32api.error: %s\n'
                                  % edata)
        raise ImportError

    def maphex(s):
        return hex(int(s))[2:]

    l = map(maphex,
            string.split(s))
    l.insert(0,'"#')
    l.append('"')
   
    return string.join(l,"")

if __name__ == "__main__":
    class MyApp(wxApp):
        def OnInit(self):
            about = HTMLDialog(NULL,
                   name="AugerMate",
                   versionfmt="(This is merely an example of an HTMLDialog.)<p>"
                              "HTMLDialog.py version %g",
                   text="<b><i>AugerMate</i></b> is a program for the "
                   "determination of an optimal (or at least "
                   "near-optimal) set of auger sizes to "
                   "use for a highwall mining operation"
                   ".<p>The program was written by "
                   "<b>Dr. Chris Fama</b>, between April and November "
                   "1999, for <b>(company)</b>.  Please "
                   "contact <i>(address)</i> with any queries.<p>"
                   "This code uses modules by third-party authors:"
                   " the Python Imaging Library by Frederick Lundh (see "
                   "<a href=\"http://www.pythonware.com/products/pil/\">http://www.pythonware.com/products/pil/</a>),"
                   " the `DISLIN for Python' graphics package by Helmut Michels "
                   "(see <a href=\"http://www.mpae.gwdg.de/dislin/dislin.html\">http://www.mpae.gwdg.de/dislin/dislin.html</a>),"
                   " the `wxPython' graphical user interface package (based on "
                   "`wxWindows' by Julian Smart <i>et. al.</i>) by Robin Dunn "
                   "(see <a href=\"http://alldunn.com/wxPython//\">http://alldunn.com/wxPython/</a>),"
                   " and Gordon McMillan's `Win32 Installer' (see "
                   " <a href=\"http://starship.python.net/crew/gmcm/install.html\">http://starship.python.net/crew/gmcm/install.html</a>.",
                   caption="About AugerMate",
                   version=__version__)
            about.ShowModal()
            about.Destroy()
            return true


    app = MyApp(0)
    app.MainLoop()
    sys.exit()


_______________________________________________
wxPython-users maillist  -  [hidden email]
http://starship.python.net/mailman/listinfo/wxpython-users