def create_scrolled_panel(self, parent): """ Returns a panel that can scroll its contents. """ sa = QScrollArea(check_parent(parent)) sa.setFrameShape(QFrame.NoFrame) sa.setWidgetResizable(True) return control_adapter_for(sa)
class SimpleEditor ( Editor ): """ Simple style of editor for lists, which displays a scrolling list box with only one item visible at a time. A icon next to the list box displays a menu of operations on the list. """ #-- Class Constants -------------------------------------------------------- # Whether the list is displayed in a single row: single_row = True # Menu for modifying the list list_menu = """ Add &Before [_menu_before]: self.add_before() Add &After [_menu_after]: self.add_after() --- &Delete [_menu_delete]: self.delete_item() --- Move &Up [_menu_up]: self.move_up() Move &Down [_menu_down]: self.move_down() Move to &Top [_menu_top]: self.move_top() Move to &Bottom [_menu_bottom]: self.move_bottom() """ empty_list_menu = """ Add: self.add_empty() """ #-- Facet Definitions ------------------------------------------------------ # The kind of editor to create for each list item: kind = Str # Is the list of items being edited mutable? mutable = Bool( True ) #-- Public Methods --------------------------------------------------------- def init ( self, parent ): """ Finishes initializing the editor by creating the underlying toolkit widget. """ # Initialize the facet handler to use: facet_handler = self.factory.facet_handler if facet_handler is None: facet_handler = self.object.base_facet( self.name ).handler self._facet_handler = facet_handler # Create a scrolled window to hold all of the list item controls: self.control = QScrollArea( parent ) self.control.setFrameShape( QFrame.NoFrame ) # Create a widget with a grid layout as the container. self._list_pane = QWidget() layout = QGridLayout( self._list_pane ) layout.setMargin( 0 ) # Remember the editor to use for each individual list item: editor = self.factory.editor if editor is None: editor = facet_handler.item_facet.get_editor() self._editor = getattr( editor, self.kind ) # Set up the additional 'list items changed' event handler needed for # a list based facet: self.context_object.on_facet_set( self.update_editor_item, self.extended_name + '_items?', dispatch = 'ui' ) self.set_tooltip() def dispose ( self ): """ Disposes of the contents of an editor. """ self.context_object.on_facet_set( self.update_editor_item, self.extended_name + '_items?', remove = True ) super( SimpleEditor, self ).dispose() def update_editor ( self ): """ Updates the editor when the object facet changes externally to the editor. """ # Disconnect the editor from any control about to be destroyed: self._dispose_items() list_pane = self._list_pane layout = list_pane.layout() # Create all of the list item facet editors: facet_handler = self._facet_handler resizable = ((facet_handler.minlen != facet_handler.maxlen) and self.mutable) item_facet = facet_handler.item_facet values = self.value index = 0 is_fake = (resizable and (len( values ) == 0)) if is_fake: values = [ item_facet.default_value()[1] ] editor = self._editor # FIXME: Add support for more than one column. for value in values: if resizable: control = IconButton( '@facets:list_editor', self.popup_menu ) layout.addWidget( control, index, 0 ) try: proxy = ListItemProxy( self.object, self.name, index, item_facet, value ) if resizable: control.proxy = proxy peditor = editor( self.ui, proxy, 'value', self.description ).set( object_name = '' ) peditor.prepare( list_pane ) pcontrol = peditor.control pcontrol.proxy = proxy except: if not is_fake: raise pcontrol = QPushButton( 'sample', list_pane ) if isinstance( pcontrol, QWidget ): layout.addWidget( pcontrol, index, 1 ) else: layout.addLayout( pcontrol, index, 1 ) index += 1 if is_fake: self._cur_control = control self.empty_list() control.setParent( None ) if self.single_row: rows = 1 else: rows = self.factory.rows #list_pane.SetSize( wx.Size( # width + ((facet_handler.maxlen > rows) * scrollbar_dx), # height * rows ) ) # QScrollArea can have problems if the widget being scrolled is set too # early (ie. before it contains something). if self.control.widget() is None: self.control.setWidget( list_pane ) def update_editor_item ( self, object, name, old, event ): """ Updates the editor when an item in the object facet changes externally to the editor. """ # If this is not a simple, single item update, rebuild entire editor: if (len( event.removed ) != 1) or (len( event.added ) != 1): self.update_editor() return # Otherwise, find the proxy for this index and update it with the # changed value: for control in self.control.widget().children(): if isinstance( control, QLayout ): continue proxy = control.proxy if proxy.index == event.index: proxy.value = event.added[0] break def empty_list ( self ): """ Creates an empty list entry (so the user can add a new item). """ control = IconButton( '@facets:list_editor', self.popup_menu ) control.is_empty = True proxy = ListItemProxy( self.object, self.name, -1, None, None ) pcontrol = QLabel( ' (Empty List)' ) pcontrol.proxy = control.proxy = proxy self.reload_sizer( [ ( control, pcontrol ) ] ) def reload_sizer ( self, controls, extra = 0 ): """ Reloads the layout from the specified list of ( button, proxy ) pairs. """ layout = self._list_pane.layout() child = layout.takeAt( 0 ) while child is not None: child = layout.takeAt( 0 ) del child index = 0 for control, pcontrol in controls: layout.addWidget( control ) layout.addWidget( pcontrol ) control.proxy.index = index index += 1 def get_info ( self ): """ Returns the associated object list and current item index. """ proxy = self._cur_control.proxy return ( proxy.list, proxy.index ) def popup_empty_menu ( self, control ): """ Displays the empty list editor popup menu. """ self._cur_control = control control.PopupMenuXY( MakeMenu( self.empty_list_menu, self, True, control ).menu, 0, 0 ) def popup_menu ( self ): """ Displays the list editor popup menu. """ self._cur_control = control = self.control.sender() proxy = control.proxy index = proxy.index menu = MakeMenu( self.list_menu, self, True, control ).menu len_list = len( proxy.list ) not_full = ( len_list < self._facet_handler.maxlen ) self._menu_before.enabled( not_full ) self._menu_after.enabled( not_full ) self._menu_delete.enabled( len_list > self._facet_handler.minlen ) self._menu_up.enabled( index > 0 ) self._menu_top.enabled( index > 0 ) self._menu_down.enabled( index < (len_list - 1) ) self._menu_bottom.enabled( index < (len_list - 1) ) menu.exec_( control.mapToGlobal( QPoint( 0, 0 ) ) ) def add_item ( self, offset ): """ Adds a new value at the specified list index. """ list, index = self.get_info() index += offset item_facet = self._facet_handler.item_facet dv = item_facet.default_value() if dv[0] == 7: func, args, kw = dv[1] if kw is None: kw = {} value = func( *args, **kw ) else: value = dv[1] self.value = list[ : index ] + [ value ] + list[ index: ] self.update_editor() def add_before ( self ): """ Inserts a new item before the current item. """ self.add_item( 0 ) def add_after ( self ): """ Inserts a new item after the current item. """ self.add_item( 1 ) def add_empty ( self ): """ Adds a new item when the list is empty. """ list, index = self.get_info() self.add_item( 0 ) def delete_item ( self ): """ Delete the current item. """ list, index = self.get_info() self.value = list[ : index ] + list[ index + 1: ] self.update_editor() def move_up ( self ): """ Move the current item up one in the list. """ list, index = self.get_info() self.value = (list[ :index - 1 ] + [ list[ index ], list[ index - 1 ] ] + list[ index + 1: ]) def move_down ( self ): """ Moves the current item down one in the list. """ list, index = self.get_info() self.value = ( list[ : index ] + [ list[ index + 1 ], list[ index ] ] + list[ index + 2: ] ) def move_top ( self ): """ Moves the current item to the top of the list. """ list, index = self.get_info() self.value = [ list[ index ] ] + list[ : index ] + list[ index + 1: ] def move_bottom ( self ): """ Moves the current item to the bottom of the list. """ list, index = self.get_info() self.value = list[ : index ] + list[ index + 1: ] + [ list[ index ] ] #-- Private Methods -------------------------------------------------------- def _dispose_items ( self ): """ Disposes of each current list item. """ list_pane = self._list_pane layout = list_pane.layout() for control in list_pane.children(): editor = getattr( control, '_editor', None ) if editor is not None: editor.dispose() editor.control = None elif control is not layout: control.setParent( None ) del control
class PointsWidget(QWidget): """ PointsWidget """ activeLandmarkChanged = Signal(int) landmarkDeleted = Signal(int) def __init__(self): super(PointsWidget, self).__init__() self.landmarkWidgets = [] self.activeIndex = 0 self.scrollArea = QScrollArea(self) self.scrollArea.setFrameShape(QFrame.NoFrame) self.scrollArea.setAutoFillBackground(False) self.scrollArea.setAttribute(Qt.WA_TranslucentBackground) self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scrollArea.setWidgetResizable(True) landmarkLocationsLayout = QGridLayout() landmarkLocationsLayout.setSpacing(0) landmarkLocationsLayout.setContentsMargins(0, 0, 0, 0) landmarkLocationsLayout.setAlignment(Qt.AlignTop) self.landmarkLocationsWidget = QWidget() Style.styleWidgetForTab(self.landmarkLocationsWidget) self.landmarkLocationsWidget.setLayout(landmarkLocationsLayout) self.scrollArea.setWidget(self.landmarkLocationsWidget) layout = QGridLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.scrollArea) self.setLayout(layout) @Slot(list) def setPoints(self, points): self._clearLandmarkWidgets() layout = self.landmarkLocationsWidget.layout() for index in range(len(points)): landmarkWidget = LandmarkLocationWidget() landmarkWidget.setIndex(index) landmarkWidget.active = (index == self.activeIndex) landmarkWidget.activated.connect(self.activateLandmark) landmarkWidget.deleted.connect(self.deleteLandmark) landmarkWidget.setLandmarkSet(points[index]) layout.addWidget(landmarkWidget, index, 0) self.landmarkWidgets.append(landmarkWidget) def _clearLandmarkWidgets(self): layout = self.landmarkLocationsWidget.layout() for widget in self.landmarkWidgets: widget.activated.disconnect() layout.removeWidget(widget) widget.deleteLater() self.landmarkWidgets = [] @Slot(int, object) def activateLandmark(self, index, state): if not state: self.activeIndex = len(self.landmarkWidgets) else: self.activeIndex = index self.activeLandmarkChanged.emit(self.activeIndex) @Slot(int) def deleteLandmark(self, index): self.activateLandmark(index, False) self.landmarkDeleted.emit(index)
class RenderParameterWidget(QWidget): """ RenderParameterWidget is a widget that is shown in the render property widget. It holds a combo box with which different visualizations can be chosen. Beneath the combo box it displays a widget in a scroll view that contains widgets with which parameters of the visualization can be adjusted. """ def __init__(self, renderController, parent=None): super(RenderParameterWidget, self).__init__(parent=parent) self.renderController = renderController self.renderController.visualizationChanged.connect(self.visualizationLoaded) self.paramWidget = None self.visTypeComboBox = QComboBox() for visualizationType in self.renderController.visualizationTypes: self.visTypeComboBox.addItem(visualizationType) layout = QGridLayout() layout.setAlignment(Qt.AlignTop) layout.setSpacing(10) layout.setContentsMargins(10, 0, 10, 0) if len(self.renderController.visualizationTypes) > 1: layout.addWidget(QLabel("Visualization type:"), 0, 0) layout.addWidget(self.visTypeComboBox, 0, 1) self.setLayout(layout) self.scrollArea = QScrollArea() self.scrollArea.setFrameShape(QFrame.NoFrame) self.scrollArea.setAutoFillBackground(False) self.scrollArea.setAttribute(Qt.WA_TranslucentBackground) self.scrollArea.setWidgetResizable(True) self.visTypeComboBox.currentIndexChanged.connect(self.visTypeComboBoxChanged) def UpdateWidgetFromRenderWidget(self): """ Update the parameter widget with a widget from the render widget. """ # Add the scroll area for the parameter widget if it is not there yet layout = self.layout() if layout.indexOf(self.scrollArea) == -1: layout.addWidget(self.scrollArea, 1, 0, 1, 2) self.setLayout(layout) # Clear the previous parameter widget if self.paramWidget is not None: self.paramWidget.setParent(None) if self.renderController.visualization is not None: self.renderController.visualization.disconnect(SIGNAL("updatedTransferFunction"), self.transferFunctionChanged) # Get a new parameter widget from the render widget self.paramWidget = self.renderController.getParameterWidget() Style.styleWidgetForTab(self.paramWidget) self.scrollArea.setWidget(self.paramWidget) if self.renderController.visualization is not None: self.renderController.visualization.updatedTransferFunction.connect(self.transferFunctionChanged) self.visTypeComboBox.setCurrentIndex(self.visTypeComboBox.findText(self.renderController.visualizationType)) @Slot(int) def visTypeComboBoxChanged(self, index): """ Slot that changes the render type. Also updates parameters and makes sure that the renderWidget renders with the new visualizationType. :type index: any """ self.renderController.setVisualizationType(self.visTypeComboBox.currentText()) self.UpdateWidgetFromRenderWidget() self.renderController.updateVisualization() def visualizationLoaded(self, visualization): self.UpdateWidgetFromRenderWidget() @Slot() def transferFunctionChanged(self): """ Slot that can be used when a transfer function has changed so that the render will be updated afterwards. Should be called on valueChanged by the widgets from the parameter widget. """ self.renderController.updateVisualization()
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)