Implemented configuration window

This commit is contained in:
Martin Zietz
2021-02-02 13:11:27 +01:00
parent 4dc296a6f0
commit 27c804904b
3 changed files with 177 additions and 32 deletions
+164 -19
View File
@@ -25,12 +25,12 @@ class HelmholtzGUI(Tk):
mainArea.grid_rowconfigure(0, weight=1)
mainArea.grid_columnconfigure(0, weight=1)
self.frames = {} # dictionary for storing all pages
self.pages = {} # dictionary for storing all pages
for F in [ManualMode]:
frame = F(mainArea, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
for P in [ManualMode, Configuration]:
page = P(mainArea, self)
self.pages[P] = page
page.grid(row=0, column=0, sticky="nsew")
status_frame = Frame(self)
status_frame.pack(side="bottom", fill="x", expand=False)
@@ -46,7 +46,8 @@ class HelmholtzGUI(Tk):
self.show_frame(ManualMode)
def show_frame(self, key):
frame = self.frames[key] # gets correct page from the dictionary
frame = self.pages[key] # gets correct page from the dictionary
frame.page_switch() # update displays in this page with window-specific update function
frame.tkraise() # brings this frame to the front
@@ -59,15 +60,18 @@ class TopMenu:
ModeSelector = Menu(menu)
menu.add_cascade(label="Mode", menu=ModeSelector)
ModeSelector.add_command(label="Static Manual Input", command=lambda: self.manual_mode(window))
ModeSelector.add_command(label="Configuration...", command=lambda: self.configuration(window))
@staticmethod
def manual_mode(window):
window.show_frame(ManualMode)
@staticmethod
def configuration(window):
window.show_frame(Configuration)
class ManualMode(Frame):
# ToDo: Display maximum values
# ToDo: Add option to cancel ambient field
# ToDo: Add buttons to safe and set to 0
def __init__(self, parent, controller):
@@ -88,7 +92,6 @@ class ManualMode(Frame):
# content: [function to call on button press, unit text to be displayed]
self.modes = {"Magnetic Field": [self.execute_field, "\u03BCT", self.update_max_fields],
"Current": [self.execute_current, "A", self.update_max_currents]}
# "Raw Current": [self.input_raw_current, "A"]} ToDo (optional): make functions for this
self.unit = StringVar()
default_mode = list(self.modes.keys())[0]
@@ -143,7 +146,6 @@ class ManualMode(Frame):
self.buttons_frame = Frame(self)
self.buttons_frame.grid_rowconfigure(ALL, weight=1)
self.buttons_frame.grid_columnconfigure(ALL, weight=1)
self.buttons_frame.grid_columnconfigure(2, weight=1, minsize=20)
self.buttons_frame.grid(row=row_counter, column=0)
Label(self.buttons_frame, text="").grid(row=0, column=0) # add spacer
@@ -151,25 +153,28 @@ class ManualMode(Frame):
# add button for executing the current entries
execute_button = Button(self.buttons_frame, text="Execute!", command=self.execute,
pady=5, padx=5, font=BIG_BUTTON_FONT)
execute_button.grid(row=row_counter, column=0)
execute_button.grid(row=row_counter, column=0, padx=5)
# add button for reinitialization
reinit_button = Button(self.buttons_frame, text="Reinitialize", command=func.setup_axes,
pady=5, padx=5, font=BIG_BUTTON_FONT)
reinit_button.grid(row=row_counter, column=1)
reinit_button.grid(row=row_counter, column=1, padx=5)
row_counter = row_counter + 1
# Add spacer to Frame below
Label(self, text="", pady=10).grid(row=row_counter, column=0)
Label(self, text="", pady=10).grid(row=row_counter, column=0) # add spacer
self.input_mode.trace_add('write', self.change_mode_callback) # call mode change function on dropdown change
self.input_mode.set(default_mode) # call up default mode at the start
self.compensate.trace_add('write', self.change_mode_callback) # call mode change function on dropdown change
self.compensate.trace_add('write', self.change_mode_callback) # call mode change function on checkbox change
def page_switch(self): # function that is called when switching to this page in the UI
self.modes[self.input_mode.get()][2]() # update max values, e.g. calls update_max_fields function
def change_mode_callback(self, var, index, mode): # not sure what the parameters are for, but they are necessary
self.unit.set(self.modes[self.input_mode.get()][1]) # change unit text
self.modes[self.input_mode.get()][2]() # update max values
self.modes[self.input_mode.get()][2]() # update max values, e.g. calls update_max_fields function
def update_max_fields(self): # update labels with maximum allowable field values
self.compensate_checkbox.config(state=NORMAL)
@@ -203,11 +208,16 @@ class ManualMode(Frame):
function_to_call(vector) # call function
# ToDo: update status display here
@staticmethod
def execute_field(vector):
def execute_field(self, vector):
func.ui_print("field executing", vector)
try:
func.set_field_simple(vector * 1e-6) # ToDo: change to set_field
comp = self.compensate.get()
if comp == 0:
func.set_field(vector * 1e-6)
elif comp == 1:
func.set_field_simple(vector * 1e-6)
else:
func.ui_print("Unexpected value encountered: compensate =", comp)
except ValueError as e:
func.ui_print(e)
@@ -220,6 +230,141 @@ class ManualMode(Frame):
func.ui_print(e)
class Configuration(Frame):
# generate configuration window to set program constants
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.grid_rowconfigure(ALL, weight=1)
self.grid_columnconfigure(ALL, weight=1)
row_counter = 0
# Serial port settings frame:
port_frame = Frame(self)
port_frame.grid_rowconfigure(ALL, weight=1)
port_frame.grid_columnconfigure(ALL, weight=1)
port_frame.grid(row=row_counter, column=0, sticky=W)
entry_texts = ["XY PSU Serial Port:", "Z PSU Serial Port:"]
self.XY_port = StringVar(value=g.XY_PORT)
self.Z_port = StringVar(value=g.Z_PORT)
port_vars = [self.XY_port, self.Z_port]
row = 0
for text in entry_texts:
field = ttk.Entry(port_frame, textvariable=port_vars[row])
field.grid(row=row, column=1, sticky=W)
axis_label = Label(port_frame, text=text, padx=5, pady=10)
axis_label.grid(row=row, column=0, sticky=W)
unit_label = Label(port_frame, text="e.g. COM10")
unit_label.grid(row=row, column=2, sticky=W)
row += 1
row_counter += 1
Label(self, text="", pady=0).grid(row=row_counter, column=0) # add spacer
row_counter += 1
value_frame = Frame(self)
value_frame.grid_rowconfigure(ALL, weight=1)
value_frame.grid_columnconfigure(ALL, weight=1)
value_frame.grid(row=row_counter, column=0)
# Setup dictionary to generate entry table from
# {Key: [[x-value,y-value,z-value], unit, description, config file key, unit conversion factor]}
self.entries = {
"Coil Constants:": [[DoubleVar() for _ in range(3)], "\u03BCT/A", "", "coil_const", 1e6],
"Ambient Field:": [[DoubleVar() for _ in range(3)], "\u03BCT/A", "Field to be compensated", "ambient_field", 1e6],
"Resistances:": [[DoubleVar() for _ in range(3)], "\u03A9", "Resistance of coils + equipment", "resistance", 1],
"Max. Power:": [[DoubleVar() for _ in range(3)], "W", "Max. allowed power", "max_watts", 1],
"Max. Voltage:": [[DoubleVar() for _ in range(3)], "V", "Max. allowed voltage, must not exceed 16V!", "max_volts", 1],
"Arduino Pins:": [[IntVar() for _ in range(3)], "-", "Should be 15, 16, 17", "relay_pin", 1]
}
self.update_fields() # set current values from config file
# Fill in header (axis names):
col = 1
for text in ["X-Axis", "Y-Axis", "Z-Axis"]:
label = Label(value_frame, text=text, font=SUB_HEADER_FONT)
label.grid(row=0, column=col, sticky="ew")
col += 1
# generate table with entries, unit labels and descriptions:
row = 1
for key in self.entries.keys():
for axis in range(3): # generate entry fields
field = ttk.Entry(value_frame, textvariable=self.entries[key][0][axis], width=10)
field.grid(row=row, column=axis+1, sticky=W, padx=2)
axis_label = Label(value_frame, text=key, padx=5, pady=5)
axis_label.grid(row=row, column=0, sticky=W)
unit_label = Label(value_frame, text=self.entries[key][1])
unit_label.grid(row=row, column=4, sticky=W)
description_label = Label(value_frame, text=self.entries[key][2])
description_label.grid(row=row, column=5, sticky=W)
row = row + 1
row_counter += 1
Label(self, text="", pady=10).grid(row=row_counter, column=0) # add spacer
row_counter += 1
# Setup buttons
# Setup frame to house buttons:
self.buttons_frame = Frame(self)
self.buttons_frame.grid_rowconfigure(ALL, weight=1)
self.buttons_frame.grid_columnconfigure(ALL, weight=1)
self.buttons_frame.grid(row=row_counter, column=0, sticky=W, padx=20)
# Create and place buttons
implement_button = Button(self.buttons_frame, text="Update and Reconnect", command=self.implement,
pady=5, padx=5, font=BIG_BUTTON_FONT)
implement_button.grid(row=0, column=0, padx=5)
restore_button = Button(self.buttons_frame, text="Restore Defaults", command=self.restore_defaults,
pady=5, padx=5, font=BIG_BUTTON_FONT)
restore_button.grid(row=0, column=1, padx=5)
row_counter += 1
Label(self, text="", pady=10).grid(row=row_counter, column=0) # add spacer
def page_switch(self): # function that is called when switching to this window
self.update_fields()
def restore_defaults(self):
func.create_default_config(g.CONFIG_FILE) # overwrite config file with default
func.setup_axes() # setup everything with the defaults ToDo: take out?
self.update_fields() # update fields in config window
def update_fields(self):
# set current values for all entry variables from config file
self.XY_port.set(g.XY_PORT)
self.Z_port.set(g.Z_PORT)
for key in self.entries.keys():
for i in [0, 1, 2]:
value = func.read_config(g.AXIS_NAMES[i], self.entries[key][3]) # get value from config file
self.entries[key][0][i].set(value) # set initial value on variable
type_value = self.entries[key][0][i].get() # get value with correct data type
factor = self.entries[key][4] # get unit conversion factor
self.entries[key][0][i].set(round(type_value * factor, 3)) # set value with correct unit conversion
def implement(self): # update config file with user inputs into entry fields and reinitialize
# ToDo: Error handling with warning messages to user if wrong data format or high/low value is entered
func.edit_config("PORTS", "xy_port", self.XY_port.get())
func.edit_config("PORTS", "z_port", self.Z_port.get())
for key in self.entries.keys():
for i in [0, 1, 2]:
value = self.entries[key][0][i].get()
factor = self.entries[key][4] # get unit conversion factor
if factor not in [0, 1]: # prevent conversion of int variables to float representation
value = value / factor # implement unit conversion
print(key, value)
func.edit_config(g.AXIS_NAMES[i], self.entries[key][3], value)
func.setup_axes()
class StatusDisplay(Frame):
def __init__(self, parent, controller):
@@ -269,7 +414,7 @@ class StatusDisplay(Frame):
self.update_labels(controller)
def update_labels(self, controller):
# ToDo: do this with a dictionary
# ToDo (optional): do this with a dictionary
g.ARDUINO.update_status_info()
i = 0
for axis in g.AXES: