code cleanup and comments

UI: CSV mode
This commit is contained in:
Martin Zietz
2021-02-17 11:51:20 +01:00
parent f5dc7f097e
commit 056fd3efdd
2 changed files with 53 additions and 33 deletions
+52 -33
View File
@@ -315,27 +315,31 @@ class ManualMode(Frame):
class ExecuteCSVMode(Frame):
# generate configuration window to set program constants
# Mode for executing magnetic field sequences from csv files.
# Inherits the Frame object from Tkinter and is placed in the mainArea of the application window.
# Multithreading is used to execute the sequence without crashing the UI
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.parent = parent
self.parent = parent # parent UI object, e.g. the mainArea frame
self.controller = controller # object on which mainloop() is running, usually main window
# Functional init:
self.csv_thread = None # the thread object for executing csv
self.csv_thread = None # the thread object for executing a csv sequence
self.sequence_array = None # array containing the values from the csv file
# Build UI:
self.grid_rowconfigure(ALL, weight=1)
self.grid_rowconfigure(ALL, weight=1) # configure rows and columns of the Tkinter grid to expand with window
self.grid_columnconfigure(ALL, weight=1)
row_counter = 0
self.row_elements = [] # make list of elements in rows to calculate height available for plot
row_counter = 0 # keep track of which grid row we are in
# setup heading
self.row_elements = [] # make list of elements in rows to later calculate height available for plot
# setup headline
header = Label(self, text="Execute CSV Mode", font=HEADER_FONT, pady=3)
header.grid(row=row_counter, column=0, padx=100, sticky=W)
self.row_elements.append(header)
self.row_elements.append(header) # add to list of row elements
row_counter += 1
@@ -346,38 +350,46 @@ class ExecuteCSVMode(Frame):
self.top_buttons_frame.grid_columnconfigure(ALL, weight=1)
self.top_buttons_frame.grid(row=row_counter, column=0, sticky=W, padx=20)
self.row_elements.append(self.top_buttons_frame)
self.row_elements.append(self.top_buttons_frame) # add frame to list of row elements
# Create and place buttons
# Create and place buttons:
# add button for selecting a csv file to execute:
self.select_file_button = Button(self.top_buttons_frame, text="Select csv file...", command=self.load_csv,
pady=5, padx=5, font=SMALL_BUTTON_FONT)
self.select_file_button.grid(row=0, column=0, padx=5)
# add button to start running the sequence from the file:
self.execute_button = Button(self.top_buttons_frame, text="Run Sequence", command=self.run_sequence,
pady=5, padx=5, font=SMALL_BUTTON_FONT, state="disabled")
self.execute_button.grid(row=0, column=1, padx=5)
# add button to stop/interrupt the sequence:
self.stop_button = Button(self.top_buttons_frame, text="Stop Run", command=self.stop_run,
pady=5, padx=5, font=SMALL_BUTTON_FONT, state="disabled")
self.stop_button.grid(row=0, column=2, padx=5)
# add button for reinitialization
# add button to reinitialize devices (e.g. after reconnecting a device)
self.reinit_button = Button(self.top_buttons_frame, text="Reinitialize Devices", command=self.reinitialize,
pady=5, padx=5, font=SMALL_BUTTON_FONT)
self.reinit_button.grid(row=0, column=3, padx=5)
row_counter += 1
# setup testing checkboxes
# setup checkboxes to disable checks if devices are connected (enables testing with some devices missing)
# setup frame to house checkboxes:
self.checkbox_frame = Frame(self)
self.checkbox_frame.grid_rowconfigure(ALL, weight=1)
self.checkbox_frame.grid_columnconfigure(ALL, weight=1)
self.checkbox_frame.grid(row=row_counter, column=0, sticky=W, padx=20)
self.row_elements.append(self.checkbox_frame)
self.row_elements.append(self.checkbox_frame) # add frame to list of row elements
# setup label to explain checkbox function:
checkbox_label = Label(self.checkbox_frame, text="Disable device connection checks:")
checkbox_label.grid(row=0, column=0, sticky=W, padx=3)
self.xy_override = BooleanVar(value=False)
self.z_override = BooleanVar(value=False)
self.arduino_override = BooleanVar(value=False)
# create variables for the checkboxes:
self.xy_override = BooleanVar(value=False) # True to disable connection check for XY PSU
self.z_override = BooleanVar(value=False) # True to disable connection check for Z PSU
self.arduino_override = BooleanVar(value=False) # True to disable connection check for arduino
# create checkboxes:
xy_checkbox = Checkbutton(self.checkbox_frame, text="XY PSU",
variable=self.xy_override, onvalue=True, offvalue=False)
xy_checkbox.grid(row=0, column=1, padx=3)
@@ -390,7 +402,7 @@ class ExecuteCSVMode(Frame):
row_counter += 1
# make frame for plot of csv values
# make frame for plot of csv values (plot is generated and placed in display_plot() method)
self.plotFrame = Frame(self)
self.plotFrame.grid_rowconfigure(0, weight=1)
self.plotFrame.grid_columnconfigure(0, weight=1)
@@ -402,44 +414,50 @@ class ExecuteCSVMode(Frame):
def load_csv(self): # load in csv file to be executed
directory = os.path.abspath(os.getcwd()) # get project directory
# open file selection dialogue and save path of selected file
# open file selection dialogue and store path of selected file
filename = filedialog.askopenfilename(initialdir=directory, title="Select CSV File",
filetypes=(("Comma Separated Values", "*.csv*"), ("All Files", "*.*")))
if exists(filename): # does the file exist?
ui_print("File selected:", filename)
try:
try: # try to read data to an array
self.sequence_array = csv.read_csv_to_array(filename) # read array from csv
except BaseException as e:
except BaseException as e: # something went wrong, probably wrong format in csv
# display error messages:
ui_print("Error while opening file:", e)
messagebox.showerror("Error!", "Error while opening file: \n%s" % e)
try:
try: # try to check the values and display the plot
csv.check_array_ok(self.sequence_array) # check for values exceeding limits
self.display_plot() # plot data and display
except BaseException as e:
except BaseException as e: # something went wrong, probably wrong format in csv
# display error messages:
ui_print("Error while processing data from file:", e)
messagebox.showerror("Error!", "Error while processing data from file: \n%s" % e)
else:
self.execute_button["state"] = "normal" # activate run button
else: # nothing went wrong
self.execute_button["state"] = "normal" # activate run button --> enable execution
elif filename == '': # this happens when file selection window is closed without selecting a file
ui_print("No file selected, could not load.")
else:
else: # file does not exist
# display error messages:
ui_print("Selected file", filename, "does not exist, could not load.")
messagebox.showerror("File not found", "Selected file %s does not exist, could not load." % filename)
def run_sequence(self):
def run_sequence(self): # called on run button press, starts thread for executing the sequence
# (de)activate buttons as needed:
self.select_file_button["state"] = "disabled"
self.execute_button["state"] = "disabled"
self.stop_button["state"] = "normal"
self.reinit_button["state"] = "disabled"
# setup thread for running the sequence:
# More info: https://www.tutorialspoint.com/python/python_multithreading.htm
g.threadLock = threading.Lock() # create thread locking object, used to ensure all devices switch at once later
# create separate thread to run sequence execution in:
# create thread object:
self.csv_thread = csv.ExecCSVThread(self.sequence_array, self, self.controller)
self.csv_thread.start() # start thread
def stop_run(self):
self.csv_thread.stop() # this will cause the csv loop to end
def stop_run(self): # called on stop button press, interrupts sequence execution
self.csv_thread.stop() # call stop method of thread object, this will cause the csv loop to end
# (de)activate buttons as needed:
self.select_file_button["state"] = "normal"
self.execute_button["state"] = "normal"
@@ -459,19 +477,20 @@ class ExecuteCSVMode(Frame):
if logger.event_logging: # data should be logged when test stand is commanded
logger.log_datapoint() # log data
def display_plot(self):
def display_plot(self): # generate and display a plot of the data loaded from a csv file
# calculate available height for plot (in pixels):
height_others = 0
height_others = 0 # initialize variable to calculate height of other widgets
for element in self.row_elements: # go through all rows in the widget except the plot frame
height_others += element.winfo_height() # add up heights
height = self.parent.winfo_height() - height_others - 50 # set to height of parent frame - other rows - margin
# calculate available plot height:
height = self.parent.winfo_height() - height_others - 50 # height of parent frame - other widgets - margin
width = min(self.parent.winfo_width() - 100, 1100) # set width to available space but max. 1100
figure = csv.plot_field_sequence(self.sequence_array, width, height) # create figure to be displayed
plotCanvas = FigureCanvasTkAgg(figure, self.plotFrame) # create canvas to draw figure on
plotCanvas.draw() # equivalent to matplotlib.show()
plotCanvas.get_tk_widget().grid(row=0, column=0, sticky="nesw") # place canvas in UI
plotCanvas.get_tk_widget().grid(row=0, column=0, sticky="nesw") # place canvas in the UI
class Configuration(Frame):
+1
View File
@@ -36,6 +36,7 @@ class ExecCSVThread(Thread):
self.parent.stop_button["state"] = "disabled"
self.parent.reinit_button["state"] = "normal"
# setup ability to interrupt thread (https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread)
def stop(self): # stop thread execution, can be called from another thread to kill this one
self.__stop_event.set()