forked from zietzm/Helmholtz_Test_Bench
Implemented configuration window
This commit is contained in:
+164
-19
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user