From 177928d82ea555e048fb0b856872759ecb265559 Mon Sep 17 00:00:00 2001 From: Martin Zietz Date: Sat, 20 Feb 2021 18:08:41 +0100 Subject: [PATCH] code cleanup and comments --- Arduino/arduino.py | 1 + User_Interface.py | 4 ++- csv_threading.py | 3 ++- globals.py | 44 ++++++++++++++++++-------------- main.py | 49 +++++++++++++++++++----------------- LICENSE => pyps2000b/LICENSE | 0 pyps2000b/PS2000B.py | 2 ++ 7 files changed, 59 insertions(+), 44 deletions(-) rename LICENSE => pyps2000b/LICENSE (100%) diff --git a/Arduino/arduino.py b/Arduino/arduino.py index 597c58b..37f0cfd 100644 --- a/Arduino/arduino.py +++ b/Arduino/arduino.py @@ -1,3 +1,4 @@ +# This file enables control of a connected Arduino microcontroller. #!/usr/bin/env python import logging import itertools diff --git a/User_Interface.py b/User_Interface.py index 29ffda6..94cb111 100644 --- a/User_Interface.py +++ b/User_Interface.py @@ -1,19 +1,21 @@ # This file contains classes to build all elements of the graphical user interface. # These classes also contain the methods that are executed when UI elements are activated. +# import packages for user interface: from tkinter import * from tkinter import ttk from tkinter import messagebox from tkinter import filedialog - from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +# import general packages: import numpy as np import os from os.path import exists import threading from datetime import datetime +# import other project files: import globals as g import cage_func as func import csv_threading as csv diff --git a/csv_threading.py b/csv_threading.py index 3dbd1cb..6ec1fc8 100644 --- a/csv_threading.py +++ b/csv_threading.py @@ -1,13 +1,14 @@ # tThis 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. +# import packages: import time import pandas from threading import * 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 diff --git a/globals.py b/globals.py index 116049d..281add2 100644 --- a/globals.py +++ b/globals.py @@ -1,43 +1,49 @@ +# This file is used to hold global variables that are used by more than one file of the program. +# Instead of always passing all variables to functions, this file can simply be imported to get them. + import numpy as np -# global variables set in other files -XY_DEVICE = None -Z_DEVICE = None -ARDUINO = None +XY_DEVICE = None # XY PSU object will be stored here (class PS2000B) +Z_DEVICE = None # Z PSU object will be stored here (class PS2000B) +ARDUINO = None # Arduino object will be stored here (class ArduinoCtrl) +# Axis objects will be stored here (class Axis) X_AXIS = None Y_AXIS = None Z_AXIS = None AXES = None # list containing [X_AXIS, Y_AXIS, Z_AXIS] -app = None +app = None # Main Tkinter application object will be stored here (class HelmholtzGUI) -AXIS_NAMES = ["X-Axis", "Y-Axis", "Z-Axis"] +AXIS_NAMES = ["X-Axis", "Y-Axis", "Z-Axis"] # list with the names of each axis, used mainly for printing functions -global XY_PORT -global Z_PORT +global XY_PORT # serial port for XY PSU will be stored here (string) +global Z_PORT # serial port for Z PSU will be stored here (string) -global PORTS +global PORTS # list containing [XY_PORT, XY_PORT, Z_PORT], used in loops where info on each axis is needed -global threadLock +global threadLock # thread locking object, used to force threads to perform actions in a certain order (threading.Lock) -exitFlag = True # False when main window is open, false otherwise +exitFlag = True # False when main window is open, True otherwise -# Default Constants and maximum/minimum values (warning messages will be generated if these are exceeded) -# format: [[default values], [maximum values], [minimum values]] -# ToDo: check actual maximum ratings -# ToDo: Add maximum current: 5A (BA Blessing page 30), remove max_watts (there for testing with resistors) +# Create dictionaries with default Constants and maximum/minimum values +# Used to create default configs and to check if user inputs are within safe limits +# ToDo: check actual maximum ratings (or refine after testing) # ToDo: put this into a config file + +# Dictionary for numerical values: +# format: key: [default values], [maximum values], [minimum values] default_arrays = { "coil_const": np.array([[38.6, 38.45, 37.9], [50, 50, 50], [0, 0, 0]]) * 1e-6, # Coil constants [x,y,z] [T/A] - "ambient_field": np.array([[30, 30, 30], [200, 200, 200], [-200, -200, -200]]) * 1e-6, # background magnetic field [T] + "ambient_field": np.array([[30, 30, 30], [200, 200, 200], [-200, -200, -200]]) * 1e-6, # ambient magnetic field [T] "resistance": np.array([[1.7, 1.7, 1.7], [5, 5, 5], [1, 1, 1]], dtype=float), # resistance of circuits [Ohm] - "max_volts": np.array([[14, 14, 14], [16, 16, 16], [0, 0, 0]], dtype=float), # max. allowed voltage, limited to 16V by used diodes! [V] + "max_volts": np.array([[14, 14, 14], [16, 16, 16], [0, 0, 0]], dtype=float), # max. voltage, limited to 16V by used diodes! [V] "max_amps": np.array([[4.5, 4.5, 4.5], [6, 6, 6], [0, 0, 0]], dtype=float), # max. allowed current (A) "relay_pin": [[15, 16, 17], [15, 16, 17], [15, 16, 17]] # pins on the arduino for reversing [x,y,z] polarity } +# Dictionary for PSU serial ports: default_ports = { - "xy_port": "COM1", # Serial port where PSU for X- and Y-Axes is connected - "z_port": "COM2", # Serial port where PSU for Z-Axis is connected + "xy_port": "COM1", # Default serial port where PSU for X- and Y-Axes is connected + "z_port": "COM2", # Default serial port where PSU for Z-Axis is connected } diff --git a/main.py b/main.py index 5fb229d..3428d40 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,11 @@ +# Main file of the program. Run this file to start the application. + +# import packages: from os.path import exists 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 @@ -12,64 +16,63 @@ import csv_logging as log def program_end(): # called on exception or when user closes application + # safely shuts everything down and saves any unsaved data + g.exitFlag = True # tell everything else the application has been closed - if g.app is not None: - if g.app.pages[ui.ExecuteCSVMode].csv_thread is not None: # end possible csv execution thread - g.app.pages[ui.ExecuteCSVMode].csv_thread.stop() # stop thread + 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 + func.shut_down_all() # shut down devices - if log.unsaved_data: # There is logged data that has not been saved yet + if log.unsaved_data: # Check if there is logged data that has not been saved yet # open pop-up to ask user if he wants to save the data: save_log = messagebox.askquestion("Save log data?", "There seems to be unsaved logging data. " "Do you wish to write it to a file now?") if save_log == 'yes': # user has chosen yes filepath = log.select_file() # let user select a file to write to log.write_to_file(log.log_data, filepath) # write the data to the chosen file + if g.app is not None: g.app.destroy() # close application try: # start normal operations - config.CONFIG_FILE = 'config.ini' + config.CONFIG_FILE = 'config.ini' # set the config file path # ToDo: remember what the last config file was - if not exists(config.CONFIG_FILE): + if not exists(config.CONFIG_FILE): # config file does not exist yet print("Config file not found, creating new from defaults.") - config.reset_config_to_default() - config.write_config_to_file(config.CONFIG_OBJECT) + config.reset_config_to_default() # create configuration object from defaults + config.write_config_to_file(config.CONFIG_OBJECT) # write the configuration object to a new file - config.CONFIG_OBJECT = config.get_config_from_file(config.CONFIG_FILE) + config.CONFIG_OBJECT = config.get_config_from_file(config.CONFIG_FILE) # read configuration data from config file print("Starting setup...") - func.setup_all() # initiate communication, set handles + func.setup_all() # initiate communication with devices and initialize all major program objects print("\nOpening User Interface...") - g.app = HelmholtzGUI() - g.exitFlag = False - g.app.state('zoomed') # open maximized + g.app = HelmholtzGUI() # initialize user interface + g.exitFlag = False # tell all functions that the user interface is now running + g.app.state('zoomed') # open UI in maximized window g.app.StatusDisplay.continuous_label_update(g.app, 500) # initiate regular Status Display updates (ms) ui_print("Program Initialized") config.check_config(config.CONFIG_OBJECT) # check config for values exceeding limits - ui_print("\nStarting setup...") # do it again, so it is printed in the UI console ToDo: do it only once - func.setup_all() # initiate communication, set handles + ui_print("\nStarting setup...") # do setup again, so it is printed in the UI console ToDo: do it only once + func.setup_all() # initiate communication with devices and initialize all major program objects g.app.protocol("WM_DELETE_WINDOW", program_end) # call program_end function if user closes the application - g.app.mainloop() + g.app.mainloop() # start main program loop -except Exception as e: # if there is an error, print what happened +except Exception as e: # An error has occurred somewhere in the program print("\nAn error occurred, Shutting down.") # shop pup-up error message: message = "%s.\nSee python console traceback for more details. " \ "\nShutting down devices, check equipment to confirm." % e messagebox.showerror("Error!", message) - print(traceback.print_exc()) + print(traceback.print_exc()) # print error traceback in the python console program_end() # safely close everything and shut down devices - -# ToDo: rework window closing code https://bytes.com/topic/python/answers/431323-detect-tkinter-window-being-closed -# https://stackoverflow.com/questions/14694408/runtimeerror-main-thread-is-not-in-main-loop - -# ToDo: logging diff --git a/LICENSE b/pyps2000b/LICENSE similarity index 100% rename from LICENSE rename to pyps2000b/LICENSE diff --git a/pyps2000b/PS2000B.py b/pyps2000b/PS2000B.py index be9d496..a1a3d7a 100644 --- a/pyps2000b/PS2000B.py +++ b/pyps2000b/PS2000B.py @@ -1,3 +1,5 @@ +# This file enables communication with PS2000B Power Supply Units. + #!/usr/bin/env python3 # coding=utf-8 # Python access to Elektro Automatik PS 2000 B devices via USB/serial