Fixed csv_threading.py

This commit is contained in:
2021-09-28 17:46:09 +02:00
parent 4bb6536a84
commit d02bde9631
3 changed files with 152 additions and 122 deletions
+61 -49
View File
@@ -14,7 +14,7 @@ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
# import general packages:
import numpy as np
import os
from os.path import exists
import os.path
import threading
from datetime import datetime
from math import pi
@@ -51,6 +51,19 @@ class HelmholtzGUI(Tk):
self.Menu = TopMenu(self) # display dropdown menu bar at the top (see TopMenu class for details)
# setup status display and output console
status_frame = Frame(self) # create frame to house them
status_frame.pack(side="bottom", fill="x", expand=False) # place at bottom of main window, expand to full width
status_frame.grid_columnconfigure(1, weight=1) # make column 1, (output console), expand to fill full width
# initialize and place status display:
self.StatusDisplay = StatusDisplay(status_frame, self)
self.StatusDisplay.grid(row=0, column=0, sticky="nesw")
# initialize and place output console:
self.OutputConsole = OutputConsole(status_frame)
self.OutputConsole.grid(row=0, column=1, sticky="nesw")
main_area = Frame(self, padx=10, pady=10) # create main area Frame where controls of each mode are displayed
main_area.pack(side="top", fill="both", expand=True) # pack main area at the top of the window
@@ -72,19 +85,6 @@ class HelmholtzGUI(Tk):
self.pages[P] = page # add the page to the dictionary
page.grid(row=0, column=0, sticky="nsew") # place all pages in the same place in the GUI
# setup status display and output console
status_frame = Frame(self) # create frame to house them
status_frame.pack(side="bottom", fill="x", expand=False) # place at bottom of main window, expand to full width
status_frame.grid_columnconfigure(1, weight=1) # make column 1, (output console), expand to fill full width
# initialize and place status display:
self.StatusDisplay = StatusDisplay(status_frame, self)
self.StatusDisplay.grid(row=0, column=0, sticky="nesw")
# initialize and place output console:
self.OutputConsole = OutputConsole(status_frame)
self.OutputConsole.grid(row=0, column=1, sticky="nesw")
self.show_frame(ManualMode) # show manual mode to start with
def show_frame(self, key): # method to switch between pages in the main area
@@ -361,16 +361,19 @@ class ExecuteCSVMode(Frame):
# Functional init:
self.csv_thread = None # the thread object for executing a csv sequence
self.sequence_array = None # array containing the values from the csv file
self.sequence_array_ok = False # Is the data valid?
# Build UI:
self.grid_rowconfigure(ALL, weight=1) # configure rows and columns of the Tkinter grid to expand with window
self.grid_columnconfigure(ALL, weight=1)
# Tkinter variables for axis hardware checks. Controlled by 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
# --- UI ELEMENTS ---
row_counter = 0 # keep track of which grid row we are in
self.row_elements = [] # make list of elements in rows to later calculate height available for plot
# setup headline
# setup header
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) # add to list of row elements
@@ -380,8 +383,6 @@ class ExecuteCSVMode(Frame):
# Setup buttons
# Setup frame to house buttons:
self.top_buttons_frame = Frame(self)
self.top_buttons_frame.grid_rowconfigure(ALL, weight=1)
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) # add frame to list of row elements
@@ -419,10 +420,6 @@ class ExecuteCSVMode(Frame):
checkbox_label = Label(self.checkbox_frame, text="Disable device connection checks:")
checkbox_label.grid(row=0, column=0, sticky=W, padx=3)
# 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)
@@ -437,10 +434,12 @@ class ExecuteCSVMode(Frame):
row_counter += 1
# 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)
self.plotFrame.grid(row=row_counter, column=0, sticky="nsw", padx=10, pady=10)
self.plot_frame = Frame(self)
self.plot_frame.grid_rowconfigure(0, weight=1)
self.plot_frame.grid_columnconfigure(0, weight=1)
self.plot_frame.grid(row=row_counter, column=0, sticky="nsw", padx=10, pady=10)
self.plot_canvas = None # Is generated upon plotting
def page_switch(self): # function that is called when switching to this window
# every class in the UI needs this, even if it doesn't do anything
@@ -451,19 +450,22 @@ class ExecuteCSVMode(Frame):
# 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)
if os.path.exists(filename): # does the file exist?
ui_print("CSV file selected:", filename)
try: # try to read data to an array
self.sequence_array = csv_threading.read_csv_to_array(filename) # read array from csv
except BaseException as e: # something went wrong, probably wrong format in csv
except Exception as e: # something went wrong, probably wrong format in csv
self.sequence_array_ok = False
# display error messages:
ui_print("Error while opening file:", e)
messagebox.showerror("Error!", "Error while opening file: \n%s" % e)
try: # try to check the values and display the plot
csv_threading.check_array_ok(self.sequence_array) # check for values exceeding limits
self.sequence_array_ok = True # Has nothing to do with limits. Just means the data was parsed
self.display_plot() # plot data and display
except BaseException as e: # something went wrong, probably wrong format in csv
except Exception as e: # something went wrong, probably wrong format in csv
self.sequence_array_ok = False
# display error messages:
ui_print("Error while processing data from file:", e)
messagebox.showerror("Error!", "Error while processing data from file: \n%s" % e)
@@ -477,19 +479,18 @@ class ExecuteCSVMode(Frame):
messagebox.showerror("File not found", "Selected file %s does not exist, could not load." % filename)
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 thread object:
try:
self.csv_thread = csv_threading.ExecCSVThread(self.sequence_array, self, self.controller)
self.csv_thread.start() # start thread
self.csv_thread = csv_threading.ExecCSVThread(self.sequence_array, self, self.controller, logging_enabled)
self.csv_thread.start() # start thread
# (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"
except DeviceAccessError as e:
ui_print(e)
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
@@ -505,27 +506,38 @@ class ExecuteCSVMode(Frame):
logger.log_datapoint() # log data
def reinitialize(self): # called on "Reinitialize devices" button press
func.setup_all() # reinitialize all PSUs and the Arduino
# reinitialize all PSUs and the Arduino
g.CAGE_DEVICE.reconnect_hardware()
# log change to the log file if user has selected event logging in the Configure Logging window
logger = self.controller.pages[ConfigureLogging] # get object of logging configurator
if logger.event_logging: # data should be logged when test bench is commanded
logger.log_datapoint() # log data
def display_plot(self): # generate and display a plot of the data loaded from a csv file
def display_plot(self):
"""Generate and display a plot of the data loaded from a csv file"""
if not self.sequence_array_ok:
return
# calculate available height for plot (in pixels):
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
# 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
# Create plot
figure = csv_threading.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 the UI
# Clear previous plots first
try:
self.plot_canvas.get_tk_widget().destroy()
except:
pass
# Show new plot
self.plot_canvas = FigureCanvasTkAgg(figure, self.plot_frame) # create canvas to draw figure on
self.plot_canvas.draw() # equivalent to matplotlib.show()
self.plot_canvas.get_tk_widget().grid(row=0, column=0, sticky="nesw") # place canvas in the UI
class CalibrateAmbientField(Frame):