def __init__(self, dart, costMeasure, parent, name=None): self.__base.__init__(self, parent) if name: self.setObjectName(name) self.dart = dart self.costMeasure = costMeasure self.ui = Ui_DartNavigator() self.ui.setupUi(self) self.connect(self.ui.nextPhiButton, QtCore.SIGNAL("clicked()"), self.nextPhi) self.connect(self.ui.prevPhiButton, QtCore.SIGNAL("clicked()"), self.prevPhi) self.connect(self.ui.nextAlphaButton, QtCore.SIGNAL("clicked()"), self.nextAlpha) self.connect(self.ui.nextSigmaButton, QtCore.SIGNAL("clicked()"), self.nextSigma) self.connect(self.ui.prevSigmaButton, QtCore.SIGNAL("clicked()"), self.prevSigma) self.connect(self.ui.continuousCheckBox, QtCore.SIGNAL("toggled(bool)"), self.toggleContinuous) self.timer = QtCore.QTimer(self) self.connect(self.timer, QtCore.SIGNAL("timeout()"), self.highlightNext) self._darthighlighter = DartHighlighter(parent.map, parent.viewer) self.updateLabel()
def __init__(self, dart, costMeasure, parent, name = None): self.__base.__init__(self, parent) if name: self.setObjectName(name) self.dart = dart self.costMeasure = costMeasure self.ui = Ui_DartNavigator() self.ui.setupUi(self) self.connect(self.ui.nextPhiButton, QtCore.SIGNAL("clicked()"), self.nextPhi) self.connect(self.ui.prevPhiButton, QtCore.SIGNAL("clicked()"), self.prevPhi) self.connect(self.ui.nextAlphaButton, QtCore.SIGNAL("clicked()"), self.nextAlpha) self.connect(self.ui.nextSigmaButton, QtCore.SIGNAL("clicked()"), self.nextSigma) self.connect(self.ui.prevSigmaButton, QtCore.SIGNAL("clicked()"), self.prevSigma) self.connect(self.ui.continuousCheckBox, QtCore.SIGNAL("toggled(bool)"), self.toggleContinuous) self.timer = QtCore.QTimer(self) self.connect(self.timer, QtCore.SIGNAL("timeout()"), self.highlightNext) self._darthighlighter = DartHighlighter(parent.map, parent.viewer) self.updateLabel()
class DartNavigator(QtGui.QDialog): __base = QtGui.QDialog def __init__(self, dart, costMeasure, parent, name = None): self.__base.__init__(self, parent) if name: self.setObjectName(name) self.dart = dart self.costMeasure = costMeasure self.ui = Ui_DartNavigator() self.ui.setupUi(self) self.connect(self.ui.nextPhiButton, QtCore.SIGNAL("clicked()"), self.nextPhi) self.connect(self.ui.prevPhiButton, QtCore.SIGNAL("clicked()"), self.prevPhi) self.connect(self.ui.nextAlphaButton, QtCore.SIGNAL("clicked()"), self.nextAlpha) self.connect(self.ui.nextSigmaButton, QtCore.SIGNAL("clicked()"), self.nextSigma) self.connect(self.ui.prevSigmaButton, QtCore.SIGNAL("clicked()"), self.prevSigma) self.connect(self.ui.continuousCheckBox, QtCore.SIGNAL("toggled(bool)"), self.toggleContinuous) self.timer = QtCore.QTimer(self) self.connect(self.timer, QtCore.SIGNAL("timeout()"), self.highlightNext) self._darthighlighter = DartHighlighter(parent.map, parent.viewer) self.updateLabel() def closeEvent(self, e): self._darthighlighter.highlight(None) self.__base.closeEvent(self, e) if e.isAccepted(): self.deleteLater() # like QtCore.Qt.WDestructiveClose ;-) def highlightNext(self): self.activePerm() self.updateLabel() def setDart(self, dart): self.dart = dart self.updateLabel() def toggleContinuous(self, onoff): if onoff: self.timer.start(1200) else: self.timer.stop() self.ui.nextPhiButton.setToggleButton(onoff) self.ui.prevPhiButton.setToggleButton(onoff) self.ui.nextAlphaButton.setToggleButton(onoff) self.ui.nextSigmaButton.setToggleButton(onoff) self.ui.prevSigmaButton.setToggleButton(onoff) def moveDart(self, perm): perm() self.updateLabel() self.activePerm = perm def nextPhi(self): self.moveDart(self.dart.nextPhi) def prevPhi(self): self.moveDart(self.dart.prevPhi) def nextAlpha(self): self.moveDart(self.dart.nextAlpha) def nextSigma(self): self.moveDart(self.dart.nextSigma) def prevSigma(self): self.moveDart(self.dart.prevSigma) def updateLabel(self): self._darthighlighter.highlight(self.dart.label()) dartDesc = "Dart %d, length %.1f, partial area %.1f, %d points" % ( self.dart.label(), self.dart.edge().length(), self.dart.partialArea(), len(self.dart)) if self.costMeasure: dartDesc += "\nassociated cost: %s" % self.costMeasure(self.dart) self.ui.dartLabel.setText(dartDesc) for node, nodeLabel in ((self.dart.startNode(), self.ui.startNodeLabel), (self.dart.endNode(), self.ui.endNodeLabel)): nodeLabel.setText( "Node %d (deg. %d)\nat %s" % ( node.label(), node.degree(), node.position())) if self.dart.map().mapInitialized(): leftFace = self.dart.leftFace() rightFace = self.dart.rightFace() self.ui.faceLabel.setText( """Left: %s\nRight: %s""" % (str(leftFace)[8:-1], str(rightFace)[8:-1])) self.setWindowTitle("DartNavigator(%d)" % (self.dart.label(), )) self.emit(QtCore.SIGNAL('updateDart'), self.dart)
def __init__(self, map, preparedImage=None, immediateShow=True, faceMeans=None): self.__base.__init__(self) self.tool = None self._togglingGUI = False self._faceMeans = None # temp. needed (for _enableImageActions) self.ui = Ui_DisplaySettings() self.ui.setupUi(self) # for backward compatibility: if hasattr(preparedImage, "imageSize") and hasattr(map, "width"): map, preparedImage = preparedImage, map elif preparedImage is None: preparedImage = map.labelImage() if preparedImage is None: preparedImage = vigra.ScalarImage(map.imageSize()) self.viewer = None # setImage would norm. pass the image on if hasattr(preparedImage, "orig"): self.images = { "original": preparedImage.view, "colored": preparedImage.orig, "bi": preparedImage.bi.gm, } preparedImage = preparedImage.view else: self.images = {} # auto-detects role colored/original: self.setImage(preparedImage, normalize=False) self.image = preparedImage self.viewer = VigraQt.OverlayViewer(self) self.setCentralWidget(self.viewer) self._setImage(self.image, normalize=False) self.viewer.autoZoom() # convenience: self.addOverlay = self.viewer.addOverlay self.removeOverlay = self.viewer.removeOverlay # self.overlays = self.viewer.overlays self.map = map if not faceMeans and hasattr(map, "faceMeans"): faceMeans = map.faceMeans self.setFaceMeans(faceMeans) self._attachedHooks = None self._normalizeStates = [False, False, True, True, False] if self.image.channels == 3: self._backgroundMode = 1 self.ui.displayColoredAction.setChecked(True) else: self._backgroundMode = 0 self.ui.displayOriginalAction.setChecked(True) self._enableImageActions() self.connect(self.ui.backgroundGroup, QtCore.SIGNAL("selected(QAction*)"), self.setBackgroundMode) self.connect(self.ui.nodeDisplayAction, QtCore.SIGNAL("toggled(bool)"), self.toggleNodeDisplay) self.connect(self.ui.edgeDisplayAction, QtCore.SIGNAL("toggled(bool)"), self.toggleEdgeDisplay) self.connect(self.ui.normalizeAction, QtCore.SIGNAL("toggled(bool)"), self.toggleNormalize) self.connect(self.ui.mapCleanupAction, QtCore.SIGNAL("activated()"), self.cleanupMap) self.connect(self.ui.paintbrushAction, QtCore.SIGNAL("toggled(bool)"), self.activatePaintbrush) self.connect(self.ui.scissorsAction, QtCore.SIGNAL("toggled(bool)"), self.activateScissors) self.connect(self.ui.navigateAction, QtCore.SIGNAL("toggled(bool)"), self.activateNavigator) self.setWindowTitle("Map Display") self.edgeOverlay = MapEdges(self.viewer) self.edgeOverlay.setMap(map) self.edgeOverlay.setPen(QtCore.Qt.red) # protectedColor = QtCore.Qt.green, # protectedWidth = 2) self.viewer.addOverlay(self.edgeOverlay) self.edgeOverlay.visible = self.ui.edgeDisplayAction.isChecked() # self.nodeOverlay = MapNodes(map, QtCore.Qt.blue) # self.viewer.addOverlay(self.nodeOverlay) # self.nodeOverlay.visible = self.ui.nodeDisplayAction.isChecked() self._dh = DartHighlighter(map, self.viewer) self.dn = None if immediateShow: self.show()
class MapDisplay(QtGui.QMainWindow): __base = QtGui.QMainWindow # actually, this has only documenting effect; since this inherits # PyQt widgets, any attribute may be used: __slots__ = ("tool", "viewer", "images", "image", "map", "nodeOverlay", "edgeOverlay", "addOverlay", "removeOverlay", "_togglingGUI", "_backgroundMode", "_normalizeStates", "_faceMeans", "_attachedHooks", "dn", "_dh") def __init__(self, map, preparedImage=None, immediateShow=True, faceMeans=None): self.__base.__init__(self) self.tool = None self._togglingGUI = False self._faceMeans = None # temp. needed (for _enableImageActions) self.ui = Ui_DisplaySettings() self.ui.setupUi(self) # for backward compatibility: if hasattr(preparedImage, "imageSize") and hasattr(map, "width"): map, preparedImage = preparedImage, map elif preparedImage is None: preparedImage = map.labelImage() if preparedImage is None: preparedImage = vigra.ScalarImage(map.imageSize()) self.viewer = None # setImage would norm. pass the image on if hasattr(preparedImage, "orig"): self.images = { "original": preparedImage.view, "colored": preparedImage.orig, "bi": preparedImage.bi.gm, } preparedImage = preparedImage.view else: self.images = {} # auto-detects role colored/original: self.setImage(preparedImage, normalize=False) self.image = preparedImage self.viewer = VigraQt.OverlayViewer(self) self.setCentralWidget(self.viewer) self._setImage(self.image, normalize=False) self.viewer.autoZoom() # convenience: self.addOverlay = self.viewer.addOverlay self.removeOverlay = self.viewer.removeOverlay # self.overlays = self.viewer.overlays self.map = map if not faceMeans and hasattr(map, "faceMeans"): faceMeans = map.faceMeans self.setFaceMeans(faceMeans) self._attachedHooks = None self._normalizeStates = [False, False, True, True, False] if self.image.channels == 3: self._backgroundMode = 1 self.ui.displayColoredAction.setChecked(True) else: self._backgroundMode = 0 self.ui.displayOriginalAction.setChecked(True) self._enableImageActions() self.connect(self.ui.backgroundGroup, QtCore.SIGNAL("selected(QAction*)"), self.setBackgroundMode) self.connect(self.ui.nodeDisplayAction, QtCore.SIGNAL("toggled(bool)"), self.toggleNodeDisplay) self.connect(self.ui.edgeDisplayAction, QtCore.SIGNAL("toggled(bool)"), self.toggleEdgeDisplay) self.connect(self.ui.normalizeAction, QtCore.SIGNAL("toggled(bool)"), self.toggleNormalize) self.connect(self.ui.mapCleanupAction, QtCore.SIGNAL("activated()"), self.cleanupMap) self.connect(self.ui.paintbrushAction, QtCore.SIGNAL("toggled(bool)"), self.activatePaintbrush) self.connect(self.ui.scissorsAction, QtCore.SIGNAL("toggled(bool)"), self.activateScissors) self.connect(self.ui.navigateAction, QtCore.SIGNAL("toggled(bool)"), self.activateNavigator) self.setWindowTitle("Map Display") self.edgeOverlay = MapEdges(self.viewer) self.edgeOverlay.setMap(map) self.edgeOverlay.setPen(QtCore.Qt.red) # protectedColor = QtCore.Qt.green, # protectedWidth = 2) self.viewer.addOverlay(self.edgeOverlay) self.edgeOverlay.visible = self.ui.edgeDisplayAction.isChecked() # self.nodeOverlay = MapNodes(map, QtCore.Qt.blue) # self.viewer.addOverlay(self.nodeOverlay) # self.nodeOverlay.visible = self.ui.nodeDisplayAction.isChecked() self._dh = DartHighlighter(map, self.viewer) self.dn = None if immediateShow: self.show() def __del__(self): # delete tool (which may reference the viewer & map) self.setTool(None) def _dartNavigatorDestroyed(self, dn): """HACK: this is needed because of the deleteLater()...""" # FIXME: cannot reliably detect identity of half-destructed # object anymore: #if dn is self.dn: self.dn = None def _labelImage(self): result = self.map.labelImage() if result is None: result = maputils.drawLabelImage(self.map) return result def setMap(self, map): attached = self.detachHooks() self.setTool(None) self.map = map self.edgeOverlay.setMap(map) #self.nodeOverlay.setMap(map) self._dh.setMap(map) updatedDisplayImage = None if self._backgroundMode == 3: updatedDisplayImage = self._labelImage() self._faceMeans = None if hasattr(map, "faceMeans"): self.setFaceMeans(map.faceMeans) if self._backgroundMode == 4: updatedDisplayImage = \ self._faceMeans.regionImage(self._labelImage()) self._enableImageActions() if updatedDisplayImage: self._setImage(updatedDisplayImage, self._normalizeStates[self._backgroundMode]) if attached: self.attachHooks() self.viewer.update() def setFaceMeans(self, faceMeans): self._faceMeans = faceMeans self._enableImageActions() def _adjustSize(self): pass # don't change window size out of a sudden def statusMessage(self, msg): # workaround - don't know why connecting the PYSIGNAL directly # does not work (maybe QtCore.SIGNAL("captionChanged(QString)") # would work?) self.statusBar().showMessage(msg) def currentRole(self): return ("original", "colored", "bi", "labels", "faceMeans")[self._backgroundMode] def setBackgroundMode(self, mode): if type(mode) != int: mode = [ self.ui.displayOriginalAction, self.ui.displayColoredAction, self.ui.displayBIAction, self.ui.displayLabelsAction, self.ui.displayMeansAction ].index(mode) displayImage = None if mode == 0: displayImage = self.images["original"] elif mode == 1: displayImage = self.images["colored"] elif mode == 2: displayImage = self.images["bi"] elif mode == 3: displayImage = self._labelImage() elif mode == 4: displayImage = self._faceMeans.regionImage(self._labelImage()) else: sys.stderr.write("Unknown background mode %d!\n" % mode) return self.image = displayImage self._backgroundMode = mode normalize = self._normalizeStates[mode] if self.ui.normalizeAction.isChecked() != normalize: self.ui.normalizeAction.setChecked(normalize) else: self._setImage(self.image, normalize) def toggleNodeDisplay(self, onoff): return # TEMP if self.nodeOverlay.visible != onoff: self.nodeOverlay.visible = onoff self.viewer.update() def toggleEdgeDisplay(self, onoff): if self.edgeOverlay.visible != onoff: self.edgeOverlay.visible = onoff self.viewer.update() def toggleNormalize(self, normalize): self._normalizeStates[self._backgroundMode] = normalize self._setImage(self.image, normalize) def attachHooks(self): return self.nodeOverlay.attachHooks() self.edgeOverlay.attachHooks() self._attachedHooks = (self.map.addMergeFacesCallbacks( None, self._postMergeFacesHook), self.map.addRemoveBridgeCallbacks( self._preRemoveBridgeHook, self._postRemoveBridgeHook)) def detachHooks(self): """Detaches / removes callbacks from the map's hooks. Returns True if successful, False if already detached.""" return results = (self.nodeOverlay.detachHooks(), self.edgeOverlay.detachHooks()) if self._attachedHooks: for h in self._attachedHooks: h.disconnect() self._attachedHooks = None if results == (True, True): return True else: if results == (False, False): return False def _redisplayROIImage(self, roi): roi &= Rect2D(self.map.imageSize()) roiImage = self._labelImage().subImage(roi) if self._backgroundMode > 3: roiImage = self._faceMeans.regionImage(roiImage) # FIXME: use global normalization here self.viewer.replaceROI(roiImage.toPNM(vigra.BYTE), QtCore.QPoint(*roi.upperLeft())) def _postMergeFacesHook(self, survivor): if self._backgroundMode < 3: return if survivor.label(): self._redisplayROIImage(intPos(survivor.boundingBox())) def _preRemoveBridgeHook(self, dart): if self._backgroundMode >= 3: self._bridgeROI = intPos(dart.edge().boundingBox()) return True def _postRemoveBridgeHook(self, dart): if self._backgroundMode < 3: return self._redisplayROIImage(self._bridgeROI) def showEvent(self, e): self.attachHooks() return self.__base.showEvent(self, e) def hideEvent(self, e): self.detachHooks() return self.__base.hideEvent(self, e) def showMarkedEdges(self, colorMarked=QtCore.Qt.green, colorUnmarked=None, markFlags=flag_constants.ALL_PROTECTION): edgeColors = [None] * self.map.maxEdgeLabel() for edge in self.map.edgeIter(): if edge.flag(markFlags): edgeColors[edge.label()] = colorMarked else: edgeColors[edge.label()] = colorUnmarked self.edgeOverlay.colors = edgeColors self.viewer.update() def showAllEdges(self): self.edgeOverlay.colors = None self.viewer.update() def setTool(self, tool=None): """Deactivates and destroys old tool, activates/sets new one if tool != None. `tool` can be either a tool object or 1 for the MapSearcher tool 2 for the ActivatePaintbrush 3 for the IntelligentScissors 4 for the SeedSelector""" if self._togglingGUI: return # no recursion please if self.tool: self.tool.disconnectViewer() if self.tool.parent() == self: self.tool.deleteLater() self.tool = None if tool == 1: self.tool = tools.MapSearcher(self.map, self) elif tool == 2: self.tool = tools.ActivePaintbrush(self.map, self) elif tool == 3: if self.edgeOverlay.color == QtCore.Qt.red: self.edgeOverlay.color = QtCore.Qt.black self.nodeOverlay.visible = False self.tool = tools.IntelligentScissors(self.map, self.edgeOverlay, self) if self._faceMeans: tools.activeCostMeasure = \ statistics.HyperbolicInverse(self._faceMeans.faceMeanDiff) elif tool == 4: self.tool = tools.SeedSelector(map=self.map, parent=self) elif hasattr(tool, "disconnectViewer"): self.tool = tool elif tool != None: print "setTool: invalid argument, tool deactivated now." print " give 1 for MapSearcher, 2 for ActivePaintbrush, 3 for IntelligentScissors, 4 for SeedSelector" tool = 0 self._togglingGUI = True self.ui.scissorsAction.setChecked(tool == 3) self.ui.paintbrushAction.setChecked(tool == 2) self.ui.navigateAction.setChecked(tool == 1) self._togglingGUI = False def activateScissors(self, onoff=True): self.setTool(onoff and 3 or None) def activatePaintbrush(self, onoff=True): self.setTool(onoff and 2 or None) def activateNavigator(self, onoff=True): self.setTool(onoff and 1 or None) def cleanupMap(self): removeCruft(self.map, 7) def createDartNavigator(self, dart, costMeasure, parent): """Factory for DartNavigator, allows to use more specialized dart navigators in subclasses.""" self.dn = DartNavigator(dart, costMeasure, self) def navigate(self, dart, center=True, costMeasure=None, new=False): if type(dart) == int: dart = self.map.dart(dart) elif hasattr(dart, "anchor"): dart = dart.anchor() elif hasattr(dart, "contours"): dart = list(dart.contours()) if new or not self.dn: self.createDartNavigator(dart, costMeasure, self) self.connect(self.dn, QtCore.SIGNAL("destroyed(QObject*)"), self._dartNavigatorDestroyed) else: if costMeasure: self.dn.costMeasure = costMeasure self.dn.setDart(dart) self.dn.show() if center: if isinstance(dart, (list, tuple)): dart = dart[0] self.viewer.center = dart[0] self.viewer.optimizeUpperLeft() def _enableImageActions(self): self.ui.displayBIAction.setEnabled("bi" in self.images) self.ui.displayOriginalAction.setEnabled("original" in self.images) self.ui.displayColoredAction.setEnabled("colored" in self.images) self.ui.displayLabelsAction.setEnabled(True) self.ui.displayMeansAction.setEnabled(bool(self._faceMeans)) def _setImage(self, image, normalize): self.image = image if hasattr(image, 'qimage'): qImage = image.qimage(normalize) else: qImage = qimage2ndarray.array2qimage(image, normalize) self.viewer.setImage(qImage) def setImage(self, image, normalize=None, role=None): """Replace displayed background image. You may pass role as one of ('original', 'colored', 'bi') to replace one of the predefined image slots (keyboard shortcuts 1-5).""" if role == None: if image.channels == 3: self.images["original"] = vigra.transformImage( # image, "\l x: RGB2Lab(x)")[0] image, "\l x: norm(x)/%r" % math.sqrt(3)) role = "colored" else: role = "original" self.images[role] = image self._enableImageActions() if normalize == None: normalize = self._normalizeStates[self._backgroundMode] if self.viewer and role == self.currentRole(): self._setImage(image, normalize) def highlight(self, darts): """highlight(darts) Highlight the given darts (can be any iterable returning labels or Dart objects).""" self._dh.highlight(darts) def saveFig(self, basepath, roi=None, scale=None, bgFilename=None, faceMeans=False, similarity=None, skipBorder=False): """Saves an XFig file as <basepath>.fig (and the pixel background as <basepath>_bg.png if `bgFilename` is not given) and returns the FigExporter object. If `roi` is not None, it determines the ROI to be saved. `roi` can be a BoundingBox or Rect2D object, a string like "10,10-40,30" (see fig.parseGeometry) or a tuple usable as Rect2D constructor arguments. You can also specify `roi` = True to export the visible image region. scale is the size of a pixel in fig units (450 = 1cm) and defaults to a size resulting in approx. 20cm width of the output file. If bgFilename is given, it is supposed to be a background filename for the picture box in the XFig file, or False if no background is desired.""" def addMapOverlayWithoutBorder(*args, **kwargs): kwargs["skipBorder"] = True return addMapOverlay(*args, **kwargs) overlayHandler = \ skipBorder and addMapOverlayWithoutBorder or addMapOverlay if bgFilename == None and faceMeans: bgFilename = False fe = figexport.exportImageWindow(self.viewer, basepath, roi=roi, scale=scale, bgFilename=bgFilename, overlayHandler=overlayHandler) if faceMeans in (None, True): faceMeans = self._faceMeans if faceMeans: fe.addMapFaces(self.map, faceMeans, similarity, depth=900) fe.f.save() return fe def saveEPS(self, basepath, *args, **kwargs): """display.saveEPS(basepath, roi = None, scale = None) Saves an XFig file as <basepath>.fig (see saveFig() documentation for details) and calls fig2dev to create an additional <basepath>.eps. Returns the final filename (result of calling fig.File.fig2dev).""" fe = self.saveFig(basepath, *args, **kwargs) return fe.f.fig2dev(lang="eps") def savePDF(self, basepath, *args, **kwargs): """display.savePDF(basepath, roi = None, scale = None) Saves an XFig file as <basepath>.fig (see saveFig() documentation for details) and calls fig2dev to create an additional <basepath>.pdf. Returns the final filename (result of calling fig.File.fig2dev).""" fe = self.saveFig(basepath, *args, **kwargs) return fe.f.fig2dev(lang="pdf")
def __init__(self, map, preparedImage = None, immediateShow = True, faceMeans = None): self.__base.__init__(self) self.tool = None self._togglingGUI = False self._faceMeans = None # temp. needed (for _enableImageActions) self.ui = Ui_DisplaySettings() self.ui.setupUi(self) # for backward compatibility: if hasattr(preparedImage, "imageSize") and hasattr(map, "width"): map, preparedImage = preparedImage, map elif preparedImage is None: preparedImage = map.labelImage() if preparedImage is None: preparedImage = vigra.ScalarImage(map.imageSize()) self.viewer = None # setImage would norm. pass the image on if hasattr(preparedImage, "orig"): self.images = { "original" : preparedImage.view, "colored" : preparedImage.orig, "bi" : preparedImage.bi.gm, } preparedImage = preparedImage.view else: self.images = {} # auto-detects role colored/original: self.setImage(preparedImage, normalize = False) self.image = preparedImage self.viewer = VigraQt.OverlayViewer(self) self.setCentralWidget(self.viewer) self._setImage(self.image, normalize = False) self.viewer.autoZoom() # convenience: self.addOverlay = self.viewer.addOverlay self.removeOverlay = self.viewer.removeOverlay # self.overlays = self.viewer.overlays self.map = map if not faceMeans and hasattr(map, "faceMeans"): faceMeans = map.faceMeans self.setFaceMeans(faceMeans) self._attachedHooks = None self._normalizeStates = [False, False, True, True, False] if self.image.channels == 3: self._backgroundMode = 1 self.ui.displayColoredAction.setChecked(True) else: self._backgroundMode = 0 self.ui.displayOriginalAction.setChecked(True) self._enableImageActions() self.connect(self.ui.backgroundGroup, QtCore.SIGNAL("selected(QAction*)"), self.setBackgroundMode) self.connect(self.ui.nodeDisplayAction, QtCore.SIGNAL("toggled(bool)"), self.toggleNodeDisplay) self.connect(self.ui.edgeDisplayAction, QtCore.SIGNAL("toggled(bool)"), self.toggleEdgeDisplay) self.connect(self.ui.normalizeAction, QtCore.SIGNAL("toggled(bool)"), self.toggleNormalize) self.connect(self.ui.mapCleanupAction, QtCore.SIGNAL("activated()"), self.cleanupMap) self.connect(self.ui.paintbrushAction, QtCore.SIGNAL("toggled(bool)"), self.activatePaintbrush) self.connect(self.ui.scissorsAction, QtCore.SIGNAL("toggled(bool)"), self.activateScissors) self.connect(self.ui.navigateAction, QtCore.SIGNAL("toggled(bool)"), self.activateNavigator) self.setWindowTitle("Map Display") self.edgeOverlay = MapEdges(self.viewer) self.edgeOverlay.setMap(map) self.edgeOverlay.setPen(QtCore.Qt.red) # protectedColor = QtCore.Qt.green, # protectedWidth = 2) self.viewer.addOverlay(self.edgeOverlay) self.edgeOverlay.visible = self.ui.edgeDisplayAction.isChecked() # self.nodeOverlay = MapNodes(map, QtCore.Qt.blue) # self.viewer.addOverlay(self.nodeOverlay) # self.nodeOverlay.visible = self.ui.nodeDisplayAction.isChecked() self._dh = DartHighlighter(map, self.viewer) self.dn = None if immediateShow: self.show()
class MapDisplay(QtGui.QMainWindow): __base = QtGui.QMainWindow # actually, this has only documenting effect; since this inherits # PyQt widgets, any attribute may be used: __slots__ = ("tool", "viewer", "images", "image", "map", "nodeOverlay", "edgeOverlay", "addOverlay", "removeOverlay", "_togglingGUI", "_backgroundMode", "_normalizeStates", "_faceMeans", "_attachedHooks", "dn", "_dh") def __init__(self, map, preparedImage = None, immediateShow = True, faceMeans = None): self.__base.__init__(self) self.tool = None self._togglingGUI = False self._faceMeans = None # temp. needed (for _enableImageActions) self.ui = Ui_DisplaySettings() self.ui.setupUi(self) # for backward compatibility: if hasattr(preparedImage, "imageSize") and hasattr(map, "width"): map, preparedImage = preparedImage, map elif preparedImage is None: preparedImage = map.labelImage() if preparedImage is None: preparedImage = vigra.ScalarImage(map.imageSize()) self.viewer = None # setImage would norm. pass the image on if hasattr(preparedImage, "orig"): self.images = { "original" : preparedImage.view, "colored" : preparedImage.orig, "bi" : preparedImage.bi.gm, } preparedImage = preparedImage.view else: self.images = {} # auto-detects role colored/original: self.setImage(preparedImage, normalize = False) self.image = preparedImage self.viewer = VigraQt.OverlayViewer(self) self.setCentralWidget(self.viewer) self._setImage(self.image, normalize = False) self.viewer.autoZoom() # convenience: self.addOverlay = self.viewer.addOverlay self.removeOverlay = self.viewer.removeOverlay # self.overlays = self.viewer.overlays self.map = map if not faceMeans and hasattr(map, "faceMeans"): faceMeans = map.faceMeans self.setFaceMeans(faceMeans) self._attachedHooks = None self._normalizeStates = [False, False, True, True, False] if self.image.channels == 3: self._backgroundMode = 1 self.ui.displayColoredAction.setChecked(True) else: self._backgroundMode = 0 self.ui.displayOriginalAction.setChecked(True) self._enableImageActions() self.connect(self.ui.backgroundGroup, QtCore.SIGNAL("selected(QAction*)"), self.setBackgroundMode) self.connect(self.ui.nodeDisplayAction, QtCore.SIGNAL("toggled(bool)"), self.toggleNodeDisplay) self.connect(self.ui.edgeDisplayAction, QtCore.SIGNAL("toggled(bool)"), self.toggleEdgeDisplay) self.connect(self.ui.normalizeAction, QtCore.SIGNAL("toggled(bool)"), self.toggleNormalize) self.connect(self.ui.mapCleanupAction, QtCore.SIGNAL("activated()"), self.cleanupMap) self.connect(self.ui.paintbrushAction, QtCore.SIGNAL("toggled(bool)"), self.activatePaintbrush) self.connect(self.ui.scissorsAction, QtCore.SIGNAL("toggled(bool)"), self.activateScissors) self.connect(self.ui.navigateAction, QtCore.SIGNAL("toggled(bool)"), self.activateNavigator) self.setWindowTitle("Map Display") self.edgeOverlay = MapEdges(self.viewer) self.edgeOverlay.setMap(map) self.edgeOverlay.setPen(QtCore.Qt.red) # protectedColor = QtCore.Qt.green, # protectedWidth = 2) self.viewer.addOverlay(self.edgeOverlay) self.edgeOverlay.visible = self.ui.edgeDisplayAction.isChecked() # self.nodeOverlay = MapNodes(map, QtCore.Qt.blue) # self.viewer.addOverlay(self.nodeOverlay) # self.nodeOverlay.visible = self.ui.nodeDisplayAction.isChecked() self._dh = DartHighlighter(map, self.viewer) self.dn = None if immediateShow: self.show() def __del__(self): # delete tool (which may reference the viewer & map) self.setTool(None) def _dartNavigatorDestroyed(self, dn): """HACK: this is needed because of the deleteLater()...""" # FIXME: cannot reliably detect identity of half-destructed # object anymore: #if dn is self.dn: self.dn = None def _labelImage(self): result = self.map.labelImage() if result is None: result = maputils.drawLabelImage(self.map) return result def setMap(self, map): attached = self.detachHooks() self.setTool(None) self.map = map self.edgeOverlay.setMap(map) #self.nodeOverlay.setMap(map) self._dh.setMap(map) updatedDisplayImage = None if self._backgroundMode == 3: updatedDisplayImage = self._labelImage() self._faceMeans = None if hasattr(map, "faceMeans"): self.setFaceMeans(map.faceMeans) if self._backgroundMode == 4: updatedDisplayImage = \ self._faceMeans.regionImage(self._labelImage()) self._enableImageActions() if updatedDisplayImage: self._setImage(updatedDisplayImage, self._normalizeStates[self._backgroundMode]) if attached: self.attachHooks() self.viewer.update() def setFaceMeans(self, faceMeans): self._faceMeans = faceMeans self._enableImageActions() def _adjustSize(self): pass # don't change window size out of a sudden def statusMessage(self, msg): # workaround - don't know why connecting the PYSIGNAL directly # does not work (maybe QtCore.SIGNAL("captionChanged(QString)") # would work?) self.statusBar().showMessage(msg) def currentRole(self): return ("original", "colored", "bi", "labels", "faceMeans")[ self._backgroundMode] def setBackgroundMode(self, mode): if type(mode) != int: mode = [self.ui.displayOriginalAction, self.ui.displayColoredAction, self.ui.displayBIAction, self.ui.displayLabelsAction, self.ui.displayMeansAction].index(mode) displayImage = None if mode == 0: displayImage = self.images["original"] elif mode == 1: displayImage = self.images["colored"] elif mode == 2: displayImage = self.images["bi"] elif mode == 3: displayImage = self._labelImage() elif mode == 4: displayImage = self._faceMeans.regionImage(self._labelImage()) else: sys.stderr.write("Unknown background mode %d!\n" % mode) return self.image = displayImage self._backgroundMode = mode normalize = self._normalizeStates[mode] if self.ui.normalizeAction.isChecked() != normalize: self.ui.normalizeAction.setChecked(normalize) else: self._setImage(self.image, normalize) def toggleNodeDisplay(self, onoff): return # TEMP if self.nodeOverlay.visible != onoff: self.nodeOverlay.visible = onoff self.viewer.update() def toggleEdgeDisplay(self, onoff): if self.edgeOverlay.visible != onoff: self.edgeOverlay.visible = onoff self.viewer.update() def toggleNormalize(self, normalize): self._normalizeStates[self._backgroundMode] = normalize self._setImage(self.image, normalize) def attachHooks(self): return self.nodeOverlay.attachHooks() self.edgeOverlay.attachHooks() self._attachedHooks = ( self.map.addMergeFacesCallbacks(None, self._postMergeFacesHook), self.map.addRemoveBridgeCallbacks(self._preRemoveBridgeHook, self._postRemoveBridgeHook)) def detachHooks(self): """Detaches / removes callbacks from the map's hooks. Returns True if successful, False if already detached.""" return results = (self.nodeOverlay.detachHooks(), self.edgeOverlay.detachHooks()) if self._attachedHooks: for h in self._attachedHooks: h.disconnect() self._attachedHooks = None if results == (True, True): return True else: if results == (False, False): return False def _redisplayROIImage(self, roi): roi &= Rect2D(self.map.imageSize()) roiImage = self._labelImage().subImage(roi) if self._backgroundMode > 3: roiImage = self._faceMeans.regionImage(roiImage) # FIXME: use global normalization here self.viewer.replaceROI(roiImage.toPNM(vigra.BYTE), QtCore.QPoint(*roi.upperLeft())) def _postMergeFacesHook(self, survivor): if self._backgroundMode < 3: return if survivor.label(): self._redisplayROIImage(intPos(survivor.boundingBox())) def _preRemoveBridgeHook(self, dart): if self._backgroundMode >= 3: self._bridgeROI = intPos(dart.edge().boundingBox()) return True def _postRemoveBridgeHook(self, dart): if self._backgroundMode < 3: return self._redisplayROIImage(self._bridgeROI) def showEvent(self, e): self.attachHooks() return self.__base.showEvent(self, e) def hideEvent(self, e): self.detachHooks() return self.__base.hideEvent(self, e) def showMarkedEdges(self, colorMarked = QtCore.Qt.green, colorUnmarked = None, markFlags = flag_constants.ALL_PROTECTION): edgeColors = [None] * self.map.maxEdgeLabel() for edge in self.map.edgeIter(): if edge.flag(markFlags): edgeColors[edge.label()] = colorMarked else: edgeColors[edge.label()] = colorUnmarked self.edgeOverlay.colors = edgeColors self.viewer.update() def showAllEdges(self): self.edgeOverlay.colors = None self.viewer.update() def setTool(self, tool = None): """Deactivates and destroys old tool, activates/sets new one if tool != None. `tool` can be either a tool object or 1 for the MapSearcher tool 2 for the ActivatePaintbrush 3 for the IntelligentScissors 4 for the SeedSelector""" if self._togglingGUI: return # no recursion please if self.tool: self.tool.disconnectViewer() if self.tool.parent() == self: self.tool.deleteLater() self.tool = None if tool == 1: self.tool = tools.MapSearcher(self.map, self) elif tool == 2: self.tool = tools.ActivePaintbrush(self.map, self) elif tool == 3: if self.edgeOverlay.color == QtCore.Qt.red: self.edgeOverlay.color = QtCore.Qt.black self.nodeOverlay.visible = False self.tool = tools.IntelligentScissors( self.map, self.edgeOverlay, self) if self._faceMeans: tools.activeCostMeasure = \ statistics.HyperbolicInverse(self._faceMeans.faceMeanDiff) elif tool == 4: self.tool = tools.SeedSelector(map = self.map, parent = self) elif hasattr(tool, "disconnectViewer"): self.tool = tool elif tool != None: print "setTool: invalid argument, tool deactivated now." print " give 1 for MapSearcher, 2 for ActivePaintbrush, 3 for IntelligentScissors, 4 for SeedSelector" tool = 0 self._togglingGUI = True self.ui.scissorsAction.setChecked(tool == 3) self.ui.paintbrushAction.setChecked(tool == 2) self.ui.navigateAction.setChecked(tool == 1) self._togglingGUI = False def activateScissors(self, onoff = True): self.setTool(onoff and 3 or None) def activatePaintbrush(self, onoff = True): self.setTool(onoff and 2 or None) def activateNavigator(self, onoff = True): self.setTool(onoff and 1 or None) def cleanupMap(self): removeCruft(self.map, 7) def createDartNavigator(self, dart, costMeasure, parent): """Factory for DartNavigator, allows to use more specialized dart navigators in subclasses.""" self.dn = DartNavigator(dart, costMeasure, self) def navigate(self, dart, center = True, costMeasure = None, new = False): if type(dart) == int: dart = self.map.dart(dart) elif hasattr(dart, "anchor"): dart = dart.anchor() elif hasattr(dart, "contours"): dart = list(dart.contours()) if new or not self.dn: self.createDartNavigator(dart, costMeasure, self) self.connect(self.dn, QtCore.SIGNAL("destroyed(QObject*)"), self._dartNavigatorDestroyed) else: if costMeasure: self.dn.costMeasure = costMeasure self.dn.setDart(dart) self.dn.show() if center: if isinstance(dart, (list, tuple)): dart = dart[0] self.viewer.center = dart[0] self.viewer.optimizeUpperLeft() def _enableImageActions(self): self.ui.displayBIAction.setEnabled("bi" in self.images) self.ui.displayOriginalAction.setEnabled("original" in self.images) self.ui.displayColoredAction.setEnabled("colored" in self.images) self.ui.displayLabelsAction.setEnabled(True) self.ui.displayMeansAction.setEnabled(bool(self._faceMeans)) def _setImage(self, image, normalize): self.image = image if hasattr(image, 'qimage'): qImage = image.qimage(normalize) else: qImage = qimage2ndarray.array2qimage(image, normalize) self.viewer.setImage(qImage) def setImage(self, image, normalize = None, role = None): """Replace displayed background image. You may pass role as one of ('original', 'colored', 'bi') to replace one of the predefined image slots (keyboard shortcuts 1-5).""" if role == None: if image.channels == 3: self.images["original"] = vigra.transformImage( # image, "\l x: RGB2Lab(x)")[0] image, "\l x: norm(x)/%r" % math.sqrt(3)) role = "colored" else: role = "original" self.images[role] = image self._enableImageActions() if normalize == None: normalize = self._normalizeStates[self._backgroundMode] if self.viewer and role == self.currentRole(): self._setImage(image, normalize) def highlight(self, darts): """highlight(darts) Highlight the given darts (can be any iterable returning labels or Dart objects).""" self._dh.highlight(darts) def saveFig(self, basepath, roi = None, scale = None, bgFilename = None, faceMeans = False, similarity = None, skipBorder = False): """Saves an XFig file as <basepath>.fig (and the pixel background as <basepath>_bg.png if `bgFilename` is not given) and returns the FigExporter object. If `roi` is not None, it determines the ROI to be saved. `roi` can be a BoundingBox or Rect2D object, a string like "10,10-40,30" (see fig.parseGeometry) or a tuple usable as Rect2D constructor arguments. You can also specify `roi` = True to export the visible image region. scale is the size of a pixel in fig units (450 = 1cm) and defaults to a size resulting in approx. 20cm width of the output file. If bgFilename is given, it is supposed to be a background filename for the picture box in the XFig file, or False if no background is desired.""" def addMapOverlayWithoutBorder(*args, **kwargs): kwargs["skipBorder"] = True return addMapOverlay(*args, **kwargs) overlayHandler = \ skipBorder and addMapOverlayWithoutBorder or addMapOverlay if bgFilename == None and faceMeans: bgFilename = False fe = figexport.exportImageWindow( self.viewer, basepath, roi = roi, scale = scale, bgFilename = bgFilename, overlayHandler = overlayHandler) if faceMeans in (None, True): faceMeans = self._faceMeans if faceMeans: fe.addMapFaces( self.map, faceMeans, similarity, depth = 900) fe.f.save() return fe def saveEPS(self, basepath, *args, **kwargs): """display.saveEPS(basepath, roi = None, scale = None) Saves an XFig file as <basepath>.fig (see saveFig() documentation for details) and calls fig2dev to create an additional <basepath>.eps. Returns the final filename (result of calling fig.File.fig2dev).""" fe = self.saveFig(basepath, *args, **kwargs) return fe.f.fig2dev(lang = "eps") def savePDF(self, basepath, *args, **kwargs): """display.savePDF(basepath, roi = None, scale = None) Saves an XFig file as <basepath>.fig (see saveFig() documentation for details) and calls fig2dev to create an additional <basepath>.pdf. Returns the final filename (result of calling fig.File.fig2dev).""" fe = self.saveFig(basepath, *args, **kwargs) return fe.f.fig2dev(lang = "pdf")
class DartNavigator(QtGui.QDialog): __base = QtGui.QDialog def __init__(self, dart, costMeasure, parent, name=None): self.__base.__init__(self, parent) if name: self.setObjectName(name) self.dart = dart self.costMeasure = costMeasure self.ui = Ui_DartNavigator() self.ui.setupUi(self) self.connect(self.ui.nextPhiButton, QtCore.SIGNAL("clicked()"), self.nextPhi) self.connect(self.ui.prevPhiButton, QtCore.SIGNAL("clicked()"), self.prevPhi) self.connect(self.ui.nextAlphaButton, QtCore.SIGNAL("clicked()"), self.nextAlpha) self.connect(self.ui.nextSigmaButton, QtCore.SIGNAL("clicked()"), self.nextSigma) self.connect(self.ui.prevSigmaButton, QtCore.SIGNAL("clicked()"), self.prevSigma) self.connect(self.ui.continuousCheckBox, QtCore.SIGNAL("toggled(bool)"), self.toggleContinuous) self.timer = QtCore.QTimer(self) self.connect(self.timer, QtCore.SIGNAL("timeout()"), self.highlightNext) self._darthighlighter = DartHighlighter(parent.map, parent.viewer) self.updateLabel() def closeEvent(self, e): self._darthighlighter.highlight(None) self.__base.closeEvent(self, e) if e.isAccepted(): self.deleteLater() # like QtCore.Qt.WDestructiveClose ;-) def highlightNext(self): self.activePerm() self.updateLabel() def setDart(self, dart): self.dart = dart self.updateLabel() def toggleContinuous(self, onoff): if onoff: self.timer.start(1200) else: self.timer.stop() self.ui.nextPhiButton.setToggleButton(onoff) self.ui.prevPhiButton.setToggleButton(onoff) self.ui.nextAlphaButton.setToggleButton(onoff) self.ui.nextSigmaButton.setToggleButton(onoff) self.ui.prevSigmaButton.setToggleButton(onoff) def moveDart(self, perm): perm() self.updateLabel() self.activePerm = perm def nextPhi(self): self.moveDart(self.dart.nextPhi) def prevPhi(self): self.moveDart(self.dart.prevPhi) def nextAlpha(self): self.moveDart(self.dart.nextAlpha) def nextSigma(self): self.moveDart(self.dart.nextSigma) def prevSigma(self): self.moveDart(self.dart.prevSigma) def updateLabel(self): self._darthighlighter.highlight(self.dart.label()) dartDesc = "Dart %d, length %.1f, partial area %.1f, %d points" % ( self.dart.label(), self.dart.edge().length(), self.dart.partialArea(), len(self.dart)) if self.costMeasure: dartDesc += "\nassociated cost: %s" % self.costMeasure(self.dart) self.ui.dartLabel.setText(dartDesc) for node, nodeLabel in ((self.dart.startNode(), self.ui.startNodeLabel), (self.dart.endNode(), self.ui.endNodeLabel)): nodeLabel.setText("Node %d (deg. %d)\nat %s" % (node.label(), node.degree(), node.position())) if self.dart.map().mapInitialized(): leftFace = self.dart.leftFace() rightFace = self.dart.rightFace() self.ui.faceLabel.setText( """Left: %s\nRight: %s""" % (str(leftFace)[8:-1], str(rightFace)[8:-1])) self.setWindowTitle("DartNavigator(%d)" % (self.dart.label(), )) self.emit(QtCore.SIGNAL('updateDart'), self.dart)