#!/usr/bin/env python3 import os import tkinter import tkinter.filedialog import tkinter.messagebox ## import requests from lxml import etree ## from . import subsections class Configurator(tkinter.Tk): def __init__(self, version = '0.2.0', *args, **kwargs): super().__init__(*args, **kwargs) maxwidth, maxheight = self.winfo_screenwidth(), self.winfo_screenheight() self.geometry('{0}x{1}+0+0'.format(maxwidth, maxheight)) self.title('AIF-NG Configuration Generator') self.xml_attrib = {('{http://www.w3.org/2001/XMLSchema-instance}' 'schemaLocation'): ('http://aif-ng.io/ ' 'http://aif-ng.io/aif.xsd'), 'version': version} self.xml_nsmap = {'xsi': 'http://www.w3.org/2001/XMLSchema-instance', None: 'http://aif-ng.io/'} self.xml = etree.Element('aif', attrib = self.xml_attrib, nsmap = self.xml_nsmap) self.xsd = None self.savefilename = None self.saveelems = [] self._initMenuBar() self._initXSD() self.build() def _initMenuBar(self): menubar = tkinter.Menu(self) # File filemenu = tkinter.Menu(menubar, tearoff = 0) filemenu.add_command(label = 'New', command = self.file_new) filemenu.add_command(label = 'Clear', command = self.file_clear) filemenu.add_command(label = 'Save', command = self.file_save) filemenu.add_command(label = 'Save as...', command = self.file_saveas) filemenu.add_command(label = 'Load...', command = self.file_load) filemenu.add_separator() filemenu.add_command(label="Exit", command = self.exit) menubar.add_cascade(label = 'File', menu = filemenu) # Help helpmenu = tkinter.Menu(menubar, tearoff = 0) helpmenu.add_command(label = 'About') menubar.add_cascade(label = 'Help', menu = helpmenu) ## self.config(menu = menubar) return() def _initXSD(self, xsdpath = None): # TODO: locally-cache XSD file? if xsdpath: xsdpath = os.path.abspath(os.path.expanduser(xsdpath)) if not os.path.isfile(xsdpath): raise ValueError(('An explicit XSD path was specified but ' 'does not exist on the local filesystem')) with open(xsdpath, 'rb') as fh: raw_xsd = fh.read() else: xsi = self.xml.nsmap.get('xsi', 'http://www.w3.org/2001/XMLSchema-instance') schemaLocation = '{{{0}}}schemaLocation'.format(xsi) schemaURL = self.xml.attrib.get(schemaLocation, 'https://aif-ng.io/aif.xsd?ref={0}'.format(self.xml.attrib['version'])) split_url = schemaURL.split() if len(split_url) == 2: # a properly defined schemaLocation schemaURL = split_url[1] else: schemaURL = split_url[0] # a LAZY schemaLocation req = requests.get(schemaURL) if not req.ok: raise RuntimeError('Could not download XSD') raw_xsd = req.content self.xsd = etree.XMLSchema(etree.XML(raw_xsd)) return() def build(self): self.saveelems.append(subsections.meta.Obj(self.xml, self)) self.saveelems.append(subsections.storage.Obj(self.xml, self)) # self.update() return() def check(self): if not self.xsd: self._initXSD() return(self.xsd.validate(self.xml)) def clearinput(self): # ??? # maybe https://stackoverflow.com/a/2260355/733214 # or maybe https://stackoverflow.com/a/19477781/733214 ? self.xml = etree.Element('aif', attrib = self.xml_attrib, nsmap = self.xml_nsmap) for i in self.saveelems: i.new() return() def exit(self): if not self.check(): tkinter.messagebox.showwarning('Incompatible Configuration', ('The configuration state as currently defined is not a valid ' 'configuration file for AIF-NG. It will not work properly until ' 'completed.')) # Check for unsaved changes here? self.destroy() return() def file_load(self): fname = tkinter.filedialog.askopenfilename(defaultextension = '.xml') with open(fname, 'rb') as fh: try: self.xml = etree.fromstring(fh.read()) except etree.XMLSyntaxError as e: tkinter.messagebox.showerror('Invalid XML', ('The imported configuration ({0}) is invalid XML: {1}').format(fname, e)) return() if not self.check(): tkinter.messagebox.showwarning('Incompatible Configuration', ('The imported configuration ({0}) is not a valid configuration file ' 'for AIF-NG. It will not work properly until completed.').format(fname)) return() def file_clear(self): self.clearinput() return() def file_new(self): self.clearinput() self.savefilename = None return() def file_save(self): self.savefile() return() def file_saveas(self): self.savefile(forcefile = True) def savefile(self, forcefile = False): if not self.check(): # TODO: make this persistently configurable? tkinter.messagebox.showwarning('Incompatible Configuration', ('The configuration state as currently defined is not a valid ' 'configuration file for AIF-NG. It will not work properly until ' 'completed.')) if not self.savefilename or forcefile: savefilename = tkinter.filedialog.asksaveasfilename(defaultextension = '.xml') if savefilename is None: # "Cancel" button return() self.savefilename = savefilename for i in self.saveelems: i.save() with open(self.savefilename, 'wb') as fh: fh.write(etree.tostring(self.xml, encoding = 'utf-8', xml_declaration = True, pretty_print = True, with_tail = True, inclusive_ns_prefixes = True)) return() # if __name__ == '__main__': # app = Configurator() # app.mainloop()