forked from zietzm/Helmholtz_Test_Bench
Fixed csv_threading.py
This commit is contained in:
+1
-1
@@ -88,7 +88,7 @@ def write_to_file(dataframe, filepath):
|
||||
except PermissionError:
|
||||
message = "No permission to write to: \n%s. \nFile may be open in another program." % filepath
|
||||
messagebox.showerror("Permission Error", message)
|
||||
except BaseException as e:
|
||||
except Exception as e:
|
||||
message = "Error while trying to write to file \n%s.\n%s" % (filepath, e)
|
||||
messagebox.showerror("Error!", message)
|
||||
else: # no exceptions occurred
|
||||
|
||||
+90
-72
@@ -1,16 +1,16 @@
|
||||
# tThis file contains code for executing a sequence of magnetic fields from a csv file.
|
||||
# This file contains code for executing a sequence of magnetic fields from a csv file.
|
||||
# To do this without crashing the UI it has to run in a separate thread using the threading module.
|
||||
# ToDo!: apparently max. 1 thread can access PSU --> continuous update + csv thread crashes program. Find solution
|
||||
|
||||
# import packages:
|
||||
import time
|
||||
from io import StringIO
|
||||
|
||||
import pandas
|
||||
import numpy as np
|
||||
from threading import *
|
||||
from tkinter import messagebox
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# import other project files:
|
||||
from src.exceptions import DeviceBusy, DeviceAccessError
|
||||
from src.utility import ui_print
|
||||
import src.user_interface as ui
|
||||
import src.globals as g
|
||||
@@ -20,114 +20,133 @@ class ExecCSVThread(Thread):
|
||||
# main class for executing a CSV sequence
|
||||
# it inherits the threading.Thread class, enabling sequence execution in a separate thread
|
||||
|
||||
def __init__(self, array, parent, controller, logging_enabled):
|
||||
def __init__(self, array, parent, controller):
|
||||
Thread.__init__(self)
|
||||
|
||||
self.array = array # numpy array containing data from csv to be executed
|
||||
self.parent = parent # object from which this class is called, here the ExecuteCSVMode object of the UI
|
||||
self.controller = controller # object on which mainloop() is running, usually the main UI window
|
||||
self.logging_enabled = logging_enabled
|
||||
|
||||
self.__stop_event = Event() # event which can be set to stop the thread execution if needed
|
||||
# Acquire cage device. This resource will only be released after the thread is ended.
|
||||
try:
|
||||
self.cage_dev = g.CAGE_DEVICE.request_proxy()
|
||||
except DeviceBusy:
|
||||
raise DeviceAccessError("Failed to acquire coil control. Required for ambient field calibration.")
|
||||
|
||||
self._stop_event = Event() # event which can be set to stop the thread execution if needed
|
||||
|
||||
def run(self): # called to start the execution of the thread
|
||||
ui_print("\nStarting Sequence Execution...")
|
||||
self.execute_sequence(self.array, 0.1, self.parent, self.controller) # run sequence
|
||||
# when the sequence has ended, reset buttons on the UI:
|
||||
if not g.exitFlag: # main window is open
|
||||
self.parent.select_file_button["state"] = "normal"
|
||||
self.parent.execute_button["state"] = "normal"
|
||||
self.parent.stop_button["state"] = "disabled"
|
||||
self.parent.reinit_button["state"] = "normal"
|
||||
try:
|
||||
self.execute_sequence(self.array, 0.1, self.parent, self.controller) # run sequence
|
||||
finally:
|
||||
self.cage_dev.idle() # set currents and voltages to 0, set arduino pins to low
|
||||
# Release the proxy so other components can use it
|
||||
self.cage_dev.close()
|
||||
|
||||
# when the sequence has ended, reset buttons on the UI:
|
||||
if not g.exitFlag: # main window is open
|
||||
self.parent.select_file_button["state"] = "normal"
|
||||
self.parent.execute_button["state"] = "normal"
|
||||
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()
|
||||
self._stop_event.set()
|
||||
|
||||
@property
|
||||
def stopped(self): # returns true if the thread has been stopped, used to check if a run should continue
|
||||
return self.__stop_event.is_set()
|
||||
return self._stop_event.is_set()
|
||||
|
||||
def execute_sequence(self, array, delay, parent, controller):
|
||||
# main execution method of the class
|
||||
# runs through array with times and desired fields and commands test bench accordingly
|
||||
|
||||
# array format: [time (s), xField (T), yField (T), zField (T)]
|
||||
func.power_down_all() # sets outputs on PSUs to 0 and Arduino pins to LOW before starting
|
||||
self.cage_dev.idle() # sets outputs on PSUs to 0 and Arduino pins to LOW before starting
|
||||
t_zero = time.time() # set reference time for start of run
|
||||
|
||||
# Check if everything is properly connected:
|
||||
all_connected = func.devices_ok(parent.xy_override.get(), parent.z_override.get(),
|
||||
parent.arduino_override.get())
|
||||
all_connected = (parent.xy_override.get() or g.CAGE_DEVICE.psu1 is not None) and\
|
||||
(parent.z_override.get() or g.CAGE_DEVICE.psu2 is not None) and\
|
||||
(parent.arduino_override.get() or g.CAGE_DEVICE.arduino is not None)
|
||||
# True or False depending on devices status, checks for some devices may be overridden by user
|
||||
if not all_connected:
|
||||
ui_print("Required devices are not present, sequence aborted.")
|
||||
messagebox.showwarning("Device Error!", "Required devices are not present, sequence aborted.")
|
||||
return
|
||||
|
||||
i = 0 # index of the current array row
|
||||
while i < len(array) and all_connected and not self.stopped() and not g.exitFlag:
|
||||
while i < len(array):
|
||||
if self.stopped or g.exitFlag:
|
||||
# Interrupt sequence
|
||||
ui_print("Sequence interrupted, powering down channels.")
|
||||
# Channels powered down in run function
|
||||
return
|
||||
|
||||
# while array is not finished, devices are connected, user has not cancelled and application is running
|
||||
|
||||
t = time.time() - t_zero # get time relative to start of run
|
||||
if t >= array[i, 0]: # time for this row has come
|
||||
g.threadLock.acquire() # execute all lines until threadLock.release() before going back to main thread
|
||||
target_t = array[i, 0] # Target execution time of data point
|
||||
if t >= target_t: # time for this row has come
|
||||
field_vec = array[i, 1:4] # extract desired field vector
|
||||
ui_print("[{:5.3f}s] B=[{:.1f}, {:.1f}, {:.1f}]\u03BCT for t={:.2f}s".format(t,
|
||||
field_vec[0] * 1e6,
|
||||
field_vec[1] * 1e6,
|
||||
field_vec[2] * 1e6,
|
||||
target_t))
|
||||
self.cage_dev.set_field_compensated(field_vec) # send field vector to test bench
|
||||
|
||||
# check if everything is still connected before sending commands:
|
||||
all_connected = func.devices_ok(parent.xy_override.get(), parent.z_override.get(),
|
||||
parent.arduino_override.get())
|
||||
if all_connected:
|
||||
field_vec = array[i, 1:4] # extract desired field vector
|
||||
ui_print("%0.5f s: t = %0.2f s, target field vector ="
|
||||
% (time.time() - t_zero, array[i, 0]), field_vec * 1e6, "\u03BCT")
|
||||
func.set_field(field_vec) # send field vector to test bench
|
||||
# log change to the log file if user has selected event logging in the Configure Logging window
|
||||
logger = controller.pages[ui.ConfigureLogging] # get object of logging configurator
|
||||
if logger.event_logging: # data should be logged when test bench is commanded
|
||||
logger.log_datapoint() # log data
|
||||
|
||||
# log change to the log file if user has selected event logging in the Configure Logging window
|
||||
logger = controller.pages[ui.ConfigureLogging] # get object of logging configurator
|
||||
if logger.event_logging: # data should be logged when test bench is commanded
|
||||
logger.log_datapoint() # log data
|
||||
i = i + 1 # next row
|
||||
|
||||
i = i + 1 # next row
|
||||
|
||||
g.threadLock.release() # allow going back to main thread now
|
||||
|
||||
elif t <= array[i, 0] - delay - 0.02: # is there enough time to sleep before the next row?
|
||||
elif t <= target_t - delay - 0.02: # is there enough time to sleep before the next row?
|
||||
time.sleep(delay) # sleep to give other threads time to run
|
||||
|
||||
if not self.stopped() and not g.exitFlag and all_connected: # sequence ended without interruption
|
||||
ui_print("Sequence executed, powering down channels.")
|
||||
elif all_connected: # interrupted by user
|
||||
ui_print("Sequence cancelled, powering down channels.")
|
||||
elif not all_connected: # interrupted by device error
|
||||
ui_print("Error with at least one device, sequence aborted.")
|
||||
messagebox.showwarning("Device Error!", "Error with at least one device, sequence aborted.")
|
||||
else: # if this happens there is a mistake in the logic above, it really should not
|
||||
# tell the user something weird happened:
|
||||
ui_print("Encountered unexpected sequence end state:"
|
||||
"\nThread Stopped:", self.stopped(), ", Application Closed:", g.exitFlag,
|
||||
", Devices connected:", all_connected)
|
||||
messagebox.showwarning("Unexpected state",
|
||||
"Encountered unexpected sequence end state, see console output for details.")
|
||||
|
||||
func.power_down_all() # set currents and voltages to 0, set arduino pins to low
|
||||
ui_print("Sequence executed, powering down channels.")
|
||||
|
||||
|
||||
def read_csv_to_array(filepath): # convert a given csv file to a numpy array
|
||||
# csv format: time (s); xField (T); yField (T); zField (T) (german excel)
|
||||
# decimal commas
|
||||
file = pandas.read_csv(filepath, sep=';', decimal=',', header=0) # read csv file without column headers
|
||||
array = file.to_numpy() # convert csv to array
|
||||
return array
|
||||
# decimal or period commas. Do not use these characters as a thousands seperator!
|
||||
with open(filepath, 'r') as csv_file:
|
||||
# Normalize seperators
|
||||
csv_string = csv_file.read().replace(',', '.')
|
||||
# read csv file without column headers
|
||||
file = pandas.read_csv(StringIO(csv_string), sep=';', decimal='.', header=0)
|
||||
array = file.to_numpy() # convert csv to array
|
||||
return array
|
||||
|
||||
|
||||
def check_array_ok(array):
|
||||
# check if any magnetic fields in an array exceed the test bench limits and if so display a warning message
|
||||
values_ok = True
|
||||
"""Checks if values are within limits, and if not shows a warning message."""
|
||||
# check if any magnetic fields in an array exceed the test bench limits
|
||||
warnings = []
|
||||
for i in [0, 1, 2]: # go through axes/columns
|
||||
max_val = g.AXES[i].max_comp_field[1] # get limits the test bench can do
|
||||
min_val = g.AXES[i].max_comp_field[0]
|
||||
data = array[:, i + 1] # extract data for this axis from array
|
||||
# noinspection PyTypeChecker
|
||||
if any(data > max_val) or any(data < min_val): # if any datapoint is out of bounds
|
||||
values_ok = False
|
||||
if not values_ok: # show warning pop-up if values are exceeding limits
|
||||
messagebox.showwarning("Value Limits Warning!", "Found field values exceeding limits of test bench."
|
||||
"\nSee plot and check values in csv.")
|
||||
# get limits the test bench can do
|
||||
min_val, max_val = g.CAGE_DEVICE.axes[i].max_comp_field
|
||||
for row_idx in range(array.shape[0]):
|
||||
data_point = array[row_idx, i + 1] # extract data for this axis from array
|
||||
if data_point > max_val or data_point < min_val:
|
||||
# Out of bounds
|
||||
warnings.append({'row': row_idx+1, 'axis': g.AXIS_NAMES[i]})
|
||||
|
||||
# show warning pop-up if values are exceeding limits
|
||||
nr_warnings = len(warnings)
|
||||
if nr_warnings > 0:
|
||||
warning_msg = "Found field values exceeding limits of test bench.\n"
|
||||
# Only print the first three warnings
|
||||
for i in range(min(nr_warnings, 3)):
|
||||
warning_msg += "[Line {}] {} exceeds limits.\n".format(warnings[i]['row'], warnings[i]['axis'])
|
||||
if nr_warnings > 3:
|
||||
warning_msg += "And {} more...".format(nr_warnings - 3)
|
||||
# Show all warnings collectively
|
||||
messagebox.showwarning("Value Limits Warning!", warning_msg)
|
||||
|
||||
|
||||
def plot_field_sequence(array, width, height): # create plot of fixed size (pixels) from array
|
||||
@@ -156,8 +175,7 @@ def plot_field_sequence(array, width, height): # create plot of fixed size (pix
|
||||
t = new_array[:, 0] # extract time column
|
||||
for i in [0, 1, 2]: # go through all three axes
|
||||
data = new_array[:, i + 1] * 1e6 # extract field column of this axis and convert to microtesla
|
||||
max_val = g.AXES[i].max_comp_field[1] * 1e6 # get limits of achievable field
|
||||
min_val = g.AXES[i].max_comp_field[0] * 1e6
|
||||
min_val, max_val = g.CAGE_DEVICE.axes[i].max_comp_field * 1e6 # get limits of achievable field
|
||||
plot = axes[i] # get appropriate subplot
|
||||
|
||||
plot.plot(t, data, linestyle='solid', marker='.') # plot data
|
||||
|
||||
+61
-49
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user