Source code for woo.qt.Inspector

# encoding: utf-8

import woo.config
if 'qt4' in woo.config.features:
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
else:
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import *

from woo.qt.ObjectEditor import *
import woo
import woo.qt
from woo.dem import *
#from woo.sparc import *
from woo.core import *

try: from woo.gl import *
except ImportError: pass


[docs]class EngineInspector(QWidget): def __init__(self,parent=None): QWidget.__init__(self,parent) grid=QGridLayout(self); grid.setSpacing(0); grid.setContentsMargins(0,0,0,0) self.serEd=SeqObject(parent=None,getter=lambda:woo.master.scene.engines,setter=lambda x:setattr(woo.master.scene,'engines',x),T=Engine,trait=[t for t in Scene._attrTraits if t.name=='engines'][0],path='woo.master.scene.engines') grid.addWidget(self.serEd) self.setLayout(grid)
#class MaterialsInspector(QWidget): # def __init__(self,parent=None): # QWidget.__init__(self,parent) # grid=QGridLayout(self); grid.setSpacing(0); grid.setContentsMargins(0,0,0,0) # self.serEd=SeqObject(parent=None,getter=lambda:O.materials,setter=lambda x:setattr(O,'materials',x),serType=Engine) # grid.addWidget(self.serEd) # self.setLayout(grid)
[docs]class CellInspector(QWidget): def __init__(self,parent=None): QWidget.__init__(self,parent) self.layout=QVBoxLayout(self) #; self.layout.setSpacing(0); self.layout.setContentsMargins(0,0,0,0) self.periCheckBox=QCheckBox('periodic boundary',self) self.periCheckBox.clicked.connect(self.update) self.layout.addWidget(self.periCheckBox) self.scroll=QScrollArea(self); self.scroll.setWidgetResizable(True) self.layout.addWidget(self.scroll) self.setLayout(self.layout) self.refresh() self.refreshTimer=QTimer(self) self.refreshTimer.timeout.connect(self.refresh) self.refreshTimer.start(1000)
[docs] def refresh(self): S=woo.master.scene self.periCheckBox.setChecked(S.periodic) editor=self.scroll.widget() if not S.periodic and editor: self.scroll.takeWidget() if (S.periodic and not editor) or (editor and editor.ser!=S.cell): self.scroll.setWidget(ObjectEditor(S.cell,parent=self,showType=True,path='woo.master.cell'))
[docs] def update(self): self.scroll.takeWidget() # do this before changing periodicity, otherwise the ObjectEditor will raise exception about None object S=woo.master.scene S.periodic=self.periCheckBox.isChecked() self.refresh()
[docs]class SceneInspector(QWidget): def __init__(self,parent=None): QWidget.__init__(self,parent) grid=QGridLayout(self); grid.setSpacing(0); grid.setContentsMargins(0,0,0,0) self.serEd=ObjectEditor(woo.master.scene,parent=self,showType=False,path='woo.master.scene') grid.addWidget(self.serEd) self.setLayout(grid)
[docs]def makeBodyLabel(b): ret=str(b.id)+u' ' if not b.shape: ret+=u'⬚' else: typeMap={'Sphere':u'⚫','Facet':u'△','FlexFacet':u'⧋','Wall':u'┃','Box':u'⎕','Cylinder':u'⌭','Clump':u'☍','InfCylinder':u'◎','Ellipsoid':u'⬯','Capsule':u'O'} ret+=typeMap.get(b.shape.__class__.__name__,u'﹖') if (b.shape.nodes)==1 and b.blocked!='': ret+=u'⚓' return ret
[docs]def getBodyIdFromLabel(label): try: return int(str(label).split()[0]) except ValueError: print('Error with label:',str(label)) return -1
[docs]class BodyInspector(QWidget): def __init__(self,parId=None,parent=None,bodyLinkCallback=None,intrLinkCallback=None): QWidget.__init__(self,parent) self.parId=(0 if parId==None else parId) if 'opengl' in woo.config.features: v=woo.qt.views() if parId==None and len(v)>0 and v[0].selection>0: self.bodyId=v[0].selection self.idGlSync=self.parId self.bodyLinkCallback,self.intrLinkCallback=bodyLinkCallback,intrLinkCallback self.bodyIdBox=QSpinBox(self) self.bodyIdBox.setMinimum(0) self.bodyIdBox.setMaximum(1000000000) self.bodyIdBox.setValue(self.parId) self.intrWithCombo=QComboBox(self); self.gotoBodyButton=QPushButton(u'→ #',self) self.gotoIntrButton=QPushButton(u'→ #+#',self) # id selector topBoxWidget=QWidget(self); topBox=QHBoxLayout(topBoxWidget); topBox.setContentsMargins(0,0,0,0); #topBox.setSpacing(0); hashLabel=QLabel('#',self); hashLabel.setFixedWidth(8) topBox.addWidget(hashLabel) topBox.addWidget(self.bodyIdBox) self.plusLabel=QLabel('+',self); topBox.addWidget(self.plusLabel) hashLabel2=QLabel('#',self); hashLabel2.setFixedWidth(8); topBox.addWidget(hashLabel2) topBox.addWidget(self.intrWithCombo) topBox.addStretch() topBox.addWidget(self.gotoBodyButton) topBox.addWidget(self.gotoIntrButton) topBoxWidget.setLayout(topBox) # forces display forcesWidget=QFrame(self); forcesWidget.setFrameShape(QFrame.Box); self.forceGrid=QGridLayout(forcesWidget); self.forceGrid.setVerticalSpacing(0); self.forceGrid.setHorizontalSpacing(9); self.forceGrid.setContentsMargins(4,4,4,4); for i,j in itertools.product((0,1,2,3),(-1,0,1,2)): lab=QLabel('<small>'+('force','torque','move','rot')[i]+'</small>' if j==-1 else ''); self.forceGrid.addWidget(lab,i,j+1); if j>=0: lab.setAlignment(Qt.AlignRight) if i>1: lab.hide() # do not show forced moves and rotations by default (they will appear if non-zero) self.showMovRot=False # self.grid=QGridLayout(self); self.grid.setSpacing(0); self.grid.setContentsMargins(0,0,0,0) self.grid.addWidget(topBoxWidget) self.grid.addWidget(forcesWidget) self.scroll=QScrollArea(self) self.scroll.setWidgetResizable(True) self.grid.addWidget(self.scroll) self.tryShowBody() self.bodyIdBox.valueChanged.connect(self.bodyIdSlot) self.gotoBodyButton.clicked.connect(self.gotoBodySlot) self.gotoIntrButton.clicked.connect(self.gotoIntrSlot) self.refreshTimer=QTimer(self) self.refreshTimer.timeout.connect(self.refreshEvent) self.refreshTimer.start(1000) self.intrWithCombo.addItems(['0']); self.intrWithCombo.setCurrentIndex(0); self.intrWithCombo.setMinimumWidth(80) if self.parId==None: self.setWindowTitle('Particle') else: self.setWindowTitle('Particle #%d'%self.parId) self.gotoBodySlot()
[docs] def displayForces(self): if self.parId==None: return S=woo.master.scene b=S.dem.par[self.parId] if not b.shape: noshow='no shape' elif len(b.shape.nodes)==0: noshow='no nodes' elif len(b.shape.nodes)>1: noshow='multinodal' elif not b.shape.nodes[0].dem: noshow='no Node.dem' else: noshow=None if noshow: self.forceGrid.itemAtPosition(0,1).widget().setText('<small>'+noshow+'</small>') for i,j in ((0,2),(0,3),(1,1),(1,2),(1,3)): self.forceGrid.itemAtPosition(i,j).widget().setText('') else: try: d=b.shape.nodes[0].dem val=[d.force,d.torque] rows=(0,1) for i,j in itertools.product(rows,(0,1,2)): self.forceGrid.itemAtPosition(i,j+1).widget().setText('<small>'+str(val[i][j])+'</small>') except IndexError:pass
[docs] def tryShowBody(self): try: if self.parId==None: raise IndexError() b=woo.master.scene.dem.par[self.parId] self.serEd=ObjectEditor(b,showType=True,parent=self,path='woo.master.scene.dem.par[%d]'%self.parId) except IndexError: if self.bodyIdBox.hasFocus(): return False self.serEd=QFrame(self) self.parId=None self.scroll.setWidget(self.serEd) return True
[docs] def changeIdSlot(self,newId): self.bodyIdBox.setValue(newId); self.bodyIdSlot(newId)
[docs] def bodyIdSlot(self,currId): self.parId=currId if not self.tryShowBody(): self.bodyIdBox.setStyleSheet('QWidget { background: red }') return # we still have focus, don't attempt to change else: self.bodyIdBox.setStyleSheet('QWidget { background: none }') # self.parId=currId # self.bodyIdBox.value() if self.parId==None: self.setWindowTitle('Particle') else: self.setWindowTitle('Particle #%d'%self.parId) self.refreshEvent()
[docs] def gotoBodySlot(self): try: id=int(getBodyIdFromLabel(self.intrWithCombo.currentText())) except ValueError: return # empty id if not self.bodyLinkCallback: self.bodyIdBox.setValue(id); self.parId=id else: self.bodyLinkCallback(id)
[docs] def gotoIntrSlot(self): ids=self.bodyIdBox.value(),getBodyIdFromLabel(self.intrWithCombo.currentText()) if not self.intrLinkCallback: self.ii=InteractionInspector(ids) self.ii.show() else: self.intrLinkCallback(ids)
[docs] def refreshEvent(self): S=woo.master.scene try: S.dem.par[self.parId] except: self.parId=None # invalidate deleted body # no body shown yet, try to get the first one... if self.parId==None and len(S.dem.par)>0: try: # print 'SET ZERO' b=S.dem.par[0]; self.bodyIdBox.setValue(0); self.parId=0 except IndexError: pass if 'opengl' in woo.config.features: v=woo.qt.views() if len(v)>0 and v[0].selection!=self.parId: if self.idGlSync==self.parId: # changed in the viewer, reset ourselves self.parId=self.idGlSync=v[0].selection; self.changeIdSlot(self.parId) return elif self.parId!=None: v[0].selection=self.idGlSync=self.parId # changed here, set in the viewer meId=self.bodyIdBox.value(); pos=self.intrWithCombo.currentIndex() try: meLabel=makeBodyLabel(S.dem.par[meId]) except IndexError: meLabel=u'…' self.plusLabel.setText(' '.join(meLabel.split()[1:])+' <b>+</b>') # do not repeat the id self.bodyIdBox.setMaximum(len(S.dem.par)-1) try: others=S.dem.par[meId].con except IndexError: others=[] #(i.id1 if i.id1!=meId else i.id2) for i in O.interactions.withBody(self.bodyIdBox.value()) if i.isReal] others.sort() self.intrWithCombo.clear() self.intrWithCombo.addItems([makeBodyLabel(S.dem.par[i]) for i in others]) if pos>self.intrWithCombo.count() or pos<0: pos=0 self.intrWithCombo.setCurrentIndex(pos); other=self.intrWithCombo.itemText(pos) if other=='': self.gotoBodyButton.setEnabled(False); self.gotoIntrButton.setEnabled(False) other=u'∅' else: self.gotoBodyButton.setEnabled(True); self.gotoIntrButton.setEnabled(True) self.gotoBodyButton.setText(u'→ %s'%other) self.gotoIntrButton.setText(u'→ %s + %s'%(meLabel,other)) self.displayForces()
[docs]class InteractionInspector(QWidget): def __init__(self,ids=None,parent=None,bodyLinkCallback=None): QWidget.__init__(self,parent) self.bodyLinkCallback=bodyLinkCallback self.ids=ids self.intrLinIxBox=QSpinBox(self) self.intrLinIxBox.setMinimum(0) self.intrLinIxBox.setMaximum(1000000000) self.gotoId1Button=QPushButton(u'#…',self) self.gotoId2Button=QPushButton(u'#…',self) self.gotoId1Button.clicked.connect(self.gotoId1Slot) self.gotoId2Button.clicked.connect(self.gotoId2Slot) self.intrLinIxBox.valueChanged.connect(self.setLinIxSlot) topBoxWidget=QWidget(self) topBox=QHBoxLayout(topBoxWidget) topBox.addWidget(self.intrLinIxBox) topBox.addWidget(self.gotoId1Button) labelPlus=QLabel('+',self); labelPlus.setAlignment(Qt.AlignHCenter) topBox.addWidget(labelPlus) topBox.addWidget(self.gotoId2Button) topBoxWidget.setLayout(topBox) self.setWindowTitle(u'No contact') self.grid=QGridLayout(self); self.grid.setSpacing(0); self.grid.setContentsMargins(0,0,0,0) self.grid.addWidget(topBoxWidget,0,0) self.scroll=QScrollArea(self) self.scroll.setWidgetResizable(True) self.grid.addWidget(self.scroll) self.refreshTimer=QTimer(self) self.refreshTimer.timeout.connect(self.refreshEvent) self.refreshTimer.start(1000) if self.ids: self.setupInteraction()
[docs] def setupInteraction(self): 'Change view; called whenever the interaction to be displayed changes' S=woo.master.scene try: if self.ids==None: raise IndexError() # to be caught right away intr=S.dem.con[self.ids] # also might raise IndexError, if the contact is dead if not intr: raise IndexError() self.intrLinIxBox.setValue(intr.linIx) self.serEd=ObjectEditor(intr,showType=True,parent=self.scroll,path='woo.master.scene.dem.con[%d,%d]'%(self.ids[0],self.ids[1])) self.scroll.setWidget(self.serEd) self.gotoId1Button.setText('#'+makeBodyLabel(S.dem.par[self.ids[0]])) self.gotoId2Button.setText('#'+makeBodyLabel(S.dem.par[self.ids[1]])) self.setWindowTitle('Contact #%d + #%d'%(self.ids[0],self.ids[1])) except (IndexError,): if self.ids: # reset view (there was an interaction) self.ids=None self.serEd=QFrame(self.scroll); self.scroll.setWidget(self.serEd) self.setWindowTitle('No contact') self.gotoId1Button.setText(u'#…'); self.gotoId2Button.setText(u'#…');
[docs] def gotoId(self,bodyId): if self.bodyLinkCallback: self.bodyLinkCallback(bodyId) else: self.bi=BodyInspector(bodyId); self.bi.show()
[docs] def setLinIxSlot(self,linIx): S=woo.master.scene try: C=S.dem.con[linIx] self.ids=C.id1,C.id2 self.setupInteraction() except IndexError: pass
[docs] def gotoId1Slot(self): self.gotoId(self.ids[0])
[docs] def gotoId2Slot(self): self.gotoId(self.ids[1])
[docs] def refreshEvent(self): S=woo.master.scene self.intrLinIxBox.setMaximum(len(S.dem.con)-1) # no ids yet -- try getting the first interaction, if it exists if not self.ids: try: i=S.dem.con[0] self.ids=i.id1,i.id2 self.setupInteraction() return except IndexError: return # no interaction exists at all try: # try to fetch the contact we have c=S.dem.con[self.ids[0],self.ids[1]] self.intrLinIxBox.setValue(c.linIx) # update linIx, it can change asynchronously except (IndexError,AttributeError): self.ids=None self.setupInteraction() # will make it empty
[docs]class SimulationInspector(QWidget): def __init__(self,parent=None): S=woo.master.scene QWidget.__init__(self,parent) self.setWindowTitle("Simulation Inspection") self.setWindowIcon(QIcon(":/woo-logo.svg")) self.tabWidget=QTabWidget(self) demField=S.dem if S.hasDem else None self.engineInspector=EngineInspector(parent=None) self.bodyInspector=BodyInspector(parent=None,intrLinkCallback=self.changeIntrIds) if demField else None self.intrInspector=InteractionInspector(parent=None,bodyLinkCallback=self.changeBodyId) if demField else None self.cellInspector=CellInspector(parent=None) self.sceneInspector=SceneInspector(parent=None) for i,name,widget in [(0,'Engines',self.engineInspector),(1,'Particles',self.bodyInspector),(2,'Contacts',self.intrInspector),(3,'Cell',self.cellInspector),(4,'Scene',self.sceneInspector)]: if widget: self.tabWidget.addTab(widget,name) # add fields for i,f in enumerate(S.fields): path='woo.master.scene.fields[%d]'%i if S.hasDem and f==S.dem: path='woo.master.scene.dem' #if S.hasSparc and f==S.sparc: path='woo.master.scene.sparc' self.tabWidget.addTab(ObjectEditor(f,parent=None,path=path,showType=True),'%d. '%i+path) grid=QGridLayout(self); grid.setSpacing(0); grid.setContentsMargins(0,0,0,0) grid.addWidget(self.tabWidget) self.setLayout(grid)
[docs] def changeIntrIds(self,ids): self.tabWidget.removeTab(2); self.intrInspector.close() self.intrInspector=InteractionInspector(ids=ids,parent=None,bodyLinkCallback=self.changeBodyId) self.tabWidget.insertTab(2,self.intrInspector,'Contacts') self.tabWidget.setCurrentIndex(2)
[docs] def changeBodyId(self,id): self.bodyInspector.changeIdSlot(id) self.tabWidget.setCurrentIndex(1)