super bare skeleton
This commit is contained in:
parent
d2ebcdd852
commit
7408a59039
1
aif_gen/__init__.py
Normal file
1
aif_gen/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import config
|
1
aif_gen/config/__init__.py
Normal file
1
aif_gen/config/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import generator
|
3
aif_gen/config/generator/__init__.py
Normal file
3
aif_gen/config/generator/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from . import subsections
|
||||
from . import main
|
||||
from . import utils
|
168
aif_gen/config/generator/main.py
Executable file
168
aif_gen/config/generator/main.py
Executable file
@ -0,0 +1,168 @@
|
||||
#!/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()
|
7
aif_gen/config/generator/subsections/__init__.py
Normal file
7
aif_gen/config/generator/subsections/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from . import meta
|
||||
from . import storage
|
||||
from . import network
|
||||
from . import system
|
||||
from . import pacman
|
||||
from . import bootloader
|
||||
from . import scripts
|
0
aif_gen/config/generator/subsections/bootloader.py
Normal file
0
aif_gen/config/generator/subsections/bootloader.py
Normal file
40
aif_gen/config/generator/subsections/meta.py
Normal file
40
aif_gen/config/generator/subsections/meta.py
Normal file
@ -0,0 +1,40 @@
|
||||
import tkinter
|
||||
##
|
||||
import aif_gen.config.generator.utils as utils
|
||||
|
||||
|
||||
class Obj(object):
|
||||
def __init__(self, xmlroot, tkroot):
|
||||
self.defaults = {'version': '0.2.0'}
|
||||
self.xml = xmlroot
|
||||
self.root = tkroot
|
||||
self.frame = tkinter.LabelFrame(self.root, text = 'META',
|
||||
bd = 1, relief = tkinter.RAISED,
|
||||
font = ('Arial Bold', 15))
|
||||
# self.frame.grid(column = 0, row = 0)
|
||||
self.frame.pack(side = 'top', fill = 'both', expand = True)
|
||||
# TODO: Currently displays if ANY nested elements hover over. We don't want that. Eff it, fix later.
|
||||
# utils.CreateToolTip(self.frame, 'This section controls information about AIF-NG itself.')
|
||||
self.version()
|
||||
|
||||
def version(self):
|
||||
# Subsection header
|
||||
frame = tkinter.LabelFrame(self.frame, text = 'VERSION',
|
||||
bd = 1, relief = tkinter.RAISED,
|
||||
font = ('Arial Bold', 12))
|
||||
# frame.grid(column = 0, row = 0)
|
||||
frame.pack(side = 'top', fill = 'both', expand = True)
|
||||
# Version entry
|
||||
self.ver = tkinter.Entry(frame)
|
||||
utils.CreateToolTip(self.ver, 'Must be a valid git reference (branch, tag, commit ID, etc.)')
|
||||
self.ver.insert(0, self.defaults['version'])
|
||||
self.ver.pack(side = 'top', fill = 'both', expand = True)
|
||||
return()
|
||||
|
||||
def new(self):
|
||||
self.ver.delete(0, tkinter.END)
|
||||
return()
|
||||
|
||||
def save(self):
|
||||
self.xml.attrib['version'] = self.ver.get()
|
||||
return()
|
0
aif_gen/config/generator/subsections/network.py
Normal file
0
aif_gen/config/generator/subsections/network.py
Normal file
0
aif_gen/config/generator/subsections/pacman.py
Normal file
0
aif_gen/config/generator/subsections/pacman.py
Normal file
0
aif_gen/config/generator/subsections/scripts.py
Normal file
0
aif_gen/config/generator/subsections/scripts.py
Normal file
43
aif_gen/config/generator/subsections/storage/__init__.py
Normal file
43
aif_gen/config/generator/subsections/storage/__init__.py
Normal file
@ -0,0 +1,43 @@
|
||||
from . import block
|
||||
from . import luks
|
||||
from . import lvm
|
||||
from . import mdadm
|
||||
from . import filesystem
|
||||
from . import mount
|
||||
|
||||
import tkinter
|
||||
##
|
||||
import aif_gen.config.generator.utils as utils
|
||||
|
||||
|
||||
class Obj(object):
|
||||
def __init__(self, xmlroot, tkroot):
|
||||
self.xml = xmlroot
|
||||
self.root = tkroot
|
||||
self.frame = tkinter.LabelFrame(self.root, text = 'STORAGE',
|
||||
bd = 1, relief = tkinter.RAISED,
|
||||
font = ('Arial Bold', 15))
|
||||
# self.frame.grid(column = 0, row = 0)
|
||||
self.frame.pack(side = 'top', fill = 'both', expand = True)
|
||||
self.vals = {}
|
||||
self.block()
|
||||
|
||||
def block(self):
|
||||
frame = tkinter.LabelFrame(self.frame, text = 'BLOCK',
|
||||
bd = 1, relief = tkinter.RAISED,
|
||||
font = ('Arial Bold', 12))
|
||||
frame.pack(side = 'top', fill = 'both', expand = True)
|
||||
# Version entry
|
||||
self.vals['block'] = tkinter.Entry(frame)
|
||||
utils.CreateToolTip(self.vals['block'], 'Path to a disk ("block") device to partition')
|
||||
self.vals['block'].insert(0, '/dev/sda')
|
||||
self.vals['block'].pack(side = 'top', fill = 'both', expand = True)
|
||||
return()
|
||||
|
||||
def new(self):
|
||||
pass
|
||||
return()
|
||||
|
||||
def save(self):
|
||||
pass
|
||||
return()
|
0
aif_gen/config/generator/subsections/storage/lvm.py
Normal file
0
aif_gen/config/generator/subsections/storage/lvm.py
Normal file
0
aif_gen/config/generator/subsections/system.py
Normal file
0
aif_gen/config/generator/subsections/system.py
Normal file
50
aif_gen/config/generator/utils.py
Normal file
50
aif_gen/config/generator/utils.py
Normal file
@ -0,0 +1,50 @@
|
||||
import tkinter
|
||||
import webbrowser
|
||||
|
||||
|
||||
# TODO: http://effbot.org/zone/tkinter-text-hyperlink.htm ?
|
||||
|
||||
class ToolTip(object):
|
||||
# https://stackoverflow.com/a/56749167/733214
|
||||
def __init__(self, widget):
|
||||
self.widget = widget
|
||||
self.tipwindow = None
|
||||
self.id = None
|
||||
self.x = self.y = 0
|
||||
|
||||
def showtip(self, text):
|
||||
self.text = text
|
||||
if self.tipwindow or not self.text:
|
||||
return()
|
||||
x, y, cx, cy = self.widget.bbox("insert")
|
||||
x = x + self.widget.winfo_rootx() + 57
|
||||
y = y + cy + self.widget.winfo_rooty() +27
|
||||
self.tipwindow = tw = tkinter.Toplevel(self.widget)
|
||||
tw.wm_overrideredirect(1)
|
||||
tw.wm_geometry('+{0}+{1}'.format(x, y))
|
||||
label = tkinter.Label(tw, text = self.text, justify = tkinter.LEFT,
|
||||
background = '#ffffe0', relief = tkinter.SOLID, borderwidth = 1,
|
||||
font = ('Tahoma', 8, 'normal'))
|
||||
label.pack(ipadx = 1)
|
||||
return()
|
||||
|
||||
def hidetip(self):
|
||||
tw = self.tipwindow
|
||||
self.tipwindow = None
|
||||
if tw:
|
||||
tw.destroy()
|
||||
return()
|
||||
|
||||
|
||||
def CreateToolTip(widget, text):
|
||||
toolTip = ToolTip(widget)
|
||||
|
||||
def enter(event):
|
||||
toolTip.showtip(text)
|
||||
|
||||
def leave(event):
|
||||
toolTip.hidetip()
|
||||
|
||||
widget.bind('<Enter>', enter)
|
||||
widget.bind('<Leave>', leave)
|
||||
return()
|
Loading…
Reference in New Issue
Block a user