ec-conf3 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  1. #!/usr/bin/env python3
  2. import sys
  3. import os
  4. import errno
  5. import getopt
  6. import stat
  7. import re
  8. import operator
  9. from xml.etree import ElementTree, ElementInclude
  10. import xml.sax
  11. def info(message, level=1):
  12. """ @brief Print info to stdout
  13. @param message string to print
  14. """
  15. if VERBOSE >= level:
  16. sys.stderr.write("*II* %s\n" % message)
  17. def warning(message):
  18. """ @brief Print warning to stdout
  19. @param message string to print
  20. """
  21. if ERROR_ON_WARNING:
  22. error(message)
  23. if WARNING:
  24. sys.stderr.write("*WW* %s\n" % message)
  25. def error(message):
  26. """ @brief Print error to stdout
  27. @param message string to print
  28. """
  29. print("*EE* %s " % message)
  30. sys.exit(1)
  31. def usage(myname):
  32. print("\nUsage: %s OPTIONS <XML_FILE>\n" % myname)
  33. print("Read configuration from an XML file and create config files.")
  34. print("""
  35. %s reads a data base of configuration parameters from an XML file.
  36. Subsequently, a number of template files are processed in order to create
  37. configuration files (targets). See ec-conf User Guide for more information.""" % myname)
  38. print("""
  39. Options: -h|--help Print this help screen.
  40. -p|--platform Set the active platform.
  41. -l|--list-platforms Lists all platforms, which are defined in the XML file, on stdout.
  42. -d|--prefix=<PATH> Creates the directory PATH (if non-existing) and writes all target
  43. files within that directory. Also sets a configuration parameter
  44. named PLT:ACTIVE:PREFIXDIR for use in the template files.
  45. -o|--overwrite-parameter <NAME>=<VALUE>
  46. Allows to set new values to configuration parameters from the
  47. command line, overwriting the values from the XML file.
  48. <NAME> must be given in the form
  49. 'COMPONENT_TYPE:COMPONENT_NAME:PARAMETER_NAME' corresponding to
  50. the place-holder syntax of ec-conf. See ec-conf user guide for details.
  51. Multiple parameters can be overwritten by repeating this option.
  52. -x|--write-xml Writes the content of <XML_FILE> in XML format to stdout.
  53. This can be used to normalise the XML file and for test purposes.
  54. -g|--gui Starts the graphical user interface. Turns off -x and -l.
  55. -v|--verbose Produces verbose output (stderr). To increase verbosity,
  56. use more than once.
  57. -w|--no-warning Turns off warnings (however, errors are displayed).
  58. -e|--error-on-warning Turns warnings into errors (i.e. ec-conf will stop).
  59. """)
  60. sys.exit(2)
  61. class TextNode(object):
  62. """Represents an XML node that contains only text, i.e. is not named and has no child nodes"""
  63. def __init__(self):
  64. self.text = ""
  65. def add_text(self, content):
  66. self.text += content
  67. class NamedNode(object):
  68. """Represents an XML node with a certain type according to the document
  69. definition. Base class for Configuration, Translation, Platform, Model,
  70. and Parameter"""
  71. def __init__(self, attr=None):
  72. self.name = None
  73. self.description = None
  74. if attr:
  75. if 'name' in list(attr.keys()):
  76. self.name = attr['name']
  77. info('Create named node: ' + self.name +
  78. ' (' + self.__class__.__name__ + ')', level=2)
  79. def set(self, key, value):
  80. self.__dict__.__setitem__(key, value)
  81. def xml(self, level=0):
  82. """Provides XML representation of the NamedNode as a string"""
  83. tabwidth = 4
  84. xml_string = ' ' * level * tabwidth
  85. xml_string += '<' + self.__class__.__name__
  86. if 'name' in self.__dict__ and self.name:
  87. xml_string += ' name="' + self.name + '"'
  88. xml_string += '>\n'
  89. for element in 'description', 'template', 'target', 'properties', 'type', 'value':
  90. if element in self.__dict__:
  91. xml_string += ' ' * (level + 1) * tabwidth
  92. xml_string += '<' + element.capitalize() + '>'
  93. if self.__dict__[element]:
  94. xml_string += self.__dict__[element]
  95. xml_string += '</' + element.capitalize() + '>\n'
  96. if level <= 1:
  97. xml_string += '\n'
  98. for element in 'translation', 'platform', 'model', 'parameter':
  99. if element in self.__dict__:
  100. for child_element in self.__dict__[element]:
  101. xml_string += child_element.xml(level + 1)
  102. xml_string += ' ' * level * tabwidth
  103. xml_string += '</' + self.__class__.__name__ + '>\n'
  104. if level > 0 and level <= 2:
  105. xml_string += '\n'
  106. return xml_string
  107. def plt(self, name=None):
  108. """Provides short-hand access to the Platform member with name 'name'."""
  109. if 'platform' in self.__dict__:
  110. if name:
  111. for plt in self.platform:
  112. if plt.name == name:
  113. return plt
  114. else:
  115. return self.platform
  116. return None
  117. def mod(self, name=None):
  118. """Provides short-hand access to the Model member with name 'name'."""
  119. if 'model' in self.__dict__:
  120. if name:
  121. for mod in self.model:
  122. if mod.name == name:
  123. return mod
  124. else:
  125. return self.model
  126. return None
  127. def par(self, name=None):
  128. """Provides short-hand access to the Parameter member with name 'name'."""
  129. if 'parameter' in self.__dict__:
  130. if name:
  131. for par in self.parameter:
  132. if par.name == name:
  133. return par
  134. else:
  135. return self.parameter
  136. return None
  137. class Translation(NamedNode):
  138. """Represents a Translation element of the XML document type"""
  139. def __init__(self, attr):
  140. super(Translation, self).__init__(attr)
  141. self.template = None
  142. self.target = None
  143. self.properties = ''
  144. self.is_active = 1
  145. def set_target(self, target):
  146. self.target = target
  147. class Platform(NamedNode):
  148. """Represents a Platform element of the XML document type"""
  149. def __init__(self, attr):
  150. super(Platform, self).__init__(attr)
  151. self.parameter = []
  152. self.translation = []
  153. class Model(NamedNode):
  154. """Represents a Parameter element of the XML document type"""
  155. def __init__(self, attr):
  156. super(Model, self).__init__(attr)
  157. self.parameter = []
  158. class Parameter(NamedNode):
  159. """Represents a Parameter element of the XML document type"""
  160. def __init__(self, attr):
  161. super(Parameter, self).__init__(attr)
  162. self.type = None
  163. self.value = None
  164. class Configuration(NamedNode, xml.sax.handler.ContentHandler):
  165. """Represents a Parameter element (which is the root element) of the XML document type"""
  166. def __init__(self):
  167. self.translation = []
  168. self.platform = []
  169. self.model = []
  170. self.xml_file = None
  171. self.active_platform = None
  172. self.__stack = []
  173. self.__types = {'Translation': Translation,
  174. 'Platform': Platform,
  175. 'Model': Model,
  176. 'Parameter': Parameter}
  177. def xml(self, level=0):
  178. return '<?xml version="1.0"?>\n\n' \
  179. + super(Configuration, self).xml(level)
  180. def startElement(self, tag, attributes):
  181. info("Processing XML element '" + str(tag) + "'", level=3)
  182. if self.__stack:
  183. if tag in self.__types:
  184. info("Adding NamedNode for element '" + str(tag) + "'", level=3)
  185. self.__stack.append(self.__types[tag](attributes))
  186. else:
  187. info("Adding TextNode for element '" + str(tag) + "'", level=3)
  188. self.__stack.append(TextNode())
  189. else:
  190. self.__stack.append(self)
  191. def characters(self, content):
  192. if isinstance(self.__stack[-1], TextNode):
  193. self.__stack[-1].add_text(content)
  194. def endElement(self, tag):
  195. element = self.__stack.pop()
  196. if self.__stack:
  197. if isinstance(element, TextNode):
  198. self.__stack[-1].__dict__[tag.lower()] = element.text
  199. else:
  200. self.__stack[-1].__dict__[tag.lower()].append(element)
  201. def parse(self, file):
  202. info("Parsing XML file '%s'" % file)
  203. # A short helper function for xml.etree.ElementInclude.include
  204. # that parses include files and catches parse errors
  205. def include_loader(href, parse, encoding=None):
  206. if parse != "xml":
  207. error("Only XML includes allowed in xi:include! (see file '%s')" % href)
  208. try:
  209. with open(href) as file:
  210. data = ElementTree.parse(file).getroot()
  211. except IOError as e:
  212. error("Can't open include file '%s' for parsing: %s" % (href, e))
  213. except ElementTree.ParseError as e:
  214. error("XML parse error in include file '%s': %s" % (href, e))
  215. return data
  216. # First parsing stage with xml.etree for include processing
  217. try:
  218. tree = ElementTree.parse(file)
  219. except IOError:
  220. error("Can't open file '%s' for parsing" % file)
  221. except ElementTree.ParseError as e:
  222. error("XML parse error in file '%s': %s" % (file, e))
  223. # Process XML include files
  224. tree_root = tree.getroot()
  225. ElementInclude.include(tree_root, loader=include_loader)
  226. # Second parsing stage with xml.sax to fill data structures
  227. # Since errors are catched in the first parsing stage, we assume
  228. # everything is fine here.
  229. xml.sax.parseString(ElementTree.tostring(tree_root, encoding="UTF-8"),self)
  230. info("Finished parsing '%s': Translation: %d Platform: %d Model: %d" % (
  231. file, len(self.translation), len(self.platform), len(self.model)))
  232. self.xml_file = file
  233. def translate(self, translation):
  234. rpm_ops = {
  235. 'ADD': operator.add,
  236. 'SUB': operator.sub,
  237. 'MUL': operator.mul,
  238. 'DIV': operator.truediv,
  239. 'POW': operator.pow,
  240. 'MOD': operator.mod
  241. }
  242. subst_re = re.compile(r"\[\[\[(?P<var>[a-zA-Z0-9_:,]+)\]\]\]")
  243. var_re = re.compile(r"^(\w{3}):([a-zA-Z0-9_]+):([a-zA-Z0-9_]+)$")
  244. def parse_var(string):
  245. # Shortcut for the 'prefixdir' parameter in template file
  246. if string.lower() == 'plt:active:prefixdir':
  247. return targetPrefixDir if targetPrefixDir else '[[[' + string + ']]]'
  248. while True:
  249. match = var_re.search(string)
  250. if not match:
  251. break
  252. (category, component, parameter) = match.groups()
  253. if category.lower() == 'plt' and component.lower() == 'active':
  254. component = self.active_platform
  255. try:
  256. string = getattr(self, category.lower())(
  257. component).par(parameter).value
  258. except BaseException:
  259. warning(
  260. "Unable to process '%s' (Line %d in '%s')" %
  261. (string, line_number, template))
  262. return '[[[' + string + ']]]'
  263. return string
  264. def parse_rpn(string):
  265. stack = []
  266. for token in string.split(','):
  267. token = parse_var(token)
  268. if not token:
  269. info(
  270. "Substitute expression with empty string (Line %d in '%s')" %
  271. (line_number, template))
  272. try:
  273. result = int(token)
  274. except ValueError:
  275. try:
  276. result = float(token)
  277. except ValueError:
  278. if token in list(rpm_ops.keys()):
  279. try:
  280. result = rpm_ops[token](
  281. stack.pop(-2), stack.pop())
  282. except IndexError:
  283. warning(
  284. "Too few arguments to execute '%s' (Line %d in '%s')" %
  285. (token, line_number, template))
  286. return "[[[" + string + "]]]"
  287. except BaseException:
  288. warning(
  289. "Unable to execute '%s' (Line %d in '%s')" %
  290. (token, line_number, template))
  291. return "[[[" + string + "]]]"
  292. else:
  293. result = token
  294. stack.append(result)
  295. if len(stack) > 1:
  296. warning(
  297. "Too many operands in '%s' (Line %d in '%s')" %
  298. (string, line_number, template))
  299. return "[[[" + string + "]]]"
  300. return result
  301. template = translation.template
  302. target = translation.target.strip()
  303. info("Translate: '%s' --> '%s'" % (template, target))
  304. try:
  305. input = open(template, 'r')
  306. except IOError:
  307. error("Can't open template file '%s' for reading" % template)
  308. if target:
  309. if targetPrefixDir:
  310. target = os.path.join(targetPrefixDir, target)
  311. path = os.path.dirname(target)
  312. try:
  313. os.makedirs(path)
  314. info("Created target prefix directory '%s'" % path)
  315. except OSError as exc:
  316. if exc.errno == errno.EEXIST and os.path.isdir(path):
  317. info(
  318. "Target prefix directory '%s' exists already" %
  319. (path))
  320. else:
  321. error(
  322. "Could not create target prefix directory '%s'" %
  323. (path))
  324. try:
  325. output = open(target, 'w')
  326. except IOError:
  327. error("Can't open target file '%s' for writing" % target)
  328. if 'executable' in translation.properties.split(','):
  329. os.chmod(target, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
  330. stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH |
  331. stat.S_IWUSR)
  332. else:
  333. output = sys.stdout
  334. line_number = 0
  335. for line in input:
  336. line_number += 1
  337. ptr = 0
  338. buf = ''
  339. for match in subst_re.finditer(line):
  340. buf += line[ptr:match.start()]
  341. var = match.groupdict()['var']
  342. try:
  343. buf += str(parse_rpn(var))
  344. except UnicodeEncodeError as e:
  345. error('Invalid character in XML file!\n'
  346. '*EE* Look up the \\uXXXX character code from the following message:\n'
  347. '*EE* %s' % str(e))
  348. ptr = match.end()
  349. buf += line[ptr:]
  350. output.write(buf)
  351. input.close()
  352. if target:
  353. output.close()
  354. def translate_all(self):
  355. for t in self.translation:
  356. self.translate(t)
  357. if self.active_platform:
  358. for t in self.plt(self.active_platform).translation:
  359. self.translate(t)
  360. def start_gui(cfg):
  361. import tkinter
  362. import tkinter.filedialog
  363. import tkinter.messagebox
  364. class VerticalScrolledFrame(tkinter.Frame):
  365. """A pure Tkinter scrollable frame that actually works!
  366. * Use the 'interior' attribute to place widgets inside the scrollable frame
  367. * Construct and pack/place/grid normally
  368. * This frame only allows vertical scrolling
  369. http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame
  370. """
  371. def __init__(self, parent, *args, **kw):
  372. tkinter.Frame.__init__(self, parent, *args, **kw)
  373. # create a canvas object and a vertical scrollbar for scrolling it
  374. vscrollbar = tkinter.Scrollbar(self, orient=tkinter.VERTICAL)
  375. vscrollbar.pack(
  376. fill=tkinter.Y,
  377. side=tkinter.RIGHT,
  378. expand=tkinter.FALSE)
  379. canvas = tkinter.Canvas(self, bd=0, highlightthickness=0,
  380. yscrollcommand=vscrollbar.set)
  381. canvas.pack(
  382. side=tkinter.LEFT,
  383. fill=tkinter.BOTH,
  384. expand=tkinter.TRUE)
  385. vscrollbar.config(command=canvas.yview)
  386. # reset the view
  387. canvas.xview_moveto(0)
  388. canvas.yview_moveto(0)
  389. # create a frame inside the canvas which will be scrolled with it
  390. self.interior = interior = tkinter.Frame(canvas)
  391. interior_id = canvas.create_window(0, 0, window=interior,
  392. anchor=tkinter.NW)
  393. # track changes to the canvas and frame width and sync them,
  394. # also updating the scrollbar
  395. def _configure_interior(event):
  396. # update the scrollbars to match the size of the inner frame
  397. size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
  398. canvas.config(scrollregion="0 0 %s %s" % size)
  399. if interior.winfo_reqwidth() != canvas.winfo_width():
  400. # update the canvas's width to fit the inner frame
  401. canvas.config(width=interior.winfo_reqwidth())
  402. interior.bind('<Configure>', _configure_interior)
  403. def _configure_canvas(event):
  404. if interior.winfo_reqwidth() != canvas.winfo_width():
  405. # update the inner frame's width to fill the canvas
  406. canvas.itemconfigure(
  407. interior_id, width=canvas.winfo_width())
  408. canvas.bind('<Configure>', _configure_canvas)
  409. return
  410. class GUI(tkinter.Tk):
  411. def __init__(self, cfg):
  412. tkinter.Tk.__init__(self)
  413. self.columnconfigure(1, weight=1)
  414. self.rowconfigure(0, pad=10)
  415. self.rowconfigure(2, weight=1)
  416. self.__cfg = cfg
  417. self.__var_list = []
  418. self.__var_dict = {}
  419. for t in self.__cfg.translation + \
  420. [t for p in self.__cfg.platform for t in p.translation]:
  421. v = tkinter.IntVar()
  422. v.set(1)
  423. v.trace(
  424. 'w', lambda n, i, m, t=t: t.set(
  425. 'is_active', int(
  426. self.globalgetvar(n))))
  427. self.__add_var(v, t)
  428. self.__active_component = tkinter.StringVar()
  429. self.__status = tkinter.StringVar()
  430. self.__component_frame = None
  431. self.__parameter_frame = None
  432. self.__translation_frame = None
  433. self.__init_top_panel()
  434. self.__init_status_panel()
  435. self.__init_main_panel()
  436. self.grid_propagate(flag=0)
  437. self.__set_status(
  438. "Welcome to EC-CONF's graphical user interface!",
  439. time=3000)
  440. def __add_var(self, var, obj=None):
  441. self.__var_list.append(var)
  442. if obj:
  443. self.__var_dict[obj] = len(self.__var_list) - 1
  444. def __set_status(self, message, time=8000):
  445. try:
  446. self.after_cancel(self.__status_after_id)
  447. except BaseException:
  448. pass
  449. self.__default_status_message = 'Basic usage: SELECT XML database' \
  450. ' and template/target files, CONFIGURE parameters, and CREATE' \
  451. ' the configuration files.'
  452. self.__status.set(message)
  453. self.__status_after_id = self.after(
  454. time, self.__status.set, self.__default_status_message)
  455. def __init_top_panel(self):
  456. w = tkinter.Label(text='The active platform is:')
  457. w.grid(row=0, column=0)
  458. if not self.__cfg.active_platform:
  459. self.__cfg.active_platform = self.__cfg.platform[0].name
  460. v = tkinter.StringVar()
  461. v.set(self.__cfg.active_platform)
  462. v.trace(
  463. 'w',
  464. lambda n, i, m: self.__set_status(
  465. "Active platform changed to '"
  466. + self.__cfg.active_platform
  467. + "'"))
  468. v.trace(
  469. 'w',
  470. lambda n, i, m: self.__fill_parameter_frame(
  471. self.__parameter_frame))
  472. v.trace(
  473. 'w',
  474. lambda n, i, m: self.__fill_component_frame(
  475. self.__component_frame))
  476. v.trace(
  477. 'w',
  478. lambda n, i, m: self.__fill_translation_frame(
  479. self.__translation_frame))
  480. v.trace('w', lambda n, i, m: self.__cfg.set(
  481. 'active_platform', self.globalgetvar(n)))
  482. self.__add_var(v)
  483. w = tkinter.OptionMenu(None, v, *
  484. [p.name for p in self.__cfg.platform])
  485. w.grid(row=0, column=1, sticky='W', padx=8)
  486. w = tkinter.Button(text='Select', width=10, height=2, bg='tan')
  487. w['command'] = self.__do_select
  488. w.grid(row=0, column=2, padx=8)
  489. w = tkinter.Button(text='Configure', width=10, height=2, bg='tan')
  490. w['command'] = self.__do_configure
  491. w.grid(row=0, column=3, padx=8)
  492. w = tkinter.Button(
  493. text='Create!',
  494. width=10,
  495. height=2,
  496. bg='darkgrey',
  497. fg='white')
  498. w['command'] = self.__do_create
  499. w.grid(row=0, column=4, padx=8)
  500. def __init_status_panel(self):
  501. w = tkinter.Label(
  502. textvariable=self.__status,
  503. height=2,
  504. bg='orange')
  505. w.grid(row=1, column=0, columnspan=5, sticky='EW', pady=5)
  506. def __init_main_panel(self):
  507. self.__init_select_panel()
  508. self.__init_configure_panel()
  509. self.__select_panel.grid_remove()
  510. self.__configure_panel.grid_remove()
  511. self.__active_main_panel = self.__select_panel
  512. self.__active_main_panel.grid()
  513. def __init_select_panel(self):
  514. self.__select_panel = tkinter.Frame()
  515. self.__select_panel.grid(
  516. row=2, column=0, columnspan=5, sticky='NEWS')
  517. self.__select_panel.columnconfigure(0, weight=1)
  518. self.__select_panel.rowconfigure(2, weight=1)
  519. f = tkinter.LabelFrame(
  520. self.__select_panel,
  521. text='XML database file')
  522. f.grid(sticky='NEWS')
  523. f.columnconfigure(1, weight=1)
  524. w = tkinter.Label(f, text='The current file is: ')
  525. w.grid(row=0, column=0, sticky='W')
  526. v = tkinter.StringVar()
  527. v.set(self.__cfg.xml_file)
  528. v.trace(
  529. 'w', lambda n, i, m: self.__cfg.set(
  530. 'xml_file', self.globalgetvar(n)))
  531. self.__add_var(v, 'xml_file')
  532. w = tkinter.Label(f, textvariable=v, bg='darkgrey', fg='white')
  533. w.grid(row=0, column=1, sticky='W')
  534. w = tkinter.Button(f, text='Save as', width=8)
  535. w['command'] = lambda: self.__save_as_xml_file()
  536. w.grid(row=0, column=2, sticky='E', padx=4, pady=5)
  537. w = tkinter.Button(f, text='Save', width=8)
  538. w['command'] = lambda: self.__save_xml_file()
  539. w.grid(row=0, column=3, sticky='E', padx=4, pady=5)
  540. tkinter.Frame(self.__select_panel).grid(pady=5)
  541. self.__translation_frame = tkinter.LabelFrame(
  542. self.__select_panel, text='Templates and Targets')
  543. self.__translation_frame.grid(sticky='NEWS')
  544. self.__translation_frame.columnconfigure(1, weight=1)
  545. self.__translation_frame.columnconfigure(3, weight=9)
  546. self.__fill_translation_frame(self.__translation_frame)
  547. def __init_configure_panel(self):
  548. self.__configure_panel = tkinter.Frame()
  549. self.__configure_panel.grid(
  550. row=2, column=0, columnspan=5, sticky='NEWS')
  551. self.__configure_panel.rowconfigure(0, weight=1)
  552. self.__configure_panel.columnconfigure(2, weight=1)
  553. # Component frame
  554. self.__component_frame = tkinter.LabelFrame(
  555. self.__configure_panel, text='Configurable components')
  556. self.__component_frame.grid(sticky='NEWS')
  557. self.__fill_component_frame(self.__component_frame)
  558. # Spacer frame
  559. f = tkinter.Frame(self.__configure_panel)
  560. f.grid(row=0, column=1, padx=2)
  561. # Parameter frame
  562. f = tkinter.LabelFrame(
  563. self.__configure_panel,
  564. text='Configuration parameters')
  565. f.grid(row=0, column=2, sticky='NEWS')
  566. f = VerticalScrolledFrame(f)
  567. f.pack(fill=tkinter.BOTH, expand=tkinter.TRUE)
  568. self.__parameter_frame = f.interior
  569. self.__parameter_frame.columnconfigure(2, weight=1)
  570. self.__fill_parameter_frame(self.__parameter_frame)
  571. def __fill_translation_frame(self, frame):
  572. for w in list(frame.children.values()):
  573. w.destroy()
  574. v = tkinter.IntVar()
  575. v.set(1)
  576. v.trace('w', lambda n,
  577. i,
  578. m: [self.__var_list[self.__var_dict[t]].set(self.globalgetvar(n))
  579. for t in self.__cfg.translation +
  580. self.__cfg.plt(self.__cfg.active_platform).translation])
  581. self.__add_var(v)
  582. w = tkinter.Checkbutton(
  583. frame, text='Activate/deactivate all', variable=v)
  584. w.grid(row=0, column=0, sticky='W', pady=5)
  585. r = 1
  586. for t in self.__cfg.translation + \
  587. self.__cfg.plt(self.__cfg.active_platform).translation:
  588. w = tkinter.Checkbutton(
  589. frame, text=t.description, variable=self.__var_list[self.__var_dict[t]])
  590. w.grid(row=r, column=0, sticky='W', pady=5)
  591. w = tkinter.Label(
  592. frame,
  593. text=t.template,
  594. bg='darkgrey',
  595. fg='white')
  596. w.grid(row=r, column=1, sticky='E')
  597. w = tkinter.Label(frame, text=' --> ')
  598. w.grid(row=r, column=2)
  599. v = tkinter.StringVar()
  600. v.set(t.target)
  601. v.trace(
  602. 'w', lambda n, i, m, t=t: t.set_target(
  603. self.globalgetvar(n)))
  604. self.__add_var(v)
  605. w = tkinter.Entry(frame, textvariable=v)
  606. w.grid(row=r, column=3, sticky='EW')
  607. r += 1
  608. def __fill_component_frame(self, frame):
  609. for w in list(frame.children.values()):
  610. w.destroy()
  611. if self.__cfg.active_platform:
  612. self.__active_component.set(self.__cfg.active_platform)
  613. w = tkinter.Label(frame, text='Active platform')
  614. w.pack(anchor='w')
  615. c = self.__cfg.plt(self.__cfg.active_platform)
  616. w = tkinter.Radiobutton(
  617. frame,
  618. text=c.name,
  619. variable=self.__active_component,
  620. value=c.name)
  621. w['command'] = lambda: self.__fill_parameter_frame(
  622. self.__parameter_frame)
  623. w.pack(anchor='w', pady=5)
  624. if self.__cfg.model:
  625. if not self.__active_component.get():
  626. self.set(self.__cfg.model[0].name)
  627. w = tkinter.Label(frame, text='Configurable models')
  628. w.pack(anchor='w')
  629. for c in self.__cfg.model:
  630. w = tkinter.Radiobutton(
  631. frame, text=c.name, variable=self.__active_component, value=c.name)
  632. w['command'] = lambda: self.__fill_parameter_frame(
  633. self.__parameter_frame)
  634. w.pack(anchor='w', pady=5)
  635. def __fill_parameter_frame(self, frame):
  636. for w in list(frame.children.values()):
  637. w.destroy()
  638. for (name, component) in [(c.name, c)
  639. for c in self.__cfg.platform + self.__cfg.model]:
  640. if self.__active_component.get() == name:
  641. break
  642. r = 0
  643. for p in component.par():
  644. w = tkinter.Label(
  645. frame, text=p.description, anchor="w", width=35)
  646. w.grid(row=r, column=0, sticky='W')
  647. w = tkinter.Label(frame, text='[' + str(p.name) + ']')
  648. w.grid(row=r, column=1, sticky='W', padx=20)
  649. v = tkinter.StringVar()
  650. v.set(p.value)
  651. v.trace(
  652. 'w', lambda n, i, m, p=p: p.set(
  653. 'value', self.globalgetvar(n)))
  654. self.__add_var(v)
  655. w = tkinter.Entry(frame, textvariable=v)
  656. w.grid(row=r, column=2, sticky='EW')
  657. r += 1
  658. self.__set_status(
  659. 'Configure parameters for component \'' +
  660. self.__active_component.get() +
  661. '\'')
  662. def __do_select(self):
  663. self.__active_main_panel.grid_remove()
  664. self.__active_main_panel = self.__select_panel
  665. self.__active_main_panel.grid()
  666. self.__set_status(
  667. 'Select the XML data base file and active translations in the panel below.')
  668. def __do_configure(self):
  669. self.__active_main_panel.grid_remove()
  670. self.__active_main_panel = self.__configure_panel
  671. self.__active_main_panel.grid()
  672. self.__set_status('Configure the configuration parameters for the'
  673. ' available components in the panel below.')
  674. def __do_create(self):
  675. fw = []
  676. for t in self.__cfg.translation + \
  677. self.__cfg.plt(self.__cfg.active_platform).translation:
  678. if t.is_active:
  679. self.__cfg.translate(t)
  680. fw.append(t.target)
  681. if fw:
  682. msg = 'Active target files written: ' + fw.pop()
  683. while fw:
  684. msg += ', ' + fw.pop()
  685. else:
  686. msg = 'No targets where written'
  687. self.__set_status(msg)
  688. def __save_as_xml_file(self):
  689. f = tkinter.filedialog.asksaveasfilename(
  690. title='Select a file name for saving:', filetypes=[
  691. ('XML files', '*.xml'), ('All files', '*')])
  692. if f:
  693. try:
  694. self.__var_list[self.__var_dict['xml_file']].set(
  695. os.path.relpath(f))
  696. except AttributeError:
  697. self.__var_list[self.__var_dict['xml_file']].set(
  698. os.path.realpath(f))
  699. self.__save_xml_file()
  700. else:
  701. self.__set_status("Current XML file NOT saved")
  702. def __save_xml_file(self):
  703. if os.path.isfile(self.__cfg.xml_file):
  704. msg = "The file '" + self.__cfg.xml_file + \
  705. "' exists. Do you want to replace it?"
  706. if not tkinter.messagebox.askyesno('Save XML file', msg):
  707. return
  708. try:
  709. f = open(self.__cfg.xml_file, 'w')
  710. except IOError:
  711. msg = "The file '" + self.__cfg.xml_file + "' could not be opened for writing"
  712. tkinter.messagebox.showerror('Save XML file', msg)
  713. self.__set_status("XML database NOT saved")
  714. return
  715. f.write(self.__cfg.xml())
  716. f.close()
  717. self.__set_status(
  718. "XML database saved to file '" +
  719. self.__cfg.xml_file +
  720. "'")
  721. root = GUI(cfg)
  722. root.title('ec-conf GUI')
  723. min_window_width = min(900, int(0.9 * root.winfo_screenwidth()))
  724. min_window_height = min(800, int(0.9 * root.winfo_screenheight()))
  725. root.minsize(min_window_width, min_window_height)
  726. root.resizable()
  727. root.mainloop()
  728. if __name__ == "__main__":
  729. # Try to get command line options and arguments
  730. try:
  731. opts, args = getopt.getopt(sys.argv[1:],
  732. "hp:gd:o:xlvwe",
  733. ["help", "platform=", "gui", "prefix=",
  734. "overwrite-parameter=",
  735. "write-xml", "list-platforms",
  736. "verbose", "no-warning", "error-on-warning"])
  737. except getopt.GetoptError:
  738. usage(os.path.split(sys.argv[0])[-1])
  739. # Default values, to be overwritten by command line options
  740. WARNING = True
  741. ERROR_ON_WARNING = False
  742. VERBOSE = 0
  743. writeXML = False
  744. listPlatforms = False
  745. wantGUI = False
  746. platform = None
  747. targetPrefixDir = None
  748. overwriteParameters = []
  749. # Parse command line options
  750. for opt, arg in opts:
  751. if opt in ('-h', '--help'):
  752. usage(os.path.split(sys.argv[0])[-1])
  753. elif opt in ('-p', '--platform'):
  754. platform = arg
  755. elif opt in ('-g', '--gui'):
  756. wantGUI = True
  757. elif opt in ('-d', '--prefix'):
  758. targetPrefixDir = arg
  759. elif opt in ('-o', '--overwrite-parameter'):
  760. overwriteParameters.append(arg)
  761. elif opt in ('-x', '--write-xml'):
  762. writeXML = True
  763. elif opt in ('-l', '--list-platforms'):
  764. listPlatforms = True
  765. elif opt in ('-v', '--verbose'):
  766. VERBOSE += 1
  767. elif opt in ('-w', '--no-warning'):
  768. WARNING = False
  769. elif opt in ('-e', '--error-on-warning'):
  770. ERROR_ON_WARNING = True
  771. # The XML file is all that should be left on the command line
  772. if len(args) != 1:
  773. usage(os.path.split(sys.argv[0])[-1])
  774. # Create the Configuration object and fill the data structures by parsing
  775. # the XML file
  776. cfg = Configuration()
  777. cfg.parse(args[0])
  778. info(args[0])
  779. info(cfg.parse(args[0]))
  780. info(platform)
  781. # If a platform was given on the command line, try to set it
  782. if platform:
  783. info(cfg.plt(platform))
  784. if cfg.plt(platform):
  785. cfg.active_platform = platform
  786. else:
  787. error(
  788. "Platform '%s' not defined in the configuration file '%s'" %
  789. (platform, args[0]))
  790. elif not (wantGUI or listPlatforms):
  791. warning("No active platform given")
  792. # Overwrite parameters given explicitely on the command line
  793. for arg in overwriteParameters:
  794. # split the name=value pair but make sure additional '=' are preserved
  795. name = arg.split('=')[0]
  796. value = '='.join(arg.split('=')[1:])
  797. # Maybe we want to allow overwriting with empty values?!
  798. # if so, remove the following if block
  799. if not value:
  800. warning("Parameter '%s' in --overwrite-parameter has no value" % name)
  801. continue
  802. try:
  803. (category, component, parameter) = name.split(':')
  804. except ValueError:
  805. warning(
  806. "Malformed parameter name given to --overwrite-parameter: '%s'" % name)
  807. continue
  808. if category.lower() == 'plt' and component.lower() == 'active':
  809. component = cfg.active_platform
  810. try:
  811. getattr(cfg, category.lower())(
  812. component).par(parameter).value = value
  813. info("Overwriting parameter '%s' with value '%s'" % (name, value))
  814. except AttributeError:
  815. warning(
  816. "Non-existing parameter name given to --overwrite-parameter: '%s'" % name)
  817. continue
  818. # Select activity to be done according to the command line options
  819. # Default is to translate all Translations in the Configuration
  820. if wantGUI:
  821. info("Starting GUI")
  822. start_gui(cfg)
  823. elif listPlatforms:
  824. if cfg.plt():
  825. print('\n'.join([p.name for p in cfg.plt()]))
  826. else:
  827. warning("No platforms defined in XML file")
  828. elif writeXML:
  829. sys.stdout.write(cfg.xml())
  830. else:
  831. cfg.translate_all()