import pickle import threading import os import ctypes import sys import datetime from functools import partial from PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQt5.QtGui import * import PyQt5.QtGui as QtGui import PyQt5.QtCore as QtCore import PyQt5.QtWidgets as QtWidgets from OMChem.Flowsheet import Flowsheet from ComponentSelector import * from Bin_Phase_env import * from UnitOperations import * from Streams import * from Container import * from Graphics import * ui,_ = loadUiType('main.ui') ''' MainApp class is responsible for all the main App Ui operations ''' class MainApp(QMainWindow,ui): ''' Initializing the application ''' def __init__(self): QMainWindow.__init__(self) # Loading and setting up style sheet self.setupUi(self) # Initializing attributes self.zoom_count = 0 self.thrd = None # Creating instances of classes for the main app self.container = Container(self.textBrowser, self.graphicsView) self.comp = ComponentSelector(self) self.comp.accepted.connect(self.update_compounds) # Setting up interactive canvas self.scene = self.container.graphics.get_scene() self.graphicsView.setScene(self.scene) self.graphicsView.setMouseTracking(True) self.graphicsView.keyPressEvent=self.delete_call self.setDockNestingEnabled(True) self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea) self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.addDockWidget(Qt.BottomDockWidgetArea,self.dockWidget_2) # Calling initialisation self.menu_bar() self.button_handler() self.comp.show() ''' MenuBar function handels all the all the operations of menu bar like new,zoom,comounds selector, simulation options. ''' def menu_bar(self): self.actionSelectCompounds.triggered.connect(self.select_compounds) self.actionSelectCompounds.setShortcut('Ctrl+C') self.actionZoomIn.triggered.connect(self.zoom_in) self.actionZoomIn.setShortcut('Ctrl++') self.actionNew.triggered.connect(self.new) self.actionNew.setShortcut('Ctrl+N') self.actionZoomOut.triggered.connect(self.zoom_out) self.actionZoomOut.setShortcut('Ctrl+-') self.actionResetZoom.triggered.connect(self.zoom_reset) self.actionResetZoom.setShortcut('Ctrl+R') self.actionHelp.triggered.connect(self.help) self.actionHelp.setShortcut('Ctrl+H') self.actionSequentialMode.triggered.connect(partial(self.simulate,'SM')) self.actionSequentialMode.setShortcut('Ctrl+M') self.actionEquationOriented.triggered.connect(partial(self.simulate,'EQN')) self.actionEquationOriented.setShortcut('Ctrl+E') self.actionUndo.triggered.connect(self.undo) self.actionUndo.setShortcut('Ctrl+Z') self.actionRedo.triggered.connect(self.redo) self.actionRedo.setShortcut('Ctrl+Y') self.actionSave.triggered.connect(self.save) self.actionSave.setShortcut('Ctrl+S') self.actionOpen.triggered.connect(self.open) self.actionOpen.setShortcut('Ctrl+O') self.actionTerminate.triggered.connect(self.terminate) self.actionTerminate.setShortcut('Ctrl+T') self.actionBinaryPhaseEnvelope.triggered.connect(self.bin_phase_env) self.actionViewMessageBrowser.triggered.connect(self.toggle_message_browser_view) self.actionViewComponentSelector.triggered.connect(self.toggle_component_selector_view) ''' Handles all the buttons of different components. ''' def button_handler(self): self.pushButton.clicked.connect(partial(self.component,'MaterialStream')) self.pushButton_7.clicked.connect(partial(self.component,'Mixer')) self.pushButton_14.clicked.connect(partial(self.component,'CentrifugalPump')) self.pushButton_26.clicked.connect(partial(self.component,'DistillationColumn')) self.pushButton_18.clicked.connect(partial(self.component,'ShortcutColumn')) self.pushButton_11.clicked.connect(partial(self.component,'Heater')) self.pushButton_10.clicked.connect(partial(self.component,'Splitter')) self.pushButton_9.clicked.connect(partial(self.component,'Flash')) self.pushButton_25.clicked.connect(partial(self.component,'Valve')) self.pushButton_12.clicked.connect(partial(self.component,'Cooler')) self.pushButton_13.clicked.connect(partial(self.component,'CompoundSeparator')) self.pushButton_15.clicked.connect(partial(self.component,'AdiabaticCompressor')) self.pushButton_16.clicked.connect(partial(self.component,'AdiabaticExpander')) ''' Displays help box ''' def help(self): msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Question) msgBox.setTextFormat(Qt.RichText); msgBox.setText("For any Help or Suggestion you can contact us at\n contact-om@fossee.in or at Visit fossee.in!") msgBox.setStandardButtons(QMessageBox.Ok) msgBox.exec_() ''' Creates Binary Phase envelope ''' def bin_phase_env(self): if len(self.comp.get_compounds())<2: QMessageBox.about(self, 'Important', "Please select at least 2 Compounds first") self.comp.show() else: self.bin_phase = BinPhaseEnv(self.comp) self.bin_phase.show() ''' Shows Compounds Selector Dialog ''' def select_compounds(self): self.comp.show() ''' Updates compounds after compound selected modified during simulation creation ''' def update_compounds(self): self.container.update_compounds() ''' Returns current time in a required particular format ''' def current_time(self): now = datetime.datetime.now() time = str(now.hour) + ":" + str(now.minute) + ":" +str(now.second) return time ''' Simulate function is responsible for the simulation of the designed flowsheet in a particular mode selected by the user. ''' def simulate(self,mode): self.thrd = threading.Thread(target=self.container.simulate, args=(mode,)) self.thrd.start() ''' Terminate the current running simulation ''' def terminate(self): os.chdir(self.container.flowsheet.root_dir) if self.thrd: thread_id = self.thrd.ident # print('____________________Going to terminate simulation thread with Thread ID:',thread_id,'____________________') # print('____________________Going to terminate the new process created for omc____________________') self.container.flowsheet.process.terminate() print('____________________New process created for omc is terminated.____________________') res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, ctypes.py_object(SystemExit)) self.textBrowser.append("["+str(self.current_time())+"]Simulation Terminated.") self.container.disableInterfaceforSimulation(False) # print('____________________Simulation thread terminated____________________') if res > 1: ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0) # print('Exception raise (Thread termination) failure') ''' Resets the zoom level to default scaling ''' def zoom_reset(self): if(self.zoom_count>0): for i in range(self.zoom_count): self.zoomout() elif(self.zoom_count<0): for i in range(abs(self.zoom_count)): self.zoomin() ''' ZoomOut the canvas ''' def zoom_out(self): self.graphicsView.scale(1.0/1.15,1.0/1.15) self.zoom_count -=1 ''' ZoomIn the canvas ''' def zoom_in(self): self.graphicsView.scale(1.15,1.15) self.zoom_count +=1 ''' Instantiate a NodeItem object for selected type of component and added that on canvas/flowsheeting area. ''' def component(self,unit_operation_type): if(self.comp.is_compound_selected()): self.type = unit_operation_type if(self.type=="MaterialStream"): self.obj = MaterialStream(compound_names = compound_selected) else: self.obj = eval(self.type)() self.container.add_unit_operation(self.obj) else: QMessageBox.about(self, 'Important', "Please Select Compounds first") self.comp.show() ''' New is used to delete all the existing work. ''' def new(self): self.undo_redo_helper() self.comp = ComponentSelector(self) self.textBrowser.append("[" + str(self.current_time()) + "] New flowsheet is created ... ") dock_widget_lst.clear() ''' Handels all the operations which will happen when delete button is pressed. ''' def delete_call(self,event): try: if event.key() == QtCore.Qt.Key_Delete: l=self.scene.selectedItems() self.container.delete(l) except Exception as e: print(e) ''' It helps by clearing screen and loading the objects by undo redo methods ''' def undo_redo_helper(self): for i in self.container.unit_operations: type(i).counter = 1 self.container = None for i in dock_widget_lst: i.hide() del i lst.clear() self.container = Container(self.textBrowser, self.graphicsView) compound_selected.clear() self.scene = self.container.graphics.get_scene() self.graphicsView.setScene(self.scene) self.graphicsView.setMouseTracking(True) self.graphicsView.keyPressEvent=self.delete_call ''' Function for undo ''' def undo(self): redo_data = pop('Undo') if redo_data is not None: push('Redo', redo_data) undo_data = get_last_list('Undo') messages = self.textBrowser.toPlainText() try: self.undo_redo_helper() self.container.graphics.load_canvas(undo_data, self.container) self.textBrowser.setText(messages) except Exception as e: print(e) self.textBrowser.append(messages) else: messages = self.textBrowser.toPlainText() self.textBrowser.setText(messages) self.textBrowser.append("[" + str(self.current_time()) + "] No more undo can be done!... ") ''' Function for redo ''' def redo(self): redo_data = pop('Redo') if redo_data is not None: push('Undo', redo_data) messages = self.textBrowser.toPlainText() self.undo_redo_helper() self.container.graphics.load_canvas(redo_data, self.container) self.textBrowser.setText(messages) else: messages = self.textBrowser.toPlainText() self.textBrowser.setText(messages) self.textBrowser.append("[" + str(self.current_time()) + "] No more redo can be done!... ") ''' Function for saving the current canvas items and compound_selected ''' def save(self): data = [] for i in self.container.unit_operations: data.append(i) i.saved = True data.append(compound_selected) data.append(self.container.result) file_format = 'sim' initial_path = QDir.currentPath() + ' untitled.' + file_format file_name, _ = QFileDialog.getSaveFileName(self, "Save As", initial_path, "%s Files (*.%s);; All Files (*)" % (file_format.upper(), file_format)) try: with open(file_name, 'wb') as f: pickle.dump(data, f, pickle.HIGHEST_PROTOCOL) fileName = file_name.split('/')[-1].split('.')[0] self.setWindowTitle(fileName+' - Chemical Simulator GUI') except Exception as e: pass ''' Function for loading previous saved canvas and simulation ''' def open(self): try: file_format = 'sim' initial_path = QDir.currentPath() + 'untitled.' + file_format file_name, _ = QFileDialog.getOpenFileName(self, "Open As", initial_path, "%s Files (*.%s);; All Files (*)" % (file_format.upper(), file_format)) if file_name: fileName = file_name.split('/')[-1].split('.')[0] self.setWindowTitle(fileName+' - Chemical Simulator GUI') self.undo_redo_helper() with open(file_name, 'rb') as f: obj = pickle.load(f) temp_result = obj[-1] obj.pop() compound_selected = obj[-1] obj.pop() self.comp.set_compounds(compound_selected) for i in compound_selected: self.comp.compound_selection(self.comp, i) self.comp.hide() self.container.graphics.load_canvas(obj, self.container) self.container.result = temp_result DockWidget.show_result(dock_widget_lst) for i in dock_widget_lst: #Submitting values i.param() #Disbaling input data tab for output stream for i in self.container.graphics.scene.items(): if (isinstance(i, NodeItem) and i.type == 'MaterialStream'): i.update_tooltip_selectedVar() no_input_lines = len(i.input[0].in_lines) no_output_lines = len(i.output[0].out_lines) if(no_input_lines>0): #Checks if material stream is input or output stream if it is output stream it continues i.obj.disableInputDataTab(i.dock_widget) except Exception as e: print(e) ''' Function for toggling the display of Component Selector ''' def toggle_component_selector_view(self): if(self.actionViewComponentSelector.isChecked()): self.dockWidget.show() else: self.dockWidget.hide() ''' Function for toggling the display of Message Browser ''' def toggle_message_browser_view(self): if(self.actionViewMessageBrowser.isChecked()): self.dockWidget_2.show() else: self.dockWidget_2.hide() def main(): clean_file('Undo') clean_file('Redo') app = QApplication(sys.argv) window = MainApp() window.showMaximized() app.exec() if __name__ == '__main__': main()