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)