import pickle
import threading
import os
import ctypes
import sys
import datetime
from functools import partial
import pyuac
current = os.path.dirname(os.path.realpath(__file__))
parentPath = os.path.dirname(current)
sys.path.append(parentPath)
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 python.OMChem.Flowsheet import Flowsheet
from python.utils.ComponentSelector import *
from python.utils.Bin_Phase_env import *
from python.utils.UnitOperations import *
from python.utils.Streams import *
from python.utils.Container import *
from python.utils.Graphics import *
ui,_ = loadUiType(parentPath+'/ui/utils/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.setWindowTitle('Untitled - Chemical Simulator GUI')
#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__':
if not pyuac.isUserAdmin():
pyuac.runAsAdmin()
else:
main()