def dataShape(self, shape): if shape == self._dataShape: return self._dataShape = shape if self.planes: self.planes.RemoveObserver(self.coordEventObserver) self.qvtk.renderer.RemoveViewProp(self.planes) del self.planes self.planes = SlicingPlanesWidget(shape, self.qvtk.GetInteractor()) #self.planes.SetInteractor(self.qvtk.GetInteractor()) self.coordEventObserver = self.planes.AddObserver( "CoordinatesEvent", self.slicingCallback) self.planes.SetCoordinate([0, 0, 0]) self.planes.SetPickable(False) ## Add RGB arrow axes if self.axes: self.qvtk.renderer.RemoveActor(self.axes) del self.axes self.axes = vtkAxesActor() self.axes.AxisLabelsOff() self.axes.SetTotalLength(0.5 * shape[0], 0.5 * shape[1], 0.5 * shape[2]) self.axes.SetShaftTypeToCylinder() self.qvtk.renderer.AddActor(self.axes) self.qvtk.renderer.AddActor(self.planes) self.qvtk.renderer.ResetCamera() #for some reason, we have to do this afterwards! self.planes.togglePlanesOn()
def dataShape(self, shape): if shape == self._dataShape: return self._dataShape = shape if self.planes: self.planes.RemoveObserver(self.coordEventObserver) self.qvtk.renderer.RemoveViewProp(self.planes) del self.planes self.planes = SlicingPlanesWidget(shape, self.qvtk.GetInteractor()) #self.planes.SetInteractor(self.qvtk.GetInteractor()) self.coordEventObserver = self.planes.AddObserver("CoordinatesEvent", self.slicingCallback) self.planes.SetCoordinate([0,0,0]) self.planes.SetPickable(False) ## Add RGB arrow axes if self.axes: self.qvtk.renderer.RemoveActor(self.axes) del self.axes self.axes = vtkAxesActor(); self.axes.AxisLabelsOff() self.axes.SetTotalLength(0.5*shape[0], 0.5*shape[1], 0.5*shape[2]) self.axes.SetShaftTypeToCylinder() self.qvtk.renderer.AddActor(self.axes) self.qvtk.renderer.AddActor(self.planes) self.qvtk.renderer.ResetCamera() #for some reason, we have to do this afterwards! self.planes.togglePlanesOn()
class OverviewScene(QWidget): #emitted when slice changes # int -- slice number # int -- axis number changedSlice = pyqtSignal(int,int) def resizeEvent(self, event): QWidget.resizeEvent(self,event) self.qvtk.update() #needed on OS X def slicingCallback(self, obj, event): def maybeUpdateSlice(old): num = obj.coordinate[obj.lastChangedAxis] axis = obj.lastChangedAxis if old == (num, axis): self.changedSlice.emit(num, axis) #when dragging the slice, wait for some milliseconds #to see whether the user is dragging on before #sending a signal num = obj.coordinate[obj.lastChangedAxis] axis = obj.lastChangedAxis QTimer.singleShot(50, partial(maybeUpdateSlice, (num, axis))) def ShowPlaneWidget(self, axis, show): self.planes.ShowPlane(axis, show) self.qvtk.update() def TogglePlaneWidgetX(self): self.planes.TogglePlaneWidget(0) self.qvtk.update() def TogglePlaneWidgetY(self): self.planes.TogglePlaneWidget(1) self.qvtk.update() def TogglePlaneWidgetZ(self): self.planes.TogglePlaneWidget(2) self.qvtk.update() @property def dataShape(self): return self._dataShape @dataShape.setter def dataShape(self, shape): if shape == self._dataShape: return self._dataShape = shape if self.planes: self.planes.RemoveObserver(self.coordEventObserver) self.qvtk.renderer.RemoveViewProp(self.planes) del self.planes self.planes = SlicingPlanesWidget(shape, self.qvtk.GetInteractor()) #self.planes.SetInteractor(self.qvtk.GetInteractor()) self.coordEventObserver = self.planes.AddObserver("CoordinatesEvent", self.slicingCallback) self.planes.SetCoordinate([0,0,0]) self.planes.SetPickable(False) ## Add RGB arrow axes if self.axes: self.qvtk.renderer.RemoveActor(self.axes) del self.axes self.axes = vtkAxesActor(); self.axes.AxisLabelsOff() self.axes.SetTotalLength(0.5*shape[0], 0.5*shape[1], 0.5*shape[2]) self.axes.SetShaftTypeToCylinder() self.qvtk.renderer.AddActor(self.axes) self.qvtk.renderer.AddActor(self.planes) self.qvtk.renderer.ResetCamera() #for some reason, we have to do this afterwards! self.planes.togglePlanesOn() def __init__(self, parent=None): super(OverviewScene, self).__init__(parent) self.coordEventObserver = None self.colorTable = None self.anaglyph = False self.sceneItems = [] self.cutter = 3*[None] self.objects = [] self.planes = None self.axes = None self._dataShape = None layout = QVBoxLayout() layout.setMargin(0) layout.setSpacing(0) self.qvtk = QVTKOpenGLWidget() layout.addWidget(self.qvtk) self.setLayout(layout) self.qvtk.init() hbox = QHBoxLayout(None) hbox.setMargin(0) hbox.setSpacing(5) hbox.setContentsMargins(5,3,5,3) def delete_gl_widget(): # This is called just before the app quits to avoid this error during shutdown: # QGLContext::makeCurrent: Cannot make invalid context current self.qvtk.setParent(None) del self.qvtk QApplication.instance().aboutToQuit.connect( delete_gl_widget ) b1 = QToolButton() b1.setIcon(QIcon(':icons/icons/x-axis.png')) b1.setToolTip("Show x slicing plane") b1.setCheckable(True); b1.setChecked(True) b2 = QToolButton() b2.setIcon(QIcon(':icons/icons/y-axis.png')) b2.setToolTip("Show y slicing plane") b2.setCheckable(True); b2.setChecked(True) b3 = QToolButton() b3.setIcon(QIcon(':icons/icons/z-axis.png')) b3.setToolTip("Show z slicing plane") b3.setCheckable(True); b3.setChecked(True) bAnaglyph = QToolButton() bAnaglyph.setIcon(QIcon(':icons/icons/3d_glasses.png')) bAnaglyph.setToolTip("Show in anaglyph 3D") bAnaglyph.setCheckable(True); bAnaglyph.setChecked(False) self.bUndock = QToolButton() self.bUndock.setIcon(QIcon(":/icons/icons/arrow_up.png")) self.bUndock.setToolTip("Dock/undock this view") ''' bCutter = QToolButton() bCutter.setIcon(QIcon(':icons/icons/edit-cut.png')) bCutter.setCheckable(True); bCutter.setChecked(False) self.bCutter = bCutter bExportMesh = QToolButton() bExportMesh.setIcon(QIcon(':icons/icons/document-save-as.png')) ''' hbox.addWidget(b1) hbox.addWidget(b2) hbox.addWidget(b3) hbox.addWidget(bAnaglyph) hbox.addWidget(self.bUndock) #hbox.addWidget(bCutter) hbox.addStretch() #hbox.addWidget(bExportMesh) layout.addLayout(hbox) self.connect(b1, SIGNAL("clicked()"), self.TogglePlaneWidgetX) self.connect(b2, SIGNAL("clicked()"), self.TogglePlaneWidgetY) self.connect(b3, SIGNAL("clicked()"), self.TogglePlaneWidgetZ) self.connect(bAnaglyph, SIGNAL("clicked()"), self.ToggleAnaglyph3D) #self.connect(bExportMesh, SIGNAL("clicked()"), self.exportMesh) #bCutter.toggled.connect(self.useCutterToggled) self.connect(self.qvtk, SIGNAL("objectPicked"), self.__onObjectPicked) def layerContextMenu(layer, menu): self.layerContextMenu(layer,menu) Event.register("layerContextMenuRequested", layerContextMenu) def layerContextMenu(self, layer, menu): if isinstance( layer, ColortableLayer ): def show3D(): data = layer._datasources[0].request((slice(0,1,None), slice(None,None,None), slice(None,None,None), slice(None,None,None), slice(0,1,None))).wait()[0,:,:,:,0] self.SetColorTable(layer._colorTable) self.DisplayObjectMeshes(data)#, suppressLabels=(), smooth=True): show3dAction = QAction("Show in 3D Overview", menu) show3dAction.triggered.connect(show3D) menu.addAction(show3dAction) @property def useCutter(self): return False #return self.bCutter.isChecked() def useCutterToggled(self): self.__updateCutter() if self.useCutter: for i in range(3): self.qvtk.renderer.AddActor(self.cutter[i]) else: for i in range(3): self.qvtk.renderer.RemoveActor(self.cutter[i]) self.qvtk.update() def exportMesh(self): filename = QFileDialog.getSaveFileName(self,"Save Meshes As") self.qvtk.actors.InitTraversal(); for i in range(self.qvtk.actors.GetNumberOfItems()): p = self.qvtk.actors.GetNextProp() if p.GetPickable() and self.qvtk.actors.IsItemPresent(p): vtpFilename = "%s%02d.vtp" % (filename, i) objFilename = "%s%02d.obj" % (filename, i) logger.info( "writing VTP file '%s'" % vtpFilename ) d = p.GetMapper().GetInput() w = vtkPolyDataWriter() w.SetFileTypeToASCII() w.SetInput(d) w.SetFileName("%s%02d.vtp" % (filename, i)) w.Write() logger.info( "converting to OBJ file '%s'" % objFilename ) convertVTPtoOBJ(vtpFilename, objFilename) #renWin = vtkRenderWindow() #ren = vtkRenderer() #renWin.AddRenderer(ren) #ren.AddActor(p) #exporter = vtkOBJExporter() #exporter.SetInput(renWin) #exporter.SetFilePrefix("%s%02d" % (filename, i)) #exporter.Update() def __onObjectPicked(self, coor): self.ChangeSlice( coor[0], 0) self.ChangeSlice( coor[1], 1) self.ChangeSlice( coor[2], 2) def __onLeftButtonReleased(self): logger.debug( "CLICK" ) def ToggleAnaglyph3D(self): self.anaglyph = not self.anaglyph if self.anaglyph: logger.debug( 'setting stero mode ON' ) self.qvtk.renderWindow.StereoRenderOn() self.qvtk.renderWindow.SetStereoTypeToAnaglyph() else: logger.debug( 'setting stero mode OFF' ) self.qvtk.renderWindow.StereoRenderOff() self.qvtk.update() def __updateCutter(self): if(self.useCutter): #print "Update cutter" for i in range(3): if self.cutter[i]: self.cutter[i].SetPlane(self.planes.Plane(i)) else: pass #print "Do NOT update cutter" def ChangeSlice(self, num, axis): c = copy.copy(self.planes.coordinate) c[axis] = num self.planes.SetCoordinate(c) # set the current point as the camera's focal point cam = self.qvtk.renderer.GetActiveCamera() cam.SetFocalPoint( *c ) #rotate around this point cam.Modified(); self.__updateCutter() self.qvtk.update() def display(self, axis): self.qvtk.update() def redisplay(self): self.qvtk.update() def DisplayObjectMeshes(self, v, suppressLabels=(), smooth=True): logger.debug( "OverviewScene::DisplayObjectMeshes {}".format( suppressLabels ) ) self.dlg = MeshExtractorDialog(self) self.dlg.finished.connect(self.onObjectMeshesComputed) self.dlg.show() self.dlg.run(v, suppressLabels, smooth) def SetColorTable(self, table): self.colorTable = table def onObjectMeshesComputed(self): self.dlg.accept() logger.debug( "*** Preparing 3D view ***" ) #Clean up possible previous 3D displays for c in self.cutter: if c: self.qvtk.renderer.RemoveActor(c) for a in self.objects: self.qvtk.renderer.RemoveActor(a) self.polygonAppender = vtkAppendPolyData() for g in self.dlg.extractor.meshes.values(): self.polygonAppender.AddInput(g) self.cutter[0] = Outliner(self.polygonAppender.GetOutput()) self.cutter[0].GetOutlineProperty().SetColor(1,0,0) self.cutter[1] = Outliner(self.polygonAppender.GetOutput()) self.cutter[1].GetOutlineProperty().SetColor(0,1,0) self.cutter[2] = Outliner(self.polygonAppender.GetOutput()) self.cutter[2].GetOutlineProperty().SetColor(0,0,1) for c in self.cutter: c.SetPickable(False) ## 1. Use a render window with alpha bits (as initial value is 0 (false)): #self.renderWindow.SetAlphaBitPlanes(True); ## 2. Force to not pick a framebuffer with a multisample buffer ## (as initial value is 8): #self.renderWindow.SetMultiSamples(0); ## 3. Choose to use depth peeling (if supported) (initial value is 0 (false)): #self.renderer.SetUseDepthPeeling(True); ## 4. Set depth peeling parameters ## - Set the maximum number of rendering passes (initial value is 4): #self.renderer.SetMaximumNumberOfPeels(100); ## - Set the occlusion ratio (initial value is 0.0, exact image): #self.renderer.SetOcclusionRatio(0.0); for i, g in self.dlg.extractor.meshes.items(): logger.debug( " - showing object with label = {}".format(i) ) mapper = vtkPolyDataMapper() mapper.SetInput(g) actor = vtkActor() actor.SetMapper(mapper) self.qvtk.registerObject(actor) self.objects.append(actor) if self.colorTable: c = self.colorTable[i] c = QColor.fromRgba(c) actor.GetProperty().SetColor(c.red()/255.0, c.green()/255.0, c.blue()/255.0) self.qvtk.renderer.AddActor(actor) self.qvtk.update()
class OverviewScene(QWidget): #emitted when slice changes # int -- slice number # int -- axis number changedSlice = pyqtSignal(int, int) def resizeEvent(self, event): QWidget.resizeEvent(self, event) self.qvtk.update() #needed on OS X def slicingCallback(self, obj, event): def maybeUpdateSlice(old): num = obj.coordinate[obj.lastChangedAxis] axis = obj.lastChangedAxis if old == (num, axis): self.changedSlice.emit(num, axis) #when dragging the slice, wait for some milliseconds #to see whether the user is dragging on before #sending a signal num = obj.coordinate[obj.lastChangedAxis] axis = obj.lastChangedAxis QTimer.singleShot(50, partial(maybeUpdateSlice, (num, axis))) def ShowPlaneWidget(self, axis, show): self.planes.ShowPlane(axis, show) self.qvtk.update() def TogglePlaneWidgetX(self): self.planes.TogglePlaneWidget(0) self.qvtk.update() def TogglePlaneWidgetY(self): self.planes.TogglePlaneWidget(1) self.qvtk.update() def TogglePlaneWidgetZ(self): self.planes.TogglePlaneWidget(2) self.qvtk.update() @property def dataShape(self): return self._dataShape @dataShape.setter def dataShape(self, shape): if shape == self._dataShape: return self._dataShape = shape if self.planes: self.planes.RemoveObserver(self.coordEventObserver) self.qvtk.renderer.RemoveViewProp(self.planes) del self.planes self.planes = SlicingPlanesWidget(shape, self.qvtk.GetInteractor()) #self.planes.SetInteractor(self.qvtk.GetInteractor()) self.coordEventObserver = self.planes.AddObserver( "CoordinatesEvent", self.slicingCallback) self.planes.SetCoordinate([0, 0, 0]) self.planes.SetPickable(False) ## Add RGB arrow axes if self.axes: self.qvtk.renderer.RemoveActor(self.axes) del self.axes self.axes = vtkAxesActor() self.axes.AxisLabelsOff() self.axes.SetTotalLength(0.5 * shape[0], 0.5 * shape[1], 0.5 * shape[2]) self.axes.SetShaftTypeToCylinder() self.qvtk.renderer.AddActor(self.axes) self.qvtk.renderer.AddActor(self.planes) self.qvtk.renderer.ResetCamera() #for some reason, we have to do this afterwards! self.planes.togglePlanesOn() def __init__(self, parent=None): super(OverviewScene, self).__init__(parent) self.coordEventObserver = None self.colorTable = None self.anaglyph = False self.sceneItems = [] self.cutter = 3 * [None] self.objects = [] self.planes = None self.axes = None self._dataShape = None layout = QVBoxLayout() layout.setMargin(0) layout.setSpacing(0) self.qvtk = QVTKOpenGLWidget() layout.addWidget(self.qvtk) self.setLayout(layout) self.qvtk.init() hbox = QHBoxLayout(None) hbox.setMargin(0) hbox.setSpacing(5) hbox.setContentsMargins(5, 3, 5, 3) def delete_gl_widget(): # This is called just before the app quits to avoid this error during shutdown: # QGLContext::makeCurrent: Cannot make invalid context current self.qvtk.setParent(None) del self.qvtk QApplication.instance().aboutToQuit.connect(delete_gl_widget) b1 = QToolButton() b1.setIcon(QIcon(':icons/icons/x-axis.png')) b1.setToolTip("Show x slicing plane") b1.setCheckable(True) b1.setChecked(True) b2 = QToolButton() b2.setIcon(QIcon(':icons/icons/y-axis.png')) b2.setToolTip("Show y slicing plane") b2.setCheckable(True) b2.setChecked(True) b3 = QToolButton() b3.setIcon(QIcon(':icons/icons/z-axis.png')) b3.setToolTip("Show z slicing plane") b3.setCheckable(True) b3.setChecked(True) bAnaglyph = QToolButton() bAnaglyph.setIcon(QIcon(':icons/icons/3d_glasses.png')) bAnaglyph.setToolTip("Show in anaglyph 3D") bAnaglyph.setCheckable(True) bAnaglyph.setChecked(False) self.bUndock = QToolButton() self.bUndock.setIcon(QIcon(":/icons/icons/arrow_up.png")) self.bUndock.setToolTip("Dock/undock this view") ''' bCutter = QToolButton() bCutter.setIcon(QIcon(':icons/icons/edit-cut.png')) bCutter.setCheckable(True); bCutter.setChecked(False) self.bCutter = bCutter bExportMesh = QToolButton() bExportMesh.setIcon(QIcon(':icons/icons/document-save-as.png')) ''' hbox.addWidget(b1) hbox.addWidget(b2) hbox.addWidget(b3) hbox.addWidget(bAnaglyph) hbox.addWidget(self.bUndock) #hbox.addWidget(bCutter) hbox.addStretch() #hbox.addWidget(bExportMesh) layout.addLayout(hbox) self.connect(b1, SIGNAL("clicked()"), self.TogglePlaneWidgetX) self.connect(b2, SIGNAL("clicked()"), self.TogglePlaneWidgetY) self.connect(b3, SIGNAL("clicked()"), self.TogglePlaneWidgetZ) self.connect(bAnaglyph, SIGNAL("clicked()"), self.ToggleAnaglyph3D) #self.connect(bExportMesh, SIGNAL("clicked()"), self.exportMesh) #bCutter.toggled.connect(self.useCutterToggled) self.connect(self.qvtk, SIGNAL("objectPicked"), self.__onObjectPicked) def layerContextMenu(layer, menu): self.layerContextMenu(layer, menu) Event.register("layerContextMenuRequested", layerContextMenu) def layerContextMenu(self, layer, menu): if isinstance(layer, ColortableLayer): def show3D(): data = layer._datasources[0].request( (slice(0, 1, None), slice(None, None, None), slice(None, None, None), slice(None, None, None), slice(0, 1, None))).wait()[0, :, :, :, 0] self.SetColorTable(layer._colorTable) self.DisplayObjectMeshes( data) #, suppressLabels=(), smooth=True): show3dAction = QAction("Show in 3D Overview", menu) show3dAction.triggered.connect(show3D) menu.addAction(show3dAction) @property def useCutter(self): return False #return self.bCutter.isChecked() def useCutterToggled(self): self.__updateCutter() if self.useCutter: for i in range(3): self.qvtk.renderer.AddActor(self.cutter[i]) else: for i in range(3): self.qvtk.renderer.RemoveActor(self.cutter[i]) self.qvtk.update() def exportMesh(self): filename = QFileDialog.getSaveFileName(self, "Save Meshes As") self.qvtk.actors.InitTraversal() for i in range(self.qvtk.actors.GetNumberOfItems()): p = self.qvtk.actors.GetNextProp() if p.GetPickable() and self.qvtk.actors.IsItemPresent(p): vtpFilename = "%s%02d.vtp" % (filename, i) objFilename = "%s%02d.obj" % (filename, i) logger.info("writing VTP file '%s'" % vtpFilename) d = p.GetMapper().GetInput() w = vtkPolyDataWriter() w.SetFileTypeToASCII() w.SetInput(d) w.SetFileName("%s%02d.vtp" % (filename, i)) w.Write() logger.info("converting to OBJ file '%s'" % objFilename) convertVTPtoOBJ(vtpFilename, objFilename) #renWin = vtkRenderWindow() #ren = vtkRenderer() #renWin.AddRenderer(ren) #ren.AddActor(p) #exporter = vtkOBJExporter() #exporter.SetInput(renWin) #exporter.SetFilePrefix("%s%02d" % (filename, i)) #exporter.Update() def __onObjectPicked(self, coor): self.ChangeSlice(coor[0], 0) self.ChangeSlice(coor[1], 1) self.ChangeSlice(coor[2], 2) def __onLeftButtonReleased(self): logger.debug("CLICK") def ToggleAnaglyph3D(self): self.anaglyph = not self.anaglyph if self.anaglyph: logger.debug('setting stero mode ON') self.qvtk.renderWindow.StereoRenderOn() self.qvtk.renderWindow.SetStereoTypeToAnaglyph() else: logger.debug('setting stero mode OFF') self.qvtk.renderWindow.StereoRenderOff() self.qvtk.update() def __updateCutter(self): if (self.useCutter): #print "Update cutter" for i in range(3): if self.cutter[i]: self.cutter[i].SetPlane(self.planes.Plane(i)) else: pass #print "Do NOT update cutter" def ChangeSlice(self, num, axis): c = copy.copy(self.planes.coordinate) c[axis] = num self.planes.SetCoordinate(c) # set the current point as the camera's focal point cam = self.qvtk.renderer.GetActiveCamera() cam.SetFocalPoint(*c) #rotate around this point cam.Modified() self.__updateCutter() self.qvtk.update() def display(self, axis): self.qvtk.update() def redisplay(self): self.qvtk.update() def DisplayObjectMeshes(self, v, suppressLabels=(), smooth=True): logger.debug( "OverviewScene::DisplayObjectMeshes {}".format(suppressLabels)) self.dlg = MeshExtractorDialog(self) self.dlg.finished.connect(self.onObjectMeshesComputed) self.dlg.show() self.dlg.run(v, suppressLabels, smooth) def SetColorTable(self, table): self.colorTable = table def onObjectMeshesComputed(self): self.dlg.accept() logger.debug("*** Preparing 3D view ***") #Clean up possible previous 3D displays for c in self.cutter: if c: self.qvtk.renderer.RemoveActor(c) for a in self.objects: self.qvtk.renderer.RemoveActor(a) self.polygonAppender = vtkAppendPolyData() for g in self.dlg.extractor.meshes.values(): self.polygonAppender.AddInput(g) self.cutter[0] = Outliner(self.polygonAppender.GetOutput()) self.cutter[0].GetOutlineProperty().SetColor(1, 0, 0) self.cutter[1] = Outliner(self.polygonAppender.GetOutput()) self.cutter[1].GetOutlineProperty().SetColor(0, 1, 0) self.cutter[2] = Outliner(self.polygonAppender.GetOutput()) self.cutter[2].GetOutlineProperty().SetColor(0, 0, 1) for c in self.cutter: c.SetPickable(False) ## 1. Use a render window with alpha bits (as initial value is 0 (false)): #self.renderWindow.SetAlphaBitPlanes(True); ## 2. Force to not pick a framebuffer with a multisample buffer ## (as initial value is 8): #self.renderWindow.SetMultiSamples(0); ## 3. Choose to use depth peeling (if supported) (initial value is 0 (false)): #self.renderer.SetUseDepthPeeling(True); ## 4. Set depth peeling parameters ## - Set the maximum number of rendering passes (initial value is 4): #self.renderer.SetMaximumNumberOfPeels(100); ## - Set the occlusion ratio (initial value is 0.0, exact image): #self.renderer.SetOcclusionRatio(0.0); for i, g in self.dlg.extractor.meshes.items(): logger.debug(" - showing object with label = {}".format(i)) mapper = vtkPolyDataMapper() mapper.SetInput(g) actor = vtkActor() actor.SetMapper(mapper) self.qvtk.registerObject(actor) self.objects.append(actor) if self.colorTable: c = self.colorTable[i] c = QColor.fromRgba(c) actor.GetProperty().SetColor(c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0) self.qvtk.renderer.AddActor(actor) self.qvtk.update()