<reponame>Quantum-OCS/QuOCS-pyside2interface
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#  Copyright 2021-  QuOCS Team
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

import os

from qtpy import QtCore
from qtpy import QtWidgets
import pyqtgraph as pg
import numpy as np

from quocspyside2interface.gui.tools.DropOutPlotter import DropOutPlotter
from quocspyside2interface.logic.utilities.readjson import readjson
from quocspyside2interface.gui.algorithms.dcraboptimization.dCRABSettingsDialog import dCRABSettingsDialog
from quocspyside2interface.gui.algorithms.pureparametersoptimization.DirectSearchSettingsDialog import \
    DirectSearchSettingsDialog
from quocspyside2interface.gui.uiclasses.MainWindowUI import Ui_MainWindow


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.show()


class OptimizationBasicGui:
    optimizationlogic = None
    ########################
    # Signal to logic class
    ########################
    # Start optimization
    start_optimization_signal = QtCore.Signal(dict)
    # Stop Optimization
    stop_optimization_signal = QtCore.Signal(bool)
    # Update dictionary fom plotter
    update_plotter_dictionary_signal = QtCore.Signal(int)
    # Dictionary signal
    load_dictionary_signal = QtCore.Signal(dict)
    ##################################
    # Items to plot
    ##################################
    # Logo
    logo_item = None
    # Pulse
    pulse_item = None
    ###################################
    # Windows
    ###################################
    _mw: QtWidgets.QMainWindow
    ###################################
    # Other variables
    ###################################
    # parameters list
    parameters_list = [""]
    # Current fom plotter dictionary
    fom_plotter_dict = {}
    pulses_plotter_dict = {}
    pulses_plotter_list: list = [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}]
    pulses_plot_limits: [int, int] = [0, 0]
    curr_pulse_plot_number: int = 0

    pulses_data_dict = {}
    iterations_number: list = []
    fom_evaluation: list = []
    # Optimization - Communication dictionary
    opti_comm_dict: dict = {"version": "dev"}

    #############################################
    # Improve the speed on plotting data with OpenGL
    #############################################
    try:
        import OpenGL
        pg.setConfigOption('useOpenGL', True)
        pg.setConfigOption('enableExperimental', True)
        print("Pyopengl is activated, to improve rendering performance")
    except Exception as e:
        print(f"Enabling OpenGL failed with {e}. Will result in slow rendering. Try installing PyOpenGL.")

    # The pen for figure of merit data
    pen = pg.mkPen(color=(255, 0, 0))

    def handle_ui_elements(self):
        ###################################
        # Windows
        ###################################
        self._mw = MainWindow()
        # Check if the optimization object was initialized in the sub class
        if self.optimizationlogic is None:
            raise Exception("Optimization logic object is not initialized in the sub class")
        ########################################################################
        #                            Configure widgets                         #
        ########################################################################
        self._mw.stop_optimization_button.setEnabled(False)
        self._mw.start_optimization_button.setEnabled(False)
        self._mw.main_operations_label.setText("Open or create a new configuration file")
        self._mw.optimization_details_plain_text.setReadOnly(True)
        #########################################################################
        # Connect buttons with functions
        #########################################################################
        # Start Button
        self._mw.start_optimization_button.clicked.connect(self.start_optimization)
        # Stop Button
        self._mw.stop_optimization_button.clicked.connect(self.stop_optimization)
        # Drop out plotter
        self._mw.drop_out_fom_button.clicked.connect(self.drop_out_plotter)
        # Connect spinbox with function
        self._mw.select_parameter_spinbox.setMinimum(1)
        self._mw.select_parameter_spinbox.valueChanged.connect(self._update_parameter_choice)
        # Connect file menu action
        # self._mw.new_action.triggered.connect(self._get_pure_parameters_optimization_dialog)
        self._mw.parameters_optimization_action.triggered.connect(self._get_pure_parameters_optimization_dialog)
        self._mw.dcrab_optimization_action.triggered.connect(self._get_dcrab_optimization_dialog)
        self._mw.load_file_action.triggered.connect(self._load_configuration_file)
        # Other operations
        self.fom_plotter_dict["0"] = self._mw.fom_plotter
        self.pulses_plotter_dict["0"] = self._mw.pulses_plotter
        self.pulses_data_dict["0"] = None
        self.pulses_plotter_list[0]["plotter"]["pyqt_obj"] = self._mw.pulses_plotter
        # self._mw.previous_pulse_plot_button.setEnabled(False)
        # self._mw.next_pulse_plot_button.setEnabled(False)
        self._mw.previous_pulse_plot_button.clicked.connect(self.previous_plot_pulse)
        self._mw.next_pulse_plot_button.clicked.connect(self.next_plot_pulse)
        # Disabled dropout buttons atm
        self._mw.drop_out_fom_button.setEnabled(False)
        self._mw.drop_out_pulse_button.setEnabled(False)
        self._mw.drop_out_parameter_button.setEnabled(False)

        #########################################################################
        # Signals
        #########################################################################
        # Start optimization signal
        self.start_optimization_signal.connect(self.optimizationlogic.start_optimization)
        # Update status optimization signal from is_running logic to the optimization logic
        # TODO check the status optimization here
        # Update status optimization signal from Optimization logic to GUI
        self.optimizationlogic.is_running_signal.connect(self.finished_optimization)
        # Update fom plotter dictionary
        self.update_plotter_dictionary_signal.connect(self.update_fom_plotter_dictionary)
        # Stop signal
        self.stop_optimization_signal.connect(self.optimizationlogic.handle_exit_obj.set_is_user_running)
        # Update message signal
        self.optimizationlogic.message_label_signal.connect(self.label_messages)
        # Update the plot data signal
        self.optimizationlogic.fom_plot_signal.connect(self.update_fom_graph)
        # Update the pulse plot
        self.optimizationlogic.controls_update_signal.connect(self.controls_update)
        # Update the parameters array
        # self.optimizationlogic.parameters_update_signal.connect(self.update_parameters_list)
        # Dictionaries signal
        self.load_dictionary_signal.connect(self.update_optimization_dictionary)

    def next_plot_pulse(self):
        # [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}]
        # TODO Put limits on pulse index
        next_conditions = [self.curr_pulse_plot_number >= self.pulses_plot_limits[0],
                           self.curr_pulse_plot_number < self.pulses_plot_limits[1] - 1]
        if all(next_conditions):
            self.curr_pulse_plot_number += 1
            self.pulses_plotter_list[0]["pulse_index"] += 1


    def previous_plot_pulse(self):
        # [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}]
        # TODO Put limits on pulse_index
        previous_conditions = [self.curr_pulse_plot_number > self.pulses_plot_limits[0],
                               self.curr_pulse_plot_number <= self.pulses_plot_limits[1] - 1]
        if all(previous_conditions):
            self.curr_pulse_plot_number -= 1
            self.pulses_plotter_list[0]["pulse_index"] -= 1

    @QtCore.Slot(int)
    def update_fom_plotter_dictionary(self, id_window):
        """Remove plotter from the dictionary"""
        del self.fom_plotter_dict[str(id_window)]

    def _get_pure_parameters_optimization_dialog(self):
        print("Try to open pure parametrization settings")
        pure_parameter_optimization_dialog = \
            DirectSearchSettingsDialog(load_full_dictionary_signal=self.load_dictionary_signal)
        pure_parameter_optimization_dialog.exec_()

    def _get_dcrab_optimization_dialog(self):
        print("Try to open dcrab optimization settings")
        dcrab_optimization_dialog = dCRABSettingsDialog(load_full_dictionary_signal=self.load_dictionary_signal)
        dcrab_optimization_dialog.exec_()

    def _load_configuration_file(self):
        #
        print("Try to load an optimization Dialog")
        # Get the file
        filename = QtWidgets.QFileDialog.getOpenFileName(self._mw,
                                                         "Open credential file", os.getcwd(), "json (*.json)",
                                                         options=QtWidgets.QFileDialog.DontUseNativeDialog)
        err_stat, user_data = readjson(filename[0])
        # Open the json file and get the dictionary
        if err_stat != 0:
            # TODO Error in the GUI
            pass
        else:
            implemented_algorithms = {"dCRAB": dCRABSettingsDialog, "DirectSearchMethod": DirectSearchSettingsDialog}
            opti_algorithm_name = user_data["optimization_dictionary"]["opti_algorithm_name"]
            if opti_algorithm_name in implemented_algorithms:
                print("I am loading {0} dialog".format(opti_algorithm_name))
                optimization_dialog = implemented_algorithms[opti_algorithm_name](loaded_dictionary=user_data,
                                                                                  load_full_dictionary_signal=self.load_dictionary_signal)
                optimization_dialog.exec_()
            else:
                # TODO Raise error in the GUI
                pass

    def drop_out_plotter(self):
        """Drop out the plotter"""
        id_plotter_window = len(self.fom_plotter_dict)
        plotter_window = DropOutPlotter(id_plotter_window, self.update_plotter_dictionary_signal, parent=self._mw)
        self.fom_plotter_dict[str(id_plotter_window)] = plotter_window.fom_plotter
        plotter_window.show()

    def _update_parameters_list(self, parameters_list):
        """Update the parameters list at every iteration"""
        self.parameters_list = parameters_list
        # Update parameter range in the spinbox
        self._mw.select_parameter_spinbox.setMaximum(len(parameters_list))
        # Update parameter also in the label
        self._update_parameter_choice()

    def _update_parameter_choice(self):
        """display in the parameter label the parameter you choose"""
        parameter_index = self._mw.select_parameter_spinbox.value() - 1
        if parameter_index >=0:
            parameter_value = str(self.parameters_list[parameter_index])
            self._mw.value_parameter_label.setText(parameter_value)

    @QtCore.Slot()
    def finished_optimization(self):
        """The optimization is finished. Update buttons"""
        # Disable the stop button
        self._mw.stop_optimization_button.setEnabled(False)
        # Enable the start button
        self._mw.start_optimization_button.setEnabled(True)

    @QtCore.Slot(list, list, list)
    def controls_update(self, pulses_list, time_grids_list, parameters_list):
        self.pulses_plot_limits = [0, len(pulses_list)]
        self._update_pulses_graph(time_grids_list, pulses_list)
        self._update_parameters_list(parameters_list)

    def _update_pulses_graph(self, time_grids_list, pulses_list):
        """Update all the current pulses plotters"""
        index_list = [i for i in range(len(pulses_list))]
        # print("I am in the update pulses module")
        for time_grid, pulse, index in zip(time_grids_list, pulses_list, index_list):
            # [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}]
            for plotter_obj in self.pulses_plotter_list:
                if plotter_obj["pulse_index"] == index:
                    self._mw.pulse_index_label.setText("Pulse {0}".format(self.curr_pulse_plot_number))
                    if plotter_obj["plotter"]["data_obj"] is None:
                        # print("First iteration")
                        pen = pg.mkPen(color=(255, 0, 0))
                        plotter_obj["plotter"]["data_obj"] = plotter_obj["plotter"]["pyqt_obj"].plot(time_grid, pulse,
                                                                                                     pen=pen)
                    else:
                        # print("Update")
                        plotter_obj["plotter"]["data_obj"].setData(time_grid, pulse)

    @QtCore.Slot(int, float)
    def update_fom_graph(self, iteration_number, fom):
        """update all the current fom plotters"""
        # TODO Substitute scatter plot with fom plotter
        self.fom_evaluation.append(fom)
        self.iterations_number.append(iteration_number)
        # Convert to numpy array to plot faster with pyqt
        fom_evaluation = np.asarray(self.fom_evaluation, dtype="float")
        iterations_number = np.asarray(self.iterations_number, dtype="float")
        for plotter_id in self.fom_plotter_dict:
            # If the first iteration create the line
            if len(self.fom_evaluation) == 1:
                # TODO annotate the fom_data type before the constructor
                pen = self.pen
                # self.fom_data = self.fom_plotter_dict[plotter_id].plot(iterations_number, fom_evaluation,
                #                                                        pen=pen, symbol='o')
                # Plot the first graph
                self.fom_data = self.fom_plotter_dict[plotter_id].plot(iterations_number, fom_evaluation,
                                                                       pen=pen, symbol='o')
            else:
                # Set the data
                self.fom_data.setData(iterations_number, fom_evaluation)

    @QtCore.Slot(dict)
    def update_optimization_dictionary(self, opti_comm_dict):
        # Update the optimization dictionary in the optimization details and in the internal dictionary before starting
        # the optimization
        self.opti_comm_dict = opti_comm_dict
        # Update opti dictionary
        self._mw.optimization_details_plain_text.setPlainText(str(opti_comm_dict))
        # Enable start button
        self._mw.start_optimization_button.setEnabled(True)
        # Update label message
        self.label_messages("It is possible to start the optimization")

    def clear_fom_graph(self):
        """Clear the data points and the list"""
        for plotter_id in self.fom_plotter_dict:
            self.fom_plotter_dict[plotter_id].clear()
        self.fom_evaluation = []
        self.iterations_number = []

    def start_optimization(self):
        """Emit the start optimization signal"""
        # Disable buttons and clear canvas
        self.running_mode_buttons_p1()
        # Send the optimization dictionary with the comm fom settings to the optimization logic interface
        self.start_optimization_signal.emit(self.opti_comm_dict)
        # Enable buttons
        self.running_mode_buttons_p2()
        
    def running_mode_buttons_p1(self):
        # Disable the start button
        self._mw.start_optimization_button.setEnabled(False)
        # Send the signal to the handle exit obj
        # TODO check if it is really compulsory. An activation signal is already send in the logic part
        # self.stop_optimization_signal.emit(True)
        # Remove the logo from the canvas
        if self.logo_item is not None:
            self._mw.fom_plotter.removeItem(self.logo_item)
        # Clear the figure of merit graph
        self.clear_fom_graph()
    
    def running_mode_buttons_p2(self):
        # Enable stop optimization button
        self._mw.stop_optimization_button.setEnabled(True)

    def stop_optimization(self):
        """ Stop the optimization by the button"""
        # Disable the stop button
        self._mw.stop_optimization_button.setEnabled(False)
        # Send the signal to the handle exit obj
        self.stop_optimization_signal.emit(False)
        # Enable the start button
        self._mw.start_optimization_button.setEnabled(True)

    @QtCore.Slot(str)
    def label_messages(self, message):
        """Update the label with the message"""
        self._mw.main_operations_label.setText(message)
        # Catch particular messages
        if message == "start":
            self.running_mode_buttons_p1()
            self.running_mode_buttons_p2()
            

# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#  Copyright 2021-  QuOCS Team
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

import os

from qtpy import QtCore
from qtpy import QtWidgets
import pyqtgraph as pg
import numpy as np

from quocspyside2interface.gui.tools.DropOutPlotter import DropOutPlotter
from quocspyside2interface.logic.utilities.readjson import readjson
from quocspyside2interface.gui.algorithms.dcraboptimization.dCRABSettingsDialog import dCRABSettingsDialog
from quocspyside2interface.gui.algorithms.pureparametersoptimization.DirectSearchSettingsDialog import \
    DirectSearchSettingsDialog
from quocspyside2interface.gui.uiclasses.MainWindowUI import Ui_MainWindow


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.show()


class OptimizationBasicGui:
    optimizationlogic = None
    ########################
    # Signal to logic class
    ########################
    # Start optimization
    start_optimization_signal = QtCore.Signal(dict)
    # Stop Optimization
    stop_optimization_signal = QtCore.Signal(bool)
    # Update dictionary fom plotter
    update_plotter_dictionary_signal = QtCore.Signal(int)
    # Dictionary signal
    load_dictionary_signal = QtCore.Signal(dict)
    ##################################
    # Items to plot
    ##################################
    # Logo
    logo_item = None
    # Pulse
    pulse_item = None
    ###################################
    # Windows
    ###################################
    _mw: QtWidgets.QMainWindow
    ###################################
    # Other variables
    ###################################
    # parameters list
    parameters_list = [""]
    # Current fom plotter dictionary
    fom_plotter_dict = {}
    pulses_plotter_dict = {}
    pulses_plotter_list: list = [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}]
    pulses_plot_limits: [int, int] = [0, 0]
    curr_pulse_plot_number: int = 0

    pulses_data_dict = {}
    iterations_number: list = []
    fom_evaluation: list = []
    # Optimization - Communication dictionary
    opti_comm_dict: dict = {"version": "dev"}

    #############################################
    # Improve the speed on plotting data with OpenGL
    #############################################
    try:
        import OpenGL
        pg.setConfigOption('useOpenGL', True)
        pg.setConfigOption('enableExperimental', True)
        print("Pyopengl is activated, to improve rendering performance")
    except Exception as e:
        print(f"Enabling OpenGL failed with {e}. Will result in slow rendering. Try installing PyOpenGL.")

    # The pen for figure of merit data
    pen = pg.mkPen(color=(255, 0, 0))

    def handle_ui_elements(self):
        ###################################
        # Windows
        ###################################
        self._mw = MainWindow()
        # Check if the optimization object was initialized in the sub class
        if self.optimizationlogic is None:
            raise Exception("Optimization logic object is not initialized in the sub class")
        ########################################################################
        #                            Configure widgets                         #
        ########################################################################
        self._mw.stop_optimization_button.setEnabled(False)
        self._mw.start_optimization_button.setEnabled(False)
        self._mw.main_operations_label.setText("Open or create a new configuration file")
        self._mw.optimization_details_plain_text.setReadOnly(True)
        #########################################################################
        # Connect buttons with functions
        #########################################################################
        # Start Button
        self._mw.start_optimization_button.clicked.connect(self.start_optimization)
        # Stop Button
        self._mw.stop_optimization_button.clicked.connect(self.stop_optimization)
        # Drop out plotter
        self._mw.drop_out_fom_button.clicked.connect(self.drop_out_plotter)
        # Connect spinbox with function
        self._mw.select_parameter_spinbox.setMinimum(1)
        self._mw.select_parameter_spinbox.valueChanged.connect(self._update_parameter_choice)
        # Connect file menu action
        # self._mw.new_action.triggered.connect(self._get_pure_parameters_optimization_dialog)
        self._mw.parameters_optimization_action.triggered.connect(self._get_pure_parameters_optimization_dialog)
        self._mw.dcrab_optimization_action.triggered.connect(self._get_dcrab_optimization_dialog)
        self._mw.load_file_action.triggered.connect(self._load_configuration_file)
        # Other operations
        self.fom_plotter_dict["0"] = self._mw.fom_plotter
        self.pulses_plotter_dict["0"] = self._mw.pulses_plotter
        self.pulses_data_dict["0"] = None
        self.pulses_plotter_list[0]["plotter"]["pyqt_obj"] = self._mw.pulses_plotter
        # self._mw.previous_pulse_plot_button.setEnabled(False)
        # self._mw.next_pulse_plot_button.setEnabled(False)
        self._mw.previous_pulse_plot_button.clicked.connect(self.previous_plot_pulse)
        self._mw.next_pulse_plot_button.clicked.connect(self.next_plot_pulse)
        # Disabled dropout buttons atm
        self._mw.drop_out_fom_button.setEnabled(False)
        self._mw.drop_out_pulse_button.setEnabled(False)
        self._mw.drop_out_parameter_button.setEnabled(False)

        #########################################################################
        # Signals
        #########################################################################
        # Start optimization signal
        self.start_optimization_signal.connect(self.optimizationlogic.start_optimization)
        # Update status optimization signal from is_running logic to the optimization logic
        # TODO check the status optimization here
        # Update status optimization signal from Optimization logic to GUI
        self.optimizationlogic.is_running_signal.connect(self.finished_optimization)
        # Update fom plotter dictionary
        self.update_plotter_dictionary_signal.connect(self.update_fom_plotter_dictionary)
        # Stop signal
        self.stop_optimization_signal.connect(self.optimizationlogic.handle_exit_obj.set_is_user_running)
        # Update message signal
        self.optimizationlogic.message_label_signal.connect(self.label_messages)
        # Update the plot data signal
        self.optimizationlogic.fom_plot_signal.connect(self.update_fom_graph)
        # Update the pulse plot
        self.optimizationlogic.controls_update_signal.connect(self.controls_update)
        # Update the parameters array
        # self.optimizationlogic.parameters_update_signal.connect(self.update_parameters_list)
        # Dictionaries signal
        self.load_dictionary_signal.connect(self.update_optimization_dictionary)

    def next_plot_pulse(self):
        # [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}]
        # TODO Put limits on pulse index
        next_conditions = [self.curr_pulse_plot_number >= self.pulses_plot_limits[0],
                           self.curr_pulse_plot_number < self.pulses_plot_limits[1] - 1]
        if all(next_conditions):
            self.curr_pulse_plot_number += 1
            self.pulses_plotter_list[0]["pulse_index"] += 1


    def previous_plot_pulse(self):
        # [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}]
        # TODO Put limits on pulse_index
        previous_conditions = [self.curr_pulse_plot_number > self.pulses_plot_limits[0],
                               self.curr_pulse_plot_number <= self.pulses_plot_limits[1] - 1]
        if all(previous_conditions):
            self.curr_pulse_plot_number -= 1
            self.pulses_plotter_list[0]["pulse_index"] -= 1

    @QtCore.Slot(int)
    def update_fom_plotter_dictionary(self, id_window):
        """Remove plotter from the dictionary"""
        del self.fom_plotter_dict[str(id_window)]

    def _get_pure_parameters_optimization_dialog(self):
        print("Try to open pure parametrization settings")
        pure_parameter_optimization_dialog = \
            DirectSearchSettingsDialog(load_full_dictionary_signal=self.load_dictionary_signal)
        pure_parameter_optimization_dialog.exec_()

    def _get_dcrab_optimization_dialog(self):
        print("Try to open dcrab optimization settings")
        dcrab_optimization_dialog = dCRABSettingsDialog(load_full_dictionary_signal=self.load_dictionary_signal)
        dcrab_optimization_dialog.exec_()

    def _load_configuration_file(self):
        #
        print("Try to load an optimization Dialog")
        # Get the file
        filename = QtWidgets.QFileDialog.getOpenFileName(self._mw,
                                                         "Open credential file", os.getcwd(), "json (*.json)",
                                                         options=QtWidgets.QFileDialog.DontUseNativeDialog)
        err_stat, user_data = readjson(filename[0])
        # Open the json file and get the dictionary
        if err_stat != 0:
            # TODO Error in the GUI
            pass
        else:
            implemented_algorithms = {"dCRAB": dCRABSettingsDialog, "DirectSearchMethod": DirectSearchSettingsDialog}
            opti_algorithm_name = user_data["optimization_dictionary"]["opti_algorithm_name"]
            if opti_algorithm_name in implemented_algorithms:
                print("I am loading {0} dialog".format(opti_algorithm_name))
                optimization_dialog = implemented_algorithms[opti_algorithm_name](loaded_dictionary=user_data,
                                                                                  load_full_dictionary_signal=self.load_dictionary_signal)
                optimization_dialog.exec_()
            else:
                # TODO Raise error in the GUI
                pass

    def drop_out_plotter(self):
        """Drop out the plotter"""
        id_plotter_window = len(self.fom_plotter_dict)
        plotter_window = DropOutPlotter(id_plotter_window, self.update_plotter_dictionary_signal, parent=self._mw)
        self.fom_plotter_dict[str(id_plotter_window)] = plotter_window.fom_plotter
        plotter_window.show()

    def _update_parameters_list(self, parameters_list):
        """Update the parameters list at every iteration"""
        self.parameters_list = parameters_list
        # Update parameter range in the spinbox
        self._mw.select_parameter_spinbox.setMaximum(len(parameters_list))
        # Update parameter also in the label
        self._update_parameter_choice()

    def _update_parameter_choice(self):
        """display in the parameter label the parameter you choose"""
        parameter_index = self._mw.select_parameter_spinbox.value() - 1
        if parameter_index >=0:
            parameter_value = str(self.parameters_list[parameter_index])
            self._mw.value_parameter_label.setText(parameter_value)

    @QtCore.Slot()
    def finished_optimization(self):
        """The optimization is finished. Update buttons"""
        # Disable the stop button
        self._mw.stop_optimization_button.setEnabled(False)
        # Enable the start button
        self._mw.start_optimization_button.setEnabled(True)

    @QtCore.Slot(list, list, list)
    def controls_update(self, pulses_list, time_grids_list, parameters_list):
        self.pulses_plot_limits = [0, len(pulses_list)]
        self._update_pulses_graph(time_grids_list, pulses_list)
        self._update_parameters_list(parameters_list)

    def _update_pulses_graph(self, time_grids_list, pulses_list):
        """Update all the current pulses plotters"""
        index_list = [i for i in range(len(pulses_list))]
        # print("I am in the update pulses module")
        for time_grid, pulse, index in zip(time_grids_list, pulses_list, index_list):
            # [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}]
            for plotter_obj in self.pulses_plotter_list:
                if plotter_obj["pulse_index"] == index:
                    self._mw.pulse_index_label.setText("Pulse {0}".format(self.curr_pulse_plot_number))
                    if plotter_obj["plotter"]["data_obj"] is None:
                        # print("First iteration")
                        pen = pg.mkPen(color=(255, 0, 0))
                        plotter_obj["plotter"]["data_obj"] = plotter_obj["plotter"]["pyqt_obj"].plot(time_grid, pulse,
                                                                                                     pen=pen)
                    else:
                        # print("Update")
                        plotter_obj["plotter"]["data_obj"].setData(time_grid, pulse)

    @QtCore.Slot(int, float)
    def update_fom_graph(self, iteration_number, fom):
        """update all the current fom plotters"""
        # TODO Substitute scatter plot with fom plotter
        self.fom_evaluation.append(fom)
        self.iterations_number.append(iteration_number)
        # Convert to numpy array to plot faster with pyqt
        fom_evaluation = np.asarray(self.fom_evaluation, dtype="float")
        iterations_number = np.asarray(self.iterations_number, dtype="float")
        for plotter_id in self.fom_plotter_dict:
            # If the first iteration create the line
            if len(self.fom_evaluation) == 1:
                # TODO annotate the fom_data type before the constructor
                pen = self.pen
                # self.fom_data = self.fom_plotter_dict[plotter_id].plot(iterations_number, fom_evaluation,
                #                                                        pen=pen, symbol='o')
                # Plot the first graph
                self.fom_data = self.fom_plotter_dict[plotter_id].plot(iterations_number, fom_evaluation,
                                                                       pen=pen, symbol='o')
            else:
                # Set the data
                self.fom_data.setData(iterations_number, fom_evaluation)

    @QtCore.Slot(dict)
    def update_optimization_dictionary(self, opti_comm_dict):
        # Update the optimization dictionary in the optimization details and in the internal dictionary before starting
        # the optimization
        self.opti_comm_dict = opti_comm_dict
        # Update opti dictionary
        self._mw.optimization_details_plain_text.setPlainText(str(opti_comm_dict))
        # Enable start button
        self._mw.start_optimization_button.setEnabled(True)
        # Update label message
        self.label_messages("It is possible to start the optimization")

    def clear_fom_graph(self):
        """Clear the data points and the list"""
        for plotter_id in self.fom_plotter_dict:
            self.fom_plotter_dict[plotter_id].clear()
        self.fom_evaluation = []
        self.iterations_number = []

    def start_optimization(self):
        """Emit the start optimization signal"""
        # Disable buttons and clear canvas
        self.running_mode_buttons_p1()
        # Send the optimization dictionary with the comm fom settings to the optimization logic interface
        self.start_optimization_signal.emit(self.opti_comm_dict)
        # Enable buttons
        self.running_mode_buttons_p2()
        
    def running_mode_buttons_p1(self):
        # Disable the start button
        self._mw.start_optimization_button.setEnabled(False)
        # Send the signal to the handle exit obj
        # TODO check if it is really compulsory. An activation signal is already send in the logic part
        # self.stop_optimization_signal.emit(True)
        # Remove the logo from the canvas
        if self.logo_item is not None:
            self._mw.fom_plotter.removeItem(self.logo_item)
        # Clear the figure of merit graph
        self.clear_fom_graph()
    
    def running_mode_buttons_p2(self):
        # Enable stop optimization button
        self._mw.stop_optimization_button.setEnabled(True)

    def stop_optimization(self):
        """ Stop the optimization by the button"""
        # Disable the stop button
        self._mw.stop_optimization_button.setEnabled(False)
        # Send the signal to the handle exit obj
        self.stop_optimization_signal.emit(False)
        # Enable the start button
        self._mw.start_optimization_button.setEnabled(True)

    @QtCore.Slot(str)
    def label_messages(self, message):
        """Update the label with the message"""
        self._mw.main_operations_label.setText(message)
        # Catch particular messages
        if message == "start":
            self.running_mode_buttons_p1()
            self.running_mode_buttons_p2()
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #  Copyright 2021-  QuOCS Team # #  Licensed under the Apache License, Version 2.0 (the "License"); #  you may not use this file except in compliance with the License. #  You may obtain a copy of the License at # #      http://www.apache.org/licenses/LICENSE-2.0 # #  Unless required by applicable law or agreed to in writing, software #  distributed under the License is distributed on an "AS IS" BASIS, #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #  See the License for the specific language governing permissions and #  limitations under the License. # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ######################## # Signal to logic class ######################## # Start optimization # Stop Optimization # Update dictionary fom plotter # Dictionary signal ################################## # Items to plot ################################## # Logo # Pulse ################################### # Windows ################################### ################################### # Other variables ################################### # parameters list # Current fom plotter dictionary # Optimization - Communication dictionary ############################################# # Improve the speed on plotting data with OpenGL ############################################# # The pen for figure of merit data ################################### # Windows ################################### # Check if the optimization object was initialized in the sub class ######################################################################## #                            Configure widgets                         # ######################################################################## ######################################################################### # Connect buttons with functions ######################################################################### # Start Button # Stop Button # Drop out plotter # Connect spinbox with function # Connect file menu action # self._mw.new_action.triggered.connect(self._get_pure_parameters_optimization_dialog) # Other operations # self._mw.previous_pulse_plot_button.setEnabled(False) # self._mw.next_pulse_plot_button.setEnabled(False) # Disabled dropout buttons atm ######################################################################### # Signals ######################################################################### # Start optimization signal # Update status optimization signal from is_running logic to the optimization logic # TODO check the status optimization here # Update status optimization signal from Optimization logic to GUI # Update fom plotter dictionary # Stop signal # Update message signal # Update the plot data signal # Update the pulse plot # Update the parameters array # self.optimizationlogic.parameters_update_signal.connect(self.update_parameters_list) # Dictionaries signal # [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}] # TODO Put limits on pulse index # [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}] # TODO Put limits on pulse_index Remove plotter from the dictionary # # Get the file # Open the json file and get the dictionary # TODO Error in the GUI # TODO Raise error in the GUI Drop out the plotter Update the parameters list at every iteration # Update parameter range in the spinbox # Update parameter also in the label display in the parameter label the parameter you choose The optimization is finished. Update buttons # Disable the stop button # Enable the start button Update all the current pulses plotters # print("I am in the update pulses module") # [{"pulse_index": 0, "plotter": {"pyqt_obj": None, "data_obj": None}}] # print("First iteration") # print("Update") update all the current fom plotters # TODO Substitute scatter plot with fom plotter # Convert to numpy array to plot faster with pyqt # If the first iteration create the line # TODO annotate the fom_data type before the constructor # self.fom_data = self.fom_plotter_dict[plotter_id].plot(iterations_number, fom_evaluation, #                                                        pen=pen, symbol='o') # Plot the first graph # Set the data # Update the optimization dictionary in the optimization details and in the internal dictionary before starting # the optimization # Update opti dictionary # Enable start button # Update label message Clear the data points and the list Emit the start optimization signal # Disable buttons and clear canvas # Send the optimization dictionary with the comm fom settings to the optimization logic interface # Enable buttons # Disable the start button # Send the signal to the handle exit obj # TODO check if it is really compulsory. An activation signal is already send in the logic part # self.stop_optimization_signal.emit(True) # Remove the logo from the canvas # Clear the figure of merit graph # Enable stop optimization button Stop the optimization by the button # Disable the stop button # Send the signal to the handle exit obj # Enable the start button Update the label with the message # Catch particular messages