Refactor: Ugly but it works

This commit is contained in:
2020-12-26 19:03:30 -05:00
parent fd91cce506
commit dd22e82d07

284
openwrt-build-gui.py Normal file → Executable file
View File

@@ -1,133 +1,253 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import tkinter as tk import tkinter as tk
from tkinter import filedialog from tkinter import filedialog
from tkinter import ttk from tkinter import ttk
import os.path import os.path
import pandas import csv
import json
import gzip import gzip
import urllib.request import urllib.request
# This will enable file caching and some dev output #####################################################################
######################## VARIABLES ##################################
#####################################################################
# Enable file caching and some dev output
debug=True debug=True
#####################################################################
######################## FUNCTIONS ##################################
#####################################################################
class Main(tk.Frame): def get_toh():
"""This module generates information for the tkinter gui""" """
Retrieves the openwrt.org table of hardware and returns it as a list of dicts
"""
toh_cache_file = "sources/toh.tsv"
if debug is True and os.path.isfile(toh_cache_file):
with open(toh_cache_file) as infile:
toh = json.load(infile)
else:
toh_gz_handle = urllib.request.urlopen("https://openwrt.org/_media/toh_dump_tab_separated.gz")
toh_handle = gzip.open(toh_gz_handle, mode='rt', encoding='ISO-8859-1')
toh_dict = csv.DictReader(toh_handle, delimiter='\t')
# Convert the DictReader object to a native list of dicts
toh = list(toh_dict)
# Sanitize it
junk = ['', 'nan', 'NULL', '-', '?', '¿', ' ']
toh = [d for d in toh if d['target'] not in junk and d['subtarget'] not in junk]
# Save to cache file
with open(toh_cache_file, 'w') as outfile:
json.dump(toh, outfile)
return toh
#####################################################################
######################## CLASSES ####################################
#####################################################################
class DeviceFrame(tk.Frame):
"""This class renders the device information frame"""
def __init__(self, parent, *args, **kwargs): def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs) super().__init__(parent, *args, **kwargs)
# Get Table of Hardware (toh) from openwrt.org and save to a pandas df # Make a dict to hold our widgets
self.toh_df = self.get_toh_df() self.widgets = {}
# Target menu # Get the Table of Hardware (toh) data frame from openwrt.org
self.target = tk.StringVar() self.toh = get_toh()
self.targets, self.target_menu = self.gen_target_menu()
self.target.set(self.targets[0])
# Subtarget menu # Create device frame and widgets
self.subtarget = tk.StringVar() self.device_frame = tk.LabelFrame(self, text="Select Device")
self.subtargets, self.subtarget_menu = self.gen_subtarget_menu() self.device_frame.grid(row=0, column=0, sticky=(tk.W + tk.E))
self.subtarget.set(self.subtargets[0]) self.info_frame = tk.LabelFrame(self, text="Info")
self.target.trace("w", lambda var_name, var_index, operation: self.gen_subtarget_menu(regen=True)) self.info_frame.grid(row=1, column=0, sticky=(tk.W + tk.E))
self.version_frame = tk.LabelFrame(self, text="Version")
self.version_frame.grid(row=2, column=0, sticky=(tk.W + tk.E))
# Backup file self.targets_widget(parent)
self.backup_file = tk.StringVar() self.subtargets_widget(parent)
self.backup_file_entry = ttk.Entry(self, textvariable=self.backup_file) self.info_widget(parent)
self.backup_file_button = ttk.Button(self, text="Select backup file", command=self.open_sysbackup) self.version_widget(parent)
# Device Info # Create some traces to repopulate the subtarget menu and info widget
self.device_info = tk.StringVar() parent.target.trace("w", lambda var_name, var_index, operation: self.subtargets_widget(parent, regen=True))
parent.subtarget.trace("w", lambda var_name, var_index, operation: self.info_widget(parent, regen=True))
# Place the widgets in the device frame
self.widgets['Target'].grid(row=0, column=0)
self.widgets['Subtarget'].grid(row=1, column=0)
self.widgets['Version'].grid(row=0, column=0)
self.widgets['Info'].grid(row=0, column=1)
# Layout def targets_widget(self, parent):
self.target_menu.grid(row=0, column=0, sticky=(tk.W + tk.E)) """A Combobox of targets from the ToH"""
self.subtarget_menu.grid(row=0, column=1, sticky=(tk.W + tk.E))
self.backup_file_entry.grid(row=1, column=1, sticky=(tk.W + tk.E)) targets = [k['target'] for k in self.toh]
self.backup_file_button.grid(row=1, column=0, sticky=(tk.W)) targets = sorted(set(targets))
self.columnconfigure(1, weight=1) parent.target.set(targets[0])
self.widgets['Target'] = LabelInput(
self.device_frame,
label='Target:',
input_class=ttk.Combobox,
input_var=parent.target,
options_list=targets
)
def subtargets_widget(self, parent, regen=None):
"""A Combobox of subtargets of the current target"""
regen = regen or False
subtargets = [d['subtarget'] for d in self.toh if d['target'] == parent.target.get()]
subtargets = sorted(set(subtargets))
parent.subtarget.set(subtargets[0])
if regen is True:
self.widgets['Subtarget'].input['values'] = subtargets
return
self.widgets['Subtarget'] = LabelInput(
self.device_frame,
label='Subtarget:',
input_class=ttk.Combobox,
input_var=parent.subtarget,
options_list=subtargets
)
def info_widget(self, parent, regen=None):
"""An InfoBox of info about the current subtarget"""
regen = regen or False
# Add any stat that you wish to display to this list
stats = {'devicetype': 'Type', 'brand': 'Brand', 'model': 'Model', 'cpu': 'CPU', 'supportedsincerel': 'First Release',
'supportedcurrentrel': 'Current Release', 'packagearchitecture': 'Arch', 'wikideviurl': 'Wiki'}
for device in self.toh:
if device['target'] == parent.target.get() and device['subtarget'] == parent.subtarget.get():
break
# Stringify
stats_str = '\n'.join('{}: {}'.format(stats[k], v) for k, v in device.items() if k in stats)
parent.subtarget_info.set(stats_str)
if regen is True:
return
self.widgets['Info'] = LabelInput(
self.device_frame,
label='',
input_class=ttk.Label,
input_var=parent.subtarget_info,
textvariable=parent.subtarget_info,
)
def version_widget(self, parent):
"""A Combobox of version numbers"""
# A space-delim'd string of version numbers TODO: make automatic from scraped data (repo tags?)
versions = 'snapshot 12.09'
parent.version.set('snapshot')
self.widgets['Version'] = LabelInput(
self.version_frame,
label='',
input_class=ttk.Combobox,
input_var=parent.version,
options_list=versions
)
def get_toh_df(self): class LabelInput(tk.Frame):
"""A widget containing a label and input together."""
cache_file = "sources/toh.tsv" def __init__(self, parent, *args, label='', input_class=ttk.Entry,
input_var=None, input_args=None, label_args=None, options_list=None,
**kwargs):
super().__init__(parent)
input_args = input_args or {}
label_args = label_args or {}
options_list = options_list or []
self.variable = input_var
if debug is True and os.path.isfile(cache_file): if input_class in (ttk.Checkbutton, ttk.Button, ttk.Radiobutton):
with open(cache_file) as infile: input_args["text"] = label
try: input_args["variable"] = input_var
toh_df = pandas.read_csv(infile)
except:
os.remove(cache_file)
else: else:
toh_gz_handle = urllib.request.urlopen("https://openwrt.org/_media/toh_dump_tab_separated.gz") self.label = ttk.Label(self, text=label, **label_args)
toh_handle = gzip.open(toh_gz_handle) self.label.grid(row=0, column=0, sticky=(tk.W + tk.E))
toh_df = pandas.read_csv(toh_handle, sep="\t", encoding = "ISO-8859-1") input_args["textvariable"] = input_var
toh_df = toh_df[(toh_df.target.notnull()) & if input_class in (ttk.OptionMenu, ttk.Combobox):
(toh_df.subtarget.notnull()) & input_args["values"] = options_list
(toh_df.target != '¿') &
(toh_df.subtarget != '¿')]
if debug is True:
with open(cache_file, 'w') as outfile:
toh_df.to_csv(outfile)
return toh_df
def gen_target_menu(self, *args, **kwargs): if input_class is ttk.OptionMenu:
targets = sorted(set(self.toh_df.target.tolist())) self.input = input_class(self, self.variable, *options_list)
target_menu = ttk.OptionMenu(self, self.target, targets[0], *targets) else:
return targets, target_menu self.input = input_class(self, **input_args)
self.input.grid(row=1, column=0, sticky=(tk.W + tk.E))
self.columnconfigure(0, weight=1)
def gen_subtarget_menu(self, regen=False, *args, **kwargs): def grid(self, sticky=(tk.E + tk.W), **kwargs):
subtargets_df = self.toh_df[self.toh_df.target == self.target.get()] super().grid(sticky=sticky, **kwargs)
subtargets = sorted(set(subtargets_df.subtarget.tolist()))
if regen is False:
subtarget_menu = ttk.OptionMenu(self, self.subtarget, subtargets[0], *subtargets)
return subtargets, subtarget_menu
else: # Regen
self.subtarget_menu["menu"].delete(0, "end")
for subtarget in subtargets:
self.subtarget_menu["menu"].add_command(label=subtarget, command=lambda value=subtarget: self.subtarget.set(subtarget))
self.subtarget.set(subtargets[0])
def get(self):
if self.variable:
return self.variable.get()
elif type(self.input) == tk.Text:
return self.input.get('1.0', tk.END)
else:
return self.input.get()
def open_sysbackup(self): def set(self, value, *args, **kwargs):
file = filedialog.askopenfile(mode ='r', title="Select Backup File", filetypes=[("OpenWRT sysbackup file", ".tar.gz")]) if type(self.variable) == tk.BooleanVar:
if file is not None: self.variable.set(bool(value))
self.backup_file.set(file.name) elif self.variable:
self.variable.set(value, *args, **kwargs)
elif type(self.input).__name__.endswith('button'):
if value:
self.input.select()
else:
self.input.deselect()
elif type(self.input) == tk.Text:
self.input.delete('1.0', tk.END)
self.input.insert('1.0', value)
else:
self.input.delete(0, tk.END)
self.input.insert(0, value)
class GUI(tk.Tk): class GUI(tk.Tk):
"""Create the GUI""" """Application root window"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# Set the window properties # Set the window properties
self.title("openwrt-build") self.title("openwrt-build")
self.geometry("1280x960") self.geometry("800x600")
self.resizable(width=False, height=False) self.resizable(width=False, height=False)
# Define the UI ttk.Label(
Main(self).grid(sticky=(tk.E + tk.W + tk.N + tk.S)) self,
self.columnconfigure(0, weight=1) text="OpenWRT Image Builder",
font=("TkDefaultFont", 16)
).grid(row=0)
# Let's store the global tk vars in this top-level class
self.target = tk.StringVar()
self.subtarget = tk.StringVar()
self.subtarget_info = tk.StringVar()
self.version = tk.StringVar()
# Add each frame class
self.device_frame = DeviceFrame(self)
self.device_frame.grid(row=1, column=0, sticky=(tk.W + tk.E))
if __name__ == '__main__': if __name__ == '__main__':
app = GUI() app = GUI()
app.mainloop() app.mainloop()
#
# , usecols=['brand',
# 'model',
# 'supportedsincerel',
# 'supportedcurrentrel',
# 'target',
# 'subtarget']
#