def init(self, volumina): self.editor = volumina self.hudsShown = [True] * 3 def onViewFocused(): axis = self.editor._lastImageViewFocus self.toggleSelectedHUD.setChecked( self.editor.imageViews[axis]._hud.isVisible()) self.editor.newImageView2DFocus.connect(onViewFocused) self.layout = QHBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.setLayout(self.layout) # setup quadview axisLabels = ["X", "Y", "Z"] axisColors = [QColor("#dc143c"), QColor("green"), QColor("blue")] for i, v in enumerate(self.editor.imageViews): v.hud = ImageView2DHud(v) #connect interpreter v.hud.createImageView2DHud(axisLabels[i], 0, axisColors[i], QColor("white")) v.hud.sliceSelector.valueChanged.connect( partial(self.editor.navCtrl.changeSliceAbsolute, axis=i)) self.quadview = QuadView(self, self.editor.imageViews[2], self.editor.imageViews[0], self.editor.imageViews[1], self.editor.view3d) self.quadview.installEventFilter(self) self.quadViewStatusBar = QuadStatusBar() self.quadViewStatusBar.createQuadViewStatusBar(QColor("#dc143c"), QColor("white"), QColor("green"), QColor("white"), QColor("blue"), QColor("white")) self.quadview.addStatusBar(self.quadViewStatusBar) self.layout.addWidget(self.quadview) ## Why do we have to prevent TimerEvents reaching the SpinBoxes? # # Sometimes clicking a SpinBox once caused the value to increase by # two. This is why: # # When a MouseClicked event is received by the SpinBox it fires a timerevent to control # the repeated increase of the value as long as the mouse button is pressed. The timer # is killed when it receives a MouseRelease event. If a slot connected to the valueChanged # signal of the SpinBox takes to long to process the signal the mouse release # and timer events get queued up and sometimes the timer event reaches the widget before # the mouse release event. That's why it increases the value by another step. To prevent # this we are blocking the timer events at the cost of no autorepeat anymore. # # See also: # http://lists.trolltech.com/qt-interest/2002-04/thread00137-0.html # http://www.qtcentre.org/threads/43078-QSpinBox-Timer-Issue # http://qt.gitorious.org/qt/qt/blobs/4.8/src/gui/widgets/qabstractspinbox.cpp#line1195 self.quadview.statusBar.timeSpinBox.installEventFilter(_timerEater) def setTime(t): if t == self.editor.posModel.time: return self.editor.posModel.time = t self.quadview.statusBar.timeSpinBox.valueChanged.connect(setTime) def getTime(newT): self.quadview.statusBar.timeSpinBox.setValue(newT) self.editor.posModel.timeChanged.connect(getTime) def toggleSliceIntersection(state): self.editor.navCtrl.indicateSliceIntersection = ( state == Qt.Checked) self.quadview.statusBar.positionCheckBox.stateChanged.connect( toggleSliceIntersection) self.editor.posModel.cursorPositionChanged.connect( self._updateInfoLabels) def onShapeChanged(): singletonDims = filter( lambda (i, dim): dim == 1, enumerate(self.editor.posModel.shape5D[1:4])) if len(singletonDims) == 1: # Maximize the slicing view for this axis axis = singletonDims[0][0] self.quadview.ensureMaximized(axis) self.hudsShown[axis] = self.editor.imageViews[axis].hudVisible( ) self.editor.imageViews[axis].setHudVisible(False) self.quadViewStatusBar.showXYCoordinates() self.quadview.statusBar.positionCheckBox.setVisible(False) else: self.quadViewStatusBar.showXYZCoordinates() for i in range(3): self.editor.imageViews[i].setHudVisible(self.hudsShown[i]) self.quadview.statusBar.positionCheckBox.setVisible(True) self._setupVolumeExtent() self.editor.shapeChanged.connect(onShapeChanged) self.updateGeometry() self.update() self.quadview.update() # shortcuts self._initShortcuts()
def init(self, volumina): self.editor = volumina self.hudsShown = [True] * 3 def onViewFocused(): axis = self.editor._lastImageViewFocus self.toggleSelectedHUD.setChecked( self.editor.imageViews[axis]._hud.isVisible()) self.editor.newImageView2DFocus.connect(onViewFocused) self.layout = QHBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.setLayout(self.layout) # setup quadview axisLabels = ["X", "Y", "Z"] axisColors = [QColor("#dc143c"), QColor("green"), QColor("blue")] for i, v in enumerate(self.editor.imageViews): v.hud = ImageView2DHud(v) #connect interpreter v.hud.createImageView2DHud(axisLabels[i], 0, axisColors[i], QColor("white")) v.hud.sliceSelector.valueChanged.connect( partial(self.editor.navCtrl.changeSliceAbsolute, axis=i)) self.quadview = QuadView(self, self.editor.imageViews[2], self.editor.imageViews[0], self.editor.imageViews[1], self.editor.view3d) self.quadview.installEventFilter(self) self.quadViewStatusBar = QuadStatusBar() self.quadViewStatusBar.createQuadViewStatusBar(QColor("#dc143c"), QColor("white"), QColor("green"), QColor("white"), QColor("blue"), QColor("white")) self.quadview.addStatusBar(self.quadViewStatusBar) self.layout.addWidget(self.quadview) # Here we subscribe to the dirtyChanged() signal from all slicing views, # and show the status bar "busy indicator" if any view is dirty. # Caveat: To avoid a flickering indicator for quick updates, we use a # timer that prevents the indicator from showing for a bit. def updateDirtyStatus(fromTimer=False): # We only care about views that are both VISIBLE and DIRTY. dirties = map(lambda v: v.scene().dirty, self.editor.imageViews) visibilities = map(lambda v: v.isVisible(), self.editor.imageViews) visible_dirtiness = numpy.logical_and(visibilities, dirties) if not any(visible_dirtiness): # Not dirty: Hide immediately self.quadViewStatusBar.busyIndicator.setVisible(False) else: if fromTimer: # The timer finished and we're still dirty: # Time to show the busy indicator. self.quadViewStatusBar.busyIndicator.setVisible(True) elif not self.quadViewStatusBar.busyIndicator.isVisible( ) and not self._dirtyTimer.isActive(): # We're dirty, but delay for a bit before showing the busy indicator. self._dirtyTimer.start(750) self._dirtyTimer = QTimer() self._dirtyTimer.setSingleShot(True) self._dirtyTimer.timeout.connect( partial(updateDirtyStatus, fromTimer=True)) for i, view in enumerate(self.editor.imageViews): view.scene().dirtyChanged.connect(updateDirtyStatus) # If the user changes the position in the quad-view status bar (at the bottom), # Update the position of the whole editor. def setPositionFromQuadBar(x, y, z): self.editor.posModel.slicingPos = (x, y, z) self.editor.posModel.cursorPos = (x, y, z) self.editor.navCtrl.panSlicingViews((x, y, z), [0, 1, 2]) self.quadViewStatusBar.positionChanged.connect(setPositionFromQuadBar) ## Why do we have to prevent TimerEvents reaching the SpinBoxes? # # Sometimes clicking a SpinBox once caused the value to increase by # two. This is why: # # When a MouseClicked event is received by the SpinBox it fires a timerevent to control # the repeated increase of the value as long as the mouse button is pressed. The timer # is killed when it receives a MouseRelease event. If a slot connected to the valueChanged # signal of the SpinBox takes to long to process the signal the mouse release # and timer events get queued up and sometimes the timer event reaches the widget before # the mouse release event. That's why it increases the value by another step. To prevent # this we are blocking the timer events at the cost of no autorepeat anymore. # # See also: # http://lists.trolltech.com/qt-interest/2002-04/thread00137-0.html # http://www.qtcentre.org/threads/43078-QSpinBox-Timer-Issue # http://qt.gitorious.org/qt/qt/blobs/4.8/src/gui/widgets/qabstractspinbox.cpp#line1195 self.quadview.statusBar.timeSpinBox.installEventFilter(_timerEater) def setTime(t): if t == self.editor.posModel.time: return self.editor.posModel.time = t self.quadview.statusBar.timeSpinBox.delayedValueChanged.connect( setTime) def setTimeSpinBox(newT): self.quadview.statusBar.timeSpinBox.setValue(newT) self.editor.posModel.timeChanged.connect(setTimeSpinBox) def toggleSliceIntersection(state): self.editor.navCtrl.indicateSliceIntersection = ( state == Qt.Checked) self.quadview.statusBar.positionCheckBox.stateChanged.connect( toggleSliceIntersection) toggleSliceIntersection( self.quadview.statusBar.positionCheckBox.checkState()) self.editor.posModel.cursorPositionChanged.connect( self._updateInfoLabels) def onShapeChanged(): # By default, 3D HUD buttons are visible, # but we'll turn them off below if the dataset is 2D. for axis in [0, 1, 2]: self.editor.imageViews[axis].hud.set3DButtonsVisible(True) singletonDims = filter( lambda (i, dim): dim == 1, enumerate(self.editor.posModel.shape5D[1:4])) if len(singletonDims) == 1: # Maximize the slicing view for this axis axis = singletonDims[0][0] self.quadview.ensureMaximized(axis) self.hudsShown[axis] = self.editor.imageViews[axis].hudVisible( ) self.editor.imageViews[axis].hud.set3DButtonsVisible(False) self.quadViewStatusBar.showXYCoordinates() self.quadview.statusBar.positionCheckBox.setVisible(False) else: self.quadViewStatusBar.showXYZCoordinates() for i in range(3): self.editor.imageViews[i].setHudVisible(self.hudsShown[i]) self.quadview.statusBar.positionCheckBox.setVisible(True) if self.editor.cropModel._crop_extents[0][ 0] == None or self.editor.cropModel.cropZero(): self.quadViewStatusBar.updateShape5D( self.editor.posModel.shape5D) else: cropMin = (self.editor.posModel.time, self.editor.cropModel._crop_extents[0][0], self.editor.cropModel._crop_extents[1][0], self.editor.cropModel._crop_extents[2][0], 0) self.quadViewStatusBar.updateShape5Dcropped( cropMin, self.editor.posModel.shape5D) self._setupVolumeExtent() self.editor.shapeChanged.connect(onShapeChanged) self.updateGeometry() self.update() self.quadview.update() if hasattr(self.editor.view3d, 'bUndock'): self.editor.view3d.bUndock.clicked.connect( partial(self.quadview.on_dock, self.quadview.dock2_ofSplitHorizontal2)) # shortcuts self._initShortcuts()