from __future__ import division # Used for decimal division eg 2/3=0.66 and not '0' 6/2=3.0 and 6//2=3
import os
from PyQt4 import QtGui, QtCore
from decimal import Decimal
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.figure import Figure
from configuration.Appconfig import Appconfig
class plotWindow(QtGui.QMainWindow):
def __init__(self,fpath,projectName):
QtGui.QMainWindow.__init__(self)
self.fpath = fpath#+".cir.out"
self.projectName = projectName
self.obj_appconfig = Appconfig()
print "Path : ",self.fpath
print "Project Name : ",self.projectName
self.obj_appconfig.print_info('Ngspice simulation is called : ' + self.fpath)
self.obj_appconfig.print_info('PythonPlotting is called : ' + self.fpath)
self.combo = []
self.combo1 = []
self.combo1_rev = []
#Creating Frame
self.createMainFrame()
def createMainFrame(self):
self.mainFrame = QtGui.QWidget()
self.dpi = 100
self.fig = Figure((7.0, 7.0), dpi=self.dpi)
#Creating Canvas which will figure
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.mainFrame)
self.axes = self.fig.add_subplot(111)
self.navToolBar = NavigationToolbar(self.canvas, self.mainFrame)
#LeftVbox hold navigation tool bar and canvas
self.left_vbox = QtGui.QVBoxLayout()
self.left_vbox.addWidget(self.navToolBar)
self.left_vbox.addWidget(self.canvas)
#right VBOX is main Layout which hold right grid(bottom part) and top grid(top part)
self.right_vbox = QtGui.QVBoxLayout()
self.right_grid = QtGui.QGridLayout()
self.top_grid = QtGui.QGridLayout()
#Get DataExtraction Details
self.obj_dataext = DataExtraction()
self.plotType = self.obj_dataext.openFile(self.fpath)
self.obj_dataext.computeAxes()
self.a = self.obj_dataext.numVals()
self.chkbox=[]
########### Generating list of colors :
self.full_colors = ['r','b','g','y','c','m','k']#,(0.4,0.5,0.2),(0.1,0.4,0.9),(0.4,0.9,0.2),(0.9,0.4,0.9)]
self.color = []
for i in range(0,self.a[0]-1):
if i%7 == 0:
self.color.append(self.full_colors[0])
elif (i-1)%7 == 0:
self.color.append(self.full_colors[1])
elif (i-2)%7 == 0:
self.color.append(self.full_colors[2])
elif (i-3)%7 == 0:
self.color.append(self.full_colors[3])
elif (i-4)%7 == 0:
self.color.append(self.full_colors[4])
elif (i-5)%7 == 0:
self.color.append(self.full_colors[5])
elif (i-6)%7 == 0:
self.color.append(self.full_colors[6])
###########Color generation ends here
#Total number of voltage source
self.volts_length = self.a[1]
self.analysisType = QtGui.QLabel()
self.top_grid.addWidget(self.analysisType,0,0)
self.listNode = QtGui.QLabel()
self.top_grid.addWidget(self.listNode,1,0)
self.listBranch = QtGui.QLabel()
self.top_grid.addWidget(self.listBranch,self.a[1]+2,0)
for i in range(0,self.a[1]):#a[0]-1
self.chkbox.append(QtGui.QCheckBox(self.obj_dataext.NBList[i]))
self.chkbox[i].setToolTip('Check To Plot' )
self.top_grid.addWidget(self.chkbox[i],i+2,0)
self.colorLab = QtGui.QLabel()
self.colorLab.setText('____')
self.colorLab.setStyleSheet(self.colorName(self.color[i])+'; font-weight = bold;')
self.top_grid.addWidget(self.colorLab,i+2,1)
for i in range(self.a[1],self.a[0]-1):#a[0]-1
self.chkbox.append(QtGui.QCheckBox(self.obj_dataext.NBList[i]))
self.chkbox[i].setToolTip('Check To Plot' )
self.top_grid.addWidget(self.chkbox[i],i+3,0)
self.colorLab = QtGui.QLabel()
self.colorLab.setText('____')
self.colorLab.setStyleSheet(self.colorName(self.color[i])+'; font-weight = bold;')
self.top_grid.addWidget(self.colorLab,i+3,1)
self.clear = QtGui.QPushButton("Clear")
self.warnning = QtGui.QLabel()
self.funcName = QtGui.QLabel()
self.funcExample = QtGui.QLabel()
self.plotbtn = QtGui.QPushButton("Plot")
self.plotbtn.setToolTip('Press to Plot' )
self.text = QtGui.QLineEdit()
self.funcLabel = QtGui.QLabel()
self.palette1 = QtGui.QPalette()
self.palette2 = QtGui.QPalette()
self.plotfuncbtn = QtGui.QPushButton("Plot Function")
self.plotfuncbtn.setToolTip('Press to Plot the function' )
self.palette1.setColor(QtGui.QPalette.Foreground,QtCore.Qt.blue)
self.palette2.setColor(QtGui.QPalette.Foreground,QtCore.Qt.red)
self.funcName.setPalette(self.palette1)
self.funcExample.setPalette(self.palette2)
self.right_vbox.addLayout(self.top_grid)
self.right_vbox.addWidget(self.plotbtn)
self.right_grid.addWidget(self.funcLabel,1,0)
self.right_grid.addWidget(self.text,1,1)
self.right_grid.addWidget(self.plotfuncbtn,2,1)
self.right_grid.addWidget(self.clear,2,0)
self.right_grid.addWidget(self.warnning,3,0)
self.right_grid.addWidget(self.funcName,4,0)
self.right_grid.addWidget(self.funcExample,4,1)
self.right_vbox.addLayout(self.right_grid)
self.hbox = QtGui.QHBoxLayout()
self.hbox.addLayout(self.left_vbox)
self.hbox.addLayout(self.right_vbox)
self.widget = QtGui.QWidget()
self.widget.setLayout(self.hbox)#finalvbox
self.scrollArea = QtGui.QScrollArea()
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setWidget(self.widget)
self.finalhbox = QtGui.QHBoxLayout()
self.finalhbox.addWidget(self.scrollArea)
self.mainFrame.setLayout(self.finalhbox)
self.showMaximized()
self.listNode.setText("List of Nodes:")
self.listBranch.setText("List of Branches:")
self.funcLabel.setText("Function:")
self.funcName.setText("Examples:\
Addition:
Subtraction:
Multiplication:
Division:
Comparison:")
self.funcExample.setText("\n\nV(1) + V(2)\nV(1) - V(2)\nV(1) * V(2)\nV(1) / V(2)\nV(1) vs V(2)")
#Connecting to plot and clear function
self.connect(self.clear,QtCore.SIGNAL('clicked()'),self.pushedClear)
self.connect(self.plotfuncbtn,QtCore.SIGNAL('clicked()'), self.pushedPlotFunc)
if self.plotType[0]==0:
self.analysisType.setText("AC Analysis")
if self.plotType[1]==1:
self.connect(self.plotbtn, QtCore.SIGNAL('clicked()'), self.onPush_decade)
else:
self.connect(self.plotbtn, QtCore.SIGNAL('clicked()'), self.onPush_ac)
elif self.plotType[0]==1:
self.analysisType.setText("Transient Analysis")
self.connect(self.plotbtn, QtCore.SIGNAL('clicked()'), self.onPush_trans)
else:
self.analysisType.setText("DC Analysis")
self.connect(self.plotbtn, QtCore.SIGNAL('clicked()'), self.onPush_dc)
self.setCentralWidget(self.mainFrame)
def pushedClear(self):
#print "Calling Clear Canvas function"
self.text.clear()
self.axes.cla()
self.canvas.draw()
QtCore.SLOT('quit()')
def pushedPlotFunc(self):
#print "Calling Plot function"
self.parts = str(self.text.text())
self.parts = self.parts.split(" ")
#print "Parts :",self.parts
if self.parts[len(self.parts)-1] == '':
self.parts = self.parts[0:-1]
self.values = self.parts
self.comboAll = []
self.axes.cla()
self.plotType2 = self.obj_dataext.openFile(self.fpath)
if len(self.parts) <= 2:
self.warnning.setText("Too few arguments!\nRefer syntax below!")
QtGui.QMessageBox.about(self, "Warning!!", "Too Few Arguments/SYNTAX Error!\n Refer Examples")
else:
self.warnning.setText("")
a = []
finalResult = []
p = 0
for i in range(len(self.parts)):
#print "I",i
if i%2 == 0:
#print "I'm in:"
for j in range(len(self.obj_dataext.NBList)):
if self.parts[i]==self.obj_dataext.NBList[j]:
#print "I got you:",self.parts[i]
a.append(j)
if len(a) != len(self.parts)//2 + 1:
QtGui.QMessageBox.about(self, "Warning!!", "One of the operands doesn't belong to the above list of Nodes!!")
for i in a:
self.comboAll.append(self.obj_dataext.y[i])
for i in range(len(a)):
if a[i] == len(self.obj_dataext.NBList):
QtGui.QMessageBox.about(self, "Warning!!", "One of the operands doesn't belong to the above list!!")
self.warnning.setText("To Err Is Human!
One of the operands doesn't belong to the above list!!")
if self.parts[1] == 'vs':
if len(self.parts) > 3:
self.warnning.setText("Enter two operands only!!")
QtGui.QMessageBox.about(self, "Warning!!", "Recheck the expression syntax!")
else:
self.axes.cla()
for i in range(len(self.obj_dataext.y[a[0]])):
self.combo.append(self.obj_dataext.y[a[0]][i])
self.combo1.append(self.obj_dataext.y[a[1]][i])
self.axes.plot(self.combo,self.combo1,c=self.color[1],label=str(2))#_rev
if max(a) < self.volts_length:
self.axes.set_ylabel('Voltage(V)-->')
self.axes.set_xlabel('Voltage(V)-->')
else:
self.axes.set_ylabel('Current(I)-->')
self.axes.set_ylabel('Current(I)-->')
elif max(a) >= self.volts_length and min(a) < self.volts_length:
QtGui.QMessageBox.about(self, "Warning!!", "Do not combine Voltage and Current!!")
else:
for j in range(len(self.comboAll[0])):
for i in range(len(self.values)):
if i%2==0:
self.values[i] = str(self.comboAll[i//2][j])
re = " ".join(self.values[:])
try:
finalResult.append(eval(re))
except ArithmeticError:
QtGui.QMessageBox.about(self, "Warning!!", "Dividing by zero!!")
if self.plotType2[0]==0:
#self.setWindowTitle('AC Analysis')
if self.plotType2[1]==1:
self.axes.semilogx(self.obj_dataext.x,finalResult,c=self.color[0],label=str(1))
else:
self.axes.plot(self.obj_dataext.x,finalResult,c=self.color[0],label=str(1))
self.axes.set_xlabel('freq-->')
if max(a) < self.volts_length:
self.axes.set_ylabel('Voltage(V)-->')
else:
self.axes.set_ylabel('Current(I)-->')
elif self.plotType2[0]==1:
#self.setWindowTitle('Transient Analysis')
self.axes.plot(self.obj_dataext.x,finalResult,c=self.color[0],label=str(1))
self.axes.set_xlabel('time-->')
if max(a) < self.volts_length:
self.axes.set_ylabel('Voltage(V)-->')
else:
self.axes.set_ylabel('Current(I)-->')
else:
#self.setWindowTitle('DC Analysis')
self.axes.plot(self.obj_dataext.x,finalResult,c=self.color[0],label=str(1))
self.axes.set_xlabel('I/P Voltage-->')
if max(a) < self.volts_length:
self.axes.set_ylabel('Voltage(V)-->')
else:
self.axes.set_ylabel('Current(I)-->')
self.axes.grid(True)
self.canvas.draw()
self.combo = []
self.combo1 = []
self.combo1_rev = []
def onPush_decade(self):
#print "Calling on push Decade"
boxCheck = 0
self.axes.cla()
for i,j in zip(self.chkbox,range(len(self.chkbox))):
if i.isChecked():
boxCheck += 1
self.axes.semilogx(self.obj_dataext.x,self.obj_dataext.y[j],c=self.color[j],label=str(j+1))
self.axes.set_xlabel('freq-->')
if j < self.volts_length:
self.axes.set_ylabel('Voltage(V)-->')
else:
self.axes.set_ylabel('Current(I)-->')
self.axes.grid(True)
if boxCheck == 0:
QtGui.QMessageBox.about(self, "Warning!!","Please select at least one Node OR Branch")
self.canvas.draw()
def onPush_ac(self):
#print "Calling on push ac"
self.axes.cla()
boxCheck = 0
for i,j in zip(self.chkbox,range(len(self.chkbox))):
if i.isChecked():
boxCheck += 1
self.axes.plot(self.obj_dataext.x,self.obj_dataext.y[j],c=self.color[j],label=str(j+1))
self.axes.set_xlabel('freq-->')
if j < self.volts_length:
self.axes.set_ylabel('Voltage(V)-->')
else:
self.axes.set_ylabel('Current(I)-->')
self.axes.grid(True)
if boxCheck == 0:
QtGui.QMessageBox.about(self, "Warning!!","Please select at least one Node OR Branch")
self.canvas.draw()
def onPush_trans(self):
#print "Calling on push trans"
self.axes.cla()
boxCheck = 0
for i,j in zip(self.chkbox,range(len(self.chkbox))):
if i.isChecked():
boxCheck += 1
self.axes.plot(self.obj_dataext.x,self.obj_dataext.y[j],c=self.color[j],label=str(j+1))
self.axes.set_xlabel('time-->')
if j < self.volts_length:
self.axes.set_ylabel('Voltage(V)-->')
else:
self.axes.set_ylabel('Current(I)-->')
self.axes.grid(True)
if boxCheck == 0:
QtGui.QMessageBox.about(self, "Warning!!","Please select at least one Node OR Branch")
self.canvas.draw()
def onPush_dc(self):
#print "Calling on push dc"
boxCheck = 0
self.axes.cla()
for i,j in zip(self.chkbox,range(len(self.chkbox))):
if i.isChecked():
boxCheck += 1
self.axes.plot(self.obj_dataext.x,self.obj_dataext.y[j],c=self.color[j],label=str(j+1))
self.axes.set_xlabel('Voltage Sweep(V)-->')
if j < self.volts_length:
self.axes.set_ylabel('Voltage(V)-->')
else:
self.axes.set_ylabel('Current(I)-->')
self.axes.grid(True)
if boxCheck == 0:
QtGui.QMessageBox.about(self,"Warning!!", "Please select atleast one Node OR Branch")
self.canvas.draw()
def colorName(self,letter):
return {
'r':'color:red',
'b':'color:blue',
'g':'color:green',
'y':'color:yellow',
'c':'color:cyan',
'm':'color:magenta',
'k':'color:black'
}[letter]
class DataExtraction:
def __init__(self):
self.obj_appconfig = Appconfig()
print "Initialization"
self.data=[] #consists of all the columns of data belonging to nodes and branches
self.y=[] #stores y-axis data
self.x=[] #stores x-axis data
def numberFinder(self,fpath):
#Opening ANalysis file
with open(os.path.join(fpath,"analysis")) as f3:
self.analysisInfo = f3.read()
self.analysisInfo = self.analysisInfo.split(" ")
#Reading data file for voltage
with open(os.path.join(fpath,"plot_data_v.txt")) as f2:
self.voltData = f2.read()
self.voltData = self.voltData.split("\n")
#Initializing variable
#'p' gives no. of lines of data for each node/branch
# 'l' gives the no of partitions for a single voltage node
#'vnumber' gives total number of voltage
#'inumber' gives total number of current
p = l = vnumber = inumber = 0
#print "VoltsData : ",self.voltData
#Finding totla number of voltage node
for i in self.voltData[3:]:
#it has possible names of voltage nodes in NgSpice
if "Index" in i:#"V(" in i or "x1" in i or "u3" in i:
vnumber+=1
#print "Voltage Number :",vnumber
#Reading Current Source Data
with open (os.path.join(fpath,"plot_data_i.txt")) as f1:
self.currentData = f1.read()
self.currentData = self.currentData.split("\n")
#print "CurrentData : ",self.currentData
#Finding Number of Branch
for i in self.currentData[3:]:
if "#branch" in i:
inumber+=1
#print "Current Number :",inumber
self.dec = 0
#For AC
if self.analysisInfo[0][-3:]==".ac":
self.analysisType = 0
if "dec" in self.analysisInfo:
self.dec = 1
for i in self.voltData[3:]:
p+=1 #'p' gives no. of lines of data for each node/branch
if "Index" in i:
l+=1 # 'l' gives the no of partitions for a single voltage node
#print "l:",l
if "AC" in i: #DC for dc files and AC for ac ones
break
elif ".tran" in self.analysisInfo:
self.analysisType = 1
for i in self.voltData[3:]:
p+=1
if "Index" in i:
l+=1 # 'l' gives the no of partitions for a single voltage node
#print "l:",l
if "Transient" in i: #DC for dc files and AC for ac ones
break
# For DC:
else:
self.analysisType = 2
for i in self.voltData[3:]:
p+=1
if "Index" in i:
l+=1 # 'l' gives the no of partitions for a single voltage node
#print "l:",l
if "DC" in i: #DC for dc files and AC for ac ones
break
#print "VoltNumber",vnumber
#print "CurrentNumber",inumber
vnumber = vnumber//l #vnumber gives the no of voltage nodes
inumber = inumber//l #inumber gives the no of branches
#print "VoltNumber",vnumber
#print "CurrentNumber",inumber
p=[p,vnumber,self.analysisType,self.dec,inumber]
return p
def openFile(self,fpath):
#print "Calling Open File"
try:
with open (os.path.join(fpath,"plot_data_i.txt")) as f2:
alli = f2.read()
alli = alli.split("\n")
self.NBIList = []
with open (os.path.join(fpath,"plot_data_v.txt")) as f1:
allv = f1.read()
except Exception as e:
print "Exception Message : ",str(e)
self.obj_appconfig.print_error('Exception Message :' + str(e))
self.msg = QtGui.QErrorMessage(None)
self.msg.showMessage('Unable to open plot data files.')
self.msg.setWindowTitle("Error Message:openFile")
try:
for l in alli[3].split(" "):
if len(l)>0:
self.NBIList.append(l)
self.NBIList = self.NBIList[2:]
len_NBIList = len(self.NBIList)
#print "NBILIST : ",self.NBIList
except Exception as e:
print "Exception Message : ",str(e)
self.obj_appconfig.print_error('Exception Message :' + str(e))
self.msg = QtGui.QErrorMessage(None)
self.msg.showMessage('Error in Analysis File.')
self.msg.setWindowTitle("Error Message:openFile")
d = self.numberFinder(fpath)
d1 = int(d[0] + 1)
d2 = int(d[1])
d3 = d[2]
d4 = d[4]
dec = [d3,d[3]]
#print "No. of Nodes:", d2
self.NBList = []
allv=allv.split("\n")
for l in allv[3].split(" "):
if len(l)>0:
self.NBList.append(l)
self.NBList=self.NBList[2:]
len_NBList = len(self.NBList)
print "NBLIST",self.NBList
ivals=[]
inum = len(allv[5].split("\t"))
inum_i = len(alli[5].split("\t"))
full_data = []
# Creating list of data:
if d3 < 3 :
for i in range(1,d2):
for l in allv[3+i*d1].split(" "):
if len(l)>0:
self.NBList.append(l)
self.NBList.pop(len_NBList)
self.NBList.pop(len_NBList)
len_NBList = len(self.NBList)
for n in range(1,d4):
for l in alli[3+n*d1].split(" "):
if len(l)>0:
self.NBIList.append(l)
self.NBIList.pop(len_NBIList)
self.NBIList.pop(len_NBIList)
len_NBIList = len(self.NBIList)
p=0
k = 0
m=0
for i in alli[5:d1-1]:
if len(i.split("\t"))==inum_i:
j2=i.split("\t")
#print j2
j2.pop(0)
j2.pop(0)
j2.pop()
if d3 == 0: #not in trans
j2.pop()
#print j2
for l in range(1,d4):
j3 = alli[5+l*d1+k].split("\t")
j3.pop(0)
j3.pop(0)
if d3==0:
j3.pop() #not required for dc
j3.pop()
j2 = j2 + j3
#print j2
full_data.append(j2)
k+=1
#print "FULL DATA :",full_data
for i in allv[5:d1-1]:
if len(i.split("\t"))==inum:
j=i.split("\t")
j.pop()
if d3==0:
j.pop()
for l in range(1,d2):
j1 = allv[5+l*d1+p].split("\t")
j1.pop(0)
j1.pop(0)
if d3==0:
j1.pop() #not required for dc
if self.NBList[len(self.NBList)-1] == 'v-sweep':
self.NBList.pop()
j1.pop()
j1.pop()
j = j + j1
j = j + full_data[m]
#print j
m+=1
#print j[:20]
j = "\t".join(j[1:])
j = j.replace(",","")
ivals.append(j)
p+=1
self.data = ivals
#print "volts:",self.butnames
self.volts_length = len(self.NBList)
self.NBList = self.NBList + self.NBIList
print dec
return dec
def numVals(self):
a = self.volts_length # No of voltage nodes
b = len(self.data[0].split("\t"))
#print "numvals:",b
return [b,a]
def computeAxes(self):
nums = len(self.data[0].split("\t"))
#print "i'm nums:",nums
self.y=[]
var=self.data[0].split("\t")
for i in range(1,nums):
self.y.append([Decimal(var[i])])
for i in self.data[1:]:
temp=i.split("\t")
for j in range(1,nums):
self.y[j-1].append(Decimal(temp[j]))
for i in self.data:
temp=i.split("\t")
self.x.append(Decimal(temp[0]))