169 lines
6.8 KiB
Python
Executable File
169 lines
6.8 KiB
Python
Executable File
#!/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()
|