PageRenderTime 60ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/xml_parse.py

https://bitbucket.org/elentirmo/wxglade_elentirmo
Python | 880 lines | 826 code | 13 blank | 41 comment | 28 complexity | 68a7f7cc8cd081d4bcc65a64f12d58d1 MD5 | raw file
  1. # xml_parse.py: parsers used to load an app and to generate the code
  2. # from an xml file.
  3. # $Id: xml_parse.py,v 1.46 2007/08/07 12:16:44 agriggio Exp $
  4. #
  5. # Copyright (c) 2002-2007 Alberto Griggio <agriggio@users.sourceforge.net>
  6. # License: MIT (see license.txt)
  7. # THIS PROGRAM COMES WITH NO WARRANTY
  8. #
  9. # NOTE: custom tag handler interface (called by XmlWidgetBuilder)
  10. # class CustomTagHandler:
  11. # def start_elem(self, name, attrs):
  12. # pass
  13. # def end_elem(self, name):
  14. # return True -> the handler must be removed from the Stack
  15. # def char_data(self, data):
  16. # return False -> no further processing needed
  17. import os
  18. import common, edit_sizers
  19. from xml.sax import SAXException, make_parser
  20. from xml.sax.handler import ContentHandler
  21. # For py2app: explicitly import an XML parser driver:
  22. try:
  23. import xml.sax.drivers2.drv_pyexpat
  24. from xml.sax.drivers2 import *
  25. except ImportError:
  26. pass
  27. import traceback
  28. # ALB 2005-03-10: importing the module here prevents a segfault with python 2.4
  29. # hmmm... need to investigate this more (it seems that import of
  30. # xml.sax.expatreader should happen before something else... but what?)
  31. import xml.sax.expatreader
  32. if common.use_gui:
  33. import wx
  34. class XmlParsingError(SAXException):
  35. """\
  36. Custom exception to report problems during parsing
  37. """
  38. locator = None
  39. def __init__(self, msg):
  40. if self.locator:
  41. l = self.locator
  42. msg += ' _((line: %s, column: %s))' % (l.getLineNumber(),
  43. l.getColumnNumber())
  44. SAXException.__init__(self, msg)
  45. # end of class XmlParsingError
  46. class XmlParser(ContentHandler):
  47. """\
  48. 'abstract' base class of the parsers used to load an app and to generate
  49. the code
  50. """
  51. def __init__(self):
  52. self._objects = Stack() # stack of 'alive' objects
  53. self._windows = Stack() # stack of window objects (derived by wxWindow)
  54. self._sizers = Stack() # stack of sizer objects
  55. self._sizer_item = Stack() # stack of sizer items
  56. self._curr_prop = None # name of the current property
  57. self._curr_prop_val = [] # value of the current property (list into
  58. # which the various pieces of char data
  59. # collected are inserted)
  60. self._appl_started = False
  61. self.top = self._objects.top
  62. self.parser = make_parser()
  63. self.parser.setContentHandler(self)
  64. self.locator = None # document locator
  65. def parse(self, source):
  66. self.parser.parse(source)
  67. def parse_string(self, source):
  68. from cStringIO import StringIO
  69. source = StringIO(source)
  70. self.parser.parse(source)
  71. source.close()
  72. def setDocumentLocator(self, locator):
  73. self.locator = locator
  74. XmlParsingError.locator = locator
  75. def startElement(self, name, attrs):
  76. raise NotImplementedError
  77. def endElement(self, name, attrs):
  78. raise NotImplementedError
  79. def characters(self, data):
  80. raise NotImplementedError
  81. def pop(self):
  82. try: return self._objects.pop().pop()
  83. except AttributeError: return None
  84. # end of class XmlParser
  85. class XmlWidgetBuilder(XmlParser):
  86. """\
  87. parser used to build the tree of widgets from an xml file
  88. """
  89. def startElement(self, name, attrs):
  90. if name == 'application':
  91. # get properties of the app
  92. self._appl_started = True
  93. app = common.app_tree.app
  94. encoding = attrs.get("encoding")
  95. if encoding:
  96. try: unicode('a', encoding)
  97. except LookupError: pass
  98. else:
  99. app.encoding = encoding
  100. app.encoding_prop.set_value(encoding)
  101. path = attrs.get("path")
  102. if path:
  103. app.output_path = path
  104. app.outpath_prop.set_value(path)
  105. name = attrs.get("name")
  106. if name:
  107. app.name = name
  108. app.name_prop.toggle_active(True)
  109. app.name_prop.set_value(name)
  110. klass = attrs.get("class")
  111. if klass:
  112. app.klass = klass
  113. app.klass_prop.toggle_active(True)
  114. app.klass_prop.set_value(klass)
  115. option = attrs.get("option")
  116. if option:
  117. try: option = int(option)
  118. except ValueError: option = 0
  119. app.codegen_opt = option
  120. app.codegen_prop.set_value(option)
  121. language = attrs.get('language')
  122. if language:
  123. app.codewriters_prop.set_str_value(language)
  124. app.set_language(language)
  125. top_win = attrs.get("top_window")
  126. if top_win: self.top_window = top_win
  127. try: use_gettext = int(attrs["use_gettext"])
  128. except (KeyError, ValueError): use_gettext = False
  129. if use_gettext:
  130. app.use_gettext = True
  131. app.use_gettext_prop.set_value(True)
  132. try: is_template = int(attrs["is_template"])
  133. except (KeyError, ValueError): is_template = False
  134. app.is_template = is_template
  135. try: overwrite = int(attrs['overwrite'])
  136. except (KeyError, ValueError): overwrite = False
  137. if overwrite:
  138. app.overwrite = True
  139. app.overwrite_prop.set_value(True)
  140. # ALB 2004-01-18
  141. try: use_new_namespace = int(attrs['use_new_namespace'])
  142. except (KeyError, ValueError): use_new_namespace = False
  143. ## app.set_use_new_namespace(use_new_namespace)
  144. ## app.use_new_namespace_prop.set_value(use_new_namespace)
  145. app.set_use_old_namespace(not use_new_namespace)
  146. app.use_old_namespace_prop.set_value(not use_new_namespace)
  147. # ALB 2004-12-05
  148. indent_symbol = attrs.get("indent_symbol")
  149. if indent_symbol == 'space':
  150. app.indent_mode = 1
  151. elif indent_symbol == 'tab':
  152. app.indent_mode = 0
  153. app.indent_mode_prop.set_value (app.indent_mode)
  154. indent = attrs.get("indent_amount")
  155. if indent:
  156. try: indent_amount = int(indent)
  157. except (KeyError, ValueError): pass
  158. else:
  159. app.indent_amount = indent_amount
  160. app.indent_amount_prop.set_value (indent_amount)
  161. source_extension = attrs.get("source_extension")
  162. if source_extension and source_extension[0] == '.':
  163. app.source_ext = source_extension[1:]
  164. app.source_ext_prop.set_value (source_extension[1:])
  165. header_extension = attrs.get("header_extension")
  166. if header_extension and header_extension[0] == '.':
  167. app.header_ext = header_extension[1:]
  168. app.header_ext_prop.set_value (header_extension[1:])
  169. try:
  170. for_version = attrs['for_version']
  171. app.for_version = for_version
  172. app.for_version_prop.set_str_value(for_version)
  173. except KeyError:
  174. pass
  175. return
  176. if not self._appl_started:
  177. raise XmlParsingError(_("the root of the tree must be <application>"))
  178. if name == 'object':
  179. # create the object and push it on the appropriate stacks
  180. XmlWidgetObject(attrs, self)
  181. else:
  182. # handling of the various properties
  183. try:
  184. # look for a custom handler to push on the stack
  185. handler = self.top().obj.get_property_handler(name)
  186. if handler: self.top().prop_handlers.push(handler)
  187. # get the top custom handler and use it if there's one
  188. handler = self.top().prop_handlers.top()
  189. if handler: handler.start_elem(name, attrs)
  190. except AttributeError: pass
  191. self._curr_prop = name
  192. def endElement(self, name):
  193. if name == 'application':
  194. self._appl_started = False
  195. if hasattr(self, 'top_window'):
  196. common.app_tree.app.top_window = self.top_window
  197. common.app_tree.app.top_win_prop.SetStringSelection(
  198. self.top_window)
  199. return
  200. if name == 'object':
  201. # remove last object from the stack
  202. obj = self.pop()
  203. if obj.klass in ('sizeritem', 'sizerslot'): return
  204. si = self._sizer_item.top()
  205. if si is not None and si.parent == obj.parent:
  206. sprop = obj.obj.sizer_properties
  207. # update the values
  208. sprop['option'].set_value(si.obj.option)
  209. sprop['flag'].set_value(si.obj.flag_str())
  210. sprop['border'].set_value(si.obj.border)
  211. # call the setter functions
  212. obj.obj['option'][1](si.obj.option)
  213. obj.obj['flag'][1](si.obj.flag_str())
  214. obj.obj['border'][1](si.obj.border)
  215. else:
  216. # end of a property or error
  217. # 1: set _curr_prop value
  218. data = common._encode_from_xml("".join(self._curr_prop_val))
  219. if data:
  220. try:
  221. handler = self.top().prop_handlers.top()
  222. if not handler or handler.char_data(data):
  223. # if char_data returned False,
  224. # we don't have to call add_property
  225. self.top().add_property(self._curr_prop, data)
  226. except AttributeError: pass
  227. # 2: call custom end_elem handler
  228. try:
  229. # if there is a custom handler installed for this property,
  230. # call its end_elem function: if this returns True, remove
  231. # the handler from the Stack
  232. handler = self.top().prop_handlers.top()
  233. if handler.end_elem(name):
  234. self.top().prop_handlers.pop()
  235. except AttributeError: pass
  236. self._curr_prop = None
  237. self._curr_prop_val = []
  238. def characters(self, data):
  239. if not data or data.isspace():
  240. return
  241. if self._curr_prop is None:
  242. raise XmlParsingError(_("character data can be present only " \
  243. "inside properties"))
  244. self._curr_prop_val.append(data)
  245. # end of class XmlWidgetBuilder
  246. class ProgressXmlWidgetBuilder(XmlWidgetBuilder):
  247. """\
  248. Adds support for a progress dialog to the widget builder parser
  249. """
  250. def __init__(self, *args, **kwds):
  251. self.input_file = kwds.get('input_file')
  252. if self.input_file:
  253. del kwds['input_file']
  254. self.size = len(self.input_file.readlines())
  255. self.input_file.seek(0)
  256. self.progress = wx.ProgressDialog(_("Loading..."), _("Please wait "
  257. "while loading the app"), 20)
  258. self.step = 4
  259. self.i = 1
  260. else:
  261. self.size = 0
  262. self.progress = None
  263. XmlWidgetBuilder.__init__(self, *args, **kwds)
  264. def endElement(self, name):
  265. if self.progress:
  266. if name == 'application': self.progress.Destroy()
  267. else:
  268. if self.locator:
  269. where = self.locator.getLineNumber()
  270. value = int(round(where*20.0/self.size))
  271. else:
  272. # we don't have any information, so we update the progress
  273. # bar ``randomly''
  274. value = (self.step*self.i) % 20
  275. self.i += 1
  276. self.progress.Update(value)
  277. XmlWidgetBuilder.endElement(self, name)
  278. def parse(self, *args):
  279. try: XmlWidgetBuilder.parse(self, *args)
  280. finally:
  281. if self.progress: self.progress.Destroy()
  282. def parse_string(self, *args):
  283. try: XmlWidgetBuilder.parse_string(self, *args)
  284. finally:
  285. if self.progress: self.progress.Destroy()
  286. # end of class ProgressXmlWidgetBuilder
  287. class ClipboardXmlWidgetBuilder(XmlWidgetBuilder):
  288. """\
  289. Parser used to cut&paste widgets. The differences with XmlWidgetBuilder
  290. are:
  291. - No <application> tag in the piece of xml to parse
  292. - Fake parent, sizer and sizeritem objects to push on the three stacks:
  293. they keep info about the destination of the hierarchy of widgets (i.e.
  294. the target of the 'paste' command)
  295. - The first widget built must be hidden and shown again at the end of
  296. the operation
  297. """
  298. def __init__(self, parent, sizer, pos, option, flag, border):
  299. XmlWidgetBuilder.__init__(self)
  300. self.parent_node = parent.node
  301. class XmlClipboardObject:
  302. def __init__(self, **kwds):
  303. self.__dict__.update(kwds)
  304. par = XmlClipboardObject(obj=parent, parent=parent) # fake window obj
  305. if sizer is not None:
  306. # fake sizer object
  307. szr = XmlClipboardObject(obj=sizer, parent=parent)
  308. sizeritem = Sizeritem()
  309. sizeritem.option = option
  310. sizeritem.flag = flag
  311. sizeritem.border = border
  312. sizeritem.pos = pos
  313. # fake sizer item
  314. si = XmlClipboardObject(obj=sizeritem, parent=parent)
  315. # push the fake objects on the stacks
  316. self._objects.push(par); self._windows.push(par)
  317. if sizer is not None:
  318. self._objects.push(szr); self._sizers.push(szr)
  319. self._objects.push(si); self._sizer_item.push(si)
  320. self.depth_level = 0
  321. self._appl_started = True # no application tag when parsing from the
  322. # clipboard
  323. def startElement(self, name, attrs):
  324. if name == 'object' and attrs.has_key('name'):
  325. # generate a unique name for the copy
  326. oldname = str(attrs['name'])
  327. newname = oldname
  328. i = 0
  329. while common.app_tree.has_name(newname, node=self.parent_node):
  330. if not i: newname = '%s_copy' % oldname
  331. else: newname = '%s_copy_%s' % (oldname, i)
  332. i += 1
  333. attrs = dict(attrs)
  334. attrs['name'] = newname
  335. XmlWidgetBuilder.startElement(self, name, attrs)
  336. if name == 'object':
  337. if not self.depth_level:
  338. common.app_tree.auto_expand = False
  339. try:
  340. self.top_obj = self.top().obj
  341. except AttributeError:
  342. print _('Exception! obj: %s') % self.top_obj
  343. traceback.print_exc()
  344. self.depth_level += 1
  345. def endElement(self, name):
  346. if name == 'object':
  347. obj = self.top()
  348. self.depth_level -= 1
  349. if not self.depth_level:
  350. common.app_tree.auto_expand = True
  351. try:
  352. # show the first object and update its layout
  353. common.app_tree.show_widget(self.top_obj.node)
  354. self.top_obj.show_properties()
  355. common.app_tree.select_item(self.top_obj.node)
  356. except AttributeError:
  357. print _('Exception! obj: %s') % self.top_obj
  358. traceback.print_exc()
  359. XmlWidgetBuilder.endElement(self, name)
  360. # end of class ClipboardXmlWidgetBuilder
  361. class XmlWidgetObject:
  362. """\
  363. A class to encapsulate a widget read from an xml file: its purpose is to
  364. store various widget attributes until the widget can be created
  365. """
  366. def __init__(self, attrs, parser):
  367. # prop_handlers is a stack of custom handler functions to set
  368. # properties of this object
  369. self.prop_handlers = Stack()
  370. self.parser = parser
  371. self.in_windows, self.in_sizers = False, False
  372. try:
  373. base = attrs.get('base', None)
  374. self.klass = attrs['class']
  375. except KeyError:
  376. raise XmlParsingError(_("'object' items must have a 'class' " \
  377. "attribute"))
  378. if base is not None:
  379. # if base is not None, the object is a widget (or sizer), and not a
  380. # sizeritem
  381. sizer = self.parser._sizers.top()
  382. parent = self.parser._windows.top()
  383. if parent is not None: parent = self.parent = parent.obj
  384. else: self.parent = None
  385. sizeritem = self.parser._sizer_item.top()
  386. if sizeritem is not None: sizeritem = sizeritem.obj
  387. if sizer is not None:
  388. # we must check if the sizer on the top of the stack is
  389. # really the one we are looking for: to check this
  390. if sizer.parent != parent:
  391. sizer = None
  392. else: sizer = sizer.obj
  393. if hasattr(sizeritem, 'pos'):
  394. pos = sizeritem.pos
  395. else: pos = None
  396. if parent and hasattr(parent, 'virtual_sizer') and \
  397. parent.virtual_sizer:
  398. sizer = parent.virtual_sizer
  399. sizer.node = parent.node
  400. sizeritem = Sizeritem()
  401. if pos is None:
  402. pos = sizer.get_itempos(attrs)
  403. # build the widget
  404. if pos is not None:
  405. pos = int(pos)
  406. self.obj = common.widgets_from_xml[base](attrs, parent, sizer,
  407. sizeritem, pos)
  408. try:
  409. #self.obj.klass = self.klass
  410. self.obj.set_klass(self.klass)
  411. self.obj.klass_prop.set_value(self.klass)
  412. except AttributeError: pass
  413. # push the object on the appropriate stack
  414. if isinstance(self.obj, edit_sizers.SizerBase):
  415. self.parser._sizers.push(self)
  416. self.in_sizers = True
  417. else:
  418. self.parser._windows.push(self)
  419. self.in_windows = True
  420. elif self.klass == 'sizeritem':
  421. self.obj = Sizeritem()
  422. self.parent = self.parser._windows.top().obj
  423. self.parser._sizer_item.push(self)
  424. elif self.klass == 'sizerslot':
  425. sizer = self.parser._sizers.top().obj
  426. assert sizer is not None, \
  427. _("malformed wxg file: slots can only be inside sizers!")
  428. sizer.add_slot()
  429. self.parser._sizer_item.push(self)
  430. # push the object on the _objects stack
  431. self.parser._objects.push(self)
  432. def pop(self):
  433. if self.in_windows: return self.parser._windows.pop()
  434. elif self.in_sizers: return self.parser._sizers.pop()
  435. else: return self.parser._sizer_item.pop()
  436. def add_property(self, name, val):
  437. """\
  438. adds a property to this widget. This method is not called if there
  439. was a custom handler for this property, and its char_data method
  440. returned False
  441. """
  442. if name == 'pos': # sanity check, this shouldn't happen...
  443. print 'add_property pos'
  444. return
  445. try:
  446. self.obj[name][1](val) # call the setter for this property
  447. try:
  448. prop = self.obj.properties[name]
  449. prop.set_value(val)
  450. prop.toggle_active(True)
  451. except AttributeError: pass
  452. except KeyError:
  453. # unknown property for this object
  454. # issue a warning and ignore the property
  455. import sys
  456. print >> sys.stderr, _("Warning: property '%s' not supported by " \
  457. "this object ('%s') ") % (name, self.obj)
  458. #end of class XmlWidgetObject
  459. class CodeWriter(XmlParser):
  460. """parser used to produce the source from a given xml file"""
  461. def __init__(self, writer, input, from_string=False, out_path=None,
  462. preview=False, class_names=None):
  463. # writer: object that actually writes the code
  464. XmlParser.__init__(self)
  465. self._toplevels = Stack() # toplevel objects, i.e. instances of a
  466. # custom class
  467. self.app_attrs = {} # attributes of the app (name, class, top_window)
  468. self.top_win = '' # class name of the top window of the app (if any)
  469. self.out_path = out_path # this allows to override the output path
  470. # specified in the xml file
  471. self.code_writer = writer
  472. self.preview = preview # if True, we are generating the code for the
  473. # preview
  474. # used in the CustomWidget preview code, to generate better previews
  475. # (see widgets/custom_widget/codegen.py)
  476. self.class_names = class_names
  477. if self.class_names is None: self.class_names = set()
  478. if from_string: self.parse_string(input)
  479. else:
  480. inputfile = None
  481. try:
  482. inputfile = open(input)
  483. self.parse(inputfile)
  484. finally:
  485. if inputfile: inputfile.close()
  486. def startElement(self, name, attrs_impl):
  487. attrs = {}
  488. try:
  489. encoding = self.app_attrs['encoding']
  490. unicode('a', encoding)
  491. except (KeyError, LookupError):
  492. if name == 'application':
  493. encoding = str(attrs_impl.get('encoding', 'ISO-8859-1'))
  494. else:
  495. encoding = 'ISO-8859-1'
  496. # turn all the attribute values from unicode to str objects
  497. for attr, val in attrs_impl.items():
  498. attrs[attr] = common._encode_from_xml(val, encoding)
  499. if name == 'application':
  500. # get the code generation options
  501. self._appl_started = True
  502. self.app_attrs = attrs
  503. try:
  504. attrs['option'] = bool(int(attrs['option']))
  505. use_multiple_files = attrs['option']
  506. except (KeyError, ValueError):
  507. use_multiple_files = attrs['option'] = False
  508. if self.out_path is None:
  509. try: self.out_path = attrs['path']
  510. except KeyError:
  511. raise XmlParsingError(_("'path' attribute missing: could "
  512. "not generate code"))
  513. else: attrs['path'] = self.out_path
  514. # ALB 2004-11-01: check if the values of
  515. # use_multiple_files and out_path agree
  516. if use_multiple_files:
  517. if not os.path.isdir(self.out_path):
  518. raise IOError(_("Output path must be an existing directory"
  519. " when generating multiple files"))
  520. else:
  521. if os.path.isdir(self.out_path):
  522. raise IOError(_("Output path can't be a directory when "
  523. "generating a single file"))
  524. # initialize the writer
  525. self.code_writer.initialize(attrs)
  526. return
  527. if not self._appl_started:
  528. raise XmlParsingError(_("the root of the tree must be <application>"))
  529. if name == 'object':
  530. # create the CodeObject which stores info about the current widget
  531. CodeObject(attrs, self, preview=self.preview)
  532. if attrs.has_key('name') and \
  533. attrs['name'] == self.app_attrs.get('top_window', ''):
  534. self.top_win = attrs['class']
  535. else:
  536. # handling of the various properties
  537. try:
  538. # look for a custom handler to push on the stack
  539. w = self.top()
  540. handler = self.code_writer.get_property_handler(name, w.base)
  541. if handler: w.prop_handlers.push(handler)
  542. # get the top custom handler and use it if there's one
  543. handler = w.prop_handlers.top()
  544. if handler: handler.start_elem(name, attrs)
  545. except AttributeError:
  546. print 'ATTRIBUTE ERROR!!'
  547. traceback.print_exc()
  548. self._curr_prop = name
  549. def endElement(self, name):
  550. if name == 'application':
  551. self._appl_started = False
  552. if self.app_attrs:
  553. self.code_writer.add_app(self.app_attrs, self.top_win)
  554. # call the finalization function of the code writer
  555. self.code_writer.finalize()
  556. return
  557. if name == 'object':
  558. obj = self.pop()
  559. if obj.klass in ('sizeritem', 'sizerslot'): return
  560. # at the end of the object, we have all the information to add it
  561. # to its toplevel parent, or to generate the code for the custom
  562. # class
  563. if obj.is_toplevel and not obj.in_sizers:
  564. self.code_writer.add_class(obj)
  565. topl = self._toplevels.top()
  566. if topl:
  567. self.code_writer.add_object(topl, obj)
  568. # if the object is not a sizeritem, check whether it
  569. # belongs to some sizer (in this case,
  570. # self._sizer_item.top() doesn't return None): if so,
  571. # write the code to add it to the sizer at the top of
  572. # the stack
  573. si = self._sizer_item.top()
  574. if si is not None and si.parent == obj.parent:
  575. szr = self._sizers.top()
  576. if not szr: return
  577. self.code_writer.add_sizeritem(topl, szr, obj,
  578. si.obj.option,
  579. si.obj.flag_str(),
  580. si.obj.border)
  581. else:
  582. # end of a property or error
  583. # 1: set _curr_prop value
  584. try:
  585. encoding = self.app_attrs['encoding']
  586. unicode('a', encoding)
  587. except (KeyError, LookupError):
  588. encoding = 'ISO-8859-1'
  589. data = common._encode_from_xml(u"".join(self._curr_prop_val),
  590. encoding)
  591. if data:
  592. handler = self.top().prop_handlers.top()
  593. if not handler or handler.char_data(data):
  594. # if char_data returned False,
  595. # we don't have to call add_property
  596. self.top().add_property(self._curr_prop, data)
  597. # 2: call custom end_elem handler
  598. try:
  599. # if there is a custom handler installed for this property,
  600. # call its end_elem function: if this returns True, remove
  601. # the handler from the stack
  602. obj = self.top()
  603. handler = obj.prop_handlers.top()
  604. if handler.end_elem(name, obj):
  605. obj.prop_handlers.pop()
  606. except AttributeError: pass
  607. self._curr_prop = None
  608. self._curr_prop_val = []
  609. def characters(self, data):
  610. if not data or data.isspace(): return
  611. if self._curr_prop is None:
  612. raise XmlParsingError(_("character data can only appear inside " \
  613. "properties"))
  614. self._curr_prop_val.append(data)
  615. # end of class CodeWriter
  616. class CodeObject:
  617. """\
  618. A class to store information needed to generate the code for a given object
  619. """
  620. def __init__(self, attrs, parser, preview=False):
  621. self.parser = parser
  622. self.in_windows = self.in_sizers = False
  623. self.is_toplevel = False # if True, the object is a toplevel one:
  624. # for window objects, this means that they are
  625. # instances of a custom class, for sizers, that
  626. # they are at the top of the hierarchy
  627. self.is_container = False # if True, the widget is a container
  628. # (frame, dialog, panel, ...)
  629. self.properties = {} # properties of the widget/sizer
  630. # prop_handlers is a stack of custom handler functions to set
  631. # properties of this object
  632. self.prop_handlers = Stack()
  633. self.preview = preview
  634. try:
  635. base = attrs.get('base', None)
  636. self.klass = attrs['class']
  637. except KeyError:
  638. raise XmlParsingError(_("'object' items must have a 'class' " \
  639. "attribute"))
  640. self.parser._objects.push(self)
  641. self.parent = self.parser._windows.top()
  642. # -------- added 2002-08-26 to detect container widgets --------------
  643. if self.parent is not None:
  644. self.parent.is_container = True
  645. # -------- end added 2002-08-26 --------------------------------------
  646. self.base = None
  647. if base is not None: # this is a ``real'' object, not a sizeritem
  648. self.name = attrs['name']
  649. self.base = common.class_names[base]
  650. can_be_toplevel = common.toplevels.has_key(base)
  651. if (self.parent is None or self.klass != self.base) and \
  652. can_be_toplevel:
  653. #self.base != 'CustomWidget':
  654. self.is_toplevel = True
  655. # ALB 2005-11-19: for panel objects, if the user sets a
  656. # custom class but (s)he doesn't want the code
  657. # to be generated...
  658. if int(attrs.get('no_custom_class', False)) and \
  659. not self.preview:
  660. self.is_toplevel = False
  661. #print 'OK:', str(self)
  662. #self.in_windows = True
  663. #self.parser._windows.push(self)
  664. else:
  665. self.parser._toplevels.push(self)
  666. #------------- 2003-05-07: preview --------------------------------
  667. elif self.preview and not can_be_toplevel and \
  668. self.base != 'CustomWidget':
  669. # if this is a custom class, but not a toplevel one,
  670. # for the preview we have to use the "real" class
  671. #
  672. # ALB 2007-08-04: CustomWidgets handle this in a special way
  673. # (see widgets/custom_widget/codegen.py)
  674. self.klass = self.base
  675. #------------------------------------------------------------------
  676. # temporary hack: to detect a sizer, check whether the name
  677. # of its class contains the string 'Sizer': TODO: find a
  678. # better way!!
  679. if base.find('Sizer') != -1:
  680. self.in_sizers = True
  681. if not self.parser._sizers.count(): self.is_toplevel = True
  682. else:
  683. # the sizer is a toplevel one if its parent has not a
  684. # sizer yet
  685. sz = self.parser._sizers.top()
  686. if sz.parent != self.parent: self.is_toplevel = True
  687. self.parser._sizers.push(self)
  688. else:
  689. self.parser._windows.push(self)
  690. self.in_windows = True
  691. else: # the object is a sizeritem
  692. self.obj = Sizeritem()
  693. self.obj.flag_s = '0'
  694. self.parser._sizer_item.push(self)
  695. def __str__(self):
  696. return "<xml_code_object: %s, %s, %s>" % (self.name, self.base,
  697. self.klass)
  698. def add_property(self, name, value):
  699. if hasattr(self, 'obj'): # self is a sizeritem
  700. try:
  701. if name == 'flag':
  702. ## flag = 0
  703. ## for f in value.split('|'):
  704. ## flag |= Sizeritem.flags[f.strip()]
  705. ## setattr(self.obj, name, flag)
  706. self.obj.flag_s = value.strip()
  707. else: setattr(self.obj, name, int(value))
  708. except: raise XmlParsingError(_("property '%s' not supported by " \
  709. "'%s' objects") % (name, self.klass))
  710. self.properties[name] = value
  711. def pop(self):
  712. if self.is_toplevel and not self.in_sizers:
  713. self.parser._toplevels.pop()
  714. if self.in_windows: return self.parser._windows.pop()
  715. elif self.in_sizers: return self.parser._sizers.pop()
  716. else: return self.parser._sizer_item.pop()
  717. # end of class CodeObject
  718. class Stack:
  719. def __init__(self):
  720. self._repr = []
  721. def push(self, elem):
  722. self._repr.append(elem)
  723. def pop(self):
  724. try: return self._repr.pop()
  725. except IndexError: return None
  726. def top(self):
  727. try: return self._repr[-1]
  728. except IndexError: return None
  729. def count(self):
  730. return len(self._repr)
  731. # end of class Stack
  732. class Sizeritem:
  733. if common.use_gui:
  734. flags = { 'wxALL': wx.ALL,
  735. 'wxEXPAND': wx.EXPAND, 'wxALIGN_RIGHT': wx.ALIGN_RIGHT,
  736. 'wxALIGN_BOTTOM': wx.ALIGN_BOTTOM,
  737. 'wxALIGN_CENTER_HORIZONTAL': wx.ALIGN_CENTER_HORIZONTAL,
  738. 'wxALIGN_CENTER_VERTICAL': wx.ALIGN_CENTER_VERTICAL,
  739. 'wxLEFT': wx.LEFT, 'wxRIGHT': wx.RIGHT,
  740. 'wxTOP': wx.TOP,
  741. 'wxBOTTOM': wx.BOTTOM,
  742. 'wxSHAPED': wx.SHAPED,
  743. 'wxADJUST_MINSIZE': wx.ADJUST_MINSIZE, }
  744. import misc
  745. if misc.check_wx_version(2, 5, 2):
  746. flags['wxFIXED_MINSIZE'] = wx.FIXED_MINSIZE
  747. else:
  748. flags['wxFIXED_MINSIZE'] = 0
  749. def __init__(self):
  750. self.option = self.border = 0
  751. self.flag = 0
  752. def __getitem__(self, name):
  753. if name != 'flag':
  754. return (None, lambda v: setattr(self, name, v))
  755. def get_flag(v):
  756. val = reduce(lambda a, b: a|b,
  757. [Sizeritem.flags[t] for t in v.split("|")])
  758. setattr(self, name, val)
  759. return (None, get_flag)
  760. ## lambda v: setattr(self, name,
  761. ## reduce(lambda a,b: a|b,
  762. ## [Sizeritem.flags[t] for t in
  763. ## v.split("|")])))
  764. def flag_str(self):
  765. # returns the flag attribute as a string of tokens separated
  766. # by a '|' (used during the code generation)
  767. if hasattr(self, 'flag_s'): return self.flag_s
  768. else:
  769. try:
  770. tmp = {}
  771. for k in self.flags:
  772. if self.flags[k] & self.flag:
  773. tmp[k] = 1
  774. # patch to make wxALL work
  775. remove_wxall = 4
  776. for k in ('wxLEFT', 'wxRIGHT', 'wxTOP', 'wxBOTTOM'):
  777. if k in tmp: remove_wxall -= 1
  778. if remove_wxall:
  779. try: del tmp['wxALL']
  780. except KeyError: pass
  781. else:
  782. for k in ('wxLEFT', 'wxRIGHT', 'wxTOP', 'wxBOTTOM'):
  783. try: del tmp[k]
  784. except KeyError: pass
  785. tmp['wxALL'] = 1
  786. tmp = '|'.join(tmp.keys())
  787. except:
  788. print 'EXCEPTION: self.flags = %s, self.flag = %s' % \
  789. (self.flags, repr(self.flag))
  790. raise
  791. if tmp: return tmp
  792. else: return '0'
  793. # end of class Sizeritem