def open_connection(self): #ptvsd.debug_this_thread() # open the connection pythoncom.CoInitialize() self.connection = AspenConnection(self.app_data.simulation_file) self.connection_open.emit() if self.mode == 'open': self.create_connection_id()
def open_connection(self): # warn others that a simulation engine connection is about to be opened self.opening_connection.emit() pythoncom.CoInitialize() asp_obj = AspenConnection(self.app_data.simulation_file) self.asp_obj = asp_obj self._aspen_connection = asp_obj.get_connection_object() # emit the signal to warn others that the connection is done self.connection_opened.emit()
def main(): from tests_.mock_data import ASPEN_BKP_FILE_PATH con_obj = AspenConnection(ASPEN_BKP_FILE_PATH) sim_data = con_obj.get_simulation_data() # convert uneven dict of arrays into dataframe. Empty values are NaN df = pd.DataFrame(dict([(k, pd.Series(v)) for k, v in sim_data.items()])) print(df)
def open_simulator_gui(self, checkstate: Qt.CheckState): # check if there is a connection object and open if not if not hasattr(self, 'aspen_com'): # FIXME: set an apropriate logic that doesn't freeze the UI self.aspen_com = AspenConnection(self.app_data.simulation_file) con_obj = self.aspen_com.get_connection_object() if checkstate == Qt.Checked: con_obj.Visible = 1 else: con_obj.Visible = 0
class ConnectionWorker(QObject): # signals connection_open = pyqtSignal() input_tree_loaded = pyqtSignal() output_tree_loaded = pyqtSignal() connection_id_created = pyqtSignal(object) # emits the server ID def __init__(self, app_data: DataStorage, mode: str = 'tree', parent=None): super().__init__(parent) self.app_data = app_data if mode in ['tree', 'open']: self.mode = mode else: raise ValueError("Invalid mode!") @pyqtSlot() def open_connection(self): #ptvsd.debug_this_thread() # open the connection pythoncom.CoInitialize() self.connection = AspenConnection(self.app_data.simulation_file) self.connection_open.emit() if self.mode == 'open': self.create_connection_id() @pyqtSlot() def load_tree(self): self.open_connection() # load and store simulation data dictionary self.app_data.simulation_data = self.app_data._uneven_array_to_frame( self.connection.get_simulation_data()) # load the trees input_tree = self.connection.get_simulation_partial_io_tree('input') self.app_data.tree_model_input = input_tree self.input_tree_loaded.emit() output_tree = self.connection.get_simulation_partial_io_tree('output') self.app_data.tree_model_output = output_tree self.output_tree_loaded.emit() self.create_connection_id() @pyqtSlot() def create_connection_id(self): # create the COM server ID (this enables connection from form thread) self.connection_id = pythoncom.CoMarshalInterThreadInterfaceInStream( pythoncom.IID_IDispatch, self.connection.get_connection_object()) self.connection_id_created.emit(self.connection_id)
def on_connection_id_created(self, connection_id): # make the server available to this thread pythoncom.CoInitialize() self.aspen_com = AspenConnection(self.app_data.simulation_file) # populate internal connection variable of AspenConnection class # this is a horrendous fix... it works though! self.aspen_com._aspen = Dispatch( pythoncom.CoGetInterfaceAndReleaseStream(connection_id, pythoncom.IID_IDispatch)) # quit the thread if hasattr(self, 'connection_thread'): self.connection_thread.quit()
class ReducedSamplerThread(SamplerThread): def __init__(self, input_design_data: pd.DataFrame, app_data: DataStorage, bkp_filepath: str, parent=None): QThread.__init__(self, parent) self._input_des_data = input_design_data self._app_data = app_data # initialize pythoncom.CoInitialize() self._aspen_connection = AspenConnection(bkp_filepath) # create id self._aspen_id = pythoncom.CoMarshalInterThreadInterfaceInStream( pythoncom.IID_IDispatch, self._aspen_connection.get_connection_object()) # clean up self.finished.connect(self.__del__) def run(self): # ptvsd.debug_this_thread() # initialize pythoncom.CoInitialize() # get instance from id aspen_con = win32com.client.Dispatch( pythoncom.CoGetInterfaceAndReleaseStream(self._aspen_id, pythoncom.IID_IDispatch)) inp_data = self._app_data.input_table_data out_data = self._app_data.output_table_data # TODO: move consumed aliases from input into output collection inp_data_ph = inp_data.set_index('Alias') red_inp_alias = self._app_data.reduced_doe_d_bounds.loc[:, 'name'].tolist( ) output_mvs = inp_data.loc[~inp_data['Alias'].isin(red_inp_alias)] input_vars = inp_data_ph.loc[red_inp_alias, 'Path'].reset_index().to_dict( orient='records') output_vars = pd.concat( [out_data.loc[:, ['Alias', 'Path']], output_mvs], ignore_index=True, axis='index', sort=False).to_dict(orient='records') for row in range(self._input_des_data.shape[0]): [ var.update( {'value': self._input_des_data.loc[row, var['Alias']]}) for var in input_vars ] self.case_sampled.emit( row + 1, run_case(input_vars, output_vars, aspen_con)) if self.isInterruptionRequested(): # to allow task abortion return
def __init__(self, input_design_data: pd.DataFrame, app_data: DataStorage, bkp_filepath: str, parent=None): QThread.__init__(self, parent) self._input_des_data = input_design_data self._app_data = app_data # initialize pythoncom.CoInitialize() self._aspen_connection = AspenConnection(bkp_filepath) # create id self._aspen_id = pythoncom.CoMarshalInterThreadInterfaceInStream( pythoncom.IID_IDispatch, self._aspen_connection.get_connection_object()) # clean up self.finished.connect(self.__del__)
def __init__(self, input_design_data: pd.DataFrame, app_data: DataStorage, parent=None): QThread.__init__(self, parent) self._input_des_data = input_design_data self._app_data = app_data # https://stackoverflow.com/questions/26764978/using-win32com-with-multithreading # initialize pythoncom.CoInitialize() self._aspen_connection = AspenConnection(app_data.simulation_file) # create id self._aspen_id = pythoncom.CoMarshalInterThreadInterfaceInStream( pythoncom.IID_IDispatch, self._aspen_connection.get_connection_object()) # clean up self.finished.connect(self.__del__)
class SamplerThread(QThread): """ Sampling thread that opens the aspen connection to keep the sampling assistant GUI responsive while the sampling is performed """ case_sampled = pyqtSignal(int, object) def __init__(self, input_design_data: pd.DataFrame, app_data: DataStorage, parent=None): QThread.__init__(self, parent) self._input_des_data = input_design_data self._app_data = app_data # https://stackoverflow.com/questions/26764978/using-win32com-with-multithreading # initialize pythoncom.CoInitialize() self._aspen_connection = AspenConnection(app_data.simulation_file) # create id self._aspen_id = pythoncom.CoMarshalInterThreadInterfaceInStream( pythoncom.IID_IDispatch, self._aspen_connection.get_connection_object()) # clean up self.finished.connect(self.__del__) def __del__(self): # kill the connection on thread cleanup self._aspen_connection.close_connection() def run(self): # ptvsd.debug_this_thread() # initialize pythoncom.CoInitialize() # get instance from id aspen_con = win32com.client.Dispatch( pythoncom.CoGetInterfaceAndReleaseStream(self._aspen_id, pythoncom.IID_IDispatch)) inp_data = self._app_data.input_table_data out_data = self._app_data.output_table_data input_vars = inp_data.loc[inp_data['Type'] == self._app_data._INPUT_ALIAS_TYPES['mv'], ['Alias', 'Path']].to_dict(orient='records') output_vars = out_data.loc[:, ['Alias', 'Path']].to_dict(orient='records') for row in range(self._input_des_data.shape[0]): [ var.update( {'value': self._input_des_data.loc[row, var['Alias']]}) for var in input_vars ] self.case_sampled.emit( row + 1, run_case(input_vars, output_vars, aspen_con)) if self.isInterruptionRequested(): # to allow task abortion return
class LoadSimulationTreeDialog(QDialog): def __init__(self, application_database: DataStorage): # ------------------------ Internal Variables ------------------------- self.app_data = application_database # ------------------------ Form Initialization ------------------------ super().__init__() self.ui = Ui_Dialog() self.ui.setupUi(self) table_input = self.ui.tableViewInput table_output = self.ui.tableViewOutput self._input_table_model = VariableTableModel(self.app_data, mode='input', parent=table_input) self._output_table_model = VariableTableModel(self.app_data, mode='output', parent=table_output) table_input.setModel(self._input_table_model) table_output.setModel(self._output_table_model) table_input.setColumnWidth(0, 400) # first column resize table_output.setColumnWidth(0, 400) # create tree models self.create_tree_models() # set the delegates for the tables self._input_alias_delegate = AliasEditorDelegate(max_characters=13) self._output_alias_delegate = AliasEditorDelegate(max_characters=13) table_input.setItemDelegateForColumn(1, self._input_alias_delegate) table_output.setItemDelegateForColumn(1, self._output_alias_delegate) input_combo_list = list(self.app_data._INPUT_ALIAS_TYPES.values()) output_combo_list = list(self.app_data._OUTPUT_ALIAS_TYPES.values()) self._input_combo_delegate = ComboBoxDelegate(input_combo_list) self._output_combo_delegate = ComboBoxDelegate(output_combo_list) table_input.setItemDelegateForColumn(2, self._input_combo_delegate) table_output.setItemDelegateForColumn(2, self._output_combo_delegate) # --------------------------- Signals/Slots --------------------------- # opens the simulation connection and load its variable trees self.ui.pushButtonLoadTreeFromFile.clicked.connect(self.load_tree) # action associate with the double click of a node in the trees self.ui.treeViewInput.doubleClicked.connect(self.double_click_on_tree) self.ui.treeViewOutput.doubleClicked.connect(self.double_click_on_tree) # ok push button pressed self.ui.pushButtonOK.clicked.connect(self.ok_button_pressed) # opens simulation GUI when checkbox is toggled self.ui.openSimGUICheckBox.stateChanged.connect( self.open_simulator_gui) # --------------------------------------------------------------------- def open_simulator_gui(self, checkstate: Qt.CheckState): # check if there is a connection object and open if not if not hasattr(self, 'aspen_com'): # FIXME: set an apropriate logic that doesn't freeze the UI self.aspen_com = AspenConnection(self.app_data.simulation_file) con_obj = self.aspen_com.get_connection_object() if checkstate == Qt.Checked: con_obj.Visible = 1 else: con_obj.Visible = 0 def create_tree_models(self): input_tree_dict_model = QStandardItemModel() output_tree_model = QStandardItemModel() # place the headers texts input_tree_dict_model.setHorizontalHeaderLabels(['Input variables']) output_tree_model.setHorizontalHeaderLabels(['Output variables']) # set the models in the views self.ui.treeViewInput.setModel(input_tree_dict_model) self.ui.treeViewOutput.setModel(output_tree_model) # make the tree views headers bold self.ui.treeViewInput.header().setStyleSheet('QWidget { font: bold }') self.ui.treeViewOutput.header().setStyleSheet('QWidget { font: bold }') # check if application has already tree dictionaries. If so, populate # with those values. if len(self.app_data.tree_model_input) != 0: input_tree_dict = self.app_data.tree_model_input self.populate_tree(self.ui.treeViewInput.model(), input_tree_dict) if len(self.app_data.tree_model_output) != 0: output_tre_dict = self.app_data.tree_model_output self.populate_tree(self.ui.treeViewOutput.model(), output_tre_dict) def load_tree(self): """Opens the connection with the simulation engine and loads the variable tree. """ # disable the load tree and ok buttons self.ui.pushButtonLoadTreeFromFile.setEnabled(False) self.ui.pushButtonOK.setEnabled(False) # start the progress bar dialog progress_dialog = QProgressDialog( 'Please wait while the simulation engine is loaded...', None, 0, 0, self) progress_dialog.setWindowModality(Qt.WindowModal) progress_dialog.setWindowTitle("Loading variables") progress_dialog.show() progress_dialog.setValue(0) self.progress_dialog = progress_dialog self.connection_worker = ConnectionWorker(app_data=self.app_data, mode='tree') self.connection_thread = QThread() # connect worker signals self.connection_worker.connection_open.connect( self.on_connection_opened) self.connection_worker.input_tree_loaded.connect( self.on_input_tree_loaded) self.connection_worker.output_tree_loaded.connect( self.on_output_tree_loaded) self.connection_worker.connection_id_created.connect( self.on_connection_id_created) # move worker to thread self.connection_worker.moveToThread(self.connection_thread) # connect thread to load slot self.connection_thread.started.connect( self.connection_worker.load_tree) # start the thread self.connection_thread.start() def on_connection_opened(self): self.progress_dialog.setLabelText('Loading input tree variables...') def on_input_tree_loaded(self): self.progress_dialog.setLabelText('Loading output tree variables...') self.populate_tree(self.ui.treeViewInput.model(), self.app_data.tree_model_input) def on_output_tree_loaded(self): self.populate_tree(self.ui.treeViewOutput.model(), self.app_data.tree_model_output) # close progress dialog self.progress_dialog.setMaximum(1) self.progress_dialog.setValue(1) # Enable the load tree and ok buttons self.ui.pushButtonLoadTreeFromFile.setEnabled(True) self.ui.pushButtonOK.setEnabled(True) def on_connection_id_created(self, connection_id): # make the server available to this thread pythoncom.CoInitialize() self.aspen_com = AspenConnection(self.app_data.simulation_file) # populate internal connection variable of AspenConnection class # this is a horrendous fix... it works though! self.aspen_com._aspen = Dispatch( pythoncom.CoGetInterfaceAndReleaseStream(connection_id, pythoncom.IID_IDispatch)) # quit the thread if hasattr(self, 'connection_thread'): self.connection_thread.quit() def populate_tree(self, model: QStandardItemModel, tree_nodes: dict): """Populates the model of a tree view. Parameters ---------- model : QStandardItemModel TreeView model. tree_nodes : dict Dictionary containing the tree representation. """ # clear all the current rows model.removeRows(0, model.rowCount()) # append new rows model.appendRow(self.traverse_json_tree(tree_nodes)) def traverse_json_tree(self, root_node: dict) -> QStandardItem: """Traverses a JSON dictionary tree representation through recursion. Parameters ---------- root_node : dict Dictionary which is the root of the tree. See notes for node representation. Returns ------- QStandardItem Root of the tree as a QStandardItem. Notes ----- The `root_node` and subsequent nodes has to be in the following format: node = {'node': str_name_of_node, 'children': list_of_nodes, 'description': str_tooltip_description_of_node} Where list_of_nodes is, literally, a list of nodes. Duh. """ rn = QStandardItem(root_node['node']) rn.setEditable(False) if 'description' in root_node: rn.setToolTip(root_node['description']) if 'children' in root_node: for child in root_node['children']: rn.appendRow(self.traverse_json_tree(child)) return rn def double_click_on_tree(self): """Slot (function) that handles double click event on a single leaf node in the tree. """ tree_name = self.sender().objectName() tree_view = self.ui.treeViewInput if tree_name == "treeViewInput" \ else self.ui.treeViewOutput tree_model = tree_view.model() table_model = self.ui.tableViewInput.model() \ if tree_name == 'treeViewInput' \ else self.ui.tableViewOutput.model() # get the model index of the node clicked index_selected = tree_view.currentIndex() if not tree_model.itemFromIndex(index_selected).hasChildren(): # Only proceed if the selected node is a leaf # intialize node construction branch_list = [tree_model.data(index_selected).lstrip()] # traverse the nodes upwards (stop on root node) parent_node = tree_model.itemFromIndex(index_selected).parent() while parent_node is not None: parent_node_name = parent_node.text().lstrip() branch_list.append(parent_node_name) parent_node = parent_node.parent() # create the full path string fullpath = '\\' + '\\'.join(list(reversed(branch_list))) # verify if full path is already in the table current_paths = pd.concat([ self.app_data.input_table_data, self.app_data.output_table_data ], axis='index', ignore_index=True, sort=False)['Path'].tolist() if fullpath in current_paths: # the variable is already in table, warn the user msg_box = QMessageBox( QMessageBox.Warning, "Duplicated variable", "The selected variable is already the table!", buttons=QMessageBox.Ok, parent=None) msg_box.exec_() else: # variable not in table, insert it self.insert_new_single_row(table_model, fullpath) def insert_new_single_row(self, table_model: VariableTableModel, node_str: str): """Inserts a single variable row in the table view. Parameters ---------- table_model : VariableTableModel Which VariableTableModel to insert the row. node_str : str String containing the full path to the selected node. """ # start by inserting an empty row table_model.insertRow(table_model.rowCount()) # insert the path value n_rows = table_model.rowCount() table_model.setData(table_model.index(n_rows - 1, 0), node_str, Qt.EditRole) def ok_button_pressed(self): input_var_data = self.app_data.input_table_data output_var_data = self.app_data.output_table_data final_tab = pd.concat([input_var_data, output_var_data], axis='index', ignore_index=True, sort=False) # check if there is any duplicates between input and output is_alias_duplicated = not final_tab['Alias'].is_unique # check if there any variable without its type defined is_in_var_defined = not input_var_data['Type'].eq( 'Choose a type').any() is_out_var_defined = not output_var_data['Type'].eq( 'Choose a type').any() if not is_in_var_defined or not is_out_var_defined: # user did not define all the variable types if not is_in_var_defined and is_out_var_defined: iovartype = 'the selected input' iovartitle = 'Input' elif is_in_var_defined and not is_out_var_defined: iovartype = 'the selected output' iovartitle = 'Output' else: iovartype = 'both input and output' iovartitle = iovartype.capitalize() text = ("At least one of {0} variables does not have a defined" " type.\nYou have to define it for all of them!" ).format(iovartype) title = "{0} variable without type detected!".format(iovartitle) warn_the_user(text, title) if is_alias_duplicated: # warn the user about duplicated entries text = ("Duplicated aliases between input and output found." "\nAll the aliases together must be unique.") title = "Duplicated aliases detected!" warn_the_user(text, title) if is_in_var_defined and is_out_var_defined and \ not is_alias_duplicated: # Everything is properly set, close the dialog. self.close() def keyPressEvent(self, event: QEvent): """keyPressEvent override of the QDialog class to extend keyboard interaction with the tables (i.e. delete rows). """ if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Delete: # if the delete key was pressed widget = QApplication.focusObject() # get widget that has focus widget_name = widget.objectName() if widget_name == self.ui.tableViewInput.objectName() \ or widget_name == self.ui.tableViewOutput.objectName(): table_sel_model = widget.selectionModel() indexes = [ QPersistentModelIndex(index) for index in table_sel_model.selectedIndexes() ] for index in indexes: # delete selected rows widget.model().removeRow(index.row()) # clear selection widget.selectionModel().clearSelection() def closeEvent(self, event): # clean up of the aspen connection if there is one if hasattr(self, 'aspen_com'): self.aspen_com.close_connection() super().closeEvent(event)