From 11b5ea4e362228b5402ee60a6908d7ef8dfabd50 Mon Sep 17 00:00:00 2001 From: Leon Teichroeb Date: Mon, 2 Aug 2021 22:21:29 +0200 Subject: [PATCH] Made imports more consistent --- src/arduino_device.py | 2 +- src/cage_func.py | 10 +++++----- src/config_handling.py | 22 +++++++++++----------- src/csv_logging.py | 10 +++++----- src/csv_threading.py | 24 ++++++++++++------------ src/main.py | 19 +++++++++---------- src/socket_control.py | 8 ++++---- src/user_interface.py | 35 ++++++++++++----------------------- src/utility.py | 17 +++++++++++++++++ 9 files changed, 76 insertions(+), 71 deletions(-) create mode 100644 src/utility.py diff --git a/src/arduino_device.py b/src/arduino_device.py index 45e9fc1..4e4e298 100644 --- a/src/arduino_device.py +++ b/src/arduino_device.py @@ -1,6 +1,6 @@ from src.arduino import Arduino import src.config_handling as config -from src.user_interface import ui_print +from src.utility import ui_print import src.globals as g diff --git a/src/cage_func.py b/src/cage_func.py index 4e6e839..e39cb97 100644 --- a/src/cage_func.py +++ b/src/cage_func.py @@ -8,11 +8,11 @@ import traceback from tkinter import messagebox # import other project files -from user_interface import ui_print -from ps2000b import PS2000B -from arduino import Arduino -import config_handling as config -import globals as g +from src.utility import ui_print +from src.ps2000b import PS2000B +from src.arduino import Arduino +import src.config_handling as config +import src.globals as g class Axis: diff --git a/src/config_handling.py b/src/config_handling.py index 3b49065..e56f0e6 100644 --- a/src/config_handling.py +++ b/src/config_handling.py @@ -6,10 +6,10 @@ from configparser import ConfigParser from tkinter import messagebox # import other project files: -import globals as g -import cage_func as func -# noinspection PyPep8Naming -import user_interface as ui +import src.globals as g +import src.cage_func as func +from src.utility import ui_print +from src.user_interface import HardwareConfiguration global CONFIG_FILE # string storing the path of the used config file global CONFIG_OBJECT # object of type ConfigParser(), storing all configuration information @@ -34,7 +34,7 @@ def read_from_config(section, key, config_object): # read a specific value from value = section_obj[key] # get relevant value in the section return value except KeyError as e: # a section or key was used, that does not exist - ui.ui_print("Error while reading config file:", e) + ui_print("Error while reading config file:", e) raise KeyError("Could not find key", key, "in config file.") @@ -71,22 +71,22 @@ def edit_config(section, key, value, override=False): # edit a specific value i try: section_obj = CONFIG_OBJECT[section] # get relevant section in the config except KeyError: # there is no such section - ui.ui_print("Could not find section", section, "in config file, creating new.") + ui_print("Could not find section", section, "in config file, creating new.") CONFIG_OBJECT.add_section(section) # create the missing section section_obj = CONFIG_OBJECT[section] # get the object of the section try: section_obj[key] = str(value) # set value for correct entry in the section except KeyError: # there is no entry with this key - ui.ui_print("Could not find key", key, "in config file, creating new.") + ui_print("Could not find key", key, "in config file, creating new.") CONFIG_OBJECT.set(section, key, str(value)) # create the entry and set the value except KeyError as e: # key for section or specific value does not exist in the dictionary for max/min values - ui.ui_print("Error while editing config file:", e) + ui_print("Error while editing config file:", e) raise KeyError("Could not find key", key, "in config file.") # return an error def check_config(config_object): # check all numeric values in the config and see if they are within safe limits - ui.ui_print("Checking config file for values exceeding limits:") + ui_print("Checking config file for values exceeding limits:") concerns = {} # initialize dictionary for found problems problem_counter = 0 # count the number of values that exceed limits @@ -105,13 +105,13 @@ def check_config(config_object): # check all numeric values in the config and s if len(concerns[axis]) == 0: # no problems were found for this axis concerns[axis].append("No problems detected.") - ui.ui_print(axis, ":", *concerns[axis]) # print out results for this axis + ui_print(axis, ":", *concerns[axis]) # print out results for this axis i += 1 if problem_counter > 0: # some values are not ok # shop pup-up warning message: messagebox.showwarning("Warning!", "Found %i value(s) exceeding limits in config file. Check values " "to ensure correct operation and avoid equipment damage!" % problem_counter) - g.app.show_frame(ui.Configuration) # open configuration window so user can check values + g.app.show_frame(HardwareConfiguration) # open configuration window so user can check values def reset_config_to_default(): # reset values in config object to defaults (set in globals.py) diff --git a/src/csv_logging.py b/src/csv_logging.py index 3f20df5..e6f3f8b 100644 --- a/src/csv_logging.py +++ b/src/csv_logging.py @@ -9,8 +9,8 @@ from tkinter import filedialog from tkinter import messagebox # import other project files -import globals as g -import user_interface as ui +import src.globals as g +from src.utility import ui_print log_data = pd.DataFrame() # pandas data frame containing the logged data, in-program representation of csv/excel data unsaved_data = False # Bool to indicate if there is unsaved data, set to True each time a datapoint is logged @@ -71,7 +71,7 @@ def select_file(): # select a file to write logs to defaultextension=[("Comma Separated Values", "*.csv*")]) if filepath == '': # this happens when file selection window is closed without selecting a file - ui.ui_print("No file selected, can't save logged data.") + ui_print("No file selected, can't save logged data.") return None else: # a valid file name was entered return filepath @@ -81,7 +81,7 @@ def write_to_file(dataframe, filepath): # get global variables for use in this function: global unsaved_data if filepath is not None: # user has selected a file and no errors occurred - ui.ui_print("Writing logged data to file", filepath) + ui_print("Writing logged data to file", filepath) try: # write data collected in log_data DataFrame to csv file in german excel format: dataframe.to_csv(filepath, index=False, sep=';', decimal=',') @@ -93,7 +93,7 @@ def write_to_file(dataframe, filepath): messagebox.showerror("Error!", message) else: # no exceptions occurred unsaved_data = False # tell everything that there is no unsaved data remaining - ui.ui_print("Log data saved to", filepath) + ui_print("Log data saved to", filepath) def clear_logged_data(): # clears all logged data from data frame diff --git a/src/csv_threading.py b/src/csv_threading.py index e98ee43..b675cc4 100644 --- a/src/csv_threading.py +++ b/src/csv_threading.py @@ -11,26 +11,28 @@ from tkinter import messagebox import matplotlib.pyplot as plt # import other project files: -import user_interface as ui -import cage_func as func -import globals as g +from src.utility import ui_print +import src.user_interface as ui +import src.cage_func as func +import src.globals as g 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): + def __init__(self, array, parent, controller, logging_enabled): 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 def run(self): # called to start the execution of the thread - ui.ui_print("\nStarting Sequence Execution...") + 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 @@ -72,11 +74,9 @@ class ExecCSVThread(Thread): parent.arduino_override.get()) if all_connected: field_vec = array[i, 1:4] # extract desired field vector - ui.ui_print("%0.5f s: t = %0.2f s, target 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 - controller.StatusDisplay.update_labels() # update status display after change - # ToDo: display update takes a long time, remove for performance? # 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 @@ -91,15 +91,15 @@ class ExecCSVThread(Thread): 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.ui_print("Sequence executed, powering down channels.") + ui_print("Sequence executed, powering down channels.") elif all_connected: # interrupted by user - ui.ui_print("Sequence cancelled, powering down channels.") + ui_print("Sequence cancelled, powering down channels.") elif not all_connected: # interrupted by device error - ui.ui_print("Error with at least one device, sequence aborted.") + 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.ui_print("Encountered unexpected sequence end state:" + ui_print("Encountered unexpected sequence end state:" "\nThread Stopped:", self.stopped(), ", Application Closed:", g.exitFlag, ", Devices connected:", all_connected) messagebox.showwarning("Unexpected state", diff --git a/src/main.py b/src/main.py index 25421a8..60b7da2 100644 --- a/src/main.py +++ b/src/main.py @@ -6,14 +6,13 @@ import traceback from tkinter import messagebox # import other project files: -import cage_func as func -from user_interface import HelmholtzGUI -from user_interface import ui_print -import user_interface as ui -import globals as g -import config_handling as config -import csv_logging as log -from socket_control import SocketInterfaceThread +import src.cage_func as func +import src.globals as g +import src.config_handling as config +import src.csv_logging as log +from src.user_interface import HelmholtzGUI, ExecuteCSVMode +from src.socket_control import SocketInterfaceThread +from src.utility import ui_print def program_end(): # called on exception or when user closes application @@ -21,8 +20,8 @@ def program_end(): # called on exception or when user closes application g.exitFlag = True # tell everything else the application has been closed if g.app is not None: # the main Tkinter app object has been initialized before - if g.app.pages[ui.ExecuteCSVMode].csv_thread is not None: # check if a thread for executing CSVs exists - g.app.pages[ui.ExecuteCSVMode].csv_thread.stop() # stop the thread + if g.app.pages[ExecuteCSVMode].csv_thread is not None: # check if a thread for executing CSVs exists + g.app.pages[ExecuteCSVMode].csv_thread.stop() # stop the thread func.shut_down_all() # shut down devices diff --git a/src/socket_control.py b/src/socket_control.py index 880a556..75ed535 100644 --- a/src/socket_control.py +++ b/src/socket_control.py @@ -1,11 +1,11 @@ -import globals as g -from user_interface import ui_print -import cage_func as cage_controls - from threading import Thread import socket import numpy as np +import src.globals as g +from src.utility import ui_print +import src.cage_func as cage_controls + # --- Definition of TCP interface --- # # Clients should by default initialize a TCP connection to port 6677 diff --git a/src/user_interface.py b/src/user_interface.py index 4f3b1f4..c049965 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -17,11 +17,12 @@ import threading from datetime import datetime # import other project files: -import globals as g -import cage_func as func -import csv_threading as csv -import config_handling as config -import csv_logging as log +import src.globals as g +import src.cage_func as func +import src.csv_threading as csv +import src.config_handling as config +import src.csv_logging as log +from src.utility import ui_print # define font styles: HEADER_FONT = ("Arial", 13, "bold") @@ -55,7 +56,7 @@ class HelmholtzGUI(Tk): # switching between pages is done with show_frame() method self.pages = {} # dictionary for storing all pages (different modes, displayed in main area) - for P in [ManualMode, Configuration, ExecuteCSVMode, ConfigureLogging]: # do this for every mode page + for P in [ManualMode, HardwareConfiguration, ExecuteCSVMode, ConfigureLogging]: # do this for every mode page page = P(mainArea, self) # initialize the page with the mainArea frame as the parent 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 @@ -102,7 +103,7 @@ class TopMenu: @staticmethod def configuration(window): # switch to the settings page - window.show_frame(Configuration) + window.show_frame(HardwareConfiguration) @staticmethod def execute_csv_mode(window): # switch to the CSV execution page @@ -459,7 +460,8 @@ class ExecuteCSVMode(Frame): # 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: - self.csv_thread = csv.ExecCSVThread(self.sequence_array, self, self.controller) + + self.csv_thread = csv.ExecCSVThread(self.sequence_array, self, self.controller, logging_enabled) self.csv_thread.start() # start thread def stop_run(self): # called on stop button press, interrupts sequence execution @@ -499,8 +501,8 @@ class ExecuteCSVMode(Frame): plotCanvas.get_tk_widget().grid(row=0, column=0, sticky="nesw") # place canvas in the UI -class Configuration(Frame): - # generate settings window to set program constants +class HardwareConfiguration(Frame): + """Settings window to set program constants""" # noinspection PyUnusedLocal def __init__(self, parent, controller): @@ -1125,16 +1127,3 @@ class OutputConsole(Frame): # link scrollbar to the console scrollbar.config(command=self.console.yview) self.console.config(yscrollcommand=scrollbar.set) - - -def ui_print(*content): # prints text to built-in console, use exactly like normal print() - output = "" # initialize output as empty string - for text in content: # go through all elements to be printed - output = " ".join((output, str(text))) # add element to the output string - - if not g.exitFlag: # application is still running --> output window is visible - output = "".join(("\n", output)) # begin new line each time - g.app.OutputConsole.console.insert(END, output) # print to console - g.app.OutputConsole.console.see(END) # scroll console to bottom - else: # if window is not open, do normal print - print(output) diff --git a/src/utility.py b/src/utility.py new file mode 100644 index 0000000..f2283cb --- /dev/null +++ b/src/utility.py @@ -0,0 +1,17 @@ +from tkinter import END + +import src.globals as g + + +def ui_print(*content): + """prints text to built-in console, use exactly like normal print(). Requires the ui to be initialized""" + output = "" # initialize output as empty string + for text in content: # go through all elements to be printed + output = " ".join((output, str(text))) # add element to the output string + + if not g.exitFlag and g.app is not None: # application is still running --> output window is visible + output = "".join(("\n", output)) # begin new line each time + g.app.OutputConsole.console.insert(END, output) # print to console + g.app.OutputConsole.console.see(END) # scroll console to bottom + else: # if window is not open, do normal print + print(output) \ No newline at end of file