def __init__(self): super(TransferFunctionWidget, self).__init__() self.nodes = [] self.lines = [] self.histogram = Histogram() self.histogram.enabled = False # Create a histogram widget for the background of the transfer function editor self.histogramWidget = HistogramWidget() self.histogramWidget.setHistogram(self.histogram) self.histogramWidget.setAxeMode(bottom=HistogramWidget.AxeClear, left=HistogramWidget.AxeLog) self.histogramWidget.update() self.histogramWidget._histogramItem.delegate = self Style.styleWidgetForTab(self.histogramWidget) # Invisible item that catches mouse events on top of the histogram self.transferfunctionItem = TransferFunctionItem() self.transferfunctionItem.setZValue(250) self.transferfunctionItem.delegate = self self.histogramWidget.addItem(self.transferfunctionItem) # Create a widget for editing the selected node of the transfer function self.nodeItemWidget = NodeItemWidget() self.nodeItemWidget.setEnabled(False) self.nodeItemWidget.nodeUpdated.connect(self.updateNode) self.nodeItemWidget.removePoint.connect(self.removePoint) layout = QGridLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.histogramWidget, 0, 0) layout.addWidget(self.nodeItemWidget, 1, 0) self.setLayout(layout)
class TransferFunctionWidget(QWidget): """ TransferFunctionWidget """ valueChanged = Signal(object) def __init__(self): super(TransferFunctionWidget, self).__init__() self.nodes = [] self.lines = [] self.histogram = Histogram() self.histogram.enabled = False # Create a histogram widget for the background of the transfer function editor self.histogramWidget = HistogramWidget() self.histogramWidget.setHistogram(self.histogram) self.histogramWidget.setAxeMode(bottom=HistogramWidget.AxeClear, left=HistogramWidget.AxeLog) self.histogramWidget.update() self.histogramWidget._histogramItem.delegate = self Style.styleWidgetForTab(self.histogramWidget) # Invisible item that catches mouse events on top of the histogram self.transferfunctionItem = TransferFunctionItem() self.transferfunctionItem.setZValue(250) self.transferfunctionItem.delegate = self self.histogramWidget.addItem(self.transferfunctionItem) # Create a widget for editing the selected node of the transfer function self.nodeItemWidget = NodeItemWidget() self.nodeItemWidget.setEnabled(False) self.nodeItemWidget.nodeUpdated.connect(self.updateNode) self.nodeItemWidget.removePoint.connect(self.removePoint) layout = QGridLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.histogramWidget, 0, 0) layout.addWidget(self.nodeItemWidget, 1, 0) self.setLayout(layout) def setImageData(self, imageData): # Clear any previous nodes for node in self.nodes: self.histogramWidget.scene().removeItem(node) self.nodes = [] # Clear any previous lines for line in self.lines: self.histogramWidget.scene().removeItem(line) self.lines = [] bins = DataAnalyzer.histogramForData(imageData, 256) self.histogram.bins = bins self.histogram.enabled = True self.range = imageData.GetScalarRange() # Create and add nodes from the transfer function self.updateNodes() self.valueChanged.emit(self) def resizeEvent(self, event): self.histogramWidget.update() self.updateNodes() def updateNodes(self): for index in range(len(self.transferFunction.points)): point = self.transferFunction.points[index] if index < len(self.nodes): # Just update the node item nodeItem = self.nodes[index] else: # Create a new node item nodeItem = TransferFunctionNodeItem() nodeItem.setSceneBoundsItem(self.histogramWidget._histogramItem) nodeItem.setZValue(300) nodeItem.delegate = self self.histogramWidget.scene().addItem(nodeItem) self.nodes.append(nodeItem) nodeItem.setPosition([(point.value - self.range[0]) / (self.range[1] - self.range[0]), point.opacity]) nodeItem.updateColor(point.color) nodeItem.node = point if nodeItem.isSelected(): self.selectedNode(nodeItem) # Clean up redundant node items if len(self.nodes) > len(self.transferFunction.points): # Remove node items from scene for index in range(len(self.transferFunction.points), len(self.nodes)): nodeItem = self.nodes[index] self.histogramWidget.scene().removeItem(nodeItem) # Remove them from the nodes del self.nodes[len(self.transferFunction.points):] assert len(self.nodes) == len(self.transferFunction.points) self.updateLines() def updateLines(self): pen = QPen(QColor.fromHsl(0, 100, 100)) sortedNodes = sorted(self.nodes, key=lambda x: x.pos().x()) for index in range(len(self.nodes)-1): node = sortedNodes[index] nextNode = sortedNodes[index+1] if index < len(self.lines): # Just update the line segment lineItem = self.lines[index] else: # Create a new line segment lineItem = QGraphicsLineItem() lineItem.setZValue(250) lineItem.setPen(pen) self.histogramWidget.scene().addItem(lineItem) self.lines.append(lineItem) line = QLineF(node.pos(), nextNode.pos()) lineItem.setLine(line) # Clean up redundent lines if len(self.lines) >= len(self.nodes): # Remove the redundant line segments from the scene for index in range(len(self.nodes)-1, len(self.lines)): lineItem = self.lines[index] self.histogramWidget.scene().removeItem(lineItem) # Delete the line segments from the list del self.lines[len(self.nodes)-1:] assert len(self.lines) == len(self.nodes) - 1 self.histogramWidget._scene.update() def nodeUpdated(self, node): position = node.getPosition() index = self._indexForNode(node) self.transferFunction.updatePointAtIndex(index, position) self.nodeItemWidget.setNode(self.transferFunction.points[index]) self.updateNodes() self.valueChanged.emit(self) @Slot(object) def updateNode(self, point): index = self._indexForPoint(point) nodeItem = self.nodes[index] nodeItem.updateColor(point.color) self.transferFunction.updateTransferFunction() self.valueChanged.emit(self) def selectedNode(self, nodeItem): self.nodeItemWidget.setNode(nodeItem.node) def addNodeAtCoord(self, coord): self.transferFunction.addPointAtCoord(coord, [1, 1, 1]) self.updateNodes() self.valueChanged.emit(self) def removePoint(self, point): index = self._indexForPoint(point) self.transferFunction.removePointAtIndex(index) self.updateNodes() def unselect(self): for node in self.nodes: if node.isSelected(): node.setSelected(False) self.nodeItemWidget.setNode(None) self.updateNodes() def _indexForPoint(self, point): for index in range(len(self.transferFunction.points)): if point == self.transferFunction.points[index]: return index def _indexForNode(self, node): for index in range(len(self.nodes)): if node == self.nodes[index]: return index
class TransferFunctionWidget(QWidget): """ TransferFunctionWidget """ valueChanged = Signal(object) def __init__(self): super(TransferFunctionWidget, self).__init__() self.nodes = [] self.lines = [] self.histogram = Histogram() self.histogram.enabled = False # Create a histogram widget for the background of the transfer function editor self.histogramWidget = HistogramWidget() self.histogramWidget.setHistogram(self.histogram) self.histogramWidget.setAxeMode(bottom=HistogramWidget.AxeClear, left=HistogramWidget.AxeLog) self.histogramWidget.update() self.histogramWidget._histogramItem.delegate = self Style.styleWidgetForTab(self.histogramWidget) # Invisible item that catches mouse events on top of the histogram self.transferfunctionItem = TransferFunctionItem() self.transferfunctionItem.setZValue(250) self.transferfunctionItem.delegate = self self.histogramWidget.addItem(self.transferfunctionItem) # Create a widget for editing the selected node of the transfer function self.nodeItemWidget = NodeItemWidget() self.nodeItemWidget.setEnabled(False) self.nodeItemWidget.nodeUpdated.connect(self.updateNode) self.nodeItemWidget.removePoint.connect(self.removePoint) layout = QGridLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.histogramWidget, 0, 0) layout.addWidget(self.nodeItemWidget, 1, 0) self.setLayout(layout) def setImageData(self, imageData): # Clear any previous nodes for node in self.nodes: self.histogramWidget.scene().removeItem(node) self.nodes = [] # Clear any previous lines for line in self.lines: self.histogramWidget.scene().removeItem(line) self.lines = [] bins = DataAnalyzer.histogramForData(imageData, 256) self.histogram.bins = bins self.histogram.enabled = True self.range = imageData.GetScalarRange() # Create and add nodes from the transfer function self.updateNodes() self.valueChanged.emit(self) def resizeEvent(self, event): self.histogramWidget.update() self.updateNodes() def updateNodes(self): for index in range(len(self.transferFunction.points)): point = self.transferFunction.points[index] if index < len(self.nodes): # Just update the node item nodeItem = self.nodes[index] else: # Create a new node item nodeItem = TransferFunctionNodeItem() nodeItem.setSceneBoundsItem( self.histogramWidget._histogramItem) nodeItem.setZValue(300) nodeItem.delegate = self self.histogramWidget.scene().addItem(nodeItem) self.nodes.append(nodeItem) nodeItem.setPosition([(point.value - self.range[0]) / (self.range[1] - self.range[0]), point.opacity]) nodeItem.updateColor(point.color) nodeItem.node = point if nodeItem.isSelected(): self.selectedNode(nodeItem) # Clean up redundant node items if len(self.nodes) > len(self.transferFunction.points): # Remove node items from scene for index in range(len(self.transferFunction.points), len(self.nodes)): nodeItem = self.nodes[index] self.histogramWidget.scene().removeItem(nodeItem) # Remove them from the nodes del self.nodes[len(self.transferFunction.points):] assert len(self.nodes) == len(self.transferFunction.points) self.updateLines() def updateLines(self): pen = QPen(QColor.fromHsl(0, 100, 100)) sortedNodes = sorted(self.nodes, key=lambda x: x.pos().x()) for index in range(len(self.nodes) - 1): node = sortedNodes[index] nextNode = sortedNodes[index + 1] if index < len(self.lines): # Just update the line segment lineItem = self.lines[index] else: # Create a new line segment lineItem = QGraphicsLineItem() lineItem.setZValue(250) lineItem.setPen(pen) self.histogramWidget.scene().addItem(lineItem) self.lines.append(lineItem) line = QLineF(node.pos(), nextNode.pos()) lineItem.setLine(line) # Clean up redundent lines if len(self.lines) >= len(self.nodes): # Remove the redundant line segments from the scene for index in range(len(self.nodes) - 1, len(self.lines)): lineItem = self.lines[index] self.histogramWidget.scene().removeItem(lineItem) # Delete the line segments from the list del self.lines[len(self.nodes) - 1:] assert len(self.lines) == len(self.nodes) - 1 self.histogramWidget._scene.update() def nodeUpdated(self, node): position = node.getPosition() index = self._indexForNode(node) self.transferFunction.updatePointAtIndex(index, position) self.nodeItemWidget.setNode(self.transferFunction.points[index]) self.updateNodes() self.valueChanged.emit(self) @Slot(object) def updateNode(self, point): index = self._indexForPoint(point) nodeItem = self.nodes[index] nodeItem.updateColor(point.color) self.transferFunction.updateTransferFunction() self.valueChanged.emit(self) def selectedNode(self, nodeItem): self.nodeItemWidget.setNode(nodeItem.node) def addNodeAtCoord(self, coord): self.transferFunction.addPointAtCoord(coord, [1, 1, 1]) self.updateNodes() self.valueChanged.emit(self) def removePoint(self, point): index = self._indexForPoint(point) self.transferFunction.removePointAtIndex(index) self.updateNodes() def unselect(self): for node in self.nodes: if node.isSelected(): node.setSelected(False) self.nodeItemWidget.setNode(None) self.updateNodes() def _indexForPoint(self, point): for index in range(len(self.transferFunction.points)): if point == self.transferFunction.points[index]: return index def _indexForNode(self, node): for index in range(len(self.nodes)): if node == self.nodes[index]: return index