# file: imld_gui_window.py # # This script implements the main window, menu bar and 3 widgets in the ISIP # Machine Learning Demo user interface #------------------------------------------------------------------------------ # import system modules # import numpy as np import os import time import sys import inspect # import library modules # from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure from matplotlib.colors import cnames from matplotlib.pyplot import colormaps from PyQt5 import QtCore, QtGui, QtWidgets # import local modules # import imld_model as model #import custom_algorithms import nedc_file_tools as nft import nedc_ml_tools as ml from imld_constants_file import CONFIG_DIR #------------------------------------------------------------------------------ # # define global variables # #------------------------------------------------------------------------------ # define window parameter # INPUT_DISP_ROW = 0 INPUT_DISP_COL = 0 OUTPUT_DISP_ROW = 2 OUTPUT_DISP_COL = 0 PROCES_ROW = 0 PROCES_COL = 1 PROCES_ROW_SPAN = 0 PROCES_COL_SPAN = 2 WINDOW_X = 800 WINDOW_Y = 768 SURFACE_COLOR = 'winter' COLORS = [*cnames] DEFAULT = 1 # get the imld - ml tools parameter file # PARAMS_FILE = 'imld_alg_params.txt' PARAMS_PATH = os.path.join(CONFIG_DIR, PARAMS_FILE) CUSTOM_FILE = 'custom_algorithms.py' CUSTOM_PATH = os.path.join(CONFIG_DIR, CUSTOM_FILE) WIDGET_KEYS = {} class MainWindow(QtWidgets.QMainWindow): ''' class: MainWindow description: this class uses the QMainWindow parent class, which provides a main application window. the methods of this class handles the graphics and layout of the UI and the logic that cinnects the UI to specific actions ''' def __init__(self, event_loop_a): ''' method: MainWindow::constructor arguments: event_loop_a: the event loop return: None description: a constructor for initializing the MainWindow class ''' super(MainWindow, self).__init__() # add all classes from QMainWindow so they may be referenced # by self.class # QtWidgets.QMainWindow.__init__(self) # store reference to parent loop # self.parent_loop = event_loop_a # create instance of widgets including the train display, eval # display and progress description # self.input_display = InputDisplay() self.output_display = OutputDisplay() self.process_desc = ProcessDescription() # create instance of menu bar at top of window using MenuBar # self.menu_bar = MenuBar() # create layout for applet, set style of frame and lay out # widgets in a grid # self.central_widget = QtWidgets.QFrame() self.central_widget.setFrameShape(QtWidgets.QFrame.StyledPanel) self.layout = QtWidgets.QGridLayout(self.central_widget) self.central_widget.setStyleSheet("background-color: white;") # initialize window # self.initUI() # # end of method def initUI(self): ''' method: MainWindow::initUI arguments: None return: None description: this method lays out and positions widgets, creates a meny bar, and connects the menu created in the MenuBar class to the actions required when the user selects an option. ''' # give the main window a title # self.setWindowTitle("ISIP Machine Learning Demonstration") # set app icon scriptDir = os.path.dirname(os.path.realpath(__file__)) app_icon = QtGui.QIcon() app_icon.addFile(scriptDir + os.path.sep + 'logo_16.png', \ QtCore.QSize(16, 16)) app_icon.addFile(scriptDir + os.path.sep + 'logo_24.png', \ QtCore.QSize(24, 24)) app_icon.addFile(scriptDir + os.path.sep + 'logo_32.png', \ QtCore.QSize(32, 32)) app_icon.addFile(scriptDir + os.path.sep + 'logo_48.png', \ QtCore.QSize(48, 48)) app_icon.addFile(scriptDir + os.path.sep + 'logo_256.png', \ QtCore.QSize(256, 256)) self.setWindowIcon(QtGui.QIcon(scriptDir + os.path.sep + 'logo.png')) self.setWindowIcon(app_icon) # layout the widgets' positions in window: # self.setCentralWidget(self.central_widget) self.layout.addWidget(self.input_display, INPUT_DISP_ROW, INPUT_DISP_COL) self.layout.addWidget(self.output_display, OUTPUT_DISP_ROW, OUTPUT_DISP_COL) #self.layout.addWidget() self.layout.addWidget(self.process_desc, PROCES_ROW, PROCES_COL, PROCES_ROW_SPAN, PROCES_COL_SPAN) # make sure that the columns are shared equally # self.layout.setColumnStretch(0, 1) self.layout.setColumnStretch(1, 1) # creates menu bar # self.setMenuBar(self.menu_bar) self.menu_bar.setNativeMenuBar(False) # --------------------------------------------------------------------- # connect pattern bar actions to event loop # --------------------------------------------------------------------- # IMLD menu # self.menu_bar.quit.triggered.connect( QtWidgets.QApplication.quit) # create instance of text edit for IMLD > About menu # self.menu_bar.about_menu.triggered.connect( self.parent_loop.about_IMLD) # file menu (load and save) # load: # self.menu_bar.load_train_menu.triggered.connect( self.parent_loop.prompt_for_load_train) self.menu_bar.load_eval_menu.triggered.connect( self.parent_loop.prompt_for_load_eval) # save: # self.menu_bar.save_train_menu.triggered.connect( self.parent_loop.prompt_for_save_train) self.menu_bar.save_eval_menu.triggered.connect( self.parent_loop.prompt_for_save_eval) # model: # self.menu_bar.save_model_menu.triggered.connect( self.parent_loop.prompt_for_save_model) self.menu_bar.load_model_menu.triggered.connect( self.parent_loop.prompt_for_load_model) # params: # self.menu_bar.save_params_menu.triggered.connect( self.parent_loop.prompt_for_save_params) self.menu_bar.load_params_menu.triggered.connect( self.parent_loop.prompt_for_load_params) # when Edit > Set Range menu option selected, open set plot # ranges window # self.menu_bar.set_range_menu.triggered.connect( self.parent_loop.set_plot_ranges) self.menu_bar.set_scolor_menu.triggered.connect( self.parent_loop.surface_color_show) # when Edit > Set Gaussian menu option selected, open gaussian # pattern window # self.menu_bar.set_gaussian_menu.triggered.connect( self.parent_loop.prompt_set_gauss_prop) # when Edit > Normalize Data menu option selected, call normalize # data # self.menu_bar.set_normalize_menu.triggered.connect( self.parent_loop.normalize_data) # when Edit > Clear Descriptions selected, clear the window # self.menu_bar.clear_des_menu.triggered.connect( self.process_desc.output.clear) # when Edit > Clear Train # self.menu_bar.clear_train_data.triggered.connect( lambda checked: self.parent_loop.clear_input()) self.menu_bar.clear_train_result.triggered.connect( lambda checked: self.parent_loop.clear_input_result()) self.menu_bar.clear_train_all.triggered.connect( lambda checked: self.parent_loop.clear_input()) # when Edit > Clear Eval # self.menu_bar.clear_eval_data.triggered.connect( lambda checked: self.parent_loop.clear_output()) self.menu_bar.clear_eval_result.triggered.connect( lambda checked: self.parent_loop.clear_output_result()) self.menu_bar.clear_eval_all.triggered.connect( lambda checked: self.parent_loop.clear_output()) # when Edit > Clear All, clear Train, Eval, and Process Log # self.menu_bar.clear_all_menu.triggered.connect( lambda checked: self.parent_loop.clear_input()) self.menu_bar.clear_all_menu.triggered.connect( lambda checked: self.parent_loop.clear_output()) self.menu_bar.clear_all_menu.triggered.connect( self.process_desc.output.clear) self.menu_bar.reset_menu.triggered.connect( lambda checked: self.parent_loop.reset_window()) # When View > Print Confusion Matrix # self.menu_bar.set_confusion_matrix.triggered.connect( lambda checked: self.parent_loop.set_confusion_matrix()) # when Classes > Add Class, Delete Class # self.menu_bar.add_class_menu.triggered.connect( self.parent_loop.add_class_show) # resets the color chosen when adding classes # self.menu_bar.add_class_menu.triggered.connect( self.parent_loop.reset_color) # handles the signal of the added class # self.menu_bar.class_group.triggered.connect( (lambda checked: self.parent_loop.handled_signal( self.menu_bar.class_group.sender()))) # removes the class selected # self.menu_bar.delete_class_menu.triggered.connect( lambda checked: self.parent_loop.remove_classes()) # create connections to pop-up parameter selection windows for each # algorithm option # for widget in WIDGET_KEYS.values(): widget.triggered.connect(self.parent_loop.prompt_algo) # patterns menu: # These will bring up the parameter windows from the _show methods # in event_loop when the menu option is selected. # # handles connecting GUI to draw point and gaussian # self.menu_bar.draw_points_menu.triggered.connect( self.parent_loop.set_point) # self.menu_bar.draw_points_menu.triggered.connect( # self.menu_bar.draw_gauss_menu.setChecked(False)) self.menu_bar.draw_gauss_menu.triggered.connect( self.parent_loop.set_gauss) # handles connecting GUI to showing two gaussian # self.menu_bar.two_gauss_menu_t.triggered.connect( self.parent_loop.two_gauss_show) self.menu_bar.two_gauss_menu_e.triggered.connect( self.parent_loop.two_gauss_show) # handles connecting GUI to showing four gaussian # self.menu_bar.four_gauss_menu_t.triggered.connect( self.parent_loop.four_gauss_show) self.menu_bar.four_gauss_menu_e.triggered.connect( self.parent_loop.four_gauss_show) # handles connecting GUI to showing over gaussian # self.menu_bar.over_gauss_menu_t.triggered.connect( self.parent_loop.over_gauss_show) self.menu_bar.over_gauss_menu_e.triggered.connect( self.parent_loop.over_gauss_show) # handles connecting GUI to showing two ellipse # self.menu_bar.two_ellipse_menu_t.triggered.connect( self.parent_loop.two_ellipse_show) self.menu_bar.two_ellipse_menu_e.triggered.connect( self.parent_loop.two_ellipse_show) # handles connecting GUI to showing four ellipse # self.menu_bar.four_ellipse_menu_t.triggered.connect( self.parent_loop.four_ellipse_show) self.menu_bar.four_ellipse_menu_e.triggered.connect( self.parent_loop.four_ellipse_show) # handles connecting GUI to showing rotated ellipse # self.menu_bar.rotated_ellipse_menu_t.triggered.connect( self.parent_loop.rotated_ellipse_show) self.menu_bar.rotated_ellipse_menu_e.triggered.connect( self.parent_loop.rotated_ellipse_show) # handles connecting GUI to showing toroidal # self.menu_bar.toroidal_menu_t.triggered.connect( self.parent_loop.toroidal_show) self.menu_bar.toroidal_menu_e.triggered.connect( self.parent_loop.toroidal_show) # handles connecting GUI to showing yin_yang symbol # self.menu_bar.yin_yang_menu_t.triggered.connect( self.parent_loop.yin_yang_show) self.menu_bar.yin_yang_menu_e.triggered.connect( self.parent_loop.yin_yang_show) # progress menu menu # self.menu_bar.train_menu.triggered.connect( self.parent_loop.train_complete) self.menu_bar.eval_menu.triggered.connect( self.parent_loop.eval_complete) # resize to specified dimension # self.resize(WINDOW_X, WINDOW_Y) # finally, show the window # self.show() # exit gracefully # return None # # end of method # # end of class class MenuBar(QtWidgets.QMenuBar): ''' class: MenuBar desription: this class creates a horizontal menu bar to the top of the program. the menu bar adds options, suboptions, and other formatting items to the menu bar ''' def __init__(self): ''' method: MenuBar::constructor arguments: None return: None description: initializes the MenuBar object of for the GUI ''' # inherit methods of PyQt object QMenuBar # QtWidgets.QMenuBar.__init__(self) # create menu bar options # self.imld_menu = self.addMenu('IMLD') self.file_menu = self.addMenu('File') self.edit_menu = self.addMenu('Edit') self.view_menu = self.addMenu('View') self.class_menu = self.addMenu('Classes') self.pattern_menu = self.addMenu('Patterns') self.demo_menu = self.addMenu('Demo') self.algo_menu = self.addMenu('Algorithms') self.process_menu = self.addMenu('Process') # IMLD menu bar # create options # self.about_menu = QtWidgets.QAction('About', self) self.quit = QtWidgets.QAction('Quit', self) # add options to IMLD slide down menu # self.imld_menu.addAction(self.about_menu) self.imld_menu.addAction(self.quit) # File menu bar # creates options for file slide down (load train) # self.load_train_menu = QtWidgets.QAction('Load Train Data', self) self.load_eval_menu = QtWidgets.QAction('Load Eval Data', self) # adds options to file slide down menu as well as a separator to # physically separate these from the next options # self.file_menu.addAction(self.load_train_menu) self.file_menu.addAction(self.load_eval_menu) self.file_menu.addSeparator() # creates options to save train/eval data # self.save_train_menu = QtWidgets.QAction('Save Train As...', self) self.save_eval_menu = QtWidgets.QAction('Save Eval As...', self) # adds save options to file menu # self.file_menu.addAction(self.save_train_menu) self.file_menu.addAction(self.save_eval_menu) self.file_menu.addSeparator() # creates options to save and load a model # self.load_model_menu = QtWidgets.QAction('Load Model', self) self.save_model_menu = QtWidgets.QAction('Save Model As...', self) # adds save options to file menu # self.file_menu.addAction(self.save_model_menu) self.file_menu.addAction(self.load_model_menu) self.file_menu.addSeparator() # creates options to save train/eval data # self.load_params_menu = QtWidgets.QAction('Load Parameters', self) self.save_params_menu = QtWidgets.QAction('Save Parameters As...', self) # adds save options to file menu # self.file_menu.addAction(self.save_params_menu) self.file_menu.addAction(self.load_params_menu) self.file_menu.addSeparator() # add 'Settings' sub menu to 'Edit' menu # self.settings_menu = self.edit_menu.addMenu("Settings") self.edit_menu.addSeparator() # create 'Set Ranges', 'Set Color', and 'Set Gaussian' options under # 'Settings' sub-menu # self.set_range_menu = QtWidgets.QAction('Set Ranges') self.set_gaussian_menu = QtWidgets.QAction('Set Gaussian') self.set_scolor_menu = QtWidgets.QAction('Set Color') self.set_normalize_menu = QtWidgets.QAction('Normalize Data', self, checkable = True, checked = False) # add options to 'Settings' menu # self.settings_menu.addAction(self.set_range_menu) self.settings_menu.addAction(self.set_gaussian_menu) self.settings_menu.addAction(self.set_scolor_menu) self.settings_menu.addAction(self.set_normalize_menu) # create 'Clear' options # self.clear_des_menu = QtWidgets.QAction('Clear Process Log') self.clear_all_menu = QtWidgets.QAction('Clear All') self.reset_menu = QtWidgets.QAction('Reset') self.clear_train_data = QtWidgets.QAction('Clear Data') self.clear_train_result = QtWidgets.QAction('Clear Results') self.clear_train_all = QtWidgets.QAction('Clear All') self.clear_eval_data = QtWidgets.QAction('Clear Data') self.clear_eval_result = QtWidgets.QAction('Clear Results') self.clear_eval_all = QtWidgets.QAction('Clear All') # add 'Clear' options under 'Edit' menu # self.edit_menu.addAction(self.clear_des_menu) # create Clear Train options # self.clear_train = self.edit_menu.addMenu('Clear Train') self.clear_train.addAction(self.clear_train_data) self.clear_train.addAction(self.clear_train_result) self.clear_train.addAction(self.clear_train_all) # create Clear Eval options # self.clear_eval = self.edit_menu.addMenu('Clear Eval') self.clear_eval.addAction(self.clear_eval_data) self.clear_eval.addAction(self.clear_eval_result) self.clear_eval.addAction(self.clear_eval_all) # create Clear All menu # self.edit_menu.addAction(self.clear_all_menu) self.edit_menu.addAction(self.reset_menu) # View menu bar self.set_confusion_matrix = QtWidgets.QAction('Print Confusion Matrix', self, checkable = True, checked = True) # add the confusion matrix toggle to the view menu # self.view_menu.addAction(self.set_confusion_matrix) # Classes menu bar # create an action group so that the checkable option can be # made exclusive # self.class_group = QtWidgets.QActionGroup(self.class_menu) # create sub-menu options for "Classes" menu # self.add_class_menu = QtWidgets.QAction("Add Class") self.delete_class_menu = QtWidgets.QAction("Delete Class") # add sub-menus Add Class and Delete Class, to "Classes" menu group # self.class_menu.addAction(self.add_class_menu) self.class_menu.addAction(self.delete_class_menu) self.class_menu.addSeparator() # Patterns menu bar # create options to add to pattern slide down # (Draw Options) # self.draw_points_menu = QtWidgets.QAction('Draw Points', self, checkable = True, checked = True) self.draw_gauss_menu = QtWidgets.QAction('Draw Gaussian', self, checkable = True) self.pattern_menu.addAction(self.draw_points_menu) self.pattern_menu.addAction(self.draw_gauss_menu) # Demo menu bar # create options for use of predefined demos # two gaussian - add options to create in Train or Eval window # self.two_gauss_menu = self.demo_menu.addMenu("Two Gaussian") self.two_gauss_menu_t = QtWidgets.QAction('Train') self.two_gauss_menu_e = QtWidgets.QAction('Eval') label = QtWidgets.QLabel("Set Parameters") label_action = QtWidgets.QWidgetAction(self) label_action.setDefaultWidget(label) self.two_gauss_menu.addAction(label_action) self.two_gauss_menu.addAction(self.two_gauss_menu_t) self.two_gauss_menu.addAction(self.two_gauss_menu_e) # four gaussian - add options to create in Train or Eval window # self.four_gauss_menu = self.demo_menu.addMenu("Four Gaussian") self.four_gauss_menu_t = QtWidgets.QAction('Train') self.four_gauss_menu_e = QtWidgets.QAction('Eval') label = QtWidgets.QLabel("Set Parameters") label_action = QtWidgets.QWidgetAction(self) label_action.setDefaultWidget(label) self.four_gauss_menu.addAction(label_action) self.four_gauss_menu.addAction(self.four_gauss_menu_t) self.four_gauss_menu.addAction(self.four_gauss_menu_e) # overlapping gaussian - add options to create in Train or Eval window # self.over_gauss_menu = \ self.demo_menu.addMenu("Overlapping Gaussian") self.over_gauss_menu_t = QtWidgets.QAction('Train') self.over_gauss_menu_e = QtWidgets.QAction('Eval') label = QtWidgets.QLabel("Set Parameters") label_action = QtWidgets.QWidgetAction(self) label_action.setDefaultWidget(label) self.over_gauss_menu.addAction(label_action) self.over_gauss_menu.addAction(self.over_gauss_menu_t) self.over_gauss_menu.addAction(self.over_gauss_menu_e) # two ellipses - add options to create in Train or Eval window # self.two_ellipse_menu = self.demo_menu.addMenu("Two Ellipses") self.two_ellipse_menu_t = QtWidgets.QAction('Train') self.two_ellipse_menu_e = QtWidgets.QAction('Eval') label = QtWidgets.QLabel("Set Parameters") label_action = QtWidgets.QWidgetAction(self) label_action.setDefaultWidget(label) self.two_ellipse_menu.addAction(label_action) self.two_ellipse_menu.addAction(self.two_ellipse_menu_t) self.two_ellipse_menu.addAction(self.two_ellipse_menu_e) # four ellipses - add options to create in Train or Eval window # self.four_ellipse_menu = self.demo_menu.addMenu("Four Ellipses") self.four_ellipse_menu_t = QtWidgets.QAction('Train') self.four_ellipse_menu_e = QtWidgets.QAction('Eval') label = QtWidgets.QLabel("Set Parameters") label_action = QtWidgets.QWidgetAction(self) label_action.setDefaultWidget(label) self.four_ellipse_menu.addAction(label_action) self.four_ellipse_menu.addAction(self.four_ellipse_menu_t) self.four_ellipse_menu.addAction(self.four_ellipse_menu_e) # rotated ellipses - add options to create in Train or Eval window # self.rotated_ellipse_menu = \ self.demo_menu.addMenu("Rotated Ellipses") self.rotated_ellipse_menu_t = QtWidgets.QAction('Train') self.rotated_ellipse_menu_e = QtWidgets.QAction('Eval') label = QtWidgets.QLabel("Set Parameters") label_action = QtWidgets.QWidgetAction(self) label_action.setDefaultWidget(label) self.rotated_ellipse_menu.addAction(label_action) self.rotated_ellipse_menu.addAction(self.rotated_ellipse_menu_t) self.rotated_ellipse_menu.addAction(self.rotated_ellipse_menu_e) # toroidal - add options to create in Train or Eval window # self.toroidal_menu = self.demo_menu.addMenu("Toroidal") self.toroidal_menu_t = QtWidgets.QAction('Train') self.toroidal_menu_e = QtWidgets.QAction('Eval') label = QtWidgets.QLabel("Set Parameters") label_action = QtWidgets.QWidgetAction(self) label_action.setDefaultWidget(label) self.toroidal_menu.addAction(label_action) self.toroidal_menu.addAction(self.toroidal_menu_t) self.toroidal_menu.addAction(self.toroidal_menu_e) # yin yang - add options to create in Train or Eval window # self.yin_yang_menu = self.demo_menu.addMenu("Yin-Yang") self.yin_yang_menu_t = QtWidgets.QAction('Train') self.yin_yang_menu_e = QtWidgets.QAction('Eval') label = QtWidgets.QLabel("Set Parameters") label_action = QtWidgets.QWidgetAction(self) label_action.setDefaultWidget(label) self.yin_yang_menu.addAction(label_action) self.yin_yang_menu.addAction(self.yin_yang_menu_t) self.yin_yang_menu.addAction(self.yin_yang_menu_e) # Algorithm Menu Bar # create group so that we can later make them checkable and exclusive # (only one can be checked at a time) # self.algo_group = QtWidgets.QActionGroup(self.algo_menu) # create a drop-down option for every algorithm. set its "data" # attribute to the parameters for that algorithm # for key in nft.load_parameters(PARAMS_PATH, 'ALGS')['ALGS']: # if there is an unknown key, check the custom algorithms to see # if the key is included # if key not in ml.ALGS: with open(CUSTOM_PATH, 'r') as script_file: python_script = script_file.read() # Create a dictionary to capture the namespace script_namespace = {} # Execute the Python script and capture the namespace exec(python_script, script_namespace) ml.ALGS[key] = script_namespace.get(key)() ''' # for each algorithm created, compare it to the unknown key. if # the key and algorithm names match, add the corresponding # class to the ml tools dictionary # for name, obj in inspect.getmembers(sys.modules['custom_algorithms']): if name == key: ml.ALGS[key] = obj() break ''' # load the parameters for the current algorithm # params = nft.load_parameters(PARAMS_PATH, key) # create a option to choose the algorithm from the dropdown # WIDGET_KEYS[key] = QtWidgets.QAction(params["name"], checkable=True) WIDGET_KEYS[key].setData({'abv_name':key, 'params':params}) self.algo_menu.addAction(self.algo_group.addAction(WIDGET_KEYS[key])) # Process menu bar # create sub-menu options to add to the "Process" menu option # self.train_menu = QtWidgets.QAction("Train ...") self.eval_menu = QtWidgets.QAction("Evaluate ...") # add sub-menus to "Process" menu # self.process_menu.addAction(self.train_menu) self.process_menu.addAction(self.eval_menu) # set keyboard shortcuts for menu options # self.train_menu.setShortcut("Ctrl+T") self.eval_menu.setShortcut("Ctrl+E") self.setStyleSheet(""" QMenuBar { color: rgb(255,255,255); background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 lightgray, stop:1 darkgray); border-radius: 5px padding: 2px 10px; spacing: 3px; } """) # exit gracefully # return None # # end of method # # end of class class InputDisplay(QtWidgets.QWidget): ''' class: InputDisplay description: this class sets up the train window/graph. this is known as the "input display" ''' def __init__(self, parent=None): ''' method: InputDisplay::constructor arguments: parents (None): the parent widget (widget that drew child widget) return: None description: initialize the train window window/graph ''' super(InputDisplay, self).__init__(parent) # set up Canvas # self.canvas = FigureCanvas(Figure(tight_layout=True)) self.label = QtWidgets.QLabel() # set up the x and y coordinate arrays # self.x = np.empty((0, 0)) self.y = np.empty((0, 0)) # set up the covariance of the gaussian # when user draws # self.cov = [[0.05, 0.0], [0.0, 0.05]] self.num_points = 25 # set up color variables: color bank, colors used, and # current colors # self.colors = [*cnames] self.colors_used = [] self.color_c = None self.surface_color = 'winter' # set up current class, triggered class, all_classes # self.current_class = None self.t_current_class = None self.all_classes = [] self.current_co = None # set up dictionary to store class information # self.once_c = True self.pair = 0 self.class_info = {} # set up initial press and draw flag # self.pressed = False self.draw = 'point' # Set title and maximum size in pixels for Train window # self.label.setText("Train:") self.label.setStyleSheet("color: black;") self.label.setMaximumSize(35, 10) # set up canvas layout # vertical_layout = QtWidgets.QVBoxLayout(self) vertical_layout.setContentsMargins(0, 0, 0, 0) vertical_layout.addWidget(self.label) vertical_layout.addWidget(self.canvas) # set up canvas in GUI # self.canvas.axes = self.canvas.figure.add_subplot(111) self.x_axis = [-DEFAULT, DEFAULT] self.y_axis = [-DEFAULT, DEFAULT] self.initUI() # set handles for pressing, releasing and moving # self.canvas.mpl_connect("button_press_event", self.on_press) self.canvas.mpl_connect("button_release_event", self.on_release) self.canvas.mpl_connect("motion_notify_event", self.on_move) # exit gracefully # return None # # end of method def initUI(self): ''' method: InputDisplay::initUI arguments: None return: None description: this method creates the input display graph overlay ''' self.canvas.axes.set_xlim([self.x_axis[0], self.x_axis[-1]]) self.canvas.axes.set_ylim([self.y_axis[0], self.y_axis[-1]]) # set range and grid # self.canvas.axes.set_axisbelow(True) # set visual aspects of grid # self.canvas.axes.grid(which='major', color='grey', linestyle='-', linewidth=0.25, alpha=0.50) self.canvas.axes.tick_params(labelsize=8) self.x_axislim = self.canvas.axes.get_xlim() self.y_axislim = self.canvas.axes.get_ylim() self.x_axis = np.linspace(self.x_axislim[0], self.x_axislim[1], 9) self.y_axis = np.linspace(self.y_axislim[0], self.y_axislim[1], 9) self.canvas.axes.set_xticks(self.x_axis) self.canvas.axes.set_yticks(self.y_axis) # exit gracefully # return None # # end of method def clear_plot(self): ''' method: InputDisplay::clear_plot arguments: None return: None description: this method clears any data that is show in the input display ''' # clear the input canvas and reset with the intial UI # self.canvas.axes.clear() self.initUI() # iterate through class info and reset all information while keeping # the color of the class # count = 0 for key in self.class_info: # reset x and y coordinates # self.class_info[key][1] = np.empty((0, 0)) self.class_info[key][2] = np.empty((0, 0)) # reset class plot with same color # self.class_info[key][0] = self.canvas.axes.scatter\ (self.class_info[key][1], self.class_info[key][2], s=1) self.class_info[key][0].set_color(self.class_info[key][4]) # set current class to the key # self.current_class = key self.class_info[self.current_class][0].set_gid\ (np.full((1, np.shape(self.class_info\ [self.current_class][1])[0]), count)) self.canvas.draw_idle() count = count + 1 self.canvas.draw_idle() # exit gracefully # return None # # end of method def clear_result_plot(self): ''' method: InputDisplay::clear_result_plot arguments: None return: None description: this method clears the results from the plit and keeps the data plotted ''' # remove canvas and reinsert the initial UI # self.canvas.axes.clear() self.initUI() # iterate through class dictionary and replot the graphs # without results # count = 0 for key in self.class_info: self.class_info[key][0] = \ self.canvas.axes.scatter(self.class_info[key][1], self.class_info[key][2], s=1) self.class_info[key][0].set_color(self.class_info[key][4]) self.current_class = key self.class_info[self.current_class][0].set_gid( np.full((1, np.shape(self.class_info[self.current_class][1])[0]), count)) count = count + 1 self.canvas.draw_idle() # exit gracefully # return None # # end of method def remove_class(self, name=None): ''' method: InputDisplay::remove_class arguments: name (None): the current class that is chosen to be removed return: None description: this method removes a single class using the delete class button ''' if self.class_info: # clear the visual plot # self.canvas.axes.clear() self.initUI() # set the current class and reset the colors used # tmp = self.current_class if self.class_info[self.current_class] is not None: color = self.class_info[self.current_class][4] if color in self.colors_used: self.colors_used.remove(color) else: self.colors.append(color) self.class_info.pop(self.current_class) # Replot the remaining classes from class_info with their # appropriate colors # count = 0 for key in self.class_info: self.class_info[key][0] = self.canvas.axes.scatter\ (self.class_info[key][1], self.class_info[key][2], s=1) self.class_info[key][0].set_color(self.class_info[key][4]) self.current_class = key self.class_info[self.current_class][0].set_gid( np.full((1, np.shape(self.class_info\ [self.current_class][1])[0]), count)) self.canvas.draw_idle() count = count + 1 self.t_current_class.remove(tmp) self.canvas.draw_idle() else: pass # exit gracefully # return None # # end of method def on_press(self, event): ''' method: InputDisplay::on_press arguments: event: the event loop for this app return: None description: this method calculates where the mouse is on the display and records both the x and y coordinates of the data and stores them into a dictionary where the class is lined with its data. there are two options: either a click and drag for points or use of gaussian plots. this makes up the training data ''' # check if current class name is a string and if not make it a string # if not isinstance(self.current_class, str): self.current_class = "%s" % self.current_class # find the index of current class in total list # self.pair = self.t_current_class.index(self.current_class) # if the current class is not in the class dictionary or is # initialized to None, initialize the class in the dictionary # if self.current_class not in self.class_info or \ (self.class_info[self.current_class] == None): self.class_info[self.current_class] = [self.current_c, self.x, self.y, self.once_c] # check to make sure mouse is within data input window # if isinstance(event.xdata, float) and isinstance(event.ydata, float): if self.x_axis[0] <= event.xdata <= self.x_axis[-1] \ and self.y_axis[0] <= event.ydata <= self.y_axis[-1]: # set the draw to point and track the x and y data # if self.draw == "point": self.pressed = True if self.class_info[self.current_class][3]: self.class_info[self.current_class][0] = \ self.canvas.axes.scatter(None, None, s=1) self.class_info[self.current_class][3] = False self.class_info[self.current_class][1] = \ np.append(self.class_info[self.current_class][1], event.xdata) self.class_info[self.current_class][2] = \ np.append(self.class_info[self.current_class][2], event.ydata) self.class_info[self.current_class][0].set_color\ (self.class_info[self.current_class][4]) self.canvas.draw_idle() # set the draw to gaussian and track the data produced by # the target x,y and the gaussian formula # else: self.pressed = True if self.class_info[self.current_class][3]: self.class_info[self.current_class][0] = \ self.canvas.axes.scatter(None, None, s=1) self.class_info[self.current_class][0].set_color\ (self.class_info[self.current_class][4]) self.class_info[self.current_class][3] = False mu = [event.xdata, event.ydata] cov = self.cov gauss_plot = np.random.multivariate_normal(mu, cov, self.num_points) self.class_info[self.current_class][1] = \ np.append(self.class_info[self.current_class][1], gauss_plot[:, 0]) self.class_info[self.current_class][2] = \ np.append(self.class_info[self.current_class][2], gauss_plot[:, 1]) self.class_info[self.current_class][0].set_offsets( np.column_stack((self.class_info\ [self.current_class][1], self.class_info\ [self.current_class][2]))) self.canvas.draw_idle() # exit gracefully # return None # # end of method def on_move(self, event): ''' method: InputDisplay::on_move arguments: event: the event loop for this app return: None description: this method records the x and y data while the mouse is moving ''' if self.pressed: # check to make sure mouse is within data input window # if isinstance(event.xdata, float) and \ isinstance(event.ydata, float): if self.x_axis[0] <= event.xdata <= self.x_axis[-1] \ and self.y_axis[0] <= event.ydata <= self.y_axis[-1]: # if set to point, append the x and y data # of the current mouse position # if self.draw == "point": self.class_info[self.current_class][1] = \ np.append(self.class_info[self.current_class][1], event.xdata) self.class_info[self.current_class][2] = \ np.append(self.class_info[self.current_class][2], event.ydata) self.class_info[self.current_class][0].set_offsets( np.column_stack((self.class_info\ [self.current_class][1], self.class_info\ [self.current_class][2]))) self.canvas.draw_idle() # if set to gaussian, record the gaussian with # current mouse placement while moving # else: mu = [event.xdata, event.ydata] cov = self.cov gauss_plot = \ np.random.multivariate_normal(mu, cov, self.num_points) self.class_info[self.current_class][1] = \ np.append(self.class_info[self.current_class][1], gauss_plot[:, 0]) self.class_info[self.current_class][2] = \ np.append(self.class_info[self.current_class][2], gauss_plot[:, 1]) self.class_info[self.current_class][0].set_offsets( np.column_stack((self.class_info\ [self.current_class][1], self.class_info\ [self.current_class][2]))) self.canvas.draw_idle() # exit gracefully # return None # # end of method def on_release(self, event): ''' method: InputDisplay::on_release arguments: event: the event loop for this app return: None description: this method resets the pressed flag and sets a name for the class_info correlating with graphical data ''' # set the pressed flag to false # self.pressed = False # set class in dictionary to pair with the graphical data # self.class_info[self.current_class][0].\ set_gid(np.full((1, np.shape(self.class_info\ [self.current_class][1])[0]), self.pair)) self.canvas.draw_idle() # exit gracefully # return None # # end of method def find_class_c(self, sender=None): ''' method: InputDisplay::find_class_c arguments: sender: the meny item that is currently triggered return: None description: this method finds the currrent class object that is triggered and finds what the appropriate color that is paired with class ''' # check if the sender signal is triggered # if sender is not None: # find the class pair of the signal and set it to the current class # self.pair = self.all_classes.index(sender) self.current_co = sender self.current_class = self.t_current_class[self.pair] # exit gracefully # return None # # end of method def set_point(self): ''' method: InputDisplay::set_point arguments: None return: None description: this method sets the draw flag to point ''' self.draw = 'point' # exit gracefully # return None # # end of method def set_gauss(self): ''' method: InputDisplay::set_gauss arguments: None return: None description: this method sets the draw flag to gauss ''' self.draw = 'gauss' # exit gracefully # return None # # end of method # # end of class class OutputDisplay(QtWidgets.QWidget): ''' class: OutputDisplay description: this class sets up the eval window/graph (output display) ''' def __init__(self, parent=None): ''' method: OutputDisplay::constructor arguments: None return: None description: initialize the eval window/graph ''' super(OutputDisplay, self).__init__(parent) # initialize the canvas and label objects # self.canvas = FigureCanvas(Figure(tight_layout=True)) self.label = QtWidgets.QLabel() # set up the x and y coordinate arrays # self.x = np.empty((0, 0)) self.y = np.empty((0, 0)) # set up the covariance of the gaussian # when user draws # self.cov = [[0.05, 0.0], [0.0, 0.05]] self.num_points = 25 # set up color variables: color bank, colors used, and # current colors # self.colors = [*cnames] self.colors_used = [] self.color_c = None # set up current class, triggered class, all_classes # self.current_class = None self.t_current_class = None self.all_classes = [] self.current_co = None # set up dictionary to store class information # self.once_c = True self.pair = 0 self.class_info = {} # set up initial press and draw flag # self.pressed = False self.draw = 'point' # sets title and maximum size in pixels for Eval window # self.label.setText("Eval:") self.label.setStyleSheet("color: black;") self.label.setMaximumSize(35, 10) # defines the layout for the widget # vertical_layout = QtWidgets.QVBoxLayout(self) vertical_layout.setContentsMargins(0, 0, 0, 0) vertical_layout.addWidget(self.label) vertical_layout.addWidget(self.canvas) self.canvas.axes = self.canvas.figure.add_subplot(111) self.x_axis = [-DEFAULT, DEFAULT] self.y_axis = [-DEFAULT, DEFAULT] self.initUI() self.canvas.mpl_connect("button_press_event", self.on_press) self.canvas.mpl_connect("button_release_event", self.on_release) self.canvas.mpl_connect("motion_notify_event", self.on_move) # exit gracefully # return None # # end of method def initUI(self): ''' method: OutputDisplay::initUI arguments: None return: None description: this method adds the eval window to the GUI ''' self.canvas.axes.set_xlim([self.x_axis[0], self.x_axis[-1]]) self.canvas.axes.set_ylim([self.y_axis[0], self.y_axis[-1]]) # set range and grid # self.canvas.axes.set_axisbelow(True) # set visual aspects of grid # self.canvas.axes.grid(which='major', color='grey', linestyle='-', linewidth=0.25, alpha=0.50) self.canvas.axes.tick_params(labelsize=8) self.x_axislim = self.canvas.axes.get_xlim() self.y_axislim = self.canvas.axes.get_ylim() self.x_axis = np.linspace(self.x_axislim[0], self.x_axislim[1], 9) self.y_axis = np.linspace(self.y_axislim[0], self.y_axislim[1], 9) self.canvas.axes.set_xticks(self.x_axis) self.canvas.axes.set_yticks(self.y_axis) # exit gracefully # return None # # end of method def clear_plot(self): ''' method: OutputDisplay::clear_plot arguments: None return: None description: this method clears any data that is shown in the output display ''' # clear the input canvas and reset with the intial UI # self.canvas.axes.clear() self.initUI() # iterate through class info and reset all information # while keeping the color of the class # count = 0 for key in self.class_info: # reset x and y coordinates # self.class_info[key][1] = np.empty((0, 0)) self.class_info[key][2] = np.empty((0, 0)) # reset class plot with same color # self.class_info[key][0] = \ self.canvas.axes.scatter(self.class_info[key][1], self.class_info[key][2], s=1) self.class_info[key][0].set_color(self.class_info[key][4]) # set current class to the key # self.current_class = key self.class_info[self.current_class][0].set_gid( np.full((1, np.shape(self.class_info\ [self.current_class][1])[0]), count)) self.canvas.draw_idle() count = count + 1 self.canvas.draw_idle() # exit gracefully # return None # # end of method def clear_result_plot(self): ''' method: OutputDisplay::clear_result_plot arguments: None return: None description: this method clears the results from the plot and keeps the data plotted ''' # remove canvas and reinsert the initial UI # self.canvas.axes.clear() self.initUI() # iterate through class dictionary and replot the graphs without results # count = 0 for key in self.class_info: self.class_info[key][0] = self.canvas.axes.scatter\ (self.class_info[key][1], self.class_info[key][2], s=1) self.class_info[key][0].set_color(self.class_info[key][4]) self.current_class = key self.class_info[self.current_class][0].set_gid( np.full((1, np.shape(self.class_info\ [self.current_class][1])[0]), count)) count = count + 1 self.canvas.draw_idle() # exit gracefully # return None # # end of method def remove_class(self, name): ''' method: OutputDisplay::remove_class arguments: name: the current class that is chose to be removed return: None description: this method removes a singles class using the delete class button ''' if self.class_info: # clear the visual plot # self.canvas.axes.clear() self.initUI() # set the current class and reset the colors used # tmp = self.current_class if self.class_info[self.current_class] is not None: color = self.class_info[self.current_class][4] if color in self.colors_used: self.colors_used.remove(color) else: self.colors.append(color) self.class_info.pop(self.current_class) # replot the remaining classes from class_info with # their appropriate color # count = 0 for key in self.class_info: self.class_info[key][0] = \ self.canvas.axes.scatter(self.class_info[key][1], \ self.class_info[key][2], s=1) self.class_info[key][0].set_color(self.class_info[key][4]) self.current_class = key self.class_info[self.current_class][0].set_gid( np.full((1, np.shape(self.class_info\ [self.current_class][1])[0]), count)) self.canvas.draw_idle() count = count + 1 self.canvas.draw_idle() else: pass # exit gracefully # return None # # end of method def on_press(self, event): ''' method: OutputDisplay::on_press arguments: event: the event loop for this app return: None description: this method calculates where the mouse is on the display and records both the x and y coordinates of the data stores them into a dictionary where the class is linked with its data. there are two options: either a clock and drog for points or use of gaussian plots. this makes up the evaluation data ''' # check if current class name is a string and if not make it a string # if not isinstance(self.current_class, str): self.current_class = "%s" % self.current_class # find the index of current class in total list # self.pair = self.t_current_class.index(self.current_class) # if the current class is not in the class dictionary # or is initialized to None, initialize the class in the dictionary # if self.current_class not in self.class_info or \ (self.class_info[self.current_class] == None): if self.color_c == None: self.color_c = self.colors.pop() self.class_info[self.current_class] = [self.current_c, self.x, self.y, self.once_c, self.color_c] # set the draw to point and track the x and y data # if self.draw == "point": self.pressed = True if self.class_info[self.current_class][3]: self.class_info[self.current_class][0] = \ self.canvas.axes.scatter(None, None, s=1) self.class_info[self.current_class][3] = False self.class_info[self.current_class][1] = \ np.append(self.class_info[self.current_class][1], event.xdata) self.class_info[self.current_class][2] = \ np.append(self.class_info[self.current_class][2], event.ydata) self.class_info[self.current_class][0].set_color\ (self.class_info[self.current_class][4]) self.canvas.draw_idle() # set the draw to gaussian and track the data produced by the # target x, y, and the gaussian formula # else: self.pressed = True if self.class_info[self.current_class][3]: self.class_info[self.current_class][0] = \ self.canvas.axes.scatter(None, None, s=1) self.class_info[self.current_class][0].set_color\ (self.class_info[self.current_class][4]) self.class_info[self.current_class][3] = False mu = [event.xdata, event.ydata] cov = self.cov gauss_plot = np.random.multivariate_normal(mu, cov, self.num_points) self.class_info[self.current_class][1] = \ np.append(self.class_info[self.current_class][1], gauss_plot[:, 0]) self.class_info[self.current_class][2] = \ np.append(self.class_info[self.current_class][2], gauss_plot[:, 1]) self.class_info[self.current_class][0].set_offsets( np.column_stack((self.class_info[self.current_class][1], self.class_info[self.current_class][2]))) self.canvas.draw_idle() # exit gracefully # return None # # end of method def on_move(self, event): ''' method: OutputDisplay::on_move arguments: event: the event loop for this app return: None description: this method records the x and y data while the mouse is moving ''' if self.pressed: # if set to point, append the x and y data of the # current mouse position # if self.draw == "point": self.class_info[self.current_class][1] = \ np.append(self.class_info[self.current_class][1], \ event.xdata) self.class_info[self.current_class][2] = \ np.append(self.class_info[self.current_class][2], \ event.ydata) self.class_info[self.current_class][0].set_offsets( np.column_stack((self.class_info[self.current_class][1], self.class_info[self.current_class][2]))) self.canvas.draw_idle() # if set to gaussian, record the gaussian with current mouse # placement while moving # else: mu = [event.xdata, event.ydata] cov = self.cov gauss_plot = np.random.multivariate_normal(mu, cov, self.num_points) self.class_info[self.current_class][1] = \ np.append(self.class_info[self.current_class][1], gauss_plot[:, 0]) self.class_info[self.current_class][2] = \ np.append(self.class_info[self.current_class][2], gauss_plot[:, 1]) self.class_info[self.current_class][0].set_offsets( np.column_stack((self.class_info[self.current_class][1], self.class_info[self.current_class][2]))) self.canvas.draw_idle() # exit gracefully # return None # # end of method def on_release(self, event): ''' method: OutputDisplay::on_release arguments: event: the event loop for this app return: None description: this method resets the pressed flag and set a name for the class_info correlating with graphical data ''' # set the pressed flag to false # self.pressed = False # set class in dictionary to pair with the graphical data # self.class_info[self.current_class][0].set_gid\ (np.full((1, np.shape(self.class_info[self.current_class][1])[0]), self.pair)) self.canvas.draw_idle() # exit gracefully # return None # # end of method def find_class_c(self, sender=None): ''' method: OutputDisplay::find_class_c arguments: sender: the menu item that is currently triggered return: None description: this method finds the current class object that is triggered and finds what the appropriate color is that is paired with the class ''' # check if the sender signal is triggered # if sender is not None: # find the class pair of the signal and set it to the current class # self.pair = self.all_classes.index(sender) self.current_co = sender self.current_class = self.t_current_class[self.pair] else: pass # exit gracefully # return None # # end of method def set_point(self): ''' method: OutputDisplay::set_point arguments: None return: None description: this method sets the draw flag to point ''' self.draw = 'point' # exit gracefully # return None # # end of method def set_gauss(self): ''' method: OutputDisplay::set_gauss arguments: None return: None description: this method sets the draw flag to gauss ''' self.draw = 'gauss' # exit gracefully # return None # # end of method # # end of class class ProcessDescription(QtWidgets.QWidget): ''' class: ProcessDescription description: this class contains methods that freate the process description window which narrates the process of the algorithms as they run to evlauate the data in the train and eval windows ''' def __init__(self, parent=None): ''' method: ProcessDescription::constructor arguments: parent: the parent widget (widget that draws the child) return: None description: initialize the ProcessDescription to the GUI ''' super(ProcessDescription, self).__init__(parent) # defines the size policy for the widget # self.output = QtWidgets.QTextEdit() self.label = QtWidgets.QLabel() self.initUI() # # end of method def initUI(self): ''' method: ProcessDescription::initUI arguments: None return: None description: this method creates the gui ''' # sets the textbox to read-only # font = QtGui.QFont("Consolas") font.setStyleHint(QtGui.QFont.Monospace) self.output.setFont(font) self.output.setReadOnly(True) self.line_edit = QtGui.QTextLine() self.label.setText("Process Log:") # set size and color of title text # self.label.setStyleSheet("color: black;") self.label.setMaximumSize(124, 17) self.output.setStyleSheet("color: black;") # defines the layout for the widget # vertical_layout = QtWidgets.QVBoxLayout(self) vertical_layout.setContentsMargins(0, 0, 0, 0) vertical_layout.addWidget(self.label) vertical_layout.addWidget(self.output) # exit gracefully # return None # # end of method # # end of class # # end of file