diff --git a/coretk/coretk/configutils.py b/coretk/coretk/configutils.py deleted file mode 100644 index 267feb73..00000000 --- a/coretk/coretk/configutils.py +++ /dev/null @@ -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( - "", lambda event: canvas.configure(scrollregion=canvas.bbox("all")) - ) - canvas.bind( - "", 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 diff --git a/coretk/coretk/dialogs/emaneconfig.py b/coretk/coretk/dialogs/emaneconfig.py index b3bca2fd..d90a970c 100644 --- a/coretk/coretk/dialogs/emaneconfig.py +++ b/coretk/coretk/dialogs/emaneconfig.py @@ -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,10 +83,6 @@ 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 @@ -94,11 +90,20 @@ class EmaneConfiguration(Dialog): 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): @@ -111,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( @@ -141,17 +145,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 + self, self.app, f"{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.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 @@ -162,12 +159,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): diff --git a/coretk/coretk/widgets.py b/coretk/coretk/widgets.py index cb377dcc..9625bc67 100644 --- a/coretk/coretk/widgets.py +++ b/coretk/coretk/widgets.py @@ -3,7 +3,18 @@ import tkinter as tk from functools import partial from tkinter import ttk -from coretk.configutils import ConfigType +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): @@ -30,11 +41,8 @@ class FrameScroll(tk.LabelFrame): def _configure_frame(self, event): req_width = self.frame.winfo_reqwidth() - req_height = self.frame.winfo_reqheight() if req_width != self.canvas.winfo_reqwidth(): self.canvas.configure(width=req_width) - if req_height != self.canvas.winfo_reqheight(): - self.canvas.configure(height=req_height) self.canvas.configure(scrollregion=self.canvas.bbox("all")) def _configure_canvas(self, event): @@ -68,34 +76,45 @@ class ConfigFrame(FrameScroll): label = tk.Label(self.frame, text=option.label) label.grid(row=index, pady=pady, padx=padx, sticky="w") value = tk.StringVar() - config_type = ConfigType(option.type) - if config_type == ConfigType.BOOL: + if option.type == core_pb2.ConfigOptionType.BOOL: select = tuple(option.select) - combobox = ttk.Combobox(self.frame, textvariable=value, values=select) + combobox = ttk.Combobox( + self.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 config_type == ConfigType.STRING: + elif option.select: + value.set(option.value) + select = tuple(option.select) + combobox = ttk.Combobox( + self.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(self.frame, textvariable=value) entry.grid(row=index, column=1, sticky="ew", pady=pady) - elif config_type == ConfigType.EMANECONFIG: + elif option.type in INT_TYPES: + value.set(option.value) + entry = tk.Entry(self.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(self.frame, textvariable=value) entry.grid(row=index, column=1, sticky="ew", pady=pady) else: - logging.error("unhandled config option type: %s", config_type) + logging.error("unhandled config option type: %s", option.type) self.values[key] = value def parse_config(self): for key in self.config: option = self.config[key] value = self.values[key] - config_type = ConfigType(option.type) config_value = value.get() - if config_type == ConfigType.BOOL: + if option.type == core_pb2.ConfigOptionType.BOOL: if config_value == "On": option.value = "1" else: