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 RenderInfoWidget(QWidget): """ RenderInfoWidget shows information about the loaded dataset. Things like filenames, range of data values, size of data, etc. """ def __init__(self): super(RenderInfoWidget, self).__init__() self.scrollArea = QScrollArea() self.scrollArea.setFrameShape(QFrame.NoFrame) self.scrollArea.setAutoFillBackground(False) self.scrollArea.setAttribute(Qt.WA_TranslucentBackground) self.scrollArea.setWidgetResizable(True) Style.styleWidgetForTab(self) Style.styleWidgetForTab(self.scrollArea) @Slot(basestring) def setFile(self, fileName): """ Slot that reads properties of the dataset and displays them in a few widgets. """ if fileName is None: return self.fileName = fileName # Read info from dataset # TODO: read out the real world dimensions in inch or cm # TODO: scalar type (int, float, short, etc.) imageReader = DataReader() imageData = imageReader.GetImageData(fileName) directory, name = os.path.split(fileName) dimensions = imageData.GetDimensions() minimum, maximum = imageData.GetScalarRange() scalarType = imageData.GetScalarTypeAsString() bins = DataAnalyzer.histogramForData(imageData, 256) self.histogram = Histogram() self.histogram.bins = bins self.histogram.enabled = True self.histogramWidget = HistogramWidget() self.histogramWidget.setMinimumHeight(100) self.histogramWidget.setHistogram(self.histogram) self.histogramWidget.setAxeMode(bottom=HistogramWidget.AxeClear, left=HistogramWidget.AxeLog) Style.styleWidgetForTab(self.histogramWidget) nameText = name dimsText = "(" + str(dimensions[0]) + ", " + str(dimensions[1]) + ", " + str(dimensions[2]) + ")" voxsText = str(dimensions[0] * dimensions[1] * dimensions[2]) rangText = "[" + str(minimum) + " : " + str(maximum) + "]" typeText = scalarType layout = self.layout() if not layout: # Create a new layout layout = QGridLayout() layout.setAlignment(Qt.AlignTop) # Create string representations nameLabels = [] nameLabels.append(QLabel("File name:")) nameLabels.append(QLabel("Dimensions:")) nameLabels.append(QLabel("Voxels:")) nameLabels.append(QLabel("Range:")) nameLabels.append(QLabel("Data type:")) for label in nameLabels: label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) # Create 'dynamic' labels self.labelTitle = QLabel(nameText) self.labelDimensions = QLabel(dimsText) self.labelVoxels = QLabel(voxsText) self.labelRange = QLabel(rangText) self.labelType = QLabel(typeText) index = 0 for label in nameLabels: layout.addWidget(label, index, 0) index += 1 layout.addWidget(self.labelTitle, 0, 1) layout.addWidget(self.labelDimensions, 1, 1) layout.addWidget(self.labelVoxels, 2, 1) layout.addWidget(self.labelRange, 3, 1) layout.addWidget(self.labelType, 4, 1) layout.addWidget(self.histogramWidget, 5, 0, 1, 2) widget = QWidget() widget.setLayout(layout) Style.styleWidgetForTab(widget) self.scrollArea.setWidget(widget) scrollLayout = QGridLayout() scrollLayout.setSpacing(0) scrollLayout.setContentsMargins(0, 0, 0, 0) scrollLayout.addWidget(self.scrollArea) self.setLayout(scrollLayout) else: # Just update the text for the 'dynamic' labels self.labelTitle.setText(nameText) self.labelDimensions.setText(dimsText) self.labelVoxels.setText(voxsText) self.labelRange.setText(rangText) self.labelType.setText(typeText)
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 RenderInfoWidget(QWidget): """ RenderInfoWidget shows information about the loaded dataset. Things like filenames, range of data values, size of data, etc. """ def __init__(self): super(RenderInfoWidget, self).__init__() Style.styleWidgetForTab(self) @Slot(basestring) def setFile(self, fileName): """ Slot that reads properties of the dataset and displays them in a few widgets. """ if fileName is None: return # Read info from dataset # TODO: read out the real world dimensions in inch or cm # TODO: scalar type (int, float, short, etc.) imageReader = DataReader() imageData = imageReader.GetImageData(fileName) directory, name = os.path.split(fileName) dimensions = imageData.GetDimensions() minimum, maximum = imageData.GetScalarRange() bins = DataAnalyzer.histogramForData(imageData, 256) self.histogram = Histogram() self.histogram.bins = bins self.histogram.enabled = True self.histogramWidget = HistogramWidget() self.histogramWidget.setHistogram(self.histogram) self.histogramWidget.setAxeMode(bottom=HistogramWidget.AxeClear, left=HistogramWidget.AxeLog) Style.styleWidgetForTab(self.histogramWidget) nameText = name dimsText = "(" + str(dimensions[0]) + ", " + str(dimensions[1]) + ", " + str(dimensions[2]) + ")" voxsText = str(dimensions[0] * dimensions[1] * dimensions[2]) rangText = "[" + str(minimum) + " : " + str(maximum) + "]" layout = self.layout() if not layout: # Create a new layout layout = QGridLayout() layout.setAlignment(Qt.AlignTop) # Create string representations nameField = QLabel("File name:") dimsField = QLabel("Dimensions:") voxsField = QLabel("Voxels:") rangField = QLabel("Range:") nameField.setAlignment(Qt.AlignRight | Qt.AlignVCenter) dimsField.setAlignment(Qt.AlignRight | Qt.AlignVCenter) voxsField.setAlignment(Qt.AlignRight | Qt.AlignVCenter) rangField.setAlignment(Qt.AlignRight | Qt.AlignVCenter) # Create 'dynamic' labels self.labelTitle = QLabel(nameText) self.labelDimensions = QLabel(dimsText) self.labelVoxels = QLabel(voxsText) self.labelRange = QLabel(rangText) layout.addWidget(nameField, 0, 0) layout.addWidget(dimsField, 1, 0) layout.addWidget(voxsField, 2, 0) layout.addWidget(rangField, 3, 0) layout.addWidget(self.labelTitle, 0, 1) layout.addWidget(self.labelDimensions, 1, 1) layout.addWidget(self.labelVoxels, 2, 1) layout.addWidget(self.labelRange, 3, 1) layout.addWidget(self.histogramWidget, 4, 0, 1, 2) self.setLayout(layout) else: # Just update the text for the 'dynamic' labels self.labelTitle.setText(nameText) self.labelDimensions.setText(dimsText) self.labelVoxels.setText(voxsText) self.labelRange.setText(rangText)