def contextMenuEvent(self, event): def correctWord(cursor, word): # From QTextCursor doc: # if there is a selection, the selection is deleted and replaced return lambda: cursor.insertText(word) popup_menu = self.createStandardContextMenu() # Spellcheck the word under mouse cursor, not self.textCursor cursor = self.cursorForPosition(event.pos()) cursor.select(QTextCursor.WordUnderCursor) text = cursor.selectedText() if self.speller and text: if not self.speller.check(text): lastAction = popup_menu.actions()[0] for word in self.speller.suggest(text)[:10]: action = QAction(word, popup_menu) action.triggered.connect(correctWord(cursor, word)) action.setFont(QFont("sans", weight=QFont.Bold)) popup_menu.insertAction(lastAction, action) popup_menu.insertSeparator(lastAction) popup_menu.exec_(event.globalPos())
def contextMenuEvent(self, event): def correctWord(cursor, word): # From QTextCursor doc: # if there is a selection, the selection is deleted and replaced return lambda: cursor.insertText(word) popup_menu = self.createStandardContextMenu() paste_action = popup_menu.actions()[6] paste_formatted_action = QAction(self.tr("Paste raw HTML"), popup_menu) paste_formatted_action.triggered.connect(self.insertFromRawHtml) paste_formatted_action.setShortcut(QKeySequence("Ctrl+Shift+V")) popup_menu.insertAction(paste_action, paste_formatted_action) # Spellcheck the word under mouse cursor, not self.textCursor cursor = self.cursorForPosition(event.pos()) cursor.select(QTextCursor.WordUnderCursor) text = cursor.selectedText() if self.speller and text: if not self.speller.check(text): lastAction = popup_menu.actions()[0] for word in self.speller.suggest(text)[:10]: action = QAction(word, popup_menu) action.triggered.connect(correctWord(cursor, word)) action.setFont(QFont(None, weight=QFont.Bold)) popup_menu.insertAction(lastAction, action) popup_menu.insertSeparator(lastAction) popup_menu.exec_(event.globalPos())
def createAction(self, text, slot, icon=None, signal='triggered()'): action = QAction(text, self) action.setFont(defaultFont) if icon: action.setIcon(icon) self.connect(action, SIGNAL(signal), slot) return action
def menuTitle(icon, text, parent): eventEater = EventEater() buttonaction = QAction(parent) font = buttonaction.font() font.setBold(True) buttonaction.setFont(font) buttonaction.setText(text) buttonaction.setIcon(icon) action = QWidgetAction(parent) action.setObjectName('trayMenuTitle') titleButton = QToolButton(parent) titleButton.installEventFilter(eventEater) titleButton.setDefaultAction(buttonaction) titleButton.setDown(True) titleButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) action.setDefaultWidget(titleButton) return action
class SimpleRichText(QTextEdit): def __init__(self, focusin, focusout): QTextEdit.__init__(self) self.focusin = focusin self.focusout = focusout self.createActions() #self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) def focusOutEvent ( self, event ): #print "focus out" self.focusout() def focusInEvent ( self, event ): self.focusin() def closeEvent(self, event): event.accept() def createActions(self): self.boldAct = QAction(self.tr("&Bold"), self) self.boldAct.setCheckable(True) self.boldAct.setShortcut(self.tr("Ctrl+B")) self.boldAct.setStatusTip(self.tr("Make the text bold")) self.connect(self.boldAct, SIGNAL("triggered()"), self.setBold) self.addAction(self.boldAct) boldFont = self.boldAct.font() boldFont.setBold(True) self.boldAct.setFont(boldFont) self.italicAct = QAction(self.tr("&Italic"), self) self.italicAct.setCheckable(True) self.italicAct.setShortcut(self.tr("Ctrl+I")) self.italicAct.setStatusTip(self.tr("Make the text italic")) self.connect(self.italicAct, SIGNAL("triggered()"), self.setItalic) self.addAction(self.italicAct) def setBold(self): format = QTextCharFormat() if self.boldAct.isChecked(): weight = QFont.Bold else: weight = QFont.Normal format.setFontWeight(weight) self.setFormat(format) def setItalic(self): format = QTextCharFormat() #format.setFontItalic(self.__italic.isChecked()) format.setFontItalic(self.italicAct.isChecked()) self.setFormat(format) def setUnderline(self): format = QTextCharFormat() format.setFontUnderline(self.__underline.isChecked()) self.setFormat(format) def setFormat(self, format): self.textCursor().mergeCharFormat(format) self.mergeCurrentCharFormat(format) def bold(self): print("bold") def italic(self): print("italic")
class Viewer(QMainWindow): """High-level API to view multi-dimensional arrays. Properties: title -- window title """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) uiDirectory = os.path.split(volumina.__file__)[0] if uiDirectory == '': uiDirectory = '.' loadUi(uiDirectory + '/viewer.ui', self) self._dataShape = None self._viewerInitialized = False self.editor = None self.viewingWidget = None self.actionQuit.triggered.connect(qApp.quit) #when connecting in renderScreenshot to a partial(...) function, #we need to remember the created function to be able to disconnect #to it later self._renderScreenshotDisconnect = None self.initLayerstackModel() self.actionCurrentView = QAction(QIcon(), "Only for selected view", self.menuView) f = self.actionCurrentView.font() f.setBold(True) self.actionCurrentView.setFont(f) # Lazy import here to prevent this module from ignoring volumine.NO3D flag. from volumina.volumeEditor import VolumeEditor self.editor = VolumeEditor(self.layerstack, parent=self) #make sure the layer stack widget, which is the right widget #managed by the splitter self.splitter shows up correctly #TODO: find a proper way of doing this within the designer def adjustSplitter(): s = self.splitter.sizes() s = [int(0.66 * s[0]), s[0] - int(0.66 * s[0])] self.splitter.setSizes(s) QTimer.singleShot(0, adjustSplitter) @property def title(self): return self.windowTitle() @title.setter def title(self, t): self.setWindowTitle(t) def initLayerstackModel(self): self.layerstack = LayerStackModel() self.layerWidget.init(self.layerstack) model = self.layerstack self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled) @property def dataShape(self): return self._dataShape @dataShape.setter def dataShape(self, s): if s is None: return assert len(s) == 5 self._dataShape = s self.editor.dataShape = s if not self._viewerInitialized: self._viewerInitialized = True self.viewer.init(self.editor) #make sure the data shape is correctly set #(some signal/slot connections may be set up in the above init) self.editor.dataShape = s #FIXME: this code is broken #if its 2D, maximize the corresponding window #if len([i for i in list(self.dataShape)[1:4] if i == 1]) == 1: # viewAxis = [i for i in range(1,4) if self.dataShape[i] == 1][0] - 1 # self.viewer.quadview.switchMinMax(viewAxis) def addGrayscaleLayer(self, a, name=None, direct=False): source, self.dataShape = createDataSource(a, True) layer = GrayscaleLayer(source, direct=direct) layer.numberOfChannels = self.dataShape[-1] if name: layer.name = name self.layerstack.append(layer) return layer def addAlphaModulatedLayer(self, a, name=None): source, self.dataShape = createDataSource(a, True) layer = AlphaModulatedLayer(source) if name: layer.name = name self.layerstack.append(layer) return layer def addRGBALayer(self, a, name=None): assert a.shape[2] >= 3 sources = [None, None, None, None] for i in range(3): sources[i], self.dataShape = createDataSource(a[..., i], True) if (a.shape[-1] >= 4): sources[3], self.dataShape = createDataSource(a[..., 3], True) layer = RGBALayer(sources[0], sources[1], sources[2], sources[3]) if name: layer.name = name self.layerstack.append(layer) return layer def addRandomColorsLayer(self, a, name=None, direct=False): layer = self.addColorTableLayer(a, name, colortable=None, direct=direct) layer.colortableIsRandom = True layer.zeroIsTransparent = True return layer def addColorTableLayer(self, a, name=None, colortable=None, direct=False, clickFunctor=None): if colortable is None: colortable = self._randomColors() source, self.dataShape = createDataSource(a, True) if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct) if name: layer.name = name self.layerstack.append(layer) return layer def addRelabelingColorTableLayer(self, a, name=None, relabeling=None, colortable=None, direct=False, clickFunctor=None, right=True): if colortable is None: colortable = self._randomColors() source = RelabelingArraySource(a) if relabeling is None: source.setRelabeling(numpy.zeros(numpy.max(a) + 1, dtype=a.dtype)) else: source.setRelabeling(relabeling) if colortable is None: colortable = [QColor(0, 0, 0, 0).rgba(), QColor(255, 0, 0).rgba()] if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct, right=right) if name: layer.name = name self.layerstack.append(layer) return (layer, source) def addClickableSegmentationLayer(self, a, name=None, direct=False, colortable=None, reuseColors=True): return ClickableSegmentationLayer(a, self, name=name, direct=direct, colortable=colortable, reuseColors=reuseColors) def _randomColors(self, M=256): """Generates a pleasing color table with M entries.""" colors = [] for i in range(M): if i == 0: colors.append(QColor(0, 0, 0, 0).rgba()) else: h, s, v = random.random(), random.random(), 1.0 color = numpy.asarray(colorsys.hsv_to_rgb(h, s, v)) * 255 qColor = QColor(*color) colors.append(qColor.rgba()) #for the first 16 objects, use some colors that are easily distinguishable colors[1:17] = colortables.default16 return colors
class Viewer(QMainWindow): """High-level API to view multi-dimensional arrays. Properties: title -- window title """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) uiDirectory = os.path.split(volumina.__file__)[0] if uiDirectory == '': uiDirectory = '.' loadUi(uiDirectory + '/viewer.ui', self) self._dataShape = None self._viewerInitialized = False self.editor = None self.viewingWidget = None self.actionQuit.triggered.connect(qApp.quit) #when connecting in renderScreenshot to a partial(...) function, #we need to remember the created function to be able to disconnect #to it later self._renderScreenshotDisconnect = None self.initLayerstackModel() self.actionCurrentView = QAction(QIcon(), "Only for selected view", self.menuView) f = self.actionCurrentView.font() f.setBold(True) self.actionCurrentView.setFont(f) # Lazy import here to prevent this module from ignoring volumine.NO3D flag. from volumina.volumeEditor import VolumeEditor self.editor = VolumeEditor(self.layerstack, parent=self) #make sure the layer stack widget, which is the right widget #managed by the splitter self.splitter shows up correctly #TODO: find a proper way of doing this within the designer def adjustSplitter(): s = self.splitter.sizes() s = [int(0.66*s[0]), s[0]-int(0.66*s[0])] self.splitter.setSizes(s) QTimer.singleShot(0, adjustSplitter) @property def title(self): return self.windowTitle() @title.setter def title(self, t): self.setWindowTitle(t) def initLayerstackModel(self): self.layerstack = LayerStackModel() self.layerWidget.init(self.layerstack) model = self.layerstack self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled) @property def dataShape(self): return self._dataShape @dataShape.setter def dataShape(self, s): if s is None: return assert len(s) == 5 self._dataShape = s self.editor.dataShape = s if not self._viewerInitialized: self._viewerInitialized = True self.viewer.init(self.editor) #make sure the data shape is correctly set #(some signal/slot connections may be set up in the above init) self.editor.dataShape = s #FIXME: this code is broken #if its 2D, maximize the corresponding window #if len([i for i in list(self.dataShape)[1:4] if i == 1]) == 1: # viewAxis = [i for i in range(1,4) if self.dataShape[i] == 1][0] - 1 # self.viewer.quadview.switchMinMax(viewAxis) def addGrayscaleLayer(self, a, name=None, direct=False): source,self.dataShape = createDataSource(a,True) layer = GrayscaleLayer(source, direct=direct) layer.numberOfChannels = self.dataShape[-1] if name: layer.name = name self.layerstack.append(layer) return layer def addAlphaModulatedLayer(self, a, name=None, **kwargs): source,self.dataShape = createDataSource(a,True) layer = AlphaModulatedLayer(source, **kwargs) if name: layer.name = name self.layerstack.append(layer) return layer def addRGBALayer(self, a, name=None): assert a.shape[2] >= 3 sources = [None, None, None,None] for i in range(3): sources[i], self.dataShape = createDataSource(a[...,i], True) if(a.shape[-1] >= 4): sources[3], self.dataShape = createDataSource(a[...,3], True) layer = RGBALayer(sources[0],sources[1],sources[2], sources[3]) if name: layer.name = name self.layerstack.append(layer) return layer def addRandomColorsLayer(self, a, name=None, direct=False): layer = self.addColorTableLayer(a, name, colortable=None, direct=direct) layer.colortableIsRandom = True layer.zeroIsTransparent = True return layer def addColorTableLayer(self, a, name=None, colortable=None, direct=False, clickFunctor=None): if colortable is None: colortable = self._randomColors() source,self.dataShape = createDataSource(a,True) if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct) if name: layer.name = name self.layerstack.append(layer) return layer def addRelabelingColorTableLayer(self, a, name=None, relabeling=None, colortable=None, direct=False, clickFunctor=None, right=True): if colortable is None: colortable = self._randomColors() source = RelabelingArraySource(a) if relabeling is None: source.setRelabeling(numpy.zeros(numpy.max(a)+1, dtype=a.dtype)) else: source.setRelabeling(relabeling) if colortable is None: colortable = [QColor(0,0,0,0).rgba(), QColor(255,0,0).rgba()] if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct, right=right) if name: layer.name = name self.layerstack.append(layer) return (layer, source) def addClickableSegmentationLayer(self, a, name=None, direct=False, colortable=None, reuseColors=True): return ClickableSegmentationLayer(a, self, name=name, direct=direct, colortable=colortable, reuseColors=reuseColors) def _randomColors(self, M=256): """Generates a pleasing color table with M entries.""" colors = [] for i in range(M): if i == 0: colors.append(QColor(0, 0, 0, 0).rgba()) else: h, s, v = random.random(), random.random(), 1.0 color = numpy.asarray(colorsys.hsv_to_rgb(h, s, v)) * 255 qColor = QColor(*color) colors.append(qColor.rgba()) #for the first 16 objects, use some colors that are easily distinguishable colors[1:17] = colortables.default16 return colors
class Viewer(QMainWindow): """High-level API to view multi-dimensional arrays. Properties: title -- window title """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) uiDirectory = os.path.split(volumina.__file__)[0] if uiDirectory == '': uiDirectory = '.' loadUi(uiDirectory + '/viewer.ui', self) self._dataShape = None self._viewerInitialized = False self.editor = None self.viewingWidget = None self.actionQuit.triggered.connect(qApp.quit) #when connecting in renderScreenshot to a partial(...) function, #we need to remember the created function to be able to disconnect #to it later self._renderScreenshotDisconnect = None self.initLayerstackModel() self.actionCurrentView = QAction(QIcon(), "Only for selected view", self.menuView) f = self.actionCurrentView.font() f.setBold(True) self.actionCurrentView.setFont(f) self.editor = VolumeEditor(self.layerstack) #make sure the layer stack widget, which is the right widget #managed by the splitter self.splitter shows up correctly #TODO: find a proper way of doing this within the designer def adjustSplitter(): s = self.splitter.sizes() s = [int(0.66*s[0]), s[0]-int(0.66*s[0])] self.splitter.setSizes(s) QTimer.singleShot(0, adjustSplitter) def initLayerstackModel(self): self.layerstack = LayerStackModel() self.layerWidget.init(self.layerstack) model = self.layerstack self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled) @property def dataShape(self): return self._dataShape @dataShape.setter def dataShape(self, s): if s is None: return assert len(s) == 5 self._dataShape = s self.editor.dataShape = s if not self._viewerInitialized: self._viewerInitialized = True self.viewer.init(self.editor) #make sure the data shape is correctly set #(some signal/slot connections may be set up in the above init) self.editor.dataShape = s #if its 2D, maximize the corresponding window if len([i for i in list(self.dataShape)[1:4] if i == 1]) == 1: viewAxis = [i for i in range(1,4) if self.dataShape[i] != 1][0] - 1 self.viewer.quadview.switchMinMax(viewAxis) def addGrayscaleLayer(self, a, name=None, direct=False): source,self.dataShape = createDataSource(a,True) layer = GrayscaleLayer(source, direct=direct) if name: layer.name = name self.layerstack.append(layer) return layer def addAlphaModulatedLayer(self, a, name=None): source,self.dataShape = createDataSource(a,True) layer = AlphaModulatedLayer(source) if name: layer.name = name self.layerstack.append(layer) return layer def addRGBALayer(self, a, name=None): source,self.dataShape = createDataSource(a,True) layer = RGBALayer(source[0],source[1],source[2]) if name: layer.name = name self.layerstack.append(layer) return layer def addRandomColorsLayer(self, a, name=None, direct=False): layer = self.addColorTableLayer(a, name, colortable=None, direct=direct) layer.colortableIsRandom = True layer.zeroIsTransparent = True return layer def addColorTableLayer(self, a, name=None, colortable=None, direct=False, clickFunctor=None): if colortable is None: colortable = self._randomColors() source,self.dataShape = createDataSource(a,True) if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct) if name: layer.name = name self.layerstack.append(layer) return layer def addRelabelingColorTableLayer(self, a, name=None, relabeling=None, colortable=None, direct=False, clickFunctor=None): if colortable is None: colortable = self._randomColors() source = RelabelingArraySource(a) if relabeling is None: source.setRelabeling(numpy.zeros(numpy.max(a)+1, dtype=a.dtype)) else: source.setRelabeling(relabeling) if colortable is None: colortable = [QColor(0,0,0,0).rgba(), QColor(255,0,0).rgba()] if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct) if name: layer.name = name self.layerstack.append(layer) return (layer, source) def addClickableSegmentationLayer(self, a, name=None, direct=False): M = a.max() clickedObjects = dict() #maps from object to the label that is used for it usedLabels = set() def onClick(layer, pos5D, pos): obj = layer.data.originalData[pos5D] if obj in clickedObjects: layer._datasources[0].setRelabelingEntry(obj, 0) usedLabels.remove( clickedObjects[obj] ) del clickedObjects[obj] else: labels = sorted(list(usedLabels)) #find first free entry if labels: for l in range(1, labels[-1]+2): if l not in labels: break assert l not in usedLabels else: l = 1 usedLabels.add(l) clickedObjects[obj] = l layer._datasources[0].setRelabelingEntry(obj, l) colortable = volumina.layer.generateRandomColors(1000, "hsv", {"v": 1.0}, zeroIsTransparent=True) layer, source = self.addRelabelingColorTableLayer(a, clickFunctor=onClick, name=None, relabeling=numpy.zeros(M+1, dtype=a.dtype), colortable=colortable, direct=direct) if name is not None: layer.name = name layer.zeroIsTransparent = True layer.colortableIsRandom = True return layer def _randomColors(self, M=256): """Generates a pleasing color table with M entries.""" colors = [] for i in range(M): if i == 0: colors.append(QColor(0, 0, 0, 0).rgba()) else: h, s, v = random.random(), random.random(), 1.0 color = numpy.asarray(colorsys.hsv_to_rgb(h, s, v)) * 255 qColor = QColor(*color) colors.append(qColor.rgba()) return colors
class Viewer(QMainWindow): """High-level API to view multi-dimensional arrays. Properties: title -- window title """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) uiDirectory = os.path.split(volumina.__file__)[0] if uiDirectory == '': uiDirectory = '.' loadUi(uiDirectory + '/viewer.ui', self) self._dataShape = None self.editor = None self.actionQuit.triggered.connect(qApp.quit) #when connecting in renderScreenshot to a partial(...) function, #we need to remember the created function to be able to disconnect #to it later self._renderScreenshotDisconnect = None self.initLayerstackModel() self.actionCurrentView = QAction(QIcon(), \ "Only for selected view", self.menuView) f = self.actionCurrentView.font() f.setBold(True) self.actionCurrentView.setFont(f) #make sure the layer stack widget, which is the right widget #managed by the splitter self.splitter shows up correctly #TODO: find a proper way of doing this within the designer def adjustSplitter(): s = self.splitter.sizes() s = [int(0.66*s[0]), s[0]-int(0.66*s[0])] self.splitter.setSizes(s) QTimer.singleShot(0, adjustSplitter) def initLayerstackModel(self): self.layerstack = LayerStackModel() self.layerWidget.init(self.layerstack) model = self.layerstack self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled) def renderScreenshot(self, axis, blowup=1, filename="/tmp/volumina_screenshot.png"): """Save the complete slice as shown by the slice view 'axis' in the GUI as an image axis -- 0, 1, 2 (x, y, or z slice view) blowup -- enlarge written image by this factor filename -- output file """ print "Rendering screenshot for axis=%d to '%s'" % (axis, filename) s = self.editor.imageScenes[axis] self.editor.navCtrl.enableNavigation = False func = partial(self._renderScreenshot, s, blowup, filename) self._renderScreenshotDisconnect = func s._renderThread.patchAvailable.connect(func) nRequested = 0 for patchNumber in range(len(s._tiling)): p = s.tileProgress(patchNumber) if p < 1.0: s.requestPatch(patchNumber) nRequested += 1 print " need to compute %d of %d patches" % (nRequested, len(s._tiling)) if nRequested == 0: #If no tile needed to be requested, the 'patchAvailable' signal #of the render thread will never come. #In this case, we need to call the implementation ourselves: self._renderScreenshot(s, blowup, filename, patchNumber=0) def addLayer(self, a, display='grayscale', opacity=1.0, \ name='Unnamed Layer', visible=True, interpretChannelsAs=None): print "adding layer '%s', shape=%r, %r" % (name, a.shape, type(a)) """Adds a new layer on top of the layer stack (such that it will be above all currently defined layers). The array 'a' may be a simple numpy.ndarray or implicitly defined via a LazyflowArraySource. Returns the created Layer object. The layer can either be removed by passing this object to self.removeLayer, or by giving a unique name. """ aSlices = None #in case a needs to be split by a lazyflow operator if len(a.shape) not in [2,3,5]: raise RuntimeError("Cannot interpret array with: shape=%r" \ % a.shape) volumeImage = True if len(a.shape) == 2: volumeImage = False if len(a.shape) == 3 and a.shape[2] == 3 and interpretChannelsAs == 'RGB': volumeImage = False viewerType = "volume 5D" if not volumeImage: viewerType = "image 2D" print " treating as %s" % viewerType aType = None if 'lazyflow' in a.__class__.__module__: aType = 'lazyflow' elif hasattr(a, 'axistags'): aType = 'vigra' elif isinstance(a, numpy.ndarray): aType = 'numpy' else: aType = 'generic' # # construct a canonical form for arrays: # # 2D: x,y,c (to be viewed with image viewer) # 5D: t,x,y,z,c (to be viewed with volume viewer) # #convert from LAZYFLOW if aType == 'lazyflow': Source = LazyflowSource if volumeImage: if len(a.shape) < 5: o = Op5ifyer(a.operator.graph) o.inputs['Input'].connect(a) a = o.outputs['Output'] elif not volumeImage: o = OpMultiArraySlicer(a.operator.graph) o.inputs['Input'].connect(a) o.inputs['AxisFlag'].setValue('c') aSlices = o.outputs['Slices'] class A: pass a = A(); a.shape = aSlices[0].shape #convert from VIGRANUMPY ARRAY elif aType == 'vigra': if volumeImage: #vigra array with axistags a = a.withAxes('t', 'x', 'y', 'z', 'c').view(numpy.ndarray) else: a = a.withAxes('x', 'y', 'c').view(numpy.ndarray) Source = ArraySource #convert from NUMPY ARRAY elif aType == 'numpy': if volumeImage: if len(a.shape) == 3: a = a[numpy.newaxis, ..., numpy.newaxis] elif len(a.shape) != 5: raise RuntimeError("Cannot interpret numpy array with shape %r as 5D volume" % (a.shape,)) Source = ArraySource #convert from GENERIC ARRAY elif aType == 'generic': # not a numpy array. Maybe h5py or something else. Embed it. if(hasattr(a, 'dtype')): a = Array5d(a, dtype=a.dtype) else: a = Array5d(a, dtype=np.uint8) Source = ArraySource # # initialize the proper viewer (2D or 5D) # if not volumeImage: init = self._initImageViewing shape = a.shape[0:2] #2D instance = ImageEditor else: init = self._initVolumeViewing shape = a.shape #5D instance = VolumeEditor if self.editor is None or self.editor.dataShape != shape: if self.editor: print " new %s layer '%s', shape %r is not compatible with existing shape %r" % (viewerType, name, shape, self.editor.dataShape) if not isinstance(self.editor, instance) or self.editor is None: init() self.editor.dataShape = shape print " --> resetting viewer to shape=%r and zero layers" % (self.editor.dataShape,) self.layerstack.clear() # # create layer # if display == 'grayscale': if interpretChannelsAs == None: source = Source(a) type_info = numpy.iinfo(a.dtype) if type_info.min < 0: print "WARNING: datatype is not bound to semi-positive values" layer = GrayscaleLayer(source, range=(0, type_info.max)) elif interpretChannelsAs == "RGB": if aSlices is not None: layer = RGBALayer(LazyflowSource(aSlices[0]), LazyflowSource(aSlices[1]), LazyflowSource(aSlices[2])) else: assert len(a.shape) == 3 layer = RGBALayer(Source(a[:,:,0]), Source(a[:,:,1]), Source(a[:,:,2])) elif display == 'randomcolors': if a.dtype != numpy.uint8: print "layer '%s': implicit conversion from %s to uint8" \ % (name, a.dtype) if a.dtype == numpy.uint32: a = a.astype(numpy.uint8) else: raise RuntimeError("unhandled dtype=%r" % a.dtype) source = Source(a) layer = ColortableLayer(source, self._randomColors()) else: raise RuntimeError("unhandled type of overlay") layer.name = name layer.opacity = opacity layer.visible = visible self.layerstack.append(layer) return layer def removeLayer(self, layer): """Remove layer either by given 'Layer' object (as returned by self.addLayer), or by it's name string (as given to the name parameter in self.addLayer)""" if isinstance(layer, Layer): idx = self.layerstack.layerIndex(layer) self.layerstack.removeRows(idx, 1) else: idx = [i for i in range(len(self.layerstack)) if \ self.layerstack.data(self.layerstack.index(i)).name == layer] if len(idx) > 1: raise RuntimeError("Trying to remove layer '%s', whose name is" "ambigous as it refers to %d layers" % len(idx)) return False self.layerstack.removeRows(idx[0], 1) return True @property def title(self): """Get the window title""" return self.windowTitle() @title.setter def title(self, t): """Set the window title""" self.setWindowTitle(t) ### private implementations def _initVolumeViewing(self): self.initLayerstackModel() self.editor = VolumeEditor(self.layerstack, labelsink=None) if not isinstance(self.viewer, VolumeEditorWidget) or self.viewer.editor is None: splitterSizes = self.splitter.sizes() self.viewer.setParent(None) del self.viewer self.viewer = VolumeEditorWidget() self.splitter.insertWidget(0, self.viewer) self.splitter.setSizes(splitterSizes) self.viewer.init(self.editor) w = self.viewer self.menuView.addAction(w.allZoomToFit) self.menuView.addAction(w.allToggleHUD) self.menuView.addAction(w.allCenter) self.menuView.addSeparator() self.menuView.addAction(self.actionCurrentView) self.menuView.addAction(w.selectedZoomToFit) self.menuView.addAction(w.toggleSelectedHUD) self.menuView.addAction(w.selectedCenter) self.menuView.addAction(w.selectedZoomToOriginal) self.menuView.addAction(w.rubberBandZoom) self.editor.newImageView2DFocus.connect(self._setIconToViewMenu) def _initImageViewing(self): if not isinstance(self.viewer, ImageEditorWidget): self.initLayerstackModel() w = self.viewer if isinstance(w, VolumeEditor) and w.editor is not None: self.menuView.removeAction(w.allZoomToFit) self.menuView.removeAction(w.allToggleHUD) self.menuView.removeAction(w.allCenter) self.menuView.removeAction(self.actionCurrentView) self.menuView.removeAction(w.selectedZoomToFit) self.menuView.removeAction(w.toggleSelectedHUD) self.menuView.removeAction(w.selectedCenter) self.menuView.removeAction(w.selectedZoomToOriginal) self.menuView.removeAction(w.rubberBandZoom) #remove 3D viewer splitterSizes = self.splitter.sizes() self.viewer.setParent(None) del self.viewer self.viewer = ImageEditorWidget() self.editor = ImageEditor(layerStackModel=self.layerstack) self.viewer.init(self.editor) self.splitter.insertWidget(0, self.viewer) self.splitter.setSizes(splitterSizes) if not _has_lazyflow: self.viewer.setEnabled(False) def _renderScreenshot(self, s, blowup, filename, patchNumber): progress = 0 for patchNumber in range(len(s._tiling)): p = s.tileProgress(patchNumber) progress += p progress = progress/float(len(s._tiling)) if progress == 1.0: s._renderThread.patchAvailable.disconnect(self._renderScreenshotDisconnect) img = QImage(int(round((blowup*s.sceneRect().size().width()))), int(round((blowup*s.sceneRect().size().height()))), QImage.Format_ARGB32) screenshotPainter = QPainter(img) screenshotPainter.setRenderHint(QPainter.Antialiasing, True) s.render(screenshotPainter, QRectF(0, 0, img.width()-1, img.height()-1), s.sceneRect()) print " saving to '%s'" % filename img.save(filename) del screenshotPainter self.editor.navCtrl.enableNavigation = True def _setIconToViewMenu(self): focused = self.editor.imageViews[self.editor._lastImageViewFocus] self.actionCurrentView.setIcon(\ QIcon(focused._hud.axisLabel.pixmap())) def _randomColors(self, M=256): """Generates a pleasing color table with M entries.""" colors = [] for i in range(M): if i == 0: colors.append(QColor(0, 0, 0, 0).rgba()) else: h, s, v = random.random(), random.random(), 1.0 color = numpy.asarray(colorsys.hsv_to_rgb(h, s, v)) * 255 qColor = QColor(*color) colors.append(qColor.rgba()) return colors def show(self): if not _has_lazyflow: popUp = QMessageBox(parent=self) popUp.setTextFormat(1) popUp.setText("<font size=\"4\"> Lazyflow could not be imported:</font> <br><br><b><font size=\"4\" color=\"#8A0808\">%s</font></b>"%(exceptStr)) popUp.show() popUp.exec_() QMainWindow.show(self)