from functools import partial
from collections import defaultdict
import sys
import numpy as np
from OMChem.Flowsheet import Flowsheet
import pandas as pd
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QTextDocument ,QTextCursor ,QTextCharFormat ,QFont ,QPixmap
from PyQt5.uic import loadUiType
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QGraphicsProxyWidget, QGraphicsObject, QGraphicsEllipseItem ,QGraphicsPixmapItem,QApplication, QGraphicsView, QGraphicsScene, QHBoxLayout, QWidget, QLabel, QUndoStack
from PyQt5.QtGui import QBrush ,QTransform ,QMouseEvent
import PyQt5.QtGui as QtGui
import PyQt5.QtCore as QtCore
import PyQt5.QtWidgets as QtWidgets
from component_selector import *
from Bin_Phase_env import *
from UnitOperations import *
from Streams import *
import datetime
from container import *
from Graphics import *
import pickle
import threading
import os
import ctypes
import sys
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)
# style = open('cyan.css','r')
# style = style.read()
# self.setStyleSheet(style)
# Initializing attributes
self.zoomcount = 0
self.thrd = None
# Creating instances of classes for the main app
self.Container = Container(self.textBrowser)
self.comp = componentSelector(self)
# Setting up interactive canvas
self.scene = self.Container.graphics.getScene()
self.graphicsView.setScene(self.scene)
self.graphicsView.setMouseTracking(True)
self.graphicsView.keyPressEvent=self.deleteCall
self.setDockNestingEnabled(True)
self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea)
self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea)
self.addDockWidget(Qt.BottomDockWidgetArea,self.dockWidget_2)
# Setting up undo stack
# self.undoStack = QUndoStack(self)
# Calling initialisation functions
self.buttonHandler()
self.menuBar()
self.comp.show()
'''
MenuBar function handels all the all the operations of
menu bar like new,zoom,comounds selector, simulation options.
'''
def menuBar(self):
self.actionSelect_compounds.triggered.connect(self.selectCompounds)
self.actionSelect_compounds.setShortcut('Ctrl+C')
self.actionZoomIn.triggered.connect(self.zoomin)
self.actionZoomIn.setShortcut('Ctrl++')
self.actionNew_Flowsheet.triggered.connect(self.new)
self.actionNew_Flowsheet.setShortcut('Ctrl+N')
self.actionZoomOut.triggered.connect(self.zoomout)
self.actionZoomOut.setShortcut('Ctrl+-')
self.actionResetZoom.triggered.connect(self.zoomReset)
self.actionResetZoom.setShortcut('Ctrl+R')
self.actionHelp.triggered.connect(self.help)
self.actionHelp.setShortcut('Ctrl+H')
self.actionSequential_mode.triggered.connect(partial(self.simulate,'SM'))
self.actionSequential_mode.setShortcut('Ctrl+M')
self.actionEquation_oriented.triggered.connect(partial(self.simulate,'EQN'))
self.actionEquation_oriented.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.actionUndo_2.triggered.connect(self.undoStack.undo)
# self.actionRedo.triggered.connect(self.undoStack.redo)
self.actionSave_2.triggered.connect(self.save)
self.actionSave_2.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.actionBinary_Phase_Envelope.triggered.connect(self.BinPhaseEnv)
'''
Handles all the buttons of different components.
'''
def buttonHandler(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,'Pump'))
self.pushButton_26.clicked.connect(partial(self.component,'DistCol'))
self.pushButton_18.clicked.connect(partial(self.component,'ShortCol'))
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,'CompSep'))
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_()
def BinPhaseEnv(self):
#compounds = self.comp.getComp()
if len(self.comp.getComp())<2:
QMessageBox.about(self, 'Important', "Please select at least 2 Compounds first")
self.comp.show()
#compunds = self.comp.getComp()
else:
self.bin_phase = Bin_Phase_env(self.comp)
self.bin_phase.show()
'''
Shows Compounds Selector Dialog
'''
def selectCompounds(self):
self.comp.show()
'''
Returns currenttime in a required particular format
'''
def currentTime(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()
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.currentTime())+"] Terminating the simulation ")
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 zoomReset(self):
if(self.zoomcount>0):
for i in range(self.zoomcount):
self.zoomout()
elif(self.zoomcount<0):
for i in range(abs(self.zoomcount)):
self.zoomin()
'''
ZoomOut the canvas
'''
def zoomout(self):
self.graphicsView.scale(1.0/1.15,1.0/1.15)
self.zoomcount -=1
'''
ZoomIn the canvas
'''
def zoomin(self):
self.graphicsView.scale(1.15,1.15)
self.zoomcount +=1
'''
Instantiate a NodeItem object for selected type of
component and added that on canvas/flowsheeting area.
'''
def component(self,unitOpType):
if(self.comp.isCompSelected()):
self.type = unitOpType
if(self.type=="MaterialStream"):
self.obj = MaterialStream(CompNames=compound_selected)
else:
self.obj = eval(self.type)()
self.Container.addUnitOp(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.tableWidget.setRowCount(0)
self.textBrowser.append("[" + str(self.currentTime()) + "] New flowsheet is created ... ")
'''
It helps by clearing screen and loading the objects by undo redo methods
'''
def undo_redo_helper(self):
for i in self.Container.unitOp:
type(i).counter = 1
del self.Container
lst.clear()
self.Container = Container(self.textBrowser)
compound_selected.clear()
self.scene = self.Container.graphics.getScene()
self.graphicsView.setScene(self.scene)
self.graphicsView.setMouseTracking(True)
self.graphicsView.keyPressEvent=self.deleteCall
'''
Handels all the operations which will happen when delete button is pressed.
'''
def deleteCall(self,event):
try:
if event.key() == QtCore.Qt.Key_Delete:
l=self.scene.selectedItems()
for i in l:
eval(i.type).counter -= 1
self.Container.delete(l)
except Exception as e:
print(e)
'''
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.loadCanvas(undo_data)
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.currentTime()) + "] 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.loadCanvas(redo_data)
self.textBrowser.setText(messages)
else:
messages = self.textBrowser.toPlainText()
self.textBrowser.setText(messages)
self.textBrowser.append("[" + str(self.currentTime()) + "] 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.unitOp:
data.append(i)
print(i.pos)
data.append(compound_selected)
print(data)
fileFormat = 'sim'
initialPath = QDir.currentPath() + 'untitled.' + fileFormat
fileName, _ = QFileDialog.getSaveFileName(self, "Save As",
initialPath, "%s Files (*.%s);; All Files (*)" %
(fileFormat.upper(), fileFormat))
if fileName != "":
with open(fileName, 'wb') as f: #'saved_file.sim'
pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
'''
Function for loading previous saved canvas and simulation
'''
def open(self):
try:
fileFormat = 'sim'
initialPath = QDir.currentPath() + 'untitled.' + fileFormat
fileName, _ = QFileDialog.getOpenFileName(self, "Open As",
initialPath, "%s Files (*.%s);; All Files (*)" %
(fileFormat.upper(), fileFormat))
# if fileName != "":
# self.new()
self.undo_redo_helper()
with open(fileName, 'rb') as f:
obj = pickle.load(f)
self.Container.graphics.loadCanvas(obj)
except Exception as e:
pass
def main():
CLEAN_FILE('Undo')
CLEAN_FILE('Redo')
app = QApplication(sys.argv)
window = MainApp()
window.showMaximized()
app.exec()
if __name__ == '__main__':
main()