Merge branch 'coretk' into coretk-config

This commit is contained in:
Huy Pham 2019-11-07 07:57:48 -08:00
commit 98cfb301bb
6 changed files with 183 additions and 186 deletions

View file

@ -1,99 +0,0 @@
import enum
import logging
import tkinter as tk
from tkinter import ttk
class ConfigType(enum.Enum):
STRING = 10
BOOL = 11
EMANECONFIG = 7
def create_config(master, config, padx=2, pady=2):
"""
Creates a scrollable canvas with an embedded window for displaying configuration
options. Will use grid layout to consume row 0 and columns 0-2.
:param master: master to add scrollable canvas to
:param dict config: config option mapping keys to config options
:param int padx: x padding for widgets
:param int pady: y padding for widgets
:return: widget value mapping
"""
master.rowconfigure(0, weight=1)
master.columnconfigure(0, weight=1)
master.columnconfigure(1, weight=1)
canvas = tk.Canvas(master, highlightthickness=0)
canvas.grid(row=0, columnspan=2, sticky="nsew", padx=padx, pady=pady)
canvas.columnconfigure(0, weight=1)
canvas.rowconfigure(0, weight=1)
scroll_y = tk.Scrollbar(master, orient="vertical", command=canvas.yview)
scroll_y.grid(row=0, column=2, sticky="ns")
frame = tk.Frame(canvas, padx=padx, pady=pady)
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=3)
values = {}
for index, key in enumerate(sorted(config)):
option = config[key]
label = tk.Label(frame, text=option.label)
label.grid(row=index, pady=pady, padx=padx, sticky="ew")
value = tk.StringVar()
config_type = ConfigType(option.type)
if config_type == ConfigType.BOOL:
select = tuple(option.select)
combobox = ttk.Combobox(frame, textvariable=value, values=select)
combobox.grid(row=index, column=1, sticky="ew", pady=pady)
if option.value == "1":
value.set("On")
else:
value.set("Off")
elif config_type == ConfigType.STRING:
value.set(option.value)
entry = tk.Entry(frame, textvariable=value)
entry.grid(row=index, column=1, sticky="ew", pady=pady)
elif config_type == ConfigType.EMANECONFIG:
value.set(option.value)
entry = tk.Entry(frame, textvariable=value, bg="white")
entry.grid(row=index, column=1, sticky="ew", pady=pady)
else:
logging.error("unhandled config option type: %s", config_type)
values[key] = value
frame_id = canvas.create_window(0, 0, anchor="nw", window=frame)
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox("all"), yscrollcommand=scroll_y.set)
frame.bind(
"<Configure>", lambda event: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.bind(
"<Configure>", lambda event: canvas.itemconfig(frame_id, width=event.width)
)
return values
def parse_config(options, values):
"""
Given a set of configurations, parse out values and transform them when needed.
:param dict options: option key mapping to configuration options
:param dict values: option key mapping to widget values
:return: nothing
"""
for key in options:
option = options[key]
value = values[key]
config_type = ConfigType(option.type)
config_value = value.get()
if config_type == ConfigType.BOOL:
if config_value == "On":
option.value = "1"
else:
option.value = "0"
else:
option.value = config_value

View file

@ -28,6 +28,7 @@ class ServicesSelectDialog(Dialog):
for group in sorted(self.app.core.services):
self.groups.listbox.insert(tk.END, group)
self.groups.listbox.bind("<<ListboxSelect>>", self.handle_group_change)
self.groups.listbox.selection_set(0)
self.services = CheckboxList(
frame, text="Services", clicked=self.service_clicked
@ -46,6 +47,9 @@ class ServicesSelectDialog(Dialog):
button = tk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
# trigger group change
self.groups.listbox.event_generate("<<ListboxSelect>>")
def handle_group_change(self, event):
selection = self.groups.listbox.curselection()
if selection:
@ -53,7 +57,8 @@ class ServicesSelectDialog(Dialog):
group = self.groups.listbox.get(index)
self.services.clear()
for service in sorted(self.app.core.services[group], key=lambda x: x.name):
self.services.add(service.name)
checked = service.name in self.current_services
self.services.add(service.name, checked)
def service_clicked(self, name, var):
if var.get() and name not in self.current_services:

View file

@ -7,9 +7,9 @@ import tkinter as tk
import webbrowser
from tkinter import ttk
from coretk import configutils
from coretk.dialogs.dialog import Dialog
from coretk.images import ImageEnum, Images
from coretk.widgets import ConfigFrame
PAD_X = 2
PAD_Y = 2
@ -38,10 +38,10 @@ class EmaneConfiguration(Dialog):
self.emane_options()
self.draw_apply_and_cancel()
self.values = None
self.emane_config_frame = None
self.options = app.core.emane_config
self.model_options = None
self.model_values = None
self.model_config_frame = None
def create_text_variable(self, val):
"""
@ -75,7 +75,7 @@ class EmaneConfiguration(Dialog):
f.grid(row=0, column=0, sticky="nsew")
def save_emane_option(self):
configutils.parse_config(self.options, self.values)
self.emane_config_frame.parse_config()
self.emane_dialog.destroy()
def draw_emane_options(self):
@ -83,21 +83,27 @@ class EmaneConfiguration(Dialog):
self.emane_dialog = Dialog(
self, self.app, "emane configuration", modal=False
)
b1 = tk.Button(self.emane_dialog, text="Appy", command=self.save_emane_option)
b2 = tk.Button(
self.emane_dialog, text="Cancel", command=self.emane_dialog.destroy
)
if self.options is None:
session_id = self.app.core.session_id
response = self.app.core.client.get_emane_config(session_id)
logging.info("emane config: %s", response)
self.options = response.config
self.values = configutils.create_config(
self.emane_dialog, self.options, PAD_X, PAD_Y
)
b1.grid(row=1, column=0)
b2.grid(row=1, column=1)
self.emane_dialog.columnconfigure(0, weight=1)
self.emane_dialog.rowconfigure(0, weight=1)
self.emane_config_frame = ConfigFrame(self.emane_dialog, config=self.options)
self.emane_config_frame.draw_config()
self.emane_config_frame.grid(sticky="nsew")
frame = tk.Frame(self.emane_dialog)
frame.grid(sticky="ew")
for i in range(2):
frame.columnconfigure(i, weight=1)
b1 = tk.Button(frame, text="Appy", command=self.save_emane_option)
b1.grid(row=0, column=0, sticky="ew")
b2 = tk.Button(frame, text="Cancel", command=self.emane_dialog.destroy)
b2.grid(row=0, column=1, sticky="ew")
self.emane_dialog.show()
def save_emane_model_options(self):
@ -110,8 +116,7 @@ class EmaneConfiguration(Dialog):
model_name = self.emane_models[self.emane_model_combobox.current()]
# parse configuration
configutils.parse_config(self.model_options, self.model_values)
config = {x: self.model_options[x].value for x in self.model_options}
config = self.model_config_frame.parse_config()
# add string emane_ infront for grpc call
response = self.app.core.client.set_emane_model_config(
@ -145,17 +150,10 @@ class EmaneConfiguration(Dialog):
# create the dialog and the necessry widget
if not self.emane_model_dialog or not self.emane_model_dialog.winfo_exists():
self.emane_model_dialog = Dialog(
self, self.app, model_name + " configuration", modal=False
)
b1 = tk.Button(
self.emane_model_dialog, text="Apply", command=self.save_emane_model_options
)
b2 = tk.Button(
self.emane_model_dialog,
text="Cancel",
command=self.emane_model_dialog.destroy,
self, self.app, f"{model_name} configuration", modal=False
)
self.emane_model_dialog.columnconfigure(0, weight=1)
self.emane_model_dialog.rowconfigure(0, weight=1)
# query for configurations
session_id = self.app.core.session_id
@ -166,12 +164,20 @@ class EmaneConfiguration(Dialog):
logging.info("emane model config %s", response)
self.model_options = response.config
self.model_values = configutils.create_config(
self.emane_model_dialog, self.model_options, PAD_X, PAD_Y
self.model_config_frame = ConfigFrame(
self.emane_model_dialog, config=self.model_options
)
self.model_config_frame.grid(sticky="nsew")
self.model_config_frame.draw_config()
b1.grid(row=1, column=0, sticky="nsew")
b2.grid(row=1, column=1, sticky="nsew")
frame = tk.Frame(self.emane_model_dialog)
frame.grid(sticky="ew")
for i in range(2):
frame.columnconfigure(i, weight=1)
b1 = tk.Button(frame, text="Apply", command=self.save_emane_model_options)
b1.grid(row=0, column=0, sticky="ew")
b2 = tk.Button(frame, text="Cancel", command=self.emane_model_dialog.destroy)
b2.grid(row=0, column=1, sticky="ew")
self.emane_model_dialog.show()
def draw_option_buttons(self, parent):

View file

@ -1,8 +1,8 @@
import logging
import tkinter as tk
from coretk import configutils
from coretk.dialogs.dialog import Dialog
from coretk.widgets import ConfigFrame
PAD_X = 2
PAD_Y = 2
@ -11,25 +11,33 @@ PAD_Y = 2
class SessionOptionsDialog(Dialog):
def __init__(self, master, app):
super().__init__(master, app, "Session Options", modal=True)
self.options = None
self.values = None
self.save_button = tk.Button(self, text="Save", command=self.save)
self.cancel_button = tk.Button(self, text="Cancel", command=self.destroy)
self.config_frame = None
self.draw()
def draw(self):
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
session_id = self.app.core.session_id
response = self.app.core.client.get_session_options(session_id)
logging.info("session options: %s", response)
self.options = response.config
self.values = configutils.create_config(self, self.options, PAD_X, PAD_Y)
self.save_button.grid(row=1, pady=PAD_Y, padx=PAD_X, sticky="ew")
self.cancel_button.grid(row=1, column=1, pady=PAD_Y, padx=PAD_X, sticky="ew")
self.config_frame = ConfigFrame(self, config=response.config)
self.config_frame.draw_config()
self.config_frame.grid(sticky="nsew")
frame = tk.Frame(self)
frame.grid(sticky="ew")
for i in range(2):
frame.columnconfigure(i, weight=1)
button = tk.Button(frame, text="Save", command=self.save)
button.grid(row=0, column=0, pady=PAD_Y, padx=PAD_X, sticky="ew")
button = tk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, pady=PAD_Y, padx=PAD_X, sticky="ew")
def save(self):
configutils.parse_config(self.options, self.values)
config = self.config_frame.parse_config()
session_id = self.app.core.session_id
config = {x: self.options[x].value for x in self.options}
response = self.app.core.client.set_session_options(session_id, config)
logging.info("saved session config: %s", response)
self.destroy()

View file

@ -1,42 +1,131 @@
import logging
import tkinter as tk
from functools import partial
from tkinter import ttk
from core.api.grpc import core_pb2
INT_TYPES = {
core_pb2.ConfigOptionType.UINT8,
core_pb2.ConfigOptionType.UINT16,
core_pb2.ConfigOptionType.UINT32,
core_pb2.ConfigOptionType.UINT64,
core_pb2.ConfigOptionType.INT8,
core_pb2.ConfigOptionType.INT16,
core_pb2.ConfigOptionType.INT32,
core_pb2.ConfigOptionType.INT64,
}
class FrameScroll(tk.LabelFrame):
def __init__(self, master=None, cnf={}, **kw):
def __init__(self, master=None, cnf={}, _cls=tk.Frame, **kw):
super().__init__(master, cnf, **kw)
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.columnconfigure(1, weight=1)
self.canvas = tk.Canvas(self, highlightthickness=0)
self.canvas.grid(row=0, columnspan=2, sticky="nsew", padx=2, pady=2)
self.canvas.grid(row=0, sticky="nsew", padx=2, pady=2)
self.canvas.columnconfigure(0, weight=1)
self.canvas.rowconfigure(0, weight=1)
self.scrollbar = tk.Scrollbar(
self, orient="vertical", command=self.canvas.yview
)
self.scrollbar.grid(row=0, column=2, sticky="ns")
self.frame = tk.Frame(self.canvas, padx=2, pady=2)
self.frame.columnconfigure(0, weight=1)
self.scrollbar.grid(row=0, column=1, sticky="ns")
self.frame = _cls(self.canvas)
self.frame_id = self.canvas.create_window(0, 0, anchor="nw", window=self.frame)
self.canvas.update_idletasks()
self.canvas.configure(
scrollregion=self.canvas.bbox("all"), yscrollcommand=self.scrollbar.set
)
self.frame.bind(
"<Configure>",
lambda event: self.canvas.configure(scrollregion=self.canvas.bbox("all")),
)
self.canvas.bind(
"<Configure>",
lambda event: self.canvas.itemconfig(self.frame_id, width=event.width),
)
self.frame.bind("<Configure>", self._configure_frame)
self.canvas.bind("<Configure>", self._configure_canvas)
def _configure_frame(self, event):
req_width = self.frame.winfo_reqwidth()
if req_width != self.canvas.winfo_reqwidth():
self.canvas.configure(width=req_width)
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def _configure_canvas(self, event):
self.canvas.itemconfig(self.frame_id, width=event.width)
def clear(self):
for widget in self.frame.winfo_children():
widget.destroy()
class ConfigFrame(FrameScroll):
def __init__(self, master=None, cnf={}, config=None, **kw):
super().__init__(master, cnf, ttk.Notebook, **kw)
self.config = config
self.values = {}
def draw_config(self):
padx = 2
pady = 2
group_mapping = {}
for key in self.config:
option = self.config[key]
group = group_mapping.setdefault(option.group, [])
group.append(option)
for group_name in sorted(group_mapping):
group = group_mapping[group_name]
frame = tk.Frame(self.frame)
frame.columnconfigure(1, weight=1)
self.frame.add(frame, text=group_name)
for index, option in enumerate(sorted(group, key=lambda x: x.name)):
label = tk.Label(frame, text=option.label)
label.grid(row=index, pady=pady, padx=padx, sticky="w")
value = tk.StringVar()
if option.type == core_pb2.ConfigOptionType.BOOL:
select = tuple(option.select)
combobox = ttk.Combobox(
frame, textvariable=value, values=select, state="readonly"
)
combobox.grid(row=index, column=1, sticky="ew", pady=pady)
if option.value == "1":
value.set("On")
else:
value.set("Off")
elif option.select:
value.set(option.value)
select = tuple(option.select)
combobox = ttk.Combobox(
frame, textvariable=value, values=select, state="readonly"
)
combobox.grid(row=index, column=1, sticky="ew", pady=pady)
elif option.type == core_pb2.ConfigOptionType.STRING:
value.set(option.value)
entry = tk.Entry(frame, textvariable=value)
entry.grid(row=index, column=1, sticky="ew", pady=pady)
elif option.type in INT_TYPES:
value.set(option.value)
entry = tk.Entry(frame, textvariable=value)
entry.grid(row=index, column=1, sticky="ew", pady=pady)
elif option.type == core_pb2.ConfigOptionType.FLOAT:
value.set(option.value)
entry = tk.Entry(frame, textvariable=value)
entry.grid(row=index, column=1, sticky="ew", pady=pady)
else:
logging.error("unhandled config option type: %s", option.type)
self.values[option.name] = value
def parse_config(self):
for key in self.config:
option = self.config[key]
value = self.values[key]
config_value = value.get()
if option.type == core_pb2.ConfigOptionType.BOOL:
if config_value == "On":
option.value = "1"
else:
option.value = "0"
else:
option.value = config_value
return {x: self.config[x].value for x in self.config}
class ListboxScroll(tk.LabelFrame):
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master, cnf, **kw)
@ -51,43 +140,14 @@ class ListboxScroll(tk.LabelFrame):
self.scrollbar.config(command=self.listbox.yview)
class CheckboxList(tk.LabelFrame):
class CheckboxList(FrameScroll):
def __init__(self, master=None, cnf={}, clicked=None, **kw):
super().__init__(master, cnf, **kw)
self.clicked = clicked
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.columnconfigure(1, weight=1)
self.canvas = tk.Canvas(self, highlightthickness=0)
self.canvas.grid(row=0, columnspan=2, sticky="nsew", padx=2, pady=2)
self.canvas.columnconfigure(0, weight=1)
self.canvas.rowconfigure(0, weight=1)
self.scrollbar = tk.Scrollbar(
self, orient="vertical", command=self.canvas.yview
)
self.scrollbar.grid(row=0, column=2, sticky="ns")
self.frame = tk.Frame(self.canvas, padx=2, pady=2)
self.frame.columnconfigure(0, weight=1)
self.frame_id = self.canvas.create_window(0, 0, anchor="nw", window=self.frame)
self.canvas.update_idletasks()
self.canvas.configure(
scrollregion=self.canvas.bbox("all"), yscrollcommand=self.scrollbar.set
)
self.frame.bind(
"<Configure>",
lambda event: self.canvas.configure(scrollregion=self.canvas.bbox("all")),
)
self.canvas.bind(
"<Configure>",
lambda event: self.canvas.itemconfig(self.frame_id, width=event.width),
)
def clear(self):
for widget in self.frame.winfo_children():
widget.destroy()
def add(self, name):
var = tk.BooleanVar()
def add(self, name, checked):
var = tk.BooleanVar(value=checked)
func = partial(self.clicked, name, var)
checkbox = tk.Checkbutton(self.frame, text=name, variable=var, command=func)
checkbox.grid(sticky="w")

View file

@ -768,6 +768,23 @@ message NodeType {
}
}
message ConfigOptionType {
enum Enum {
NONE = 0;
UINT8 = 1;
UINT16 = 2;
UINT32 = 3;
UINT64 = 4;
INT8 = 5;
INT16 = 6;
INT32 = 7;
INT64 = 8;
FLOAT = 9;
STRING = 10;
BOOL = 11;
}
}
message ServiceValidationMode {
enum Enum {
BLOCKING = 0;