| 1 | #---------------------------------------------------------------------- |
| 2 | # Name: wxPython.lib.wxpTag |
| 3 | # Purpose: A wxHtmlTagHandler that knows how to build and place |
| 4 | # wxPython widgets onto web pages. |
| 5 | # |
| 6 | # Author: Robin Dunn |
| 7 | # |
| 8 | # Created: 13-Sept-1999 |
| 9 | # RCS-ID: $Id$ |
| 10 | # Copyright: (c) 1999 by Total Control Software |
| 11 | # Licence: wxWindows license |
| 12 | #---------------------------------------------------------------------- |
| 13 | # 12/13/2003 - Jeff Grimmett (grimmtooth@softhome.net) |
| 14 | # |
| 15 | # o Updated for V2.5 compatability |
| 16 | # |
| 17 | |
| 18 | ''' |
| 19 | wxPython.lib.wxpTag |
| 20 | |
| 21 | This module contains a wxHtmlTagHandler that knows how to build |
| 22 | and place wxPython widgets onto wxHtmlWindow web pages. |
| 23 | |
| 24 | You don\'t need to use anything in this module directly, just |
| 25 | importing it will create the tag handler and add it to any |
| 26 | wxHtmlWinParsers created from that time forth. |
| 27 | |
| 28 | Tags of the following form are recognised: |
| 29 | |
| 30 | <WXP class="classname" [module="modulename"] [width="num"] [height="num"]> |
| 31 | <PARAM name="parameterName" value="parameterValue> |
| 32 | ... |
| 33 | </WXP> |
| 34 | |
| 35 | where modulename is the name of a module (possibly in package |
| 36 | notation) to import and classname is the name of a class in that |
| 37 | module to create an instance of. If the module tag-attribute is not |
| 38 | given or is an empty string, then wx is used. The width and height |
| 39 | attributes are expected to be integers and will be passed to the |
| 40 | __init__ method of the class as a wxSize object named size. However, |
| 41 | if the width attribute ends with the percent (%) symbol then the value |
| 42 | will be used as a percentage of the available width and the |
| 43 | wxHtmlWindow will manage the size. |
| 44 | |
| 45 | The name-value pairs in all the nested PARAM tags are packaged up as |
| 46 | strings into a python dictionary and passed to the __init__ method of |
| 47 | the class as keyword arguments. This means that they are all |
| 48 | accessible from the __init__ method as regular parameters, or you use |
| 49 | the special Python **kw syntax in your __init__ method to get the |
| 50 | dictionary directly. |
| 51 | |
| 52 | Some parameter values are special and if they are present then they will |
| 53 | be converted from strings to alternate datatypes. They are: |
| 54 | |
| 55 | id If the value of id can be converted to an integer, it will |
| 56 | be. Otherwise it is assumed to be the name of an integer |
| 57 | variable in the module. |
| 58 | |
| 59 | colours Any value of the form "#123ABC" will automatically be |
| 60 | converted to a wxColour object. |
| 61 | |
| 62 | Py Types Any value begining with "(", "[" or "{" are expected to |
| 63 | be a Python tuple, list, or dictionary and eval() |
| 64 | will be used to convert them to that type. If the |
| 65 | eval() fails then the original string value will be |
| 66 | preserved. |
| 67 | |
| 68 | wx Types Any value begining with "wx" is expected to be an attempt |
| 69 | to create a wxPython object, such as a wxSize, etc. |
| 70 | The eval() will be used to try and construct the |
| 71 | object and if it fails then the original string value |
| 72 | will be used instead. |
| 73 | |
| 74 | An example: |
| 75 | |
| 76 | <wxp module="wx" class="Button"> |
| 77 | <param name="label" value="Click here"> |
| 78 | <param name="id" value="ID_OK"> |
| 79 | </wxp> |
| 80 | |
| 81 | Both the begining and ending WXP tags are required. |
| 82 | |
| 83 | In the future support will be added for another tag that can be |
| 84 | embedded between the two begining and ending WXP tags and will |
| 85 | facilitate calling methods of the widget to help initialize it. |
| 86 | Additionally, support may be added to fetch the module from a web |
| 87 | server as is done with java applets. |
| 88 | |
| 89 | ''' |
| 90 | #---------------------------------------------------------------------- |
| 91 | |
| 92 | import types |
| 93 | |
| 94 | import wx |
| 95 | import wx.html |
| 96 | |
| 97 | |
| 98 | #---------------------------------------------------------------------- |
| 99 | |
| 100 | WXPTAG = 'WXP' |
| 101 | PARAMTAG = 'PARAM' |
| 102 | |
| 103 | #---------------------------------------------------------------------- |
| 104 | |
| 105 | class wxpTagHandler(wx.html.HtmlWinTagHandler): |
| 106 | def __init__(self): |
| 107 | wx.html.HtmlWinTagHandler.__init__(self) |
| 108 | self.ctx = None |
| 109 | |
| 110 | def GetSupportedTags(self): |
| 111 | return WXPTAG+','+PARAMTAG |
| 112 | |
| 113 | |
| 114 | def HandleTag(self, tag): |
| 115 | name = tag.GetName() |
| 116 | if name == WXPTAG: |
| 117 | return self.HandleWxpTag(tag) |
| 118 | elif name == PARAMTAG: |
| 119 | return self.HandleParamTag(tag) |
| 120 | else: |
| 121 | raise ValueError, 'unknown tag: ' + name |
| 122 | |
| 123 | |
| 124 | def HandleWxpTag(self, tag): |
| 125 | # create a new context object |
| 126 | self.ctx = _Context() |
| 127 | |
| 128 | # find and import the module |
| 129 | modName = '' |
| 130 | if tag.HasParam('MODULE'): |
| 131 | modName = tag.GetParam('MODULE') |
| 132 | if modName: |
| 133 | self.ctx.classMod = _my_import(modName) |
| 134 | else: |
| 135 | self.ctx.classMod = wx |
| 136 | |
| 137 | # find and verify the class |
| 138 | if not tag.HasParam('CLASS'): |
| 139 | raise AttributeError, "WXP tag requires a CLASS attribute" |
| 140 | |
| 141 | className = tag.GetParam('CLASS') |
| 142 | self.ctx.classObj = getattr(self.ctx.classMod, className) |
| 143 | if type(self.ctx.classObj) not in [ types.ClassType, types.TypeType]: |
| 144 | raise TypeError, "WXP tag attribute CLASS must name a class" |
| 145 | |
| 146 | # now look for width and height |
| 147 | width = -1 |
| 148 | height = -1 |
| 149 | if tag.HasParam('WIDTH'): |
| 150 | width = tag.GetParam('WIDTH') |
| 151 | if width[-1] == '%': |
| 152 | self.ctx.floatWidth = int(width[:-1], 0) |
| 153 | width = self.ctx.floatWidth |
| 154 | else: |
| 155 | width = int(width) |
| 156 | if tag.HasParam('HEIGHT'): |
| 157 | height = int(tag.GetParam('HEIGHT')) |
| 158 | self.ctx.kwargs['size'] = wx.Size(width, height) |
| 159 | |
| 160 | # parse up to the closing tag, and gather any nested Param tags. |
| 161 | self.ParseInner(tag) |
| 162 | |
| 163 | # create the object |
| 164 | parent = self.GetParser().GetWindow() |
| 165 | if parent: |
| 166 | obj = apply(self.ctx.classObj, |
| 167 | (parent,), |
| 168 | self.ctx.kwargs) |
| 169 | obj.Show(True) |
| 170 | |
| 171 | # add it to the HtmlWindow |
| 172 | self.GetParser().GetContainer().InsertCell(wx.html.HtmlWidgetCell(obj, self.ctx.floatWidth)) |
| 173 | self.ctx = None |
| 174 | |
| 175 | return True |
| 176 | |
| 177 | |
| 178 | def HandleParamTag(self, tag): |
| 179 | if not tag.HasParam('NAME'): |
| 180 | return False |
| 181 | |
| 182 | name = tag.GetParam('NAME') |
| 183 | value = "" |
| 184 | if tag.HasParam('VALUE'): |
| 185 | value = tag.GetParam('VALUE') |
| 186 | |
| 187 | # check for a param named 'id' |
| 188 | if name == 'id': |
| 189 | theID = -1 |
| 190 | try: |
| 191 | theID = int(value) |
| 192 | except ValueError: |
| 193 | theID = getattr(self.ctx.classMod, value) |
| 194 | value = theID |
| 195 | |
| 196 | |
| 197 | # check for something that should be evaluated |
| 198 | elif value and value[0] in '[{(' or value[:2] == 'wx': |
| 199 | saveVal = value |
| 200 | try: |
| 201 | value = eval(value, self.ctx.classMod.__dict__) |
| 202 | except: |
| 203 | value = saveVal |
| 204 | |
| 205 | # convert to wx.Colour |
| 206 | elif value and value[0] == '#': |
| 207 | try: |
| 208 | red = int('0x'+value[1:3], 16) |
| 209 | green = int('0x'+value[3:5], 16) |
| 210 | blue = int('0x'+value[5:], 16) |
| 211 | value = wx.Color(red, green, blue) |
| 212 | except: |
| 213 | pass |
| 214 | |
| 215 | if self.ctx: |
| 216 | self.ctx.kwargs[str(name)] = value |
| 217 | return False |
| 218 | |
| 219 | |
| 220 | #---------------------------------------------------------------------- |
| 221 | # just a place to hold some values |
| 222 | class _Context: |
| 223 | def __init__(self): |
| 224 | self.kwargs = {} |
| 225 | self.width = -1 |
| 226 | self.height = -1 |
| 227 | self.classMod = None |
| 228 | self.classObj = None |
| 229 | self.floatWidth = 0 |
| 230 | |
| 231 | |
| 232 | #---------------------------------------------------------------------- |
| 233 | # Function to assist with importing packages |
| 234 | def _my_import(name): |
| 235 | mod = __import__(name) |
| 236 | components = name.split('.') |
| 237 | for comp in components[1:]: |
| 238 | mod = getattr(mod, comp) |
| 239 | return mod |
| 240 | |
| 241 | |
| 242 | #---------------------------------------------------------------------- |
| 243 | # Function to parse a param string (of the form 'item=value item2="value etc"' |
| 244 | # and creates a dictionary |
| 245 | def _param2dict(param): |
| 246 | i = 0; j = 0; s = len(param); d = {} |
| 247 | while 1: |
| 248 | while i<s and param[i] == " " : i = i+1 |
| 249 | if i>=s: break |
| 250 | j = i |
| 251 | while j<s and param[j] != "=": j=j+1 |
| 252 | if j+1>=s: |
| 253 | break |
| 254 | word = param[i:j] |
| 255 | i=j+1 |
| 256 | if (param[i] == '"'): |
| 257 | j=i+1 |
| 258 | while j<s and param[j] != '"' : j=j+1 |
| 259 | if j == s: break |
| 260 | val = param[i+1:j] |
| 261 | elif (param[i] != " "): |
| 262 | j=i+1 |
| 263 | while j<s and param[j] != " " : j=j+1 |
| 264 | val = param[i:j] |
| 265 | else: |
| 266 | val = "" |
| 267 | i=j+1 |
| 268 | d[word] = val |
| 269 | return d |
| 270 | |
| 271 | #---------------------------------------------------------------------- |
| 272 | |
| 273 | |
| 274 | |
| 275 | wx.html.HtmlWinParser_AddTagHandler(wxpTagHandler) |