Files
Helmholtz_Test_Bench/User_Interface.py
T
2021-01-25 15:37:42 +01:00

261 lines
9.9 KiB
Python

from tkinter import *
from tkinter import ttk
import settings as g
import cage_func as func
import random as rand
NORM_FONT = ()
SUB_HEADER_FONT = ("Arial", 9, "bold")
BIG_BUTTON_FONT = ("Arial", 11, "bold")
class HelmholtzGUI(Tk):
def __init__(self):
Tk.__init__(self)
Tk.wm_title(self, "Helmholtz Cage Control")
Tk.wm_iconbitmap(self, "Helmholtz.ico")
self.Menu = TopMenu(self) # displays menu bar at the top
mainArea = Frame(self)
mainArea.pack(side="top", fill="both", expand=False)
mainArea.grid_rowconfigure(0, weight=1)
mainArea.grid_columnconfigure(0, weight=1)
self.frames = {} # dictionary for storing all pages
for F in [TestFrame, ManualMode]:
frame = F(mainArea, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.StatusDisplay = StatusDisplay(self, self)
self.StatusDisplay.pack(side="bottom", fill="x", expand=False)
self.show_frame(ManualMode)
def show_frame(self, key):
frame = self.frames[key] # gets correct page from the dictionary
frame.tkraise() # brings this frame to the front
class TopMenu:
def __init__(self, window):
print("menu called")
menu = Menu(window)
window.config(menu=menu)
ModeSelector = Menu(menu)
menu.add_cascade(label="Mode", menu=ModeSelector)
ModeSelector.add_command(label="Static Manual Input", command=lambda: self.manual_mode(window))
def manual_mode(self, window):
print("Switching to static manual mode")
window.show_frame(ManualMode)
class TestFrame(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
print("TestFrame called")
one = Label(self, text="One", bg="red")
one.pack(fill=X)
two = Label(self, text="Two", bg="blue")
two.pack()
button = ttk.Button(self, text="Print stuff", command=lambda: print_stuff("Hello"))
button.pack()
class ManualMode(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.grid_rowconfigure(ALL, weight=1)
self.grid_columnconfigure(ALL, weight=1)
row_counter = 0
# Setup Dropdown Menu for input mode
dropdown_frame = Frame(self)
dropdown_frame.grid_rowconfigure(ALL, weight=1)
dropdown_frame.grid_columnconfigure(ALL, weight=1)
dropdown_frame.grid(row=row_counter, column=0)
self.input_mode = StringVar()
self.modes = {"Raw Current": self.input_raw_current,
"Signed Current": self.input_signed_current,
"Magnetic Field": self.input_field}
input_mode_selector = ttk.OptionMenu(dropdown_frame, self.input_mode, list(self.modes.keys())[2], *self.modes.keys())
input_mode_selector.grid(row=0, column=1, sticky=W) # place dropdown on the grid
dropdown_frame.grid_columnconfigure(1, minsize=115) # set size of column with dropdown to keep it from moving
selector_label = Label(dropdown_frame, text="Select Input Mode:", padx=10, pady=10)
selector_label.grid(row=0, column=0)
row_counter = row_counter + 1
# Setup Entry fields
self.entries_frame = Frame(self)
self.entries_frame.grid_rowconfigure(ALL, weight=1)
self.entries_frame.grid_columnconfigure(ALL, weight=1)
self.entries_frame.grid(row=row_counter, column=0)
entry_texts = ["X-Axis:", "Y-Axis:", "Z-Axis:"]
entry_vars = [StringVar for _ in range(len(entry_texts))]
row = 0
for text in entry_texts:
field = ttk.Entry(self.entries_frame, textvariable=entry_vars[row])
field.grid(row=row, column=1, sticky=W)
label = Label(self.entries_frame, text=text, padx=5, pady=10)
label.grid(row=row, column=0, sticky=W)
row = row + 1
row_counter = row_counter + 1
# Setup execute button
Label(self, text="").grid(row=row_counter, column=0) # add spacer
row_counter = row_counter + 1
execute_button = Button(self, text="Execute!", command=lambda: self.execute(),
pady=5, padx=5, font=BIG_BUTTON_FONT)
execute_button.grid(row=row_counter, column=0, columnspan=2)
# Add spacer to Frame below
row_counter = row_counter + 1
Label(self, text="", pady=10).grid(row=row_counter, column=0)
self.input_mode.trace_add('write', self.change_mode_callback) # call mode change function on dropdown change
self.input_field() # call up default mode at the start
def change_mode_callback(self, var, index, mode):
self.clear_widgets()
function_to_call = self.modes[self.input_mode.get()]
function_to_call()
print(self.entries_frame.grid_slaves(row=1, column=2))
def clear_widgets(self):
for col in [2, 3]:
for row in range(3):
try:
widget = self.entries_frame.grid_slaves(row=row, column=col)[0]
widget.grid_forget()
except IndexError:
pass
def input_field(self):
for row in range(3):
label = Label(self.entries_frame, text="\u03BCT", padx=5, pady=10)
label.grid(row=row, column=2, sticky=W)
def input_raw_current(self):
for row in range(3):
label = Label(self.entries_frame, text="A", padx=5, pady=10)
label.grid(row=row, column=2, sticky=W)
def input_signed_current(self):
for row in range(3):
label = Label(self.entries_frame, text="A", padx=5, pady=10)
label.grid(row=row, column=2, sticky=W)
def execute(self):
print("executing", self.input_mode.get())
class StatusDisplay(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent, relief=SUNKEN, bd=1)
self.grid_rowconfigure(ALL, weight=1)
self.grid_columnconfigure(ALL, weight=1)
rowCounter = 0 # keep track of which row we are at in the grid layout
x_pad = 10 # centrally set padding
col = 0
for header in ["", "X-Axis", "Y-Axis", "Z-Axis"]: # create Column headers
headLabel = Label(self, text=header, font=SUB_HEADER_FONT, borderwidth=1,
relief="flat", anchor="w", padx=x_pad)
headLabel.grid(row=rowCounter, column=col, sticky="ew")
col = col + 1 # move to next column
rowCounter = rowCounter + 1 # increase row counter to place future stuff below header
# define content of row entries
TextLabels = ["PSU Serial Port:", "PSU Channel:", "PSU Status:", "Arduino Status:", "", "Output:",
"Remote Control:",
"Voltage Setpoint:", "Actual Voltage:", "Current Setpoint:", "Actual Current:", "",
"Target Field:", "Trgt. Field Raw:", "Target Current:", "Inverted:"]
self.rowNo = len(TextLabels) # get number of label rows
self.columnNo = 4 # number of label columns
# prepare list of lists to contain all labels for row entries in all columns:
self.Labels = [[] for _ in range(self.columnNo)]
self.label_dict = {}
for name in TextLabels:
self.label_dict[name] = [StringVar() for _ in range(self.columnNo - 1)]
# add labels for row titles
self.Labels[0].append(Label(self, text=name, borderwidth=1, relief="flat", anchor="w", padx=x_pad))
for col in range(self.columnNo - 1): # add labels vor values
self.Labels[col + 1].append(Label(self, textvariable=self.label_dict[name][col],
borderwidth=1, relief="flat", anchor="w", padx=x_pad))
col = 0
for LabelCol in self.Labels: # place row entries in grid layout for all columns
for row in range(self.rowNo): # place row entries
LabelCol[row].grid(row=row + rowCounter, column=col, sticky="nsew")
col = col + 1
rowCounter = rowCounter + self.rowNo # increase row counter to place future stuff below this
self.update_labels(controller)
def update_labels(self, controller):
i = 0
for axis in g.AXES:
if axis.device is not None:
axis.update_status_info()
self.label_dict["PSU Serial Port:"][i].set(g.ports[i])
self.label_dict["PSU Channel:"][i].set(axis.channel)
self.label_dict["PSU Status:"][i].set(axis.connected)
self.label_dict["Arduino Status:"][i].set(g.ARDUINO.connected) # ToDo (optional): make this multicolumn
self.label_dict["Output:"][i].set(axis.output_active)
self.label_dict["Remote Control:"][i].set(axis.remote_ctrl_active)
self.label_dict["Voltage Setpoint:"][i].set("%0.3f V" % axis.voltage_setpoint)
self.label_dict["Actual Voltage:"][i].set("%0.3f V" % axis.voltage)
self.label_dict["Current Setpoint:"][i].set("%0.3f A" % axis.current_setpoint)
self.label_dict["Actual Current:"][i].set("%0.3f A" % axis.current)
self.label_dict["Target Field:"][i].set("%0.3f \u03BCT" % (axis.target_field * 1e6))
self.label_dict["Trgt. Field Raw:"][i].set("%0.3f \u03BCT" % (axis.target_field_comp * 1e6))
self.label_dict["Target Current:"][i].set("%0.3f A" % axis.target_current)
self.label_dict["Inverted:"][i].set(axis.polarity_switched)
i = i + 1
controller.after(2000, lambda: self.update_labels(controller))
# ToDo: remove
def print_stuff(stuff):
print(stuff)
def random_no():
return rand.uniform(0, 20)
class TestValues:
def __init__(self):
self.val1 = 0
self.val2 = 0
self.val3 = 0
def update_values(self):
self.val1 = rand.uniform(0, 20)
self.val2 = rand.uniform(0, 20)
self.val3 = rand.uniform(0, 20)