class PM_ComboBox( QComboBox ):
    """   
    The PM_ComboBox widget provides a combobox with a text label for a 
    Property Manager group box. The text label can be positioned on either 
    the left or right side of the combobox.
    
    A combobox is a combined button and popup list that provides a means of 
    presenting a list of options to the user in a way that takes up the 
    minimum amount of screen space.
    
    Also, a combobox is a selection widget that displays the current item, 
    and can pop up a list of selectable items. A combobox may be editable, 
    allowing the user to modify each item in the list.
    
    Comboboxes can contain pixmaps as well as strings; the insertItem() 
    and changeItem() functions are suitably overloaded. 
    For editable comboboxes, the function clearEdit() is provided, to 
    clear the displayed string without changing the combobox's contents.
    
    There are two signals emitted if the current item of a combobox changes, 
    currentIndexChanged() and activated(). currentIndexChanged() is always 
    emitted regardless if the change was done programmatically or by user 
    interaction, while activated() is only emitted when the change is caused 
    by user interaction. The highlighted() signal is emitted when the user 
    highlights an item in the combobox popup list. All three signals exist 
    in two versions, one with a 
    U{B{QString}<http://doc.trolltech.com/4/qstring.html>} 
    argument and one with an int argument. 
    If the user selectes or highlights a pixmap, only the int signals are 
    emitted. Whenever the text of an editable combobox is changed the 
    editTextChanged() signal is emitted.
    
    When the user enters a new string in an editable combobox, the widget may 
    or may not insert it, and it can insert it in several locations. 
    The default policy is is AtBottom but you can change this using 
    setInsertPolicy().
    
    It is possible to constrain the input to an editable combobox using 
    U{B{QValidator}<http://doc.trolltech.com/4/qvalidator.html>}
    ; see setValidator(). By default, any input is accepted.
    
    A combobox can be populated using the insert functions, insertStringList()
    and insertItem() for example. Items can be changed with changeItem(). 
    An item can be removed with removeItem() and all items can be removed 
    with clear(). The text of the current item is returned by currentText(), 
    and the text of a numbered item is returned with text(). The current item 
    can be set with setCurrentIndex(). The number of items in the combobox is 
    returned by count(); the maximum number of items can be set with 
    setMaxCount(). You can allow editing using setEditable(). For editable 
    comboboxes you can set auto-completion using setCompleter() and whether 
    or not the user can add duplicates is set with setDuplicatesEnabled().
    
    PM_ComboBox uses the model/view framework for its popup list and to store
    its items. By default a QStandardItemModel stores the items and a 
    QListView subclass displays the popuplist. You can access the model and 
    view directly (with model() and view()), but PM_ComboBox also provides 
    functions to set and get item data (e.g., setItemData() and itemText()). 
    You can also set a new model and view (with setModel() and setView()). 
    For the text and icon in the combobox label, the data in the model that 
    has the Qt.DisplayRole and Qt.DecorationRole is used.

    @cvar defaultChoices: The default choices of the combobox.
    @type defaultChoices: list
                          
    @cvar defaultIndex: The default index of the combobox.
    @type defaultIndex: int
    
    @cvar setAsDefault: Determines whether to reset the choices to 
                        I{defaultChoices} and currentIndex to I{defaultIndex}
                        when the user clicks the "Restore Defaults" button.
    @type setAsDefault: bool
    
    @cvar labelWidget: The Qt label widget of this combobox.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    
    @see: U{B{QComboBox}<http://doc.trolltech.com/4/qcombobox.html>}
    """
    
    defaultIndex   = 0
    defaultChoices = []
    setAsDefault   = True
    labelWidget    = None    
    
    def __init__(self, 
                 parentWidget, 
                 label        = '', 
                 labelColumn  = 0,
                 choices      = [],
                 index        = 0, 
                 setAsDefault = True,
                 spanWidth    = False
                 ):
        """
        Appends a QComboBox widget (with a QLabel widget) to <parentWidget>, 
        a property manager group box.
        
        Arguments:
        
        @param parentWidget: the group box containing this PM widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: label that appears to the left of (or above) this PM widget.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
                
        @param choices: list of combo box choices (strings).
        @type  choices: list
        
        @param index: initial index (choice) of combobox. (0=first item)
        @type  index: int (default 0)
        
        @param setAsDefault: if True, will restore <index> as the current index
                         when the "Restore Defaults" button is clicked.
        @type  setAsDefault: bool (default True)
        
        @param spanWidth: If True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left justified.
        @type  spanWidth: bool (default False)
        
        @see: U{B{QComboBox}<http://doc.trolltech.com/4/qcombobox.html>}
        """
        
        if 0: # Debugging code
            print "PM_ComboBox.__init__():"
            print "  label        =", label
            print "  choices      =", choices
            print "  index        =", index
            print "  setAsDefault =", setAsDefault
            print "  spanWidth    =", spanWidth
        
        QComboBox.__init__(self)
        
        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth
        
        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)
                       
        # Load QComboBox widget choices and set initial choice (index).
        for choice in choices:
            self.addItem(choice)
        self.setCurrentIndex(index)
        
        # Set default index
        self.defaultIndex   = index
        self.defaultChoices = choices
        self.setAsDefault   = setAsDefault
        
        parentWidget.addPmWidget(self)
            
    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.clear() # Generates signals!
            for choice in self.defaultChoices:
                self.addItem(choice)
            self.setCurrentIndex(self.defaultIndex)
        
    def hide(self):
        """
        Hides the combobox and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget: 
            self.labelWidget.hide()
            
    def show(self):
        """
        Unhides the combobox and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget: 
            self.labelWidget.show()

# End of PM_ComboBox ############################
Exemple #2
0
class PM_ListWidget(QListWidget):
    """
    The PM_ListWidget widget provides a QListWidget with a 
    QLabel for a Property Manager group box.
    
    @cvar defaultItems: The default list of items in the list widget.
    @type defaultItems: list
                          
    @cvar defaultRow: The default row of the list widget.
    @type defaultRow: int
    
    @cvar setAsDefault: Determines whether to reset the choices to 
                        I{defaultItems} and current row to I{defaultRow}
                        when the user clicks the "Restore Defaults" button.
    @type setAsDefault: bool
    
    @cvar labelWidget: The Qt label widget of this list widget.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    """

    defaultRow = 0
    defaultItems = []
    setAsDefault = True
    labelWidget = None

    def __init__(self,
                 parentWidget,
                 label='',
                 labelColumn=0,
                 items=[],
                 defaultRow=0,
                 setAsDefault=True,
                 heightByRows=6,
                 spanWidth=False):
        """     
        Appends a QListWidget (Qt) widget to the bottom of I{parentWidget}, 
        a Property Manager group box.
        
        @param parentWidget: The parent group box containing this widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: The label that appears to the left or right of the 
                      checkbox. 
                      
                      If spanWidth is True, the label will be displayed on
                      its own row directly above the list widget.
                      
                      To suppress the label, set I{label} to an 
                      empty string.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
        
        @param items: list of items (strings) to be inserted in the widget.
        @type  items: list
        
        @param defaultRow: The default row (item) selected, where 0 is the first 
                           row.
        @type  defaultRow: int
        
        @param setAsDefault: If True, will restore <idx> as the current index
                         when the "Restore Defaults" button is clicked.
        @type  setAsDefault: bool
        
        @param heightByRows: The height of the list widget.
        @type  heightByRows: int
        
        @param spanWidth: If True, the widget and its label will span the width
                          of the group box. Its label will appear directly above
                          the widget (unless the label is empty) and is left 
                          justified.
        @type  spanWidth: bool
        
        @see: U{B{QListWidget}<http://doc.trolltech.com/4/qlistwidget.html>}
        """

        if 0:  # Debugging code
            print "PM_ListWidget.__init__():"
            print "  label        = ", label
            print "  labelColumn  = ", labelColumn
            print "  items        = ", items
            print "  defaultRow   = ", defaultRow
            print "  setAsDefault = ", setAsDefault
            print "  heightByRows = ", heightByRows
            print "  spanWidth    = ", spanWidth

        QListWidget.__init__(self)

        self.parentWidget = parentWidget
        self.label = label
        self.labelColumn = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth = spanWidth

        if label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        # Load QComboBox widget choice items and set initial choice (index).
        self.insertItems(0, items, setAsDefault)
        self.setCurrentRow(defaultRow, setAsDefault)

        # Set height of list widget.
        margin = self.fontMetrics().leading() * 2  # Mark 2007-05-28
        height = heightByRows * self.fontMetrics().lineSpacing() + margin
        self.setMaximumHeight(height)

        #As of 2008-04-16, the items in any list widgets won't be sorted
        #automatically. It can be changes by simply uncommentting the lines
        #below -- Ninad
        ##self.setSortingEnabled(True)
        ##self.sortItems()

        self.setAlternatingRowColors(True)
        parentWidget.addPmWidget(self)

    def insertItems(self, row, items, setAsDefault=True):
        """
        Insert items of widget starting at <row>. 
        If <setAsDefault> is True, <items> become the default list of
        items for this widget. "Restore Defaults" will reset 
        the list of items to <items>.
        
        Note: <items> will always replace the list of current items
        in the widget. <row> is ignored. This is considered a bug.
        -- Mark 2007-06-04
        """
        if row <> 0:
            msg = "PM_ListWidget.insertItems(): <row> must be zero."\
                "See docstring for details:"
            print_compact_traceback(msg)
            return

        if setAsDefault:
            self.setAsDefault = setAsDefault
            self.defaultItems = items

        self.clear()
        QListWidget.insertItems(self, row, items)

    def setCurrentRow(self, row, setAsDefault=False):
        """
        Select new row. 
        
        @param row: The new row to select.
        @type  row: int
        
        @param setAsDefault: If True, I{row} becomes the default row when
                             "Restore Defaults" is clicked.
        """
        if setAsDefault:
            self.setAsDefault = setAsDefault
            self.defaultRow = row
        QListWidget.setCurrentRow(self, row)

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.insertItems(0, self.defaultItems)
            self.setCurrentRow(self.defaultRow)
            ## self.clear()
            ## for choice in self.defaultChoices:
            ##     self.addItem(choice)
            ## self.setCurrentRow(self.defaultRow)

    def hide(self):
        """
        Hides the list widget and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()

    def show(self):
        """
        Unhides the list widget and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()


# End of PM_ListWidget ############################
class Ui_DnaSequenceEditor(PM_DockWidget):
    """
    The Ui_DnaSequenceEditor class defines UI elements for the Sequence Editor
    object. The sequence editor is usually visible while in DNA edit mode.
    It is a DockWidget that is doced at the bottom of the MainWindow
    """
    _title         =  "Sequence Editor"
    _groupBoxCount = 0
    _lastGroupBox = None

    def __init__(self, win):
        """
        Constructor for the Ui_DnaSequenceEditor 
        @param win: The parentWidget (MainWindow) for the sequence editor 
        """
        
        self.win = win
        # Should parentWidget for a docwidget always be win? 
        #Not necessary but most likely it will be the case.        
        parentWidget = win 
        
        _superclass.__init__(self, parentWidget, title = self._title)
        
        #A flag used to restore the state of the Reports dock widget 
        #(which can be accessed through View  >  Reports) see self.show() and
        #self.closeEvent() for more details. 
        self._reportsDockWidget_closed_in_show_method = False
        self.setFixedHeight(90)

    def show(self):
        """
        Shows the sequence editor. While doing this, it also closes the reports
        dock widget (if visible) the state of the reports dockwidget will be
        restored when the sequence editor is closed. 
        @see:self.closeEvent()
        """
        self._reportsDockWidget_closed_in_show_method = False
        #hide the history widget first
        #(It will be shown back during self.close)
        #The history widget is hidden or shown only when both 
        # 'View > Full Screen' and View > Semi Full Screen actions 
        # are *unchecked*
        #Thus show or close methods won't do anything to history widget
        # if either of the above mentioned actions is checked.
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self.win.reportsDockWidget.isVisible():
                self.win.reportsDockWidget.close()
                self._reportsDockWidget_closed_in_show_method = True

        _superclass.show(self)  
        
    def closeEvent(self, event):
        """
        Overrides close event. Makes sure that the visible state of the reports
        widgetis restored when the sequence editor is closed. 
        @see: self.show()
        """
        _superclass.closeEvent(self, event)
       
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self._reportsDockWidget_closed_in_show_method:
                self.win.viewReportsAction.setChecked(True) 
                self._reportsDockWidget_closed_in_show_method = False

    def _loadWidgets(self):
        """
        Overrides PM.PM_DockWidget._loadWidgets. Loads the widget in this
        dockwidget.
        """
        self._loadMenuWidgets()
        self._loadTextEditWidget()


    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.loadSequenceButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Open.png")  

        self.saveSequenceButton = PM_ToolButton(
            self, 
            iconPath = "ui/actions/Properties Manager/Save_Strand_Sequence.png") 

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        editDirectionChoices = ["5' to 3'", "3' to 5'"]
        self.baseDirectionChoiceComboBox = \
            PM_ComboBox( self,
                         choices = editDirectionChoices,
                         index     = 0, 
                         spanWidth = False )   

        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)


        self.replaceLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text = "Replace")

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        # NOTE: Following needs cleanup in the PM_WidgetRow/ PM_WidgetGrid
        # but this explanation is sufficient  until thats done -- 

        # When the widget type starts with the word 'PM_' , the 
        # PM_WidgetRow treats it as a well defined widget and thus doesn't try
        # to create a QWidget object (or its subclasses)
        # This is the reason why qLabels such as self.warningSign and
        # self.phraseNotFoundLabel  are defined as PM_Labels and not 'QLabels'
        # If they were defined as 'QLabel'(s) then PM_WidgetRow would have
        # recreated the label. Since we want to show/hide the above mentioned 
        # labels (and if they were recreated as mentioned above), 
        # we would have needed to define  those something like this:
        # self.phraseNotFoundLabel = widgetRow._widgetList[-2] 
        #Cleanup in PM_widgetGrid could be to check if the widget starts with 
        #'Q'  instead of 'PM_' 


        #Widgets to include in the widget row. 
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "     Sequence direction:", 2),
                      ('PM_ComboBox',  self.baseDirectionChoiceComboBox , 3),
                      ('QLabel', "     Find:", 4),
                      ('PM_LineEdit', self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8), 
                      ('QLabel', "     Replace:", 9),
                      ('PM_TextEdit', self.replaceLineEdit, 10), 
                      ('PM_PushButton', self.replacePushButton, 11),
                      ('PM_Label', self.warningSign, 12),
                      ('PM_Label', self.phraseNotFoundLabel, 13),
                      ('QSpacerItem', 5, 5, 14) ]

        widgetRow = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList,
                                 label = "",
                                 spanWidth = True )

    def _loadTextEditWidget(self):
        """
        Load the SequenceTexteditWidgets.         
        """        
        self.sequenceTextEdit = \
            PM_TextEdit( self, 
                         label = " Sequence: ", 
                         spanWidth = False,
                         permit_enter_keystroke = False)        
        self.sequenceTextEdit.setCursorWidth(2)
        self.sequenceTextEdit.setWordWrapMode( QTextOption.WrapAnywhere )
        self.sequenceTextEdit.setFixedHeight(20)

        #The StrandSequence 'Mate' it is a read only etxtedit that shows
        #the complementary strand sequence.         
        self.sequenceTextEdit_mate = \
            PM_TextEdit(self, 
                        label = "", 
                        spanWidth = False,
                        permit_enter_keystroke = False
                        )        
        palette = getPalette(None, 
                             QPalette.Base, 
                             sequenceEditStrandMateBaseColor)
        self.sequenceTextEdit_mate.setPalette(palette)        
        self.sequenceTextEdit_mate.setFixedHeight(20)
        self.sequenceTextEdit_mate.setReadOnly(True)
        self.sequenceTextEdit_mate.setWordWrapMode(QTextOption.WrapAnywhere)

        #Important to make sure that the horizontal and vertical scrollbars 
        #for these text edits are never displayed. 
        for textEdit in (self.sequenceTextEdit, self.sequenceTextEdit_mate):
            textEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            textEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)


    def _getFindLineEditStyleSheet(self):
        """
        Return the style sheet for the findLineEdit. This sets the following 
        properties only:
         - background-color

        This style is set whenever the searchStrig can't be found (sets
        a light red color background to the lineedit when this happens)   

        @return: The line edit style sheet.
        @rtype:  str

        """
        styleSheet = \
                   "QLineEdit {\
                   background-color: rgb(255, 102, 102)\
                   }"
        #Not used:
        #  background-color: rgb(217, 255, 216)\       

        return styleSheet

    def _setFindOptionsToolButtonMenu(self):
        """
        Sets the menu for the findOptionstoolbutton that appears a small 
        menu button next to the findLineEdit.
        """
        self.findOptionsMenu = QMenu(self.findOptionsToolButton)

        self.caseSensitiveFindAction = QAction(self.findOptionsToolButton)
        self.caseSensitiveFindAction.setText('Match Case')
        self.caseSensitiveFindAction.setCheckable(True)
        self.caseSensitiveFindAction.setChecked(False)

        self.findOptionsMenu.addAction(self.caseSensitiveFindAction)
        self.findOptionsMenu.addSeparator()

        self.findOptionsToolButton.setMenu(self.findOptionsMenu)

    def _addToolTipText(self):
        """
            What's Tool Tip text for widgets in this Property Manager.  
            """ 
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_SequenceEditor
        ToolTip_SequenceEditor(self)

    def _addWhatsThisText(self):
        """
            What's This text for widgets in this Property Manager.  

            """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_SequenceEditor
        whatsThis_SequenceEditor(self)
Exemple #4
0
class PM_Slider(QSlider):
    
    labelWidget = None
    
    def __init__( self, 
                  parentWidget, 
                  orientation  = None, ##Qt.Horizontal,
                  currentValue = 0,
                  minimum      = 0,
                  maximum      = 100,
                  label        = '', 
                  labelColumn  = 0,
                  setAsDefault = True,
                  spanWidth    = True):
        """
        
        Appends a Qslider widget (with a QLabel widget) to <parentWidget>
        a property manager group box.
        
        Arguments:
        
        @param parentWidget: the group box containing this PM widget.
        @type  parentWidget: PM_GroupBox
        
        @param currentValue: 
        @type  currentValue: int
        
        @param minimum: minimum value the slider can get.
              (used to set its 'range')
        @type  minimum: int
        
        @param maximum: maximum value the slider can get.
              (used to set its 'range')
        @type  maximum: int
                
        @param label: label that appears to the left of (or above) this widget.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
                
        
        @param setAsDefault: if True, will restore value when the 
               "Restore Defaults" button is clicked.
        @type  setAsDefault: bool (default True)
        
        @param spanWidth: If True, the widget and its label will span the width
                    of the group box. Its label will appear directly above
                    the widget (unless the label is empty)and is left justified.
        @type  spanWidth: bool (default True)
        
        @see: U{B{QSlider}<http://doc.trolltech.com/4/qslider.html>}
        
        """
        
        ##QSlider.__init__(self, orientation, parentWidget)
        QSlider.__init__(self, parentWidget)
        
        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth
        
        #Ideally, this should be simply self.setOrientation(orientation)  with the 
        #default orientation = Qt.Horizontal in the init argument itself. But, 
        #apparently pylint chokes up when init argument is a Qt enum. 
        #This problem happened while running pylint 0.23 on the SEMBOT server 
        #so comitting this temporary workaround. The pylint on my machine is 
        #0.25 and it runs fine even before this workaround. Similar changes made
        #in PM_CheckBox. -- Ninad 2008-06-30
        if orientation is None:
            self.setOrientation(Qt.Horizontal)
        else:
            self.setOrientation(orientation)
        
        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)
            
        self.setValue(currentValue)
        self.setRange(minimum, maximum)
        
        self.setAsDefault   = setAsDefault
        
        if self.setAsDefault:
            self.setDefaultValue(currentValue)
        
        parentWidget.addPmWidget(self)
        
        
    
    def hide(self):
        """
        Hides the slider and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget: 
            self.labelWidget.hide()
            
    def show(self):
        """
        Unhides the slider and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget: 
            self.labelWidget.show()
    
    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setValue(self.defaultValue)
          
    
    def setDefaultValue(self, value):
        """
        Sets the Default value for the slider. This value will be set if 
        user hits Restore Defaults button in the Property Manager. 
        """
        self.defaultValue = value
        self.setAsDefault = True
Exemple #5
0
class PM_SpinBox( QSpinBox ):
    """
    The PM_SpinBox widget provides a QSpinBox (with an
    optional label) for a Property Manager group box.

    Detailed Description
    ====================
    The PM_SpinBox class provides a spin box widget (with an
    optional label) for a
    U{B{Property Manager dialog}<http://www.nanoengineer-1.net/mediawiki/
    index.php?title=Property_Manager>}.

    PM_SpinBox is designed to handle integers and discrete sets of values
    (e.g., month names); use PM_DoubleSpinBox for floating point values.

    PM_SpinBox allows the user to choose a value by clicking the up/down buttons
    or pressing up/down on the keyboard to increase/decrease the value currently
    displayed. The user can also type the value in manually. The spin box
    supports integer values but can be extended to use different strings with
    validate(), textFromValue() and valueFromText().

    Every time the value changes PM_SpinBox emits the valueChanged() signals.
    The current value can be fetched with value() and set with setValue().

    Clicking the up/down buttons or using the keyboard accelerator's up and down
    arrows will increase or decrease the current value in steps of size
    singleStep(). If you want to change this behaviour you can reimplement the
    virtual function stepBy(). The minimum and maximum value and the step size
    can be set using one of the constructors, and can be changed later with
    setMinimum(), setMaximum() and setSingleStep().

    Most spin boxes are directional, but PM_SpinBox can also operate as a circular
    spin box, i.e. if the range is 0-99 and the current value is 99, clicking
    "up" will give 0 if wrapping() is set to true. Use setWrapping() if you want
    circular behavior.

    The displayed value can be prepended and appended with arbitrary strings
    indicating, for example, currency or the unit of measurement. See
    setPrefix() and setSuffix(). The text in the spin box is retrieved with
    text() (which includes any prefix() and suffix()), or with cleanText()
    (which has no prefix(), no suffix() and no leading or trailing whitespace).

    It is often desirable to give the user a special (often default) choice
    in addition to the range of numeric values. See setSpecialValueText() for
    how to do this with QSpinBox.

    @see: U{B{QSpinBox}<http://doc.trolltech.com/4/qspinbox.html>}

    @cvar defaultValue: The default value of the spin box.
    @type defaultValue: int

    @cvar setAsDefault: Determines whether to reset the value of the
                        spin box to I{defaultValue} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: bool

    @cvar labelWidget: The Qt label widget of this spin box.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    """

    defaultValue = 0
    setAsDefault = True
    labelWidget  = None

    def __init__(self,
                 parentWidget,
                 label        = '',
                 labelColumn  = 0,
                 value        = 0,
                 setAsDefault = True,
                 minimum      = 0,
                 maximum      = 99,
                 singleStep   = 1,
                 suffix       = '',
                 spanWidth    = False
                 ):
        """
        Appends a QSpinBox (Qt) widget to the bottom of I{parentWidget},
        a Property Manager group box.

        @param parentWidget: the parent group box containing this widget.
        @type  parentWidget: PM_GroupBox

        @param label: The label that appears to the left of (or above) the
                      spin box. If label contains the relative path to an
                      icon (.png) file, that icon image will be used for the
                      label.

                      If spanWidth is True, the label will be displayed on
                      its own row directly above the spin box.

                      To suppress the label, set I{label} to an empty string.
        @type  label: str

        @param value: The initial value of the spin box.
        @type  value: int

        @param setAsDefault: Determines if the spin box value is reset when
                             the "Restore Defaults" button is clicked. If True,
                             (the default) I{value} will be used as the reset
                             value.
        @type  setAsDefault: bool

        @param minimum: The minimum value of the spin box.
        @type  minimum: int

        @param maximum: The maximum value of the spin box.
        @type  maximum: int

        @param singleStep: When the user uses the arrows to change the
                           spin box's value the value will be
                           incremented/decremented by the amount of the
                           singleStep. The default value is 1.
                           Setting a singleStep value of less than 0 does
                           nothing.
        @type  singleStep: int

        @param suffix: The suffix is appended to the end of the displayed value.
                       Typical use is to display a unit of measurement.
                       The default is no suffix. The suffix is not displayed
                       for the minimum value if specialValueText() is set.
        @type  suffix: str

        @param spanWidth: If True, the spin box and its label will span the
                          width of the group box. The label will appear
                          directly above the spin box and is left justified.
        @type  spanWidth: bool

        @see: U{B{QSpinBox}<http://doc.trolltech.com/4/qspinbox.html>}
        """

        if 0: # Debugging code
            print "PM_SpinBox.__init__():"
            print "  label        = ", label
            print "  labelColumn  = ", labelColumn
            print "  value        = ", value
            print "  setAsDefault = ", setAsDefault
            print "  minimum      = ", minimum
            print "  maximum      = ", maximum
            print "  singleStep   = ", singleStep
            print "  suffix       = ", suffix
            print "  spanWidth    = ", spanWidth

        QSpinBox.__init__(self)

        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth

        self._suppress_valueChanged_signal = False

        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        # Set QSpinBox minimum, maximum and initial value
        self.setRange(minimum, maximum)
        self.setSingleStep(singleStep)
        self.setValue(value)

        # Set default value
        self.defaultValue = value
        self.setAsDefault = setAsDefault

        # Add suffix if supplied.
        if suffix:
            self.setSuffix(suffix)

        parentWidget.addPmWidget(self)

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setValue(self.defaultValue)

    def setDefaultValue(self, value):
        """
        Sets the default value of the spin box to I{value}. The current spin box
        value is unchanged.

        @param value: The new default value of the spin box.
        @type  value: int

        @see: L{setValue}
        """
        self.setAsDefault = True
        self.defaultValue = value

    def setValue(self, value,
                 setAsDefault = True,
                 blockSignals = False):
        """
        Sets the value of the spin box to I{value}.

        setValue() will emit valueChanged() if the new value is different from
        the old one.  (and if blockSignals flag is False)

        @param value: The new spin box value.
        @type  value: int

        @param setAsDefault: Determines if the spin box value is reset when
                             the "Restore Defaults" button is clicked. If True,
                             (the default) I{value} will be used as the reset
                             value.
        @type  setAsDefault: bool

        @param blockSignals: Many times, the caller just wants to setValue
                             and don't want to send valueChanged signal.
                             If this flag is set to True, the valueChanged
                             signal won't be emitted.  The default value is
                             False.
        @type  blockSignals: bool

        @see: L{setDefaultValue}
        @see: QObject.blockSignals(bool block)

        @see: B{InsertNanotube_PropertyManager._chiralityFixup()} for an example
              use of blockSignals flag

        """
        #If blockSignals flag is True, the valueChanged signal won't be emitted
        #This is done by self.blockSignals method below.  -- Ninad 2008-08-13
        self.blockSignals(blockSignals)

        if setAsDefault:
            self.setDefaultValue(value)
        QSpinBox.setValue(self, value)

        #Make sure to always 'unblock' signals that might have been temporarily
        #blocked before calling superclass.setValue.
        self.blockSignals(False)


    def hide(self):
        """
        Hides the spin box and its label (if it has one).

        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()

    def show(self):
        """
        Unhides the spin box and its label (if it has one).

        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()



# End of PM_SpinBox ############################
class PM_ColorChooser( QWidget ):
    """
    The PM_ColorChooser widget provides a color chooser widget for a
    Property Manager group box. The PM_ColorChooser widget is a composite widget
    made from 3 other Qt widgets:
    - a QLabel
    - a QFrame and
    - a QToolButton (with a "..." text label).

    IMAGE(http://www.nanoengineer-1.net/mediawiki/images/e/e2/PM_ColorChooser1.jpg)

    The user can color using Qt's color (chooser) dialog by clicking the "..."
    button. The selected color will be used as the color of the QFrame widget.

    The parent must make the following signal-slot connection to be
    notified when the user has selected a new color via the color chooser
    dialog:

    self.connect(pmColorChooser.colorFrame, SIGNAL("editingFinished()"), self.mySlotMethod)

    @cvar setAsDefault: Determines whether to reset the value of the
                        color to I{defaultColor} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: boolean

    @cvar labelWidget: The Qt label widget of this PM widget.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}

    @cvar colorFrame: The Qt frame widget for this PM widget.
    @type colorFrame: U{B{QFrame}<http://doc.trolltech.com/4/qframe.html>}

    @cvar chooseButton: The Qt tool button widget for this PM widget.
    @type chooseButton: U{B{QToolButton}<http://doc.trolltech.com/4/qtoolbutton.html>}
    """

    defaultColor = None
    setAsDefault = True
    hidden       = False
    chooseButton = None
    customColorCount  = 0
    standardColorList = [white, black]

    def __init__(self,
                 parentWidget,
                 label        = 'Color:',
                 labelColumn  = 0,
                 color        = white,
                 setAsDefault = True,
                 spanWidth    = False,
                 ):
        """
        Appends a color chooser widget to <parentWidget>, a property manager
        group box.

        @param parentWidget: the parent group box containing this widget.
        @type  parentWidget: PM_GroupBox

        @param label: The label that appears to the left or right of the
                      color frame (and "Browse" button).

                      If spanWidth is True, the label will be displayed on
                      its own row directly above the lineedit (and button).

                      To suppress the label, set I{label} to an
                      empty string.
        @type  label: str

        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left
                            column) and 1 (right column). The default is 0
                            (left column).
        @type  labelColumn: int

        @param color: initial color. White is the default.
        @type  color: tuple of 3 floats (r, g, b)

        @param setAsDefault: if True, will restore L{color} when the
                    "Restore Defaults" button is clicked.
        @type  setAsDefault: boolean

        @param spanWidth: if True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left
                      justified.
        @type  spanWidth: boolean

        @see: U{B{QColorDialog}<http://doc.trolltech.com/4/qcolordialog.html>}
        """

        QWidget.__init__(self)

        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.color        = color
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth

        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        # Create the color frame (color swath) and "..." button.
        self.colorFrame = QFrame()
        self.colorFrame.setFrameShape(QFrame.Box)
        self.colorFrame.setFrameShadow(QFrame.Plain)

        # Set browse button text and make signal-slot connection.
        self.chooseButton = QToolButton()
        self.chooseButton.setText("...")
        self.connect(self.chooseButton, SIGNAL("clicked()"), self.openColorChooserDialog)

        # Add a horizontal spacer to keep the colorFrame and "..." squeezed
        # together, even when the PM width changes.
        self.hSpacer = QSpacerItem(10, 10,
                                   QSizePolicy.MinimumExpanding,
                                   QSizePolicy.Fixed)

        # Create vertical box layout.
        self.hBoxLayout = QHBoxLayout(self)
        self.hBoxLayout.setMargin(0)
        self.hBoxLayout.setSpacing(2)
        self.hBoxLayout.insertWidget(-1, self.colorFrame)
        self.hBoxLayout.insertWidget(-1, self.chooseButton)

        # Set this to False to make the colorFrame an expandable rectangle.
        COLORFRAME_IS_SQUARE = True
        if COLORFRAME_IS_SQUARE:
            squareSize = 20
            self.colorFrame.setMinimumSize(QSize(squareSize, squareSize))
            self.colorFrame.setMaximumSize(QSize(squareSize, squareSize))
            self.hBoxLayout.addItem(self.hSpacer)

        self.setColor(color, default = setAsDefault)

        parentWidget.addPmWidget(self)
        return

    def setColor(self, color, default = False):
        """
        Set the color.

        @param color: The color.
        @type  color: tuple of 3 floats (r, g, b)

        @param default: If True, make I{color} the default color. Default is
                        False.
        @type  default: boolean
        """
        if default:
            self.defaultColor = color
            self.setAsDefault = default
        self.color = color
        self._updateColorFrame()
        return

    def getColor(self):
        """
        Return the current color.

        @return: The current r, g, b color.
        @rtype:  Tuple of 3 floats (r, g, b)
        """
        return self.color

    def getQColor(self):
        """
        Return the current QColor.

        @return: The current color.
        @rtype:  QColor
        """
        return RGBf_to_QColor(self.color)

    def _updateColorFrame(self):
        """
        Updates the color frame with the current color.
        """
        colorframe = self.colorFrame
        try:
            qcolor = self.getQColor()
            palette = QPalette() # QPalette(qcolor) would have window color set from qcolor, but that doesn't help us here
            qcolorrole = QPalette.Window
                ## http://doc.trolltech.com/4.2/qpalette.html#ColorRole-enum says:
                ##   QPalette.Window    10    A general background color.
            palette.setColor(QPalette.Active, qcolorrole, qcolor) # used when window is in fg and has focus
            palette.setColor(QPalette.Inactive, qcolorrole, qcolor) # used when window is in bg or does not have focus
            palette.setColor(QPalette.Disabled, qcolorrole, qcolor) # used when widget is disabled
            colorframe.setPalette(palette)
            colorframe.setAutoFillBackground(True)
        except:
            print "data for following exception: ",
            print "colorframe %r has palette %r" % (colorframe, colorframe.palette())
        pass

    def openColorChooserDialog(self):
        """
        Prompts the user to choose a color and then updates colorFrame with
        the selected color.
        """
        qcolor = RGBf_to_QColor(self.color)
        if not self.color in self.standardColorList:
            QColorDialog.setCustomColor(self.customColorCount, qcolor.rgb())
            self.customColorCount += 1
        c = QColorDialog.getColor(qcolor, self)
        if c.isValid():
            self.setColor(QColor_to_RGBf(c))
            self.colorFrame.emit(SIGNAL("editingFinished()"))

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setColor(self.defaultColor)
        return

    def hide(self):
        """
        Hides the lineedit and its label (if it has one).

        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()
        return

    def show(self):
        """
        Unhides the lineedit and its label (if it has one).

        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()
        return
Exemple #7
0
class InformationWindow(QWidget):

    def __init__(self):
        QWidget.__init__(self, ctx.mainScreen)
        self.setObjectName("InformationWindow")
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.setFixedHeight(50)
        self.setMaximumWidth(800)
        self.setStyleSheet("""
            QFrame#frame { border: 1px solid rgba(255,255,255,30);
                           /*border-radius: 4px;*/
                           background-color: rgba(0,0,0,100);}

            QLabel { border:none;
                     color:#FFFFFF;}

            QProgressBar { border: 1px solid white;}

            QProgressBar::chunk { background-color: #F1610D;
                                  width: 0.5px;}
        """)

        self.gridlayout = QGridLayout(self)
        self.frame = QFrame(self)
        self.frame.setObjectName("frame")
        self.horizontalLayout = QHBoxLayout(self.frame)
        self.horizontalLayout.setContentsMargins(10, 0, 10, 0)

        # Spinner
        self.spinner = QLabel(self.frame)
        self.spinner.setMinimumSize(QSize(16, 16))
        self.spinner.setMaximumSize(QSize(16, 16))
        self.spinner.setIndent(6)
        self.movie = QMovie(':/images/working.mng')
        self.spinner.setMovie(self.movie)
        self.movie.start()
        self.horizontalLayout.addWidget(self.spinner)

        # Message
        self.label = QLabel(self.frame)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.icon = QLabel(self.frame)
        self.icon.setFixedWidth(16)
        self.icon.setFixedHeight(16)
        self.horizontalLayout.setSpacing(10)
        self.horizontalLayout.addWidget(self.icon)
        self.horizontalLayout.addWidget(self.label)

        self.gridlayout.addWidget(self.frame,0,0,1,1)

    def update(self, message, type=None, spinner=False):
        fontMetric = self.label.fontMetrics()
        textWidth = fontMetric.width(message)

        if type:
            self.icon.show()
            if type == "error":
                self.icon.setPixmap(QPixmap(":/gui/pics/dialog-error.png"))
                self.setStyleSheet(" QFrame#frame {background-color: rgba(255,0,0,100);} ")

            elif type == "warning":
                self.icon.setPixmap(QPixmap(":/gui/pics/dialog-warning.png"))
                self.setStyleSheet(" QFrame#frame {background-color: rgba(0,0,0,100);} ")

            self.setFixedWidth(textWidth + self.icon.width() + 50)
            self.label.setText(message)

        else:
            self.icon.hide()
            self.setStyleSheet(" QFrame#frame {background-color: rgba(0,0,0,100);} ")
            self.setFixedWidth(textWidth + self.icon.width() + 100)
            self.label.setText(message)

        self.spinner.setVisible(spinner)
        self.move(ctx.mainScreen.width()/2 - self.width()/2,
                  ctx.mainScreen.height() - self.height()/2 - 50)

        self.show()

    def refresh(self):
        ctx.mainScreen.processEvents()

    def show(self):
        QWidget.show(self)
        self.refresh()

    def hide(self):
        QWidget.hide(self)
        self.refresh()
class PM_FontComboBox( QFontComboBox ):
    """   
    Much (practically all) of this code was taken from PM_ComboBox with only 
    slight modifications.
    
    The PM_FontComboBox widget provides a combobox with a text label for a 
    Property Manager group box. The text label can be positioned on either 
    the left or right side of the combobox.
    
    For a more complete explanation of a ComboBox, refer to the definition 
    provided in PM_ComboBox.  In this case, the list of available fonts is 
    not provided by variable, but is read from the system.

    @cvar defaultFont: The default font of the combobox.
    @type defaultFont: QFont
    
    @cvar setAsDefault: Determines whether to reset the currentFont to 
                        the defaultFont when the user clicks the 
                        "Restore Defaults" button.
    @type setAsDefault: bool
    
    @cvar labelWidget: The Qt label widget of this combobox.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    
    @see: U{B{QComboBox}<http://doc.trolltech.com/4/qcombobox.html>}
    """
    
    defaultFont   = None
    setAsDefault   = True
    labelWidget    = None    
    
    def __init__(self, 
                 parentWidget, 
                 label        = '', 
                 labelColumn  = 0,
                 selectFont  = '', 
                 setAsDefault = True,
                 spanWidth    = False
                 ):
        """
        Appends a QFontComboBox widget (with a QLabel widget) to <parentWidget>, 
        a property manager group box.
        
        Arguments:
        
        @param parentWidget: the group box containing this PM widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: label that appears to the left of (or above) this PM widget.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
                
        @param selectFont: initial font of combobox. (''=default)
        @type  selectFont: QFont object (default = '')
        
        @param setAsDefault: if True, will restore <defaultFont> as the current 
                         Font when the "Restore Defaults" button is clicked.
        @type  setAsDefault: bool (default True)
        
        @param spanWidth: If True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left 
                      justified.
        @type  spanWidth: bool (default False)
        
        @see: U{B{QComboBox}<http://doc.trolltech.com/4/qcombobox.html>}
        """
        
        if 0: # Debugging code
            print "PM_FontComboBox.__init__():"
            print "  label        =", label
            print "  selectFont  =", selectFont
            print "  setAsDefault =", setAsDefault
            print "  spanWidth    =", spanWidth
        
        QFontComboBox.__init__(self)
        
        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth
        
        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)
                       
        # Set initial choice (selectFont).
        if selectFont != '':
            self.setCurrentFont(selectFont)
        
        self.defaultFont = self.currentFont()
       
        parentWidget.addPmWidget(self)

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setCurrentFont(self.defaultFont)
            
    def hide(self):
        """
        Hides the combobox and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget: 
            self.labelWidget.hide()
            
    def show(self):
        """
        Unhides the combobox and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget: 
            self.labelWidget.show()
Exemple #9
0
class PM_Dial(QDial):
    """
    The PM_Dial widget provides a QDial (with an
    optional label) for a Property Manager group box.
    """

    defaultValue = 0.0
    setAsDefault = True
    labelWidget = None

    def __init__(self,
                 parentWidget,
                 label='',
                 suffix='',
                 labelColumn=1,
                 value=0.0,
                 setAsDefault=True,
                 minimum=0.0,
                 maximum=360.0,
                 notchSize=1,
                 notchTarget=10.0,
                 notchesVisible=True,
                 wrapping=True,
                 spanWidth=True):
        """
        Appends a QDial (Qt) widget to the bottom of I{parentWidget},
        a Property Manager group box.

        @param parentWidget: The parent group box containing this widget.
        @type  parentWidget: PM_GroupBox

        @see: U{B{QDial}<http://doc.trolltech.com/4/qdial.html>}
        """

        if not parentWidget:
            return

        QDial.__init__(self)

        self.parentWidget = parentWidget
        self.label = label
        self.labelColumn = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth = spanWidth
        self.suffix = suffix

        if label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.updateValueLabel()
            self.labelWidget.setText(self.value_label)

        # Set QDial minimum, maximum, then value
        self.setRange(minimum, maximum)

        self.setValue(value)  # This must come after setDecimals().

        if setAsDefault:
            self.setDefaultValue(value)

        self.notchSize = notchSize
        self.setNotchTarget(notchTarget)
        self.setNotchesVisible(notchesVisible)
        self.setWrapping(wrapping)

        parentWidget.addPmWidget(self)

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setValue(self.defaultValue)

    def setDefaultValue(self, value):
        """
        Sets the default value of the dial to I{value}. The current dial
        value is unchanged.

        @param value: The new default value of the dial.
        @type  value: float

        @see: L{setValue}
        """
        self.setAsDefault = True
        self.defaultValue = value

    def setValue(self, value, setAsDefault=True):
        """
        Sets the value of the dial to I{value}.

        setValue() will emit valueChanged() if the new value is different from
        the old one.

        @param value: The new dial value.
        @type  value: float

##        @param setAsDefault: Determines if the dial value is reset when
##                             the "Restore Defaults" button is clicked. If True,
##                             (the default) I{value} will be used as the reset
##                             value.
##        @type  setAsDefault: bool

        @note: The value will be rounded so it can be displayed with the current
        setting of decimals.

        @see: L{setDefaultValue}
        """

        QDial.setValue(self, value)
        self.updateValueLabel()

    def updateValueLabel(self):
        """
        Updates a widget's label with a value.
        """
        if self.label:
            self.value_label = "   " + self.label + " " + \
                "%-4d" % int(self.value()) + " " + self.suffix
            self.labelWidget.setText(self.value_label)

    def connectWithState(self,
                         stateref,
                         set_metainfo=True,
                         debug_metainfo=False):
        """
        Connect self to the state referred to by stateref,
        so changes to self's value change that state's value
        and vice versa. By default, also set self's metainfo
        to correspond to what the stateref provides.

        @param stateref: a reference to state of type double,
                         which meets the state-reference interface StateRef_API.
        @type stateref: StateRef_API

        @param set_metainfo: whether to also set defaultValue, minimum,
        and/or maximum, if these are provided by the stateref. (This
        list of metainfo attributes will probably be extended.)

        @type set_metainfo: bool

        @param debug_metainfo: whether to print debug messages
        about the actions taken by set_metainfo, when it's true.

        @type debug_metainfo: bool
        """
        if set_metainfo:
            # Do this first, so old min/max don't prevent setting the
            # correct current value when we connect new state.
            #
            # REVIEW: the conventions for expressing a lack of
            # minimum, maximum, or defaultValue, either on self
            # or on the stateref, may need revision, so it's not
            # ambiguous whether the stateref knows the minimum (etc)
            # should be unset on self or doesn't care what it's set to.
            # Ideally, some explicit value of stateref.minimum
            # would correspond to "no minimum" (i.e. a minimum of
            # negative infinity), etc.
            # [bruce 070926]
            set_metainfo_from_stateref(self.setMinimum, stateref, 'minimum',
                                       debug_metainfo)
            set_metainfo_from_stateref(self.setMaximum, stateref, 'maximum',
                                       debug_metainfo)
            set_metainfo_from_stateref(self.setDefaultValue, stateref,
                                       'defaultValue', debug_metainfo)
        ###widget_connectWithState( self, stateref,
        ###                        QDoubleSpinBox_ConnectionWithState)
        print "PM_Dial.connectWithState: not yet implemented"  #bruce 080811 added this line
        return

    def hide(self):
        """
        Hides the dial and its label (if it has one).
        Call L{show()} to unhide the dial.

        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()

    def show(self):
        """
        Unhides the dial and its label (if it has one).

        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()
Exemple #10
0
class PM_FontComboBox(QFontComboBox):
    """
    Much (practically all) of this code was taken from PM_ComboBox with only
    slight modifications.

    The PM_FontComboBox widget provides a combobox with a text label for a
    Property Manager group box. The text label can be positioned on either
    the left or right side of the combobox.

    For a more complete explanation of a ComboBox, refer to the definition
    provided in PM_ComboBox.  In this case, the list of available fonts is
    not provided by variable, but is read from the system.

    @cvar defaultFont: The default font of the combobox.
    @type defaultFont: QFont

    @cvar setAsDefault: Determines whether to reset the currentFont to
                        the defaultFont when the user clicks the
                        "Restore Defaults" button.
    @type setAsDefault: bool

    @cvar labelWidget: The Qt label widget of this combobox.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}

    @see: U{B{QComboBox}<http://doc.trolltech.com/4/qcombobox.html>}
    """

    defaultFont = None
    setAsDefault = True
    labelWidget = None

    def __init__(self,
                 parentWidget,
                 label='',
                 labelColumn=0,
                 selectFont='',
                 setAsDefault=True,
                 spanWidth=False):
        """
        Appends a QFontComboBox widget (with a QLabel widget) to <parentWidget>,
        a property manager group box.

        Arguments:

        @param parentWidget: the group box containing this PM widget.
        @type  parentWidget: PM_GroupBox

        @param label: label that appears to the left of (or above) this PM widget.
        @type  label: str

        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left
                            column) and 1 (right column). The default is 0
                            (left column).
        @type  labelColumn: int

        @param selectFont: initial font of combobox. (''=default)
        @type  selectFont: QFont object (default = '')

        @param setAsDefault: if True, will restore <defaultFont> as the current
                         Font when the "Restore Defaults" button is clicked.
        @type  setAsDefault: bool (default True)

        @param spanWidth: If True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left
                      justified.
        @type  spanWidth: bool (default False)

        @see: U{B{QComboBox}<http://doc.trolltech.com/4/qcombobox.html>}
        """

        if 0:  # Debugging code
            print "PM_FontComboBox.__init__():"
            print "  label        =", label
            print "  selectFont  =", selectFont
            print "  setAsDefault =", setAsDefault
            print "  spanWidth    =", spanWidth

        QFontComboBox.__init__(self)

        self.parentWidget = parentWidget
        self.label = label
        self.labelColumn = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth = spanWidth

        if label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        # Set initial choice (selectFont).
        if selectFont != '':
            self.setCurrentFont(selectFont)

        self.defaultFont = self.currentFont()

        parentWidget.addPmWidget(self)

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setCurrentFont(self.defaultFont)

    def hide(self):
        """
        Hides the combobox and its label (if it has one).

        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()

    def show(self):
        """
        Unhides the combobox and its label (if it has one).

        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()
Exemple #11
0
class PM_ComboBox(QComboBox):
    """
    The PM_ComboBox widget provides a combobox with a text label for a
    Property Manager group box. The text label can be positioned on either
    the left or right side of the combobox.

    A combobox is a combined button and popup list that provides a means of
    presenting a list of options to the user in a way that takes up the
    minimum amount of screen space.

    Also, a combobox is a selection widget that displays the current item,
    and can pop up a list of selectable items. A combobox may be editable,
    allowing the user to modify each item in the list.

    Comboboxes can contain pixmaps as well as strings; the insertItem()
    and changeItem() functions are suitably overloaded.
    For editable comboboxes, the function clearEdit() is provided, to
    clear the displayed string without changing the combobox's contents.

    There are two signals emitted if the current item of a combobox changes,
    currentIndexChanged() and activated(). currentIndexChanged() is always
    emitted regardless if the change was done programmatically or by user
    interaction, while activated() is only emitted when the change is caused
    by user interaction. The highlighted() signal is emitted when the user
    highlights an item in the combobox popup list. All three signals exist
    in two versions, one with a
    U{B{QString}<http://doc.trolltech.com/4/qstring.html>}
    argument and one with an int argument.
    If the user selectes or highlights a pixmap, only the int signals are
    emitted. Whenever the text of an editable combobox is changed the
    editTextChanged() signal is emitted.

    When the user enters a new string in an editable combobox, the widget may
    or may not insert it, and it can insert it in several locations.
    The default policy is is AtBottom but you can change this using
    setInsertPolicy().

    It is possible to constrain the input to an editable combobox using
    U{B{QValidator}<http://doc.trolltech.com/4/qvalidator.html>}
    ; see setValidator(). By default, any input is accepted.

    A combobox can be populated using the insert functions, insertStringList()
    and insertItem() for example. Items can be changed with changeItem().
    An item can be removed with removeItem() and all items can be removed
    with clear(). The text of the current item is returned by currentText(),
    and the text of a numbered item is returned with text(). The current item
    can be set with setCurrentIndex(). The number of items in the combobox is
    returned by count(); the maximum number of items can be set with
    setMaxCount(). You can allow editing using setEditable(). For editable
    comboboxes you can set auto-completion using setCompleter() and whether
    or not the user can add duplicates is set with setDuplicatesEnabled().

    PM_ComboBox uses the model/view framework for its popup list and to store
    its items. By default a QStandardItemModel stores the items and a
    QListView subclass displays the popuplist. You can access the model and
    view directly (with model() and view()), but PM_ComboBox also provides
    functions to set and get item data (e.g., setItemData() and itemText()).
    You can also set a new model and view (with setModel() and setView()).
    For the text and icon in the combobox label, the data in the model that
    has the Qt.DisplayRole and Qt.DecorationRole is used.

    @cvar defaultChoices: The default choices of the combobox.
    @type defaultChoices: list

    @cvar defaultIndex: The default index of the combobox.
    @type defaultIndex: int

    @cvar setAsDefault: Determines whether to reset the choices to
                        I{defaultChoices} and currentIndex to I{defaultIndex}
                        when the user clicks the "Restore Defaults" button.
    @type setAsDefault: bool

    @cvar labelWidget: The Qt label widget of this combobox.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}

    @see: U{B{QComboBox}<http://doc.trolltech.com/4/qcombobox.html>}
    """

    defaultIndex = 0
    defaultChoices = []
    setAsDefault = True
    labelWidget = None

    def __init__(self,
                 parentWidget,
                 label='',
                 labelColumn=0,
                 choices=[],
                 index=0,
                 setAsDefault=True,
                 spanWidth=False):
        """
        Appends a QComboBox widget (with a QLabel widget) to <parentWidget>,
        a property manager group box.

        Arguments:

        @param parentWidget: the group box containing this PM widget.
        @type  parentWidget: PM_GroupBox

        @param label: label that appears to the left of (or above) this PM widget.
        @type  label: str

        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left
                            column) and 1 (right column). The default is 0
                            (left column).
        @type  labelColumn: int

        @param choices: list of combo box choices (strings).
        @type  choices: list

        @param index: initial index (choice) of combobox. (0=first item)
        @type  index: int (default 0)

        @param setAsDefault: if True, will restore <index> as the current index
                         when the "Restore Defaults" button is clicked.
        @type  setAsDefault: bool (default True)

        @param spanWidth: If True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left justified.
        @type  spanWidth: bool (default False)

        @see: U{B{QComboBox}<http://doc.trolltech.com/4/qcombobox.html>}
        """

        if 0:  # Debugging code
            print "PM_ComboBox.__init__():"
            print "  label        =", label
            print "  choices      =", choices
            print "  index        =", index
            print "  setAsDefault =", setAsDefault
            print "  spanWidth    =", spanWidth

        QComboBox.__init__(self)

        self.parentWidget = parentWidget
        self.label = label
        self.labelColumn = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth = spanWidth

        if label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        # Load QComboBox widget choices and set initial choice (index).
        for choice in choices:
            self.addItem(choice)
        self.setCurrentIndex(index)

        # Set default index
        self.defaultIndex = index
        self.defaultChoices = choices
        self.setAsDefault = setAsDefault

        parentWidget.addPmWidget(self)

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.clear()  # Generates signals!
            for choice in self.defaultChoices:
                self.addItem(choice)
            self.setCurrentIndex(self.defaultIndex)

    def hide(self):
        """
        Hides the combobox and its label (if it has one).

        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()

    def show(self):
        """
        Unhides the combobox and its label (if it has one).

        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()

    def setCurrentIndex(self, val, blockSignals=False):
        """
        Overrides the superclass method.

        @param blockSignals: Many times, the caller just wants to setCurrentIndex
                             and don't want to send valueChanged signal.
                             If this flag is set to True, the currentIdexChanged
                             signal won't be emitted.  The default value is
                             False.
        @type  blockSignals: bool

        @see: DnaDisplayStyle_PropertyManager.updateDnaDisplayStyleWidgets()
        """
        #If blockSignals flag is True, the valueChanged signal won't be emitted
        #This is done by self.blockSignals method below.  -- Ninad 2008-08-13
        self.blockSignals(blockSignals)

        QComboBox.setCurrentIndex(self, val)

        #Make sure to always 'unblock' signals that might have been temporarily
        #blocked before calling superclass.setValue.
        self.blockSignals(False)


# End of PM_ComboBox ############################
Exemple #12
0
class AppForm(QMainWindow):
  'QT form for application'
  def __init__(self, splash,args,parent=None):
    self.starttime=None # start time of data
    self.endtime=None # end time of data
    self.bbox=args.b # geographical bounding box [w,e,s,n]
    self.preferredOrigin='p' # prefferred should be 'p': preferred origin ; 'f': first origin ; 'l': last origin
    self.includeunassoc=False # should we look also at unassociated origins (default - False)
    self.minmag=0 # minimum magnitude
    self.maxmag=10.0 # maximum magnitude
    self.deltaT=60.0 # absolute time difference in seconds to associate events
    self.deltaR=100.0 # absolute distance difference in km to associate events
    self._datadict = {} # dictionary of input events
    self._refdict = {} # dictionary of reference events
    self.data2reflut = {} # input to ref look up table
    self.ref2datalut = {} # ref to input look up table
    self.Bbox = bboxForm()
    self.Bbox.setLims(*self.bbox)
    splash.showMessage('Initializing...',Qt.AlignCenter)
    QApplication.processEvents()
    QMainWindow.__init__(self, parent)
    self.setWindowTitle('SC3 XML Diff')
    self.args = args # save command line arguments
    self.meter = mpl.lines.Line2D([],[],color='r') # line for measuring distances along canvas
    splash.showMessage('Creating application...',Qt.AlignCenter)
    QApplication.processEvents()
    self.create_menu() # create the app top menu
    self.create_status_bar() # add a status bar
    self.create_main_frame() # create the main frame.
    self.create_toolbox() # create a toolbox
    if args.i:
      splash.showMessage('Reading input data...',Qt.AlignCenter)
      QApplication.processEvents()
      self.get_input_file(args.i)
    if args.r:
      self.get_ref_file(args.r)
      splash.showMessage('Reading reference data...',Qt.AlignCenter)
      QApplication.processEvents()
    self.init_connections() # initialize signal connections
    splash.showMessage('Populating tables...',Qt.AlignCenter)
    QApplication.processEvents()
    self.updatetables()

  def init_connections(self):
    '''Connect signals to functions.
       Using signals to run functions from subprocesses.'''
    self.Bbox.accepted.connect(self.onBboxAccepted) # connect bbox to dialog ok button
    self.connect(self.minmagLine, SIGNAL('ok'),self.updateMagLims)
    self.connect(self.maxmagLine, SIGNAL('ok'),self.updateMagLims)
    self.connect(self.deltaTLine, SIGNAL('ok'),self.updateDeltaT)
    self.connect(self.deltaRLine, SIGNAL('ok'),self.updateDeltaR)
    self.starttimeLine.dateTimeChanged.connect(self.updatestarttime)
    self.endtimeLine.dateTimeChanged.connect(self.updateendtime)
    self.includeunassocLine.stateChanged.connect(self.updateUnassoc)
    self.preferredOriginLine.currentIndexChanged[str].connect(self.updatePreferredOrigin)
#    self.canvas.mpl_connect('button_press_event',self.on_click) # connect click on canvas
#    self.canvas.mpl_connect('button_release_event',self.on_unclick) # connect mouse button release on canvas
#    self.canvas.mpl_connect('motion_notify_event',self.on_move) # connect mouse motion on canvas

  def create_main_frame(self):
    'Create the main frame of application'
    # create widget
    self.main_frame = QWidget()
    # create a general layout
    vbox = QVBoxLayout()
    # create a side by side layout for tables
    self.tb = self.addToolBar('Tools')
    # tool bar can be moved around
    self.tb.setMovable(True)
    self.tb.setFloatable(True)
    hsplit = QSplitter(Qt.Horizontal)
    self.headerlabels = HEADERLABELS
    ######################################
    # create a layout for reference data #
    ######################################
    vboxref = QVBoxLayout()
    # add a title
    label = QLabel('Reference Data')
    # create the reference table
    self.reftable = QTableWidget(0,len(self.headerlabels))
    # Add header to table
    self.reftable.setHorizontalHeaderLabels(self.headerlabels)
    #self.reftable.setSortingEnabled(True)
    # settings for drag&drop
    self.reftable.setDragEnabled(True)
    self.reftable.setDragDropOverwriteMode(False)
    self.reftable.setDragDropMode(3) # can drag and drop
    # connect click events to row selection
    self.reftable.cellClicked.connect(self.reftable_cellClicked)
    # can select only rows
    self.reftable.setSelectionBehavior(QAbstractItemView.SelectRows)
    # no edits please
    #self.reftable.setEditTriggers(QAbstractItemView.NoEditTriggers)
    # No auto scrolling
    self.reftable.setAutoScroll(False)
    # connect drop events to adding events
    self.reftable.dropEvent = self.reftable_dropEvent
    # add label to ref layout
    vboxref.addWidget(label)
    # add table to ref layout
    vboxref.addWidget(self.reftable)
    ##################################
    # create a layout for input data #
    ##################################
    vboxdata = QVBoxLayout()
    # add a title
    label = QLabel('Input data')
    # create the input table
    self.datatable = QTableWidget(0,len(self.headerlabels))
    # Add header to table
    self.datatable.setHorizontalHeaderLabels(self.headerlabels)
    #self.datatable.setSortingEnabled(True)
    # settings for drag&drop
    self.datatable.setDragEnabled(True)
    self.datatable.setDragDropOverwriteMode(False)
    self.datatable.setDragDropMode(3) # can drag and drop
    # connect click events to row selection
    self.datatable.cellClicked.connect(self.datatable_cellClicked)
    # can select only rows
    self.datatable.setSelectionBehavior(QAbstractItemView.SelectRows)
    # no edits please
    #self.datatable.setEditTriggers(QAbstractItemView.NoEditTriggers)
    # No auto scrolling
    self.datatable.setAutoScroll(False)
    # connect drop events to ID association
    self.datatable.dropEvent = self.datatable_dropEvent
    # add label to SC3 layout
    vboxdata.addWidget(label)
    # add table to SC3 layout
    vboxdata.addWidget(self.datatable)
    # Add layouts to side by side layout
    w = QWidget()
    w.setLayout(vboxref)
    hsplit.addWidget(w)
    w = QWidget()
    w.setLayout(vboxdata)
    hsplit.addWidget(w)
    # add side by side to general layout
    vbox.insertWidget(1,hsplit)
    # add layout to widget
    self.main_frame.setLayout(vbox)
    # set widget to be central widget
    self.setCentralWidget(self.main_frame)

  def reftable_cellClicked(self,row,col):
    'any click on table will select the whole row'
    # select all row
    self.reftable.selectRow(row)
    # clear any selection in data table
    self.datatable.clearSelection()

  def datatable_cellClicked(self,row,col):
    'any click on table will select the whole row'
    # select all row
    self.datatable.selectRow(row)
    # clear any selection in ref table
    self.reftable.clearSelection()

  def datatable_dropEvent(self,event):
    'Associate ref event with input event if ref row is dragged and dropped on input row'
    if not event.source()==self.reftable: return # make sure row is from reference table
    if not self.datatable.itemAt(event.pos()): return # make sure we dropped reference row on an input row
    # get the referrence ID
    refID = str(event.source().selectedItems()[0].text())
    # get the ID item on input table
    item = self.datatable.item(self.datatable.itemAt(event.pos()).row(),0)
    inputID = str(item.text())
    # confirm with user to associate
    if refID in self.data2reflut or inputID in self.data2reflut:
      self.statusBar().showMessage('%s and/or %s are already associated. Aborting.'%(refID,inputID) , 2000)
      return
    ok = QMessageBox.question(self,'Associate Event',
                           'Are you sure you want to associate\n%s to %s?'%(refID,inputID),
                           'No','Yes')
    if ok:
      # update the lut
      self.ref2datalut[refID]=inputID
      self.data2reflut[inputID]=refID
      self.updateStatus()
      # report to status bar
      self.statusBar().showMessage('Associated %s with %s'%(refID,inputID) , 2000)

  def reftable_dropEvent(self,event):
    'Add new events to reference table if input row is dragged and dropped on ref table'
    if not event.source()==self.datatable: return # make sure its an input row
    # get row items
    items = event.source().selectedItems()
    # get input ID
    ID = str(items[0].text())
    # make sure ID is associated already
    if ID in self.data2reflut:
      self.statusBar().showMessage('Event %s already associated with %s'%(ID,self.data2reflut[ID]) , 2000)
      return
    # disable sorting
    self.datatable.setSortingEnabled(False)
    self.reftable.setSortingEnabled(False)
    #update lut
    self.data2reflut[ID]=ID
    self.ref2datalut[ID]=ID
    # add a new row to reference table
    i = self.reftable.rowCount()
    self.reftable.insertRow(i)
    # copy line from input table
    for j,item in enumerate(items):
      newitem = QTableWidgetItem(item.text())
      self.reftable.setItem(i,j,newitem)
    # update status
    self.updateStatus()
    # sort ref table
    self.datatable.setSortingEnabled(True)
    self.reftable.setSortingEnabled(True)
    # report to statusbar
    self.statusBar().showMessage('Added Event %s'%(ID) , 2000)

  def delete_row(self):
    'Delete a selected row from table'
    # get selected row(s) on SC3 table
    items = [i for i in self.datatable.selectedItems()+self.reftable.selectedItems() if i.column()==0]
    for item in items:
      # get selected row number
      row = item.row()
      # get event ID
      ID = str(item.text())
      # remove the row from table
      item.tableWidget().removeRow(row)
      # remove lut records
      [self.data2reflut.pop(k) for k,v in self.data2reflut.items() if k==ID or v==ID]
      [self.ref2datalut.pop(k) for k,v in self.ref2datalut.items() if k==ID or v==ID]
      self.updateStatus()
      # report to statusbar
      self.statusBar().showMessage('Removed Event %s from table'%ID , 2000)
    if len(items)>1: self.statusBar().showMessage('%d Events removed from table'%len(items) , 2000)

  def clear_table(self,table):
    'Clear a table'
    for i in range(table.rowCount())[::-1]:
      table.removeRow(i)

  def clear_tables(self):
    [self.clear_table(t) for t in [self.datatable,self.reftable]]

  def create_tooltip_widget(self):
    'creates tooltip of figure elements'
    self.TTWidget = QLabel() # take a QLabel
    self.TTWidget.setFrameShape(QFrame.StyledPanel) # add a frame
    self.TTWidget.setWindowFlags(Qt.ToolTip) # make window look like a tooltip
    self.TTWidget.setAttribute(Qt.WA_TransparentForMouseEvents) # mouse events can't affet it.
    self.TTWidget.hide() # hide for now. see self.on_move function on how to use.

  def get_input_file(self,filesurl=None):
    'open a dialog to get a file name'
    if not filesurl: filesurl = QFileDialog.getOpenFileNames(self, 'Open Input data file(s)',filter='*.xml') # get the file name
    self.ref2datalut={} # init look up table
    self.data2reflut={} # init look up table
    if len(filesurl):
      xmls = [str(f) for f in filesurl]
      Dataep = concatenateSC3XML(xmls)
      if not Dataep:
        self.statusBar().showMessage('No Events in file.', 2000)
        return
      self._datadict = eparams2seisEvents(Dataep,starttime=self.starttime,endtime=self.endtime,minmag=self.minmag,maxmag=self.maxmag,bbox=self.bbox,preferred=self.preferredOrigin,includeunassoc=self.includeunassoc)
      self.statusBar().showMessage('Loaded %d events'%len(self._datadict), 2000)
    self.updatetables()

  def get_ref_file(self,filesurl=None,refFileType=None):
    'open a dialog to get a file name'
    if not filesurl:
      filesurl,refFileType = QFileDialog.getOpenFileNamesAndFilter(self, 'Open Reference data file(s)',filter='XML Files (*.xml);;CSV Files (*.csv)') # get the file name
    else:
      refFileType =  os.path.splitext(filesurl[0])[1].upper()
    self.ref2datalut={}# init look up table
    self.data2reflut={}# init look up table
    if 'XML' in refFileType:
      xmls = [str(f) for f in filesurl]
      Refep = concatenateSC3XML(xmls)
      if not Refep:
        self.statusBar().showMessage('No Events in file.', 2000)
        return
      self._refdict = eparams2seisEvents(Refep,starttime=self.starttime,endtime=self.endtime,minmag=self.minmag,maxmag=self.maxmag,bbox=self.bbox,preferred=self.preferredOrigin,includeunassoc=self.includeunassoc)
      self.statusBar().showMessage('Loaded %d events'%len(self._refdict), 2000)
      return self.updatetables()
    if 'CSV' in refFileType:
      CSVs = [str(f) for f in filesurl]
      Refdb = concatenateCSV(CSVs)
      if not len(Refdb):
        self.statusBar().showMessage('No Events in file.', 2000)
        return self.updatetables()
      self._refdict = db2seisEvents(Refdb,starttime=self.starttime,endtime=self.endtime,minmag=self.minmag,maxmag=self.maxmag,bbox=self.bbox,preferred=self.preferredOrigin,includeunassoc=self.includeunassoc)
      self.statusBar().showMessage('Loaded %d events'%len(self._refdict), 2000)
      return self.updatetables()
    self.statusBar().showMessage("Can't load reference file(s)", 3000)

  def filterEvents(self,D):
    'filter events by time,location and magnitude limits'
    [D.pop(i) for i in [k for k in D if D[k].mag>self.maxmag or D[k].mag<self.minmag]]
    if self.starttime: [D.pop(i) for i in [k for k in D if D[k].ot<self.starttime]]
    if self.endtime: [D.pop(i) for i in [k for k in D if D[k].ot>self.endtime]]
    [D.pop(i) for i in [k for k in D if D[k].lon>self.bbox[1] or D[k].lon<self.bbox[0]]]
    [D.pop(i) for i in [k for k in D if D[k].lat>self.bbox[3] or D[k].lat<self.bbox[2]]]
    return D

  def associate(self):
    'associate events'
    self.ref2datalut = {}
    self.data2reflut = {}
    # filter unwanted events
    self.refdict = {}
    self.datadict = {}
    self.refdict.update(self._refdict)
    self.datadict.update(self._datadict)
    self.refdict = self.filterEvents(self.refdict)
    self.datadict= self.filterEvents(self.datadict)
    for ref in self.refdict.keys(): # get potentials associated events for reference. (time diff,seisevent obj) list
      potentials = [(abs(self.datadict[k].ot-self.refdict[ref].ot).total_seconds(),k) for k in self.datadict if
                     abs(self.datadict[k].ot-self.refdict[ref].ot).total_seconds()<=self.deltaT
                     and
                     (self.datadict[k]-self.refdict[ref])[-1]/1000.0<=self.deltaR]

      potentials.sort() # sort according to time difference
      if potentials:
        self.ref2datalut[ref] = potentials[0][1] # get first one.
        self.data2reflut[potentials[0][1]] = ref

  def recolor_table(self,table):
    ' add colors to lines. Green - True event, Red - false event, orange - missed event'
    # mark missed events on table
    stat = HEADERLABELS.index('Status')
    for i in xrange(table.rowCount()):
      table.selectRow(i)
      for item in table.selectedItems():
        f = item.foreground()
        if str(table.item(i,stat).text())=='M':
          f.setColor(QColor('#FFA500'))
        elif str(table.item(i,stat).text())=='F':
          f.setColor(QColor('red'))
        elif str(table.item(i,stat).text())=='T':
          f.setColor(QColor('k'))
        item.setForeground(f)
    table.clearSelection()


  def recolor_tables(self):
    [self.recolor_table(table) for table in [self.datatable,self.reftable]]

  def build_tables(self):
    'populate the gui tables from dictionaries'
    # populate ref table
    self.reftable.setSortingEnabled(False)
    for i,e in enumerate(self.refdict):
      # add a row
      self.reftable.insertRow(i)
      # create an ID item
      item = QTableWidgetItem(e)
      # add ID to row
      self.reftable.setItem(i,0,item)
      # insert event date to row
      for j,v in enumerate([self.refdict[e].__dict__[k] for k in ['ot','mag','lat','lon','status','refid','SCevaluationStatus']]):
        item = QTableWidgetItem(str(v))
        self.reftable.setItem(i,j+1,item)
    # sort the table
    self.reftable.setSortingEnabled(True)
    # adjust columns size
    self.reftable.resizeColumnsToContents()
    # populate data table
    self.datatable.setSortingEnabled(False)
    for i,e in enumerate(self.datadict):
      # add a row
      self.datatable.insertRow(i)
      # create an ID cell
      item = QTableWidgetItem(e)
      # add ID to row
      self.datatable.setItem(i,0,item)
      # insert event date to row
      for j,v in enumerate([self.datadict[e].__dict__[k] for k in ['ot','mag','lat','lon','status','refid','SCevaluationStatus']]):
        item = QTableWidgetItem(str(v))
        self.datatable.setItem(i,j+1,item)
    # sort the table
    self.datatable.setSortingEnabled(True)
    # adjust columns size
    self.datatable.resizeColumnsToContents()
    # update status
    self.updateStatus()

  def updateStatus(self):
    'update status of event (T,M,F)'
    # referrece table
    stat = HEADERLABELS.index('Status')
    refID = HEADERLABELS.index('refID')
    self.reftable.setSortingEnabled(False)
    for i in xrange(self.reftable.rowCount()):
      ID = str(self.reftable.item(i,0).text())
      if ID in self.ref2datalut:
        self.reftable.item(i,stat).setText('T')
        self.reftable.item(i,refID).setText(self.ref2datalut[ID])
      else:
        self.reftable.item(i,stat).setText('M')
        self.reftable.item(i,refID).setText('')
    self.reftable.setSortingEnabled(True)
    self.datatable.setSortingEnabled(False)
    for i in xrange(self.datatable.rowCount()):
      ID = str(self.datatable.item(i,0).text())
      if ID in self.data2reflut:
        self.datatable.item(i,stat).setText('T')
        self.datatable.item(i,refID).setText(self.data2reflut[ID])
      else:
        self.datatable.item(i,stat).setText('F')
        self.datatable.item(i,refID).setText('')
    self.datatable.setSortingEnabled(True)
    # repaint missed False and true events
    self.recolor_tables()

  def updatetables(self):
    self.associate()
    self.clear_tables()
    self.build_tables()
    self.statusBar().showMessage('Tables updated',2000)

  def updateMagLims(self):
    self.minmag = float(self.minmagLine.text())
    self.maxmag = float(self.maxmagLine.text())
    self.statusBar().showMessage('Magnitude limits updated',2000)

  def updateDeltaT(self):
    self.deltaT = float(self.deltaTLine.text())
    self.statusBar().showMessage('Association time limit updated',2000)

  def updateDeltaR(self):
    self.deltaR = float(self.deltaRLine.text())
    self.statusBar().showMessage('Association distance limit updated',2000)

  def updatestarttime(self,starttime):
    self.starttime = starttime.toPyDateTime()
    self.statusBar().showMessage('Start time limit updated',2000)

  def updateendtime(self,endtime):
    self.endtime = endtime.toPyDateTime()
    self.statusBar().showMessage('End time limit updated',2000)

  def updateUnassoc(self,stat):
    self.includeunassoc = bool(stat)
    self.statusBar().showMessage('Including unassociated origins set to %s'%self.includeunassoc,2000)

  def updatePreferredOrigin(self,pref):
    self.preferredOrigin = pref[0]
    self.statusBar().showMessage('Preferred Origins set to %s'%pref,2000)

  def evaluate(self):
    i = self.headerlabels.index('Status')
    table = self.datatable
    T = len([table.item(j,i) for j in xrange(table.rowCount()) if str(table.item(j,i).text())=='T'])
    F = len([table.item(j,i) for j in xrange(table.rowCount()) if str(table.item(j,i).text())=='F'])
    table = self.reftable
    M = len([table.item(j,i) for j in xrange(table.rowCount()) if str(table.item(j,i).text())=='M'])
    self.statusBar().showMessage('T: %d ; M: %d ; F: %d ; Score: %.1f%% for %d events'%(T,M,F,100.0*T/sum([T,F,M]),sum([T,F,M])), 5000)
    return T,F,M,100.0*T/sum([T,F,M])

  def saveAs_figure(self):
    pass

  def create_menu(self):
    'Creates main menu'
    # Populate the menubar:
    # Add File submenu
    self.file_menu = self.menuBar().addMenu("&File")
    # load input data
    load_input_action = self.create_action("Load &Input",
            shortcut="Ctrl+I", slot=self.get_input_file,
            icon='document-open',tip="Load input data from a file(s)")
    # load reference data
    load_ref_action = self.create_action("Load &Reference",
            shortcut="Ctrl+R", slot=self.get_ref_file,
            icon='document-open',tip="Load reference data from a file(s)")
    # Save As...
    saveAs_action = self.create_action("S&ave As...",
            shortcut="Shift+S", slot=self.saveAs_figure,
            icon='filesaveas',tip="Save the figure")
    # Quit
    quit_action = self.create_action("&Quit", slot=self.close,
            icon='system-shutdown',shortcut="Ctrl+Q", tip="Close the application")
    # populate the file submenu
    self.add_actions(self.file_menu,
            (load_input_action,load_ref_action, saveAs_action, None, quit_action))
    # Add Edit submenu
    self.Edit_menu = self.menuBar().addMenu("&Edit")
        # delete an event
    delete_action   = self.create_action("&Delete",
            slot=self.delete_row,shortcut="Del",
            tip="Delete selected row from table")
    # populate tools submenu
    self.add_actions(self.Edit_menu,[delete_action])
    # Add help submenu
    self.help_menu = self.menuBar().addMenu("&Help")
    # Help
    help_action = self.create_action("&Help",
            shortcut='F1', slot=self.on_help,
            icon='Help',tip='help')
    # About
    about_action = self.create_action("&About",
            shortcut='F2', slot=self.on_about,
            tip='About This Application')
    # About QT
    aboutQt_action = self.create_action("&About QT",
            shortcut='F3', slot=self.on_aboutQt,
            tip='About QT')
    # License
    license_action = self.create_action("&License",
            shortcut='F4', slot=self.on_license,
            tip='Application License')
    # Populate help submenu
    self.add_actions(self.help_menu, (help_action,None,about_action,aboutQt_action,license_action))

  def create_toolbox(self):
    w = QWidget()
    l = QGridLayout(w)
    self.refreshbutton = self.create_pushButton('Refresh', slot=self.updatetables, shortcut=None, icon=None, tip='Refresh Tables')
    self.scorebutton = self.create_pushButton('Score', slot=self.evaluate, shortcut=None, icon=None, tip='Evaluate Score')
    l.addWidget(self.refreshbutton,0,0)
    l.addWidget(self.scorebutton,1,0)
    self.tb.addWidget(w)
    w = QWidget()
    l = QGridLayout(w)
    self.minmagLine = ConnectedLineEdit(str(self.minmag),0,self.maxmag)
    self.maxmagLine = ConnectedLineEdit(str(self.maxmag),self.minmag,10)
    self.minmagLine.setConnected(self.maxmagLine, 'bottom')
    self.maxmagLine.setConnected(self.minmagLine, 'top')
    l.addWidget(QLabel('Magnitude Limits'),0,1)
    l.addWidget(QLabel('Min'),1,0)
    l.addWidget(QLabel('Max'),2,0)
    l.addWidget(self.minmagLine,1,1)
    l.addWidget(self.maxmagLine,2,1)
    self.tb.addWidget(w)
    w = QWidget()
    l = QGridLayout(w)
    self.deltaTLine = ConnectedLineEdit(str(self.deltaT),0,18000,3)
    self.deltaRLine = ConnectedLineEdit(str(self.deltaR),0,1000,3)
    l.addWidget(QLabel('Association Limits'),0,1)
    l.addWidget(QLabel('Time (sec)'),1,0)
    l.addWidget(QLabel('Range (km)'),2,0)
    l.addWidget(self.deltaTLine,1,1)
    l.addWidget(self.deltaRLine,2,1)
    self.tb.addWidget(w)
    w = QWidget()
    v = QVBoxLayout(w)
    ww = QWidget()
    v.addWidget(ww)
    l = QGridLayout(ww)
    W,E,S,N = self.bbox
    self.EAST = QLabel(str(E))
    self.WEST = QLabel(str(W))
    self.NORTH = QLabel(str(N))
    self.SOUTH = QLabel(str(S))
    self.bboxbutton = self.create_pushButton('Region', slot=self.Bbox.show, shortcut=None, icon=None, tip='Define a geographic region')
    l.addWidget(self.EAST,1,2,1,1,Qt.Alignment(Qt.AlignLeft))
    l.addWidget(self.WEST,1,0,1,1,Qt.Alignment(Qt.AlignRight))
    l.addWidget(self.NORTH,0,1,1,1,Qt.Alignment(Qt.AlignHCenter))
    l.addWidget(self.SOUTH,2,1,1,1,Qt.Alignment(Qt.AlignHCenter))
    v.addWidget(self.bboxbutton)
    self.tb.addWidget(w)
    w = QWidget()
    l = QGridLayout(w)
    self.starttimeLine = QDateTimeEdit()
    self.starttimeLine.setDisplayFormat('yyyy-MM-dd HH:mm:ss')
    self.starttimeLine.setCalendarPopup(True)
    self.endtimeLine = QDateTimeEdit()
    self.endtimeLine.setDateTime(QDateTime.currentDateTimeUtc())
    self.endtimeLine.setDisplayFormat('yyyy-MM-dd HH:mm:ss')
    self.endtimeLine.setCalendarPopup(True)
    l.addWidget(QLabel('Start:'),0,0)
    l.addWidget(self.starttimeLine,0,1)
    l.addWidget(QLabel('End:'),1,0)
    l.addWidget(self.endtimeLine,1,1)
    self.tb.addWidget(w)
    w = QWidget()
    l = QGridLayout(w)
    self.preferredOriginLine = QComboBox()
    self.preferredOriginLine.addItems(['preferred','first','last'])
    self.includeunassocLine = QCheckBox('Unassociated')
    self.includeunassocLine.setCheckState(self.includeunassoc)
    l.addWidget(QLabel('Origins:'),0,0)
    l.addWidget(self.preferredOriginLine,1,0)
    l.addWidget(self.includeunassocLine,2,0)
    self.tb.addWidget(w)

  def onBboxAccepted(self):
    '''get region limits from BboxForm.
       fires when Bbox is accepted'''
    if self.Bbox.validate(): # make sure limits are acceptable
      self.bbox = self.Bbox.getLims() # get limits
      w,e,s,n = self.bbox
      self.WEST.setText(str(w))
      self.EAST.setText(str(e))
      self.NORTH.setText(str(n))
      self.SOUTH.setText(str(s))

  def add_actions(self, target, actions):
    'Utility function for menu creation'
    for action in actions:
      if action is None:
        target.addSeparator()
      else:
        target.addAction(action)

  def create_action(  self, text, slot=None, shortcut=None,
                      icon=None, tip=None, checkable=False,
                      signal="triggered()"):
    'Utility function for menu actions creation'
    action = QAction(text, self)
    action.setIconVisibleInMenu(True)
    if icon is not None:
      i = QIcon.fromTheme(icon,QIcon(":/%s.png" % icon))
      action.setIcon(i)
    if shortcut is not None:
      action.setShortcut(shortcut)
    if tip is not None:
      action.setToolTip(tip)
      action.setStatusTip(tip)
    if slot is not None:
      self.connect(action, SIGNAL(signal), slot)
    if checkable:
      action.setCheckable(True)
    return action

  def create_pushButton(self,text,toolbar=None, slot=None, shortcut=None, icon=None, tip=None):
    'Utility function for button creation'
    # create the button
    button = QPushButton(text,self)
    # populate properties
    if slot:
      # connect a function
      button.clicked.connect(slot)
    if icon:
      # add icon
      i = QIcon.fromTheme(icon,QIcon(":/%s.png" % icon))
      button.setIcon(i)
      button.setIconSize(QSize(24,24))
    if shortcut:
      # set the shortcut
      button.setShortcut(shortcut)
    if tip:
      # add tooltip and status tip
      button.setToolTip(tip)
      button.setStatusTip(tip)
    if toolbar:
      # add the button to a toolbar (or any widget)
      toolbar.addWidget(button)
    return button

  def create_status_bar(self):
    'Add a status bar'
    # set default message
    self.status_text = QLabel("Ready")
    self.connstatLabel = QLabel()
    self.statusBar().addWidget(self.status_text, 1)
    self.statusBar().addPermanentWidget(self.connstatLabel)


  def on_about(self):
    'show a messagebox about the application'
    msg = "<p align='center'><big>SCxmlDiff</big><br><br> \
    Compare two Seiscomp3 XML files<br><br> \
    <small>Created<br> \
    by<br> \
    Ran Novitsky Nof @ BSL, 2015</small><br><br>\
    <a href='http://ran.rnof.info/'>http://ran.rnof.info</a><p>"
    QMessageBox.about(self,"About", msg.strip())

  def on_aboutQt(self):
    'show a messagebox about QT'
    QMessageBox.aboutQt(self,'')

  def on_license(self):
    'GPL licanse message'
    msg = "<p><b>This</b> is a free software; you can redistribute it and/or modify it under the \
terms of the GNU General Public License as published by the Free Software \
Foundation; either version 3 of the License, or (at your option) any later \
version.</p>\
<p><b>This application</b> is distributed in the hope that it will be useful, but WITHOUT ANY \
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR \
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.</p> \
<p>You should have received a copy of the GNU General Public License along with \
this application; if not, see <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.</p>"
    QMessageBox.about(self,"Application Licanse", msg.strip())

  def on_help(self):
    'Show help on a message window. Uses the argparse help'
    msg = '<pre>'+parser.format_help()#.replace('\n','</p><p>')
    msg = msg.replace('*****@*****.**',"<a href='mailto:[email protected]'>[email protected]</a>").strip()+'<\pre>'
    QMessageBox.about(self,"Help", msg)

  def message(self,msg,title='Error'):
    'a simple message window'
    QMessageBox.about(self,title,msg)
Exemple #13
0
class InformationWindow(QWidget):
    def __init__(self):
        QWidget.__init__(self, ctx.mainScreen)
        self.setObjectName("InformationWindow")
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.setFixedHeight(50)
        self.setMaximumWidth(800)
        self.setStyleSheet("""
            QFrame#frame { border: 1px solid rgba(255,255,255,30);
                           /*border-radius: 4px;*/
                           background-color: rgba(0,0,0,100);}

            QLabel { border:none;
                     color:#FFFFFF;}

            QProgressBar { border: 1px solid white;}

            QProgressBar::chunk { background-color: #F1610D;
                                  width: 0.5px;}
        """)

        self.gridlayout = QGridLayout(self)
        self.frame = QFrame(self)
        self.frame.setObjectName("frame")
        self.horizontalLayout = QHBoxLayout(self.frame)
        self.horizontalLayout.setContentsMargins(10, 0, 10, 0)

        # Spinner
        self.spinner = QLabel(self.frame)
        self.spinner.setMinimumSize(QSize(16, 16))
        self.spinner.setMaximumSize(QSize(16, 16))
        self.spinner.setIndent(6)
        self.movie = QMovie(':/images/working.mng')
        self.spinner.setMovie(self.movie)
        self.movie.start()
        self.horizontalLayout.addWidget(self.spinner)

        # Message
        self.label = QLabel(self.frame)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.icon = QLabel(self.frame)
        self.icon.setFixedWidth(16)
        self.icon.setFixedHeight(16)
        self.horizontalLayout.setSpacing(10)
        self.horizontalLayout.addWidget(self.icon)
        self.horizontalLayout.addWidget(self.label)

        self.gridlayout.addWidget(self.frame, 0, 0, 1, 1)

    def update(self, message, type=None, spinner=False):
        fontMetric = self.label.fontMetrics()
        textWidth = fontMetric.width(message)

        if type:
            self.icon.show()
            if type == "error":
                self.icon.setPixmap(QPixmap(":/gui/pics/dialog-error.png"))
                self.setStyleSheet(
                    " QFrame#frame {background-color: rgba(255,0,0,100);} ")

            elif type == "warning":
                self.icon.setPixmap(QPixmap(":/gui/pics/dialog-warning.png"))
                self.setStyleSheet(
                    " QFrame#frame {background-color: rgba(0,0,0,100);} ")

            self.setFixedWidth(textWidth + self.icon.width() + 50)
            self.label.setText(message)

        else:
            self.icon.hide()
            self.setStyleSheet(
                " QFrame#frame {background-color: rgba(0,0,0,100);} ")
            self.setFixedWidth(textWidth + self.icon.width() + 100)
            self.label.setText(message)

        self.spinner.setVisible(spinner)
        self.move(ctx.mainScreen.width() / 2 - self.width() / 2,
                  ctx.mainScreen.height() - self.height() / 2 - 50)

        self.show()

    def refresh(self):
        ctx.mainScreen.processEvents()

    def show(self):
        QWidget.show(self)
        self.refresh()

    def hide(self):
        QWidget.hide(self)
        self.refresh()
Exemple #14
0
class PM_ListWidget( QListWidget ):
    """
    The PM_ListWidget widget provides a QListWidget with a 
    QLabel for a Property Manager group box.
    
    @cvar defaultItems: The default list of items in the list widget.
    @type defaultItems: list
                          
    @cvar defaultRow: The default row of the list widget.
    @type defaultRow: int
    
    @cvar setAsDefault: Determines whether to reset the choices to 
                        I{defaultItems} and current row to I{defaultRow}
                        when the user clicks the "Restore Defaults" button.
    @type setAsDefault: bool
    
    @cvar labelWidget: The Qt label widget of this list widget.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    """
    
    defaultRow   = 0
    defaultItems = []
    setAsDefault = True
    labelWidget  = None
    
    def __init__(self, 
                 parentWidget, 
                 label        = '', 
                 labelColumn  = 0,
                 items        = [], 
                 defaultRow   = 0, 
                 setAsDefault = True,
                 heightByRows = 6, 
                 spanWidth    = False
                 ):
        """     
        Appends a QListWidget (Qt) widget to the bottom of I{parentWidget}, 
        a Property Manager group box.
        
        @param parentWidget: The parent group box containing this widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: The label that appears to the left or right of the 
                      checkbox. 
                      
                      If spanWidth is True, the label will be displayed on
                      its own row directly above the list widget.
                      
                      To suppress the label, set I{label} to an 
                      empty string.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
        
        @param items: list of items (strings) to be inserted in the widget.
        @type  items: list
        
        @param defaultRow: The default row (item) selected, where 0 is the first 
                           row.
        @type  defaultRow: int
        
        @param setAsDefault: If True, will restore <idx> as the current index
                         when the "Restore Defaults" button is clicked.
        @type  setAsDefault: bool
        
        @param heightByRows: The height of the list widget.
        @type  heightByRows: int
        
        @param spanWidth: If True, the widget and its label will span the width
                          of the group box. Its label will appear directly above
                          the widget (unless the label is empty) and is left 
                          justified.
        @type  spanWidth: bool
        
        @see: U{B{QListWidget}<http://doc.trolltech.com/4/qlistwidget.html>}
        """
        
        if 0: # Debugging code
            print "PM_ListWidget.__init__():"
            print "  label        = ", label
            print "  labelColumn  = ", labelColumn
            print "  items        = ", items
            print "  defaultRow   = ", defaultRow
            print "  setAsDefault = ", setAsDefault
            print "  heightByRows = ", heightByRows
            print "  spanWidth    = ", spanWidth
                
        QListWidget.__init__(self)
        
        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth
        
        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)
               
        # Load QComboBox widget choice items and set initial choice (index).           
        self.insertItems(0, items, setAsDefault)
        self.setCurrentRow(defaultRow, setAsDefault)
                
        # Set height of list widget.
        margin = self.fontMetrics().leading() * 2 # Mark 2007-05-28
        height = heightByRows * self.fontMetrics().lineSpacing() + margin
        self.setMaximumHeight(height)
                     
        
        #As of 2008-04-16, the items in any list widgets won't be sorted 
        #automatically. It can be changes by simply uncommentting the lines
        #below -- Ninad
        ##self.setSortingEnabled(True)
        ##self.sortItems() 
        
        self.setAlternatingRowColors(True)                        
        parentWidget.addPmWidget(self)
        
    def insertItems(self, row, items, setAsDefault = True):
        """
        Insert items of widget starting at <row>. 
        If <setAsDefault> is True, <items> become the default list of
        items for this widget. "Restore Defaults" will reset 
        the list of items to <items>.
        
        Note: <items> will always replace the list of current items
        in the widget. <row> is ignored. This is considered a bug.
        -- Mark 2007-06-04
        """
        if row <> 0:
            msg = "PM_ListWidget.insertItems(): <row> must be zero."\
                "See docstring for details:"
            print_compact_traceback(msg)
            return
            
        if setAsDefault:
            self.setAsDefault = setAsDefault
            self.defaultItems = items
        
        self.clear()
        QListWidget.insertItems(self, row, items)
        
    def setCurrentRow(self, row, setAsDefault = False ):
        """
        Select new row. 
        
        @param row: The new row to select.
        @type  row: int
        
        @param setAsDefault: If True, I{row} becomes the default row when
                             "Restore Defaults" is clicked.
        """
        if setAsDefault:
            self.setAsDefault = setAsDefault
            self.defaultRow   = row
        QListWidget.setCurrentRow(self, row)
        
    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.insertItems(0, self.defaultItems)
            self.setCurrentRow(self.defaultRow)
            ## self.clear()
            ## for choice in self.defaultChoices:
            ##     self.addItem(choice)
            ## self.setCurrentRow(self.defaultRow)
        
    def hide(self):
        """
        Hides the list widget and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget: 
            self.labelWidget.hide()
            
    def show(self):
        """
        Unhides the list widget and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget: 
            self.labelWidget.show()

# End of PM_ListWidget ############################
Exemple #15
0
class HTMLViewerDialog(QDialog):
    """This class implements a dialog to view a piece of HTML text."""

    def __init__(self, parent, config_name=None, buttons=[], *args):
        """Creates dialog.
        'config_name' is used to get/set default window size from Config object
        'buttons' can be a list of names or (QPixmapWrapper,name[,tooltip]) tuples to provide
        custom buttons at the bottom of the dialog. When a button is clicked, the dialog
        emits SIGNAL("name").
        A "Close" button is always provided, this simply hides the dialog.
        """
        QDialog.__init__(self, parent, *args)
        self.setModal(False)
        lo = QVBoxLayout(self)
        # create viewer
        self.label = QLabel(self)
        self.label.setMargin(5)
        self.label.setWordWrap(True)
        lo.addWidget(self.label)
        self.label.hide()
        self.viewer = QTextBrowser(self)
        lo.addWidget(self.viewer)
        # self.viewer.setReadOnly(True)
        self.viewer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        QObject.connect(self.viewer, SIGNAL("anchorClicked(const QUrl &)"), self._urlClicked)
        self._source = None
        lo.addSpacing(5)
        # create button bar
        btnfr = QFrame(self)
        btnfr.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
        # btnfr.setMargin(5)
        lo.addWidget(btnfr)
        lo.addSpacing(5)
        btnfr_lo = QHBoxLayout(btnfr)
        btnfr_lo.setMargin(5)
        # add user buttons
        self._user_buttons = {}
        for name in buttons:
            if isinstance(name, str):
                btn = QPushButton(name, btnfr)
            elif isinstance(name, (list, tuple)):
                if len(name) < 3:
                    pixmap, name = name
                    tip = None
                else:
                    pixmap, name, tip = name
                btn = QPushButton(pixmap.icon(), name, btnfr)
                if tip:
                    btn.setToolTip(tip)
            self._user_buttons[name] = btn
            btn._clicked = Kittens.utils.curry(self.emit, SIGNAL(name))
            self.connect(btn, SIGNAL("clicked()"), btn._clicked)
            btnfr_lo.addWidget(btn, 1)
        # add a Close button
        btnfr_lo.addStretch(100)
        closebtn = QPushButton(pixmaps.grey_round_cross.icon(), "Close", btnfr)
        self.connect(closebtn, SIGNAL("clicked()"), self.hide)
        btnfr_lo.addWidget(closebtn, 1)
        # resize selves
        self.config_name = config_name or "html-viewer"
        width = Config.getint('%s-width' % self.config_name, 512)
        height = Config.getint('%s-height' % self.config_name, 512)
        self.resize(QSize(width, height))

    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        sz = ev.size()
        Config.set('%s-width' % self.config_name, sz.width())
        Config.set('%s-height' % self.config_name, sz.height())

    def setDocument(self, filename, empty=""):
        """Sets the HTML text to be displayed. """
        self._source = QUrl.fromLocalFile(filename)
        if os.path.exists(filename):
            self.viewer.setSource(self._source)
        else:
            self.viewer.setText(empty)

    def _urlClicked(self, url):
        path = str(url.path())
        if path:
            self.emit(SIGNAL("viewPath"), path)
        # to make sure it keeps displaying the same thing
        self.viewer.setSource(self._source)

    def reload(self):
        self.viewer.reload()

    def setLabel(self, label=None):
        if label is None:
            self.label.hide()
        else:
            self.label.setText(label)
            self.label.show()
Exemple #16
0
class HTMLViewerDialog(QDialog):
    """This class implements a dialog to view a piece of HTML text."""
    def __init__(self, parent, config_name=None, buttons=[], *args):
        """Creates dialog.
        'config_name' is used to get/set default window size from Config object
        'buttons' can be a list of names or (QPixmapWrapper,name[,tooltip]) tuples to provide
        custom buttons at the bottom of the dialog. When a button is clicked, the dialog
        emits SIGNAL("name").
        A "Close" button is always provided, this simply hides the dialog.
        """
        QDialog.__init__(self, parent, *args)
        self.setModal(False)
        lo = QVBoxLayout(self)
        # create viewer
        self.label = QLabel(self)
        self.label.setMargin(5)
        self.label.setWordWrap(True)
        lo.addWidget(self.label)
        self.label.hide()
        self.viewer = QTextBrowser(self)
        lo.addWidget(self.viewer)
        # self.viewer.setReadOnly(True)
        self.viewer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        QObject.connect(self.viewer, SIGNAL("anchorClicked(const QUrl &)"),
                        self._urlClicked)
        self._source = None
        lo.addSpacing(5)
        # create button bar
        btnfr = QFrame(self)
        btnfr.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
        # btnfr.setMargin(5)
        lo.addWidget(btnfr)
        lo.addSpacing(5)
        btnfr_lo = QHBoxLayout(btnfr)
        btnfr_lo.setMargin(5)
        # add user buttons
        self._user_buttons = {}
        for name in buttons:
            if isinstance(name, str):
                btn = QPushButton(name, btnfr)
            elif isinstance(name, (list, tuple)):
                if len(name) < 3:
                    pixmap, name = name
                    tip = None
                else:
                    pixmap, name, tip = name
                btn = QPushButton(pixmap.icon(), name, btnfr)
                if tip:
                    btn.setToolTip(tip)
            self._user_buttons[name] = btn
            btn._clicked = Kittens.utils.curry(self.emit, SIGNAL(name))
            self.connect(btn, SIGNAL("clicked()"), btn._clicked)
            btnfr_lo.addWidget(btn, 1)
        # add a Close button
        btnfr_lo.addStretch(100)
        closebtn = QPushButton(pixmaps.grey_round_cross.icon(), "Close", btnfr)
        self.connect(closebtn, SIGNAL("clicked()"), self.hide)
        btnfr_lo.addWidget(closebtn, 1)
        # resize selves
        self.config_name = config_name or "html-viewer"
        width = Config.getint('%s-width' % self.config_name, 512)
        height = Config.getint('%s-height' % self.config_name, 512)
        self.resize(QSize(width, height))

    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        sz = ev.size()
        Config.set('%s-width' % self.config_name, sz.width())
        Config.set('%s-height' % self.config_name, sz.height())

    def setDocument(self, filename, empty=""):
        """Sets the HTML text to be displayed. """
        self._source = QUrl.fromLocalFile(filename)
        if os.path.exists(filename):
            self.viewer.setSource(self._source)
        else:
            self.viewer.setText(empty)

    def _urlClicked(self, url):
        path = str(url.path())
        if path:
            self.emit(SIGNAL("viewPath"), path)
        # to make sure it keeps displaying the same thing
        self.viewer.setSource(self._source)

    def reload(self):
        self.viewer.reload()

    def setLabel(self, label=None):
        if label is None:
            self.label.hide()
        else:
            self.label.setText(label)
            self.label.show()
Exemple #17
0
class SelectNodeByNameDockWidget(PM_DockWidget):
    """
    The Ui_DnaSequenceEditor class defines UI elements for the Sequence Editor
    object. The sequence editor is usually visible while in DNA edit mode.
    It is a DockWidget that is doced at the bottom of the MainWindow
    """
    _title = "Select Node by Name"
    _groupBoxCount = 0
    _lastGroupBox = None

    def __init__(self, win):
        """
        Constructor for the Ui_DnaSequenceEditor 
        @param win: The parentWidget (MainWindow) for the sequence editor 
        """

        self.win = win
        parentWidget = win

        _superclass.__init__(self, parentWidget, title=self._title)

        win.addDockWidget(Qt.BottomDockWidgetArea, self)
        self.setFixedHeight(120)
        ##self.setFixedWidth(90)
        self.connect_or_disconnect_signals(True)

        if not self.win.selectByNameAction.isChecked():
            self.close()

    def show(self):
        """
        Overrides superclass method.
        """
        _superclass.show(self)
        val = env.prefs[dnaSearchTypeLabelChoice_prefs_key]
        self.searchTypeComboBox_indexChanged(val)

    def connect_or_disconnect_signals(self, isConnect):
        """
        Connect or disconnect widget signals sent to their slot methods.
        This can be overridden in subclasses. By default it does nothing.
        @param isConnect: If True the widget will send the signals to the slot 
                          method. 
        @type  isConnect: boolean
        """

        if isConnect:
            change_connect = self.win.connect
        else:
            change_connect = self.win.disconnect

        self._listWidget.connect_or_disconnect_signals(isConnect)

        change_connect(self.searchToolButton, SIGNAL("clicked()"),
                       self.searchNodes)

        prefs_key = dnaSearchTypeLabelChoice_prefs_key
        connect_comboBox_with_pref(self.searchTypeComboBox, prefs_key)

        change_connect(self.searchTypeComboBox,
                       SIGNAL("currentIndexChanged(int)"),
                       self.searchTypeComboBox_indexChanged)

    def searchTypeComboBox_indexChanged(self, val):
        if val == 0:
            ##self._widgetRow1.show()
            ##self._widgetRow2.hide()
            self._widgetRow1.setEnabled(True)
            self._widgetRow2.setEnabled(False)
        else:
            ##self._widgetRow2.show()
            ##self._widgetRow1.hide()
            self._widgetRow2.setEnabled(True)
            self._widgetRow1.setEnabled(False)

    def searchNodes(self):
        """
        ONLY implemented for DnaStrand or DnaSegments. 
        """

        assy = self.win.assy

        topnode = assy.part.topnode
        lst = []

        def func(node):
            if isinstance(node, assy.DnaStrandOrSegment):
                lst.append(node)

        topnode.apply2all(func)

        choice = env.prefs[dnaSearchTypeLabelChoice_prefs_key]

        if choice == 0:
            nodes = self._searchNodesByName(lst)
        elif choice == 1:
            nodes = self._searchNodesByNucleotides(lst)

        self._listWidget.insertItems(row=0, items=nodes)

    def _searchNodesByNucleotides(self, nodeList):
        lst = nodeList
        min_val = self._nucleotidesSpinBox_1.value()
        max_val = self._nucleotidesSpinBox_2.value()
        if min_val > max_val:
            print "Lower value for number of nucleotides exceeds max search value"
            return ()

        def func2(node):
            n = node.getNumberOfNucleotides()

            return (n >= min_val and n <= max_val)

        return filter(lambda m: func2(m), lst)

    def _searchNodesByName(self, nodeList):
        nodeNameString = self.findLineEdit.text()
        nodeNameString = str(nodeNameString)

        lst = nodeList

        def func2(node):
            n = len(nodeNameString)
            if len(node.name) < n:
                return False

            nameString = str(node.name[:n])

            if nameString.lower() == nodeNameString.lower():
                return True

            return False

        return filter(lambda m: func2(m), lst)

    def closeEvent(self, event):
        self.win.selectByNameAction.setChecked(False)
        _superclass.closeEvent(self, event)

    def _loadWidgets(self):
        """
        Overrides PM.PM_DockWidget._loadWidgets. Loads the widget in this
        dockwidget.
        """
        self._loadMenuWidgets()
        self._loadTableWidget()

    def _loadTableWidget(self):
        self._listWidget = PM_DnaSearchResultTable(self, self.win)

    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.searchTypeComboBox  = \
            PM_ComboBox( self,
                         label         =  "Search options:",
                         choices       =  ["By node name", "By # of bases (DNA only)"],
                         setAsDefault  =  True)

        #Find  widgets --
        self._nucleotidesSpinBox_1 = PM_SpinBox(self,
                                                label="",
                                                value=10,
                                                setAsDefault=False,
                                                singleStep=10,
                                                minimum=1,
                                                maximum=50000)

        self._nucleotidesSpinBox_2 = PM_SpinBox(self,
                                                label="",
                                                value=50,
                                                setAsDefault=False,
                                                singleStep=10,
                                                minimum=1,
                                                maximum=50000)


        self.findLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)

        self.findLineEdit.setMaximumWidth(80)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        ##self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        ##self._setFindOptionsToolButtonMenu()

        self.searchToolButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Find_Next.png")
        self.searchToolButton.setAutoRaise(False)

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Not Found")
        self.phraseNotFoundLabel.hide()

        # NOTE: Following needs cleanup in the PM_WidgetRow/ PM_WidgetGrid
        # but this explanation is sufficient  until thats done --

        # When the widget type starts with the word 'PM_' , the
        # PM_WidgetRow treats it as a well defined widget and thus doesn't try
        # to create a QWidget object (or its subclasses)
        # This is the reason why qLabels such as self.warningSign and
        # self.phraseNotFoundLabel  are defined as PM_Labels and not 'QLabels'
        # If they were defined as 'QLabel'(s) then PM_WidgetRow would have
        # recreated the label. Since we want to show/hide the above mentioned
        # labels (and if they were recreated as mentioned above),
        # we would have needed to define  those something like this:
        # self.phraseNotFoundLabel = widgetRow._widgetList[-2]
        #Cleanup in PM_widgetGrid could be to check if the widget starts with
        #'Q'  instead of 'PM_'

        #Widgets to include in the widget row.

        widgetList1 = [('QLabel', "     Search for name:", 1),
                       ('PM_LineEdit', self.findLineEdit, 2),
                       ('PM_ToolButton', self.findOptionsToolButton, 3),
                       ('PM_ToolButton', self.searchToolButton, 4),
                       ('PM_Label', self.warningSign, 5),
                       ('PM_Label', self.phraseNotFoundLabel, 6),
                       ('QSpacerItem', 5, 5, 7)]

        widgetList2 = [('QLabel', "     Number of bases: >=", 1),
                       ('PM_SpinBox', self._nucleotidesSpinBox_1, 2),
                       ('QLabel', "     <=", 3),
                       ('PM_SpinBox', self._nucleotidesSpinBox_2, 4),
                       ('QSpacerItem', 5, 5, 5)]

        widgetList3 = [('QSpacerItem', 5, 5, 1),
                       ('PM_ToolButton', self.searchToolButton, 2),
                       ('PM_Label', self.warningSign, 3),
                       ('PM_Label', self.phraseNotFoundLabel, 4),
                       ('QSpacerItem', 5, 5, 5)]

        self._widgetRow1 = PM_WidgetRow(self,
                                        title='',
                                        widgetList=widgetList1,
                                        label="",
                                        spanWidth=True)

        self._widgetRow2 = PM_WidgetRow(self,
                                        title='',
                                        widgetList=widgetList2,
                                        label="",
                                        spanWidth=True)

        self._widgetRow3 = PM_WidgetRow(self,
                                        title='',
                                        widgetList=widgetList3,
                                        label="",
                                        spanWidth=True)
class PM_DoubleSpinBox( QDoubleSpinBox ):
    """
    The PM_DoubleSpinBox widget provides a QDoubleSpinBox (with an
    optional label) for a Property Manager group box.

    Detailed Description
    ====================
    The PM_DoubleSpinBox class provides a spin box widget (with an
    optional label) for a
    U{B{Property Manager dialog}<http://www.nanoengineer-1.net/mediawiki/
    index.php?title=Property_Manager>}
    that takes floats.

    PM_DoubleSpinBox allows the user to choose a value by clicking the up and
    down buttons or by pressing Up or Down on the keyboard to increase or
    decrease the value currently displayed. The user can also type the value
    in manually. The spin box supports float values but can be extended to
    use different strings with validate(), textFromValue() and valueFromText().

    Every time the value changes PM_DoubleSpinBox emits the valueChanged()
    signal.The current value can be fetched with value() and set with setValue()

    Note: PM_DoubleSpinBox will round numbers so they can be displayed with the
    current precision. In a PM_DoubleSpinBox with decimals set to 2, calling
    setValue(2.555) will cause value() to return 2.56.

    Clicking the up and down buttons or using the keyboard accelerator's
    Up and Down arrows will increase or decrease the current value in steps
    of size singleStep(). If you want to change this behavior you can
    reimplement the virtual function stepBy(). The minimum and maximum
    value and the step size can be set using one of the constructors,
    and can be changed later with setMinimum(), setMaximum() and
    setSingleStep(). The spin box has a default precision of 2 decimal
    places but this can be changed using setDecimals().

    Most spin boxes are directional, but PM_DoubleSpinBox can also operate
    as a circular spin box, i.e. if the range is 0.0-99.9 and the current
    value is 99.9, clicking "up" will give 0 if wrapping() is set to true.
    Use setWrapping() if you want circular behavior.

    The displayed value can be prepended and appended with arbitrary strings
    indicating, for example, currency or the unit of measurement.
    See setPrefix() and setSuffix(). The text in the spin box is retrieved
    with text() (which includes any prefix() and suffix()), or with
    cleanText() (which has no prefix(), no suffix() and no leading or
    trailing whitespace).

    It is often desirable to give the user a special (often default) choice
    in addition to the range of numeric values. See setSpecialValueText()
    for how to do this with PM_DoubleSpinBox.

    @see: U{B{QDoubleSpinBox}<http://doc.trolltech.com/4/qdoublespinbox.html>}

    @cvar defaultValue: The default value of the spin box.
    @type defaultValue: float

    @cvar setAsDefault: Determines whether to reset the value of the
                        spin box to I{defaultValue} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: bool

    @cvar labelWidget: The Qt label widget of this spin box.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    """

    defaultValue = 0.0
    setAsDefault = True
    labelWidget  = None

    def __init__(self,
                 parentWidget,
                 label        = '',
                 labelColumn  = 0,
                 value        = 0.0,
                 setAsDefault = True,
                 minimum      = 0.0,
                 maximum      = 99.0,
                 singleStep   = 1.0,
                 decimals     = 1,
                 suffix       = '',
                 spanWidth    = False
                 ):
        """
        Appends a QDoubleSpinBox (Qt) widget to the bottom of I{parentWidget},
        a Property Manager group box.

        @param parentWidget: The parent group box containing this widget.
        @type  parentWidget: PM_GroupBox

        @param label: The label that appears to the left or right of the
                      spin box. If label contains the relative path to an
                      icon (.png) file, that icon image will be used for the
                      label.

                      If spanWidth is True, the label will be displayed on
                      its own row directly above the spin box.

                      To suppress the label, set I{label} to an empty string.
        @type  label: str

        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left
                            column) and 1 (right column). The default is 0
                            (left column).
        @type  labelColumn: int

        @param value: The initial value of the spin box.
        @type  value: float

        @param setAsDefault: If True, will restore I{value} when the
                             "Restore Defaults" button is clicked.
        @type  setAsDefault: bool

        @param minimum: The minimum value of the spin box.
        @type  minimum: float

        @param maximum: The maximum value of the spin box.
        @type  maximum: float

        @param singleStep: When the user uses the arrows to change the
                           spin box's value the value will be
                           incremented/decremented by the amount of the
                           singleStep. The default value is 1.0.
                           Setting a singleStep value of less than 0 does
                           nothing.
        @type  singleStep: float

        @param decimals: The precision of the spin box.
        @type  decimals: int

        @param suffix: The suffix is appended to the end of the displayed value.
                       Typical use is to display a unit of measurement.
                       The default is no suffix. The suffix is not displayed
                       for the minimum value if specialValueText() is set.
        @type  suffix: str

        @param spanWidth: If True, the spin box and its label will span the
                          width of the group box. The label will appear directly
                          above the spin box and is left justified.
        @type  spanWidth: bool

        @see: U{B{QDoubleSpinBox}<http://doc.trolltech.com/4/qdoublespinbox.html>}
        @see: B{InsertNanotube_PropertyManager._chiralityFixup()} for an example
              use of blockSignals flag
        """

        if 0: # Debugging code
            print "PropMgrSpinBox.__init__():"
            print "  label        = ", label
            print "  labelColumn  = ", labelColumn
            print "  value        = ", value
            print "  setAsDefault = ", setAsDefault
            print "  minimum      = ", minimum
            print "  maximum      = ", maximum
            print "  singleStep   = ", singleStep
            print "  decimals     = ", decimals
            print "  suffix       = ", suffix
            print "  spanWidth    = ", spanWidth

        if not parentWidget:
            return

        QDoubleSpinBox.__init__(self)

        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth

        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        # Set QDoubleSpinBox minimum, maximum, singleStep, decimals, then value
        self.setRange(minimum, maximum)
        self.setSingleStep(singleStep)
        self.setDecimals(decimals)

        self.setValue(value) # This must come after setDecimals().

        if setAsDefault:
            self.setDefaultValue(value)

        # Add suffix if supplied.
        if suffix:
            self.setSuffix(suffix)

        parentWidget.addPmWidget(self)

    def restoreDefault( self ):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setValue(self.defaultValue)

    def setDefaultValue(self, value):
        """
        Sets the default value of the spin box to I{value}. The current spin box
        value is unchanged.

        @param value: The new default value of the spin box.
        @type  value: float

        @see: L{setValue}
        """
        self.setAsDefault = True
        self.defaultValue = value

    def setValue(self,
                 value,
                 setAsDefault = True,
                 blockSignals = False):
        """
        Sets the value of the spin box to I{value}.

        setValue() will emit valueChanged() if the new value is different from
        the old one.

        @param value: The new spin box value.
        @type  value: float

##        @param setAsDefault: Determines if the spin box value is reset when
##                             the "Restore Defaults" button is clicked. If True,
##                             (the default) I{value} will be used as the reset
##                             value.
##        @type  setAsDefault: bool

        @note: The value will be rounded so it can be displayed with the current
        setting of decimals.

        @param blockSignals: Many times, the caller just wants to setValue
                             and don't want to send valueChanged signal.
                             If this flag is set to True, the valueChanged
                             signal won't be emitted.  The default value is
                             False.
        @type  blockSignals: bool

        @see: L{setDefaultValue}
        @see: QObject.blockSignals(bool block)

        """

##        if setAsDefault: ### THIS IS A BUG, if the default value of this option remains True.
##            # it also looks useless, so i'll zap it. btw that means i could zap the entire method, but i won't yet.
##            # I verified nothing calls it with changed version... not enough to prove this zapping is ok...
##            # same issue in PM_SpinBox and PropMgrBaseClass. I've discussed this with Mark & Ninad and they agree
##            # it should be changed. Ninad, feel free to clean up this method & comment when you see this.
##            # [bruce 070814]
##            self.setDefaultValue(value)


        #If blockSignals flag is True, the valueChanged signal won't be emitted
        #This is done by self.blockSignals method below.  -- Ninad 2008-08-13
        self.blockSignals(blockSignals)

        QDoubleSpinBox.setValue(self, value)

        #Make sure to always 'unblock' signals that might have been temporarily
        #blocked before calling superclass.setValue.
        self.blockSignals(False)

    def connectWithState(self, stateref,
                         set_metainfo = True,
                         debug_metainfo = False):
        """
        Connect self to the state referred to by stateref,
        so changes to self's value change that state's value
        and vice versa. By default, also set self's metainfo
        to correspond to what the stateref provides.

        @param stateref: a reference to state of type double,
                         which meets the state-reference interface StateRef_API.
        @type stateref: StateRef_API

        @param set_metainfo: whether to also set defaultValue, minimum,
        and/or maximum, if these are provided by the stateref. (This
        list of metainfo attributes will probably be extended.)

        @type set_metainfo: bool

        @param debug_metainfo: whether to print debug messages
        about the actions taken by set_metainfo, when it's true.

        @type debug_metainfo: bool
        """
        if set_metainfo:
            # Do this first, so old min/max don't prevent setting the
            # correct current value when we connect new state.
            #
            # REVIEW: the conventions for expressing a lack of
            # minimum, maximum, or defaultValue, either on self
            # or on the stateref, may need revision, so it's not
            # ambiguous whether the stateref knows the minimum (etc)
            # should be unset on self or doesn't care what it's set to.
            # Ideally, some explicit value of stateref.minimum
            # would correspond to "no minimum" (i.e. a minimum of
            # negative infinity), etc.
            # [bruce 070926]
            set_metainfo_from_stateref( self.setMinimum, stateref, 'minimum',
                                        debug_metainfo)
            set_metainfo_from_stateref( self.setMaximum, stateref, 'maximum',
                                        debug_metainfo)
            set_metainfo_from_stateref( self.setDefaultValue,
                                        stateref,
                                        'defaultValue',
                                        debug_metainfo)
        widget_connectWithState( self, stateref,
                                 QDoubleSpinBox_ConnectionWithState)
        return

    def hide( self ):
        """
        Hides the spin box and its label (if it has one).
        Call L{show()} to unhide the spin box.

        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()

    def show( self ):
        """
        Unhides the spin box and its label (if it has one).

        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()
Exemple #19
0
class PM_Dial( QDial ):
    """
    The PM_Dial widget provides a QDial (with an 
    optional label) for a Property Manager group box.
    """
    
    defaultValue = 0.0
    setAsDefault = True
    labelWidget  = None
    
    def __init__(self, 
                 parentWidget, 
                 label        = '', 
                 suffix       = '',
                 labelColumn  = 1,
                 value        = 0.0, 
                 setAsDefault = True,
                 minimum      = 0.0, 
                 maximum      = 360.0,
                 notchSize    = 1,
                 notchTarget  = 10.0,
                 notchesVisible = True,
                 wrapping     = True,
                 spanWidth    = True
                 ):
        """
        Appends a QDial (Qt) widget to the bottom of I{parentWidget}, 
        a Property Manager group box.
        
        @param parentWidget: The parent group box containing this widget.
        @type  parentWidget: PM_GroupBox
        
        @see: U{B{QDial}<http://doc.trolltech.com/4/qdial.html>}
        """
        
        if not parentWidget:
            return
        
        QDial.__init__(self)
        
        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth
        self.suffix = suffix
        
        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.updateValueLabel()
            self.labelWidget.setText(self.value_label)
                        
        # Set QDial minimum, maximum, then value
        self.setRange(minimum, maximum)
        
        self.setValue(value) # This must come after setDecimals().
        
        if setAsDefault:
            self.setDefaultValue(value)
        
        self.notchSize = notchSize
        self.setNotchTarget(notchTarget)
        self.setNotchesVisible(notchesVisible)
        self.setWrapping(wrapping)
        
        parentWidget.addPmWidget(self)
                    
    def restoreDefault( self ):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setValue(self.defaultValue)
    
    def setDefaultValue(self, value):
        """
        Sets the default value of the dial to I{value}. The current dial
        value is unchanged.
                      
        @param value: The new default value of the dial.
        @type  value: float
        
        @see: L{setValue}
        """
        self.setAsDefault = True
        self.defaultValue = value
            
    def setValue(self, value, setAsDefault = True):
        """
        Sets the value of the dial to I{value}. 
        
        setValue() will emit valueChanged() if the new value is different from
        the old one. 
        
        @param value: The new dial value.
        @type  value: float
        
##        @param setAsDefault: Determines if the dial value is reset when 
##                             the "Restore Defaults" button is clicked. If True,
##                             (the default) I{value} will be used as the reset 
##                             value.
##        @type  setAsDefault: bool
        
        @note: The value will be rounded so it can be displayed with the current
        setting of decimals.  
        
        @see: L{setDefaultValue}
        """
        
        QDial.setValue(self, value)
        self.updateValueLabel()

    def updateValueLabel(self):
        """
        Updates a widget's label with a value.
        """ 
        if self.label:
            self.value_label = "   " + self.label + " " + \
                "%-4d" % int(self.value()) + " " + self.suffix
            self.labelWidget.setText(self.value_label)
        
    def connectWithState(self, stateref,
                         set_metainfo = True,
                         debug_metainfo = False):
        """
        Connect self to the state referred to by stateref,
        so changes to self's value change that state's value
        and vice versa. By default, also set self's metainfo
        to correspond to what the stateref provides.

        @param stateref: a reference to state of type double,
                         which meets the state-reference interface StateRef_API.
        @type stateref: StateRef_API

        @param set_metainfo: whether to also set defaultValue, minimum,
        and/or maximum, if these are provided by the stateref. (This
        list of metainfo attributes will probably be extended.)
        
        @type set_metainfo: bool

        @param debug_metainfo: whether to print debug messages
        about the actions taken by set_metainfo, when it's true.
        
        @type debug_metainfo: bool
        """
        if set_metainfo:
            # Do this first, so old min/max don't prevent setting the
            # correct current value when we connect new state.
            #
            # REVIEW: the conventions for expressing a lack of
            # minimum, maximum, or defaultValue, either on self
            # or on the stateref, may need revision, so it's not
            # ambiguous whether the stateref knows the minimum (etc)
            # should be unset on self or doesn't care what it's set to.
            # Ideally, some explicit value of stateref.minimum
            # would correspond to "no minimum" (i.e. a minimum of
            # negative infinity), etc.
            # [bruce 070926]
            set_metainfo_from_stateref( self.setMinimum, stateref, 'minimum',
                                        debug_metainfo)
            set_metainfo_from_stateref( self.setMaximum, stateref, 'maximum',
                                        debug_metainfo)
            set_metainfo_from_stateref( self.setDefaultValue, 
                                        stateref, 
                                        'defaultValue',
                                        debug_metainfo)
        ###widget_connectWithState( self, stateref,
        ###                        QDoubleSpinBox_ConnectionWithState)
        print "PM_Dial.connectWithState: not yet implemented" #bruce 080811 added this line
        return
        
    def hide( self ):
        """
        Hides the dial and its label (if it has one).
        Call L{show()} to unhide the dial.
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget: 
            self.labelWidget.hide()
            
    def show( self ):
        """
        Unhides the dial and its label (if it has one). 
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget: 
            self.labelWidget.show()
Exemple #20
0
class ImageControlDialog(QDialog):
    def __init__(self, parent, rc, imgman):
        """An ImageControlDialog is initialized with a parent widget, a RenderControl object,
        and an ImageManager object"""
        QDialog.__init__(self, parent)
        image = rc.image
        self.setWindowTitle("%s: Colour Controls" % image.name)
        self.setWindowIcon(pixmaps.colours.icon())
        self.setModal(False)
        self.image = image
        self._rc = rc
        self._imgman = imgman
        self._currier = PersistentCurrier()

        # init internal state
        self._prev_range = self._display_range = None, None
        self._hist = None
        self._geometry = None

        # create layouts
        lo0 = QVBoxLayout(self)
        #    lo0.setContentsMargins(0,0,0,0)

        # histogram plot
        whide = self.makeButton("Hide", self.hide, width=128)
        whide.setShortcut(Qt.Key_F9)
        lo0.addWidget(Separator(self, "Histogram and ITF", extra_widgets=[whide]))
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        self._histplot = QwtPlot(self)
        self._histplot.setAutoDelete(False)
        lo1.addWidget(self._histplot, 1)
        lo2 = QHBoxLayout()
        lo2.setContentsMargins(0, 0, 0, 0)
        lo2.setSpacing(2)
        lo0.addLayout(lo2)
        lo0.addLayout(lo1)
        self._wautozoom = QCheckBox("autozoom", self)
        self._wautozoom.setChecked(True)
        self._wautozoom.setToolTip("""<P>If checked, then the histrogram plot will zoom in automatically when
      you narrow the current intensity range.</P>""")
        self._wlogy = QCheckBox("log Y", self)
        self._wlogy.setChecked(True)
        self._ylogscale = True
        self._wlogy.setToolTip(
            """<P>If checked, a log-scale Y axis is used for the histogram plot instead of a linear one.""")
        QObject.connect(self._wlogy, SIGNAL("toggled(bool)"), self._setHistLogScale)
        self._whistunzoom = self.makeButton("", self._unzoomHistogram, icon=pixmaps.full_range.icon())
        self._whistzoomout = self.makeButton("-", self._currier.curry(self._zoomHistogramByFactor, math.sqrt(.1)))
        self._whistzoomin = self.makeButton("+", self._currier.curry(self._zoomHistogramByFactor, math.sqrt(10)))
        self._whistzoomin.setToolTip("""<P>Click to zoom into the histogram plot by one step. This does not
      change the current intensity range.</P>""")
        self._whistzoomout.setToolTip("""<P>Click to zoom out of the histogram plot by one step. This does not
      change the current intensity range.</P>""")
        self._whistunzoom.setToolTip("""<P>Click to reset the histogram plot back to its full extent.
      This does not change the current intensity range.</P>""")
        self._whistzoom = QwtWheel(self)
        self._whistzoom.setOrientation(Qt.Horizontal)
        self._whistzoom.setMaximumWidth(80)
        self._whistzoom.setRange(10, 0)
        self._whistzoom.setStep(0.1)
        self._whistzoom.setTickCnt(30)
        self._whistzoom.setTracking(False)
        QObject.connect(self._whistzoom, SIGNAL("valueChanged(double)"), self._zoomHistogramFinalize)
        QObject.connect(self._whistzoom, SIGNAL("sliderMoved(double)"), self._zoomHistogramPreview)
        self._whistzoom.setToolTip("""<P>Use this wheel control to zoom in/out of the histogram plot.
      This does not change the current intensity range.
      Note that the zoom wheel should also respond to your mouse wheel, if you have one.</P>""")
        # This works around a stupid bug in QwtSliders -- when using the mousewheel, only sliderMoved() signals are emitted,
        # with no final  valueChanged(). If we want to do a fast preview of something on sliderMoved(), and a "slow" final
        # step on valueChanged(), we're in trouble. So we start a timer on sliderMoved(), and if the timer expires without
        # anything else happening, do a valueChanged().
        # Here we use a timer to call zoomHistogramFinalize() w/o an argument.
        self._whistzoom_timer = QTimer(self)
        self._whistzoom_timer.setSingleShot(True)
        self._whistzoom_timer.setInterval(500)
        QObject.connect(self._whistzoom_timer, SIGNAL("timeout()"), self._zoomHistogramFinalize)
        # set same size for all buttons and controls
        width = 24
        for w in self._whistunzoom, self._whistzoomin, self._whistzoomout:
            w.setMinimumSize(width, width)
            w.setMaximumSize(width, width)
        self._whistzoom.setMinimumSize(80, width)
        self._wlab_histpos_text = "(hover here for help)"
        self._wlab_histpos = QLabel(self._wlab_histpos_text, self)
        self._wlab_histpos.setToolTip("""
      <P>The plot shows a histogram of either the full image or its selected subset
      (as per the "Data subset" section below).</P>
      <P>The current intensity range is indicated by the grey box
      in the plot.</P>
      <P>Use the left mouse button to change the low intensity limit, and the right
      button (on Macs, use Ctrl-click) to change the high limit.</P>
      <P>Use Shift with the left mouse button to zoom into an area of the histogram,
      or else use the "zoom wheel" control or the plus/minus toolbuttons above the histogram to zoom in or out.
      To zoom back out to the full extent of the histogram, click on the rightmost button above the histogram.</P>
      """)
        lo2.addWidget(self._wlab_histpos, 1)
        lo2.addWidget(self._wautozoom)
        lo2.addWidget(self._wlogy, 0)
        lo2.addWidget(self._whistzoomin, 0)
        lo2.addWidget(self._whistzoom, 0)
        lo2.addWidget(self._whistzoomout, 0)
        lo2.addWidget(self._whistunzoom, 0)
        self._zooming_histogram = False

        sliced_axes = rc.slicedAxes()
        dprint(1, "sliced axes are", sliced_axes)
        self._stokes_axis = None

        # subset indication
        lo0.addWidget(Separator(self, "Data subset"))
        # sliced axis selectors
        self._wslicers = []
        if sliced_axes:
            lo1 = QHBoxLayout()
            lo1.setContentsMargins(0, 0, 0, 0)
            lo1.setSpacing(2)
            lo0.addLayout(lo1)
            lo1.addWidget(QLabel("Current slice:  ", self))
            for i, (iextra, name, labels) in enumerate(sliced_axes):
                lo1.addWidget(QLabel("%s:" % name, self))
                if name == "STOKES":
                    self._stokes_axis = iextra
                # add controls
                wslicer = QComboBox(self)
                self._wslicers.append(wslicer)
                wslicer.addItems(labels)
                wslicer.setToolTip("""<P>Selects current slice along the %s axis.</P>""" % name)
                wslicer.setCurrentIndex(self._rc.currentSlice()[iextra])
                QObject.connect(wslicer, SIGNAL("activated(int)"), self._currier.curry(self._rc.changeSlice, iextra))
                lo2 = QVBoxLayout()
                lo1.addLayout(lo2)
                lo2.setContentsMargins(0, 0, 0, 0)
                lo2.setSpacing(0)
                wminus = QToolButton(self)
                wminus.setArrowType(Qt.UpArrow)
                QObject.connect(wminus, SIGNAL("clicked()"), self._currier.curry(self._rc.incrementSlice, iextra, 1))
                if i == 0:
                    wminus.setShortcut(Qt.SHIFT + Qt.Key_F7)
                elif i == 1:
                    wminus.setShortcut(Qt.SHIFT + Qt.Key_F8)
                wplus = QToolButton(self)
                wplus.setArrowType(Qt.DownArrow)
                QObject.connect(wplus, SIGNAL("clicked()"), self._currier.curry(self._rc.incrementSlice, iextra, -1))
                if i == 0:
                    wplus.setShortcut(Qt.Key_F7)
                elif i == 1:
                    wplus.setShortcut(Qt.Key_F8)
                wminus.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
                wplus.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
                sz = QSize(12, 8)
                wminus.setMinimumSize(sz)
                wplus.setMinimumSize(sz)
                wminus.resize(sz)
                wplus.resize(sz)
                lo2.addWidget(wminus)
                lo2.addWidget(wplus)
                lo1.addWidget(wslicer)
                lo1.addSpacing(5)
            lo1.addStretch(1)
        # subset indicator
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo1.setSpacing(2)
        lo0.addLayout(lo1)
        self._wlab_subset = QLabel("Subset: xxx", self)
        self._wlab_subset.setToolTip("""<P>This indicates the current data subset to which the histogram
      and the stats given here apply. Use the "Reset to" control on the right to change the
      current subset and recompute the histogram and stats.</P>""")
        lo1.addWidget(self._wlab_subset, 1)

        self._wreset_full = self.makeButton("\u2192 full", self._rc.setFullSubset)
        lo1.addWidget(self._wreset_full)
        if sliced_axes:
            #      if self._stokes_axis is not None and len(sliced_axes)>1:
            #        self._wreset_stokes = self.makeButton(u"\u21920Stokes",self._rc.setFullSubset)
            self._wreset_slice = self.makeButton("\u2192 slice", self._rc.setSliceSubset)
            lo1.addWidget(self._wreset_slice)
        else:
            self._wreset_slice = None

        # min/max controls
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 0)
        self._wlab_stats = QLabel(self)
        lo1.addWidget(self._wlab_stats, 0)
        self._wmore_stats = self.makeButton("more...", self._showMeanStd)
        self._wlab_stats.setMinimumHeight(self._wmore_stats.height())
        lo1.addWidget(self._wmore_stats, 0)
        lo1.addStretch(1)

        # intensity controls
        lo0.addWidget(Separator(self, "Intensity mapping"))
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo1.setSpacing(2)
        lo0.addLayout(lo1, 0)
        self._range_validator = FloatValidator(self)
        self._wrange = QLineEdit(self), QLineEdit(self)
        self._wrange[0].setToolTip("""<P>This is the low end of the intensity range.</P>""")
        self._wrange[1].setToolTip("""<P>This is the high end of the intensity range.</P>""")
        for w in self._wrange:
            w.setValidator(self._range_validator)
            QObject.connect(w, SIGNAL("editingFinished()"), self._changeDisplayRange)
        lo1.addWidget(QLabel("low:", self), 0)
        lo1.addWidget(self._wrange[0], 1)
        self._wrangeleft0 = self.makeButton("\u21920", self._setZeroLeftLimit, width=32)
        self._wrangeleft0.setToolTip("""<P>Click this to set the low end of the intensity range to 0.</P>""")
        lo1.addWidget(self._wrangeleft0, 0)
        lo1.addSpacing(8)
        lo1.addWidget(QLabel("high:", self), 0)
        lo1.addWidget(self._wrange[1], 1)
        lo1.addSpacing(8)
        self._wrange_full = self.makeButton(None, self._setHistDisplayRange, icon=pixmaps.intensity_graph.icon())
        lo1.addWidget(self._wrange_full)
        self._wrange_full.setToolTip(
            """<P>Click this to reset the intensity range to the current extent of the histogram plot.</P>""")
        # add menu for display range
        range_menu = QMenu(self)
        wrange_menu = QToolButton(self)
        wrange_menu.setText("Reset to")
        wrange_menu.setToolTip("""<P>Use this to reset the intensity range to various pre-defined settings.</P>""")
        lo1.addWidget(wrange_menu)
        self._qa_range_full = range_menu.addAction(pixmaps.full_range.icon(), "Full subset",
                                                   self._rc.resetSubsetDisplayRange)
        self._qa_range_hist = range_menu.addAction(pixmaps.intensity_graph.icon(), "Current histogram limits",
                                                   self._setHistDisplayRange)
        for percent in (99.99, 99.9, 99.5, 99, 98, 95):
            range_menu.addAction("%g%%" % percent, self._currier.curry(self._changeDisplayRangeToPercent, percent))
        wrange_menu.setMenu(range_menu)
        wrange_menu.setPopupMode(QToolButton.InstantPopup)

        lo1 = QGridLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 0)
        self._wimap = QComboBox(self)
        lo1.addWidget(QLabel("Intensity policy:", self), 0, 0)
        lo1.addWidget(self._wimap, 1, 0)
        self._wimap.addItems(rc.getIntensityMapNames())
        QObject.connect(self._wimap, SIGNAL("currentIndexChanged(int)"), self._rc.setIntensityMapNumber)
        self._wimap.setToolTip("""<P>Use this to change the type of the intensity transfer function (ITF).</P>""")

        # log cycles control
        lo1.setColumnStretch(1, 1)
        self._wlogcycles_label = QLabel("Log cycles: ", self)
        lo1.addWidget(self._wlogcycles_label, 0, 1)
        #    self._wlogcycles = QwtWheel(self)
        #    self._wlogcycles.setTotalAngle(360)
        self._wlogcycles = QwtSlider(self)
        self._wlogcycles.setToolTip(
            """<P>Use this to change the log-base for the logarithmic intensity transfer function (ITF).</P>""")
        # This works around a stupid bug in QwtSliders -- see comments on histogram zoom wheel above
        self._wlogcycles_timer = QTimer(self)
        self._wlogcycles_timer.setSingleShot(True)
        self._wlogcycles_timer.setInterval(500)
        QObject.connect(self._wlogcycles_timer, SIGNAL("timeout()"), self._setIntensityLogCycles)
        lo1.addWidget(self._wlogcycles, 1, 1)
        self._wlogcycles.setRange(1., 10)
        self._wlogcycles.setStep(0.1)
        self._wlogcycles.setTracking(False)
        QObject.connect(self._wlogcycles, SIGNAL("valueChanged(double)"), self._setIntensityLogCycles)
        QObject.connect(self._wlogcycles, SIGNAL("sliderMoved(double)"), self._previewIntensityLogCycles)
        self._updating_imap = False

        # lock intensity map
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 0)
        #    lo1.addWidget(QLabel("Lock range accross",self))
        wlock = QCheckBox("Lock display range", self)
        wlock.setToolTip("""<P>If checked, then the intensity range will be locked. The ranges of all locked images
      change simultaneously.</P>""")
        lo1.addWidget(wlock)
        wlockall = QToolButton(self)
        wlockall.setIcon(pixmaps.locked.icon())
        wlockall.setText("Lock all to this")
        wlockall.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        wlockall.setAutoRaise(True)
        wlockall.setToolTip("""<P>Click this to lock together the intensity ranges of all images.</P>""")
        lo1.addWidget(wlockall)
        wunlockall = QToolButton(self)
        wunlockall.setIcon(pixmaps.unlocked.icon())
        wunlockall.setText("Unlock all")
        wunlockall.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        wunlockall.setAutoRaise(True)
        wunlockall.setToolTip("""<P>Click this to unlock the intensity ranges of all images.</P>""")
        lo1.addWidget(wunlockall)
        wlock.setChecked(self._rc.isDisplayRangeLocked())
        QObject.connect(wlock, SIGNAL("clicked(bool)"), self._rc.lockDisplayRange)
        QObject.connect(wlockall, SIGNAL("clicked()"),
                        self._currier.curry(self._imgman.lockAllDisplayRanges, self._rc))
        QObject.connect(wunlockall, SIGNAL("clicked()"), self._imgman.unlockAllDisplayRanges)
        QObject.connect(self._rc, SIGNAL("displayRangeLocked"), wlock.setChecked)

        #    self._wlock_imap_axis = [ QCheckBox(name,self) for iaxis,name,labels in sliced_axes ]
        #    for iw,w in enumerate(self._wlock_imap_axis):
        #      QObject.connect(w,SIGNAL("toggled(bool)"),self._currier.curry(self._rc.lockDisplayRangeForAxis,iw))
        #      lo1.addWidget(w,0)
        lo1.addStretch(1)

        # lo0.addWidget(Separator(self,"Colourmap"))
        # color bar
        self._colorbar = QwtPlot(self)
        lo0.addWidget(self._colorbar)
        self._colorbar.setAutoDelete(False)
        self._colorbar.setMinimumHeight(32)
        self._colorbar.enableAxis(QwtPlot.yLeft, False)
        self._colorbar.enableAxis(QwtPlot.xBottom, False)
        # color plot
        self._colorplot = QwtPlot(self)
        lo0.addWidget(self._colorplot)
        self._colorplot.setAutoDelete(False)
        self._colorplot.setMinimumHeight(64)
        self._colorplot.enableAxis(QwtPlot.yLeft, False)
        self._colorplot.enableAxis(QwtPlot.xBottom, False)
        # self._colorplot.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Preferred)
        self._colorbar.hide()
        self._colorplot.hide()
        # color controls
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 1)
        lo1.addWidget(QLabel("Colourmap:", self))
        # colormap list
        ### NB: use setIconSize() and icons in QComboBox!!!
        self._wcolmaps = QComboBox(self)
        self._wcolmaps.setIconSize(QSize(128, 16))
        self._wcolmaps.setToolTip("""<P>Use this to select a different colourmap.</P>""")
        for cmap in self._rc.getColormapList():
            self._wcolmaps.addItem(QIcon(cmap.makeQPixmap(128, 16)), cmap.name)
        lo1.addWidget(self._wcolmaps)
        QObject.connect(self._wcolmaps, SIGNAL("activated(int)"), self._rc.setColorMapNumber)
        # add widgetstack for colormap controls
        self._wcolmap_control_stack = QStackedWidget(self)
        self._wcolmap_control_blank = QWidget(self._wcolmap_control_stack)
        self._wcolmap_control_stack.addWidget(self._wcolmap_control_blank)
        lo0.addWidget(self._wcolmap_control_stack)
        self._colmap_controls = []
        # add controls to stack
        for index, cmap in enumerate(self._rc.getColormapList()):
            if isinstance(cmap, Colormaps.ColormapWithControls):
                controls = cmap.makeControlWidgets(self._wcolmap_control_stack)
                self._wcolmap_control_stack.addWidget(controls)
                QObject.connect(cmap, SIGNAL("colormapChanged"),
                                self._currier.curry(self._previewColormapParameters, index, cmap))
                QObject.connect(cmap, SIGNAL("colormapPreviewed"),
                                self._currier.curry(self._previewColormapParameters, index, cmap))
                self._colmap_controls.append(controls)
            else:
                self._colmap_controls.append(self._wcolmap_control_blank)

        # connect updates from renderControl and image
        self.image.connect(SIGNAL("slice"), self._updateImageSlice)
        QObject.connect(self._rc, SIGNAL("intensityMapChanged"), self._updateIntensityMap)
        QObject.connect(self._rc, SIGNAL("colorMapChanged"), self._updateColorMap)
        QObject.connect(self._rc, SIGNAL("dataSubsetChanged"), self._updateDataSubset)
        QObject.connect(self._rc, SIGNAL("displayRangeChanged"), self._updateDisplayRange)

        # update widgets
        self._setupHistogramPlot()
        self._updateDataSubset(*self._rc.currentSubset())
        self._updateColorMap(image.colorMap())
        self._updateIntensityMap(rc.currentIntensityMap(), rc.currentIntensityMapNumber())
        self._updateDisplayRange(*self._rc.displayRange())

    def makeButton(self, label, callback=None, width=None, icon=None):
        btn = QToolButton(self)
        #    btn.setAutoRaise(True)
        label and btn.setText(label)
        icon and btn.setIcon(icon)
        #    btn = QPushButton(label,self)
        #   btn.setFlat(True)
        if width:
            btn.setMinimumWidth(width)
            btn.setMaximumWidth(width)
        if icon:
            btn.setIcon(icon)
        if callback:
            QObject.connect(btn, SIGNAL("clicked()"), callback)
        return btn

    #  def closeEvent (self,ev):
    #    ev.ignore()
    #    self.hide()

    def hide(self):
        self._geometry = self.geometry()
        QDialog.hide(self)

    def show(self):
        dprint(4, "show entrypoint")
        if self._geometry:
            dprint(4, "setting geometry")
            self.setGeometry(self._geometry)
        if self._hist is None:
            busy = BusyIndicator()
            dprint(4, "updating histogram")
            self._updateHistogram()
            dprint(4, "updating stats")
            self._updateStats(self._subset, self._subset_range)
            busy = None
        dprint(4, "calling QDialog.show")
        QDialog.show(self)

    # number of bins used to compute intensity transfer function
    NumItfBins = 1000
    # number of bins used for displaying histograms
    NumHistBins = 500
    # number of bins used for high-res histograms
    NumHistBinsHi = 10000
    # colorbar height, as fraction of plot area
    ColorBarHeight = 0.1

    class HistLimitPicker(QwtPlotPicker):
        """Auguments QwtPlotPicker with functions for selecting hist min/max values"""

        def __init__(self, plot, label, color="green", mode=QwtPicker.PointSelection,
                     rubber_band=QwtPicker.VLineRubberBand, tracker_mode=QwtPicker.ActiveOnly, track=None):
            QwtPlotPicker.__init__(self, QwtPlot.xBottom, QwtPlot.yRight, mode, rubber_band, tracker_mode,
                                   plot.canvas())
            self.plot = plot
            self.label = label
            self.track = track
            self.color = QColor(color)
            self.setRubberBandPen(QPen(self.color))

        def trackerText(self, pos):
            x, y = self.plot.invTransform(QwtPlot.xBottom, pos.x()), self.plot.invTransform(QwtPlot.yLeft, pos.y())
            if self.track:
                text = self.track(x, y)
                if text is not None:
                    return text
            if self.label:
                text = QwtText(self.label % dict(x=x, y=y))
                text.setColor(self.color)
                return text
            return QwtText()

        def widgetLeaveEvent(self, ev):
            if self.track:
                self.track(None, None)
            QwtPlotPicker.widgetLeaveEvent(self, ev)

    class ColorBarPlotItem(QwtPlotItem):
        def __init__(self, y0, y1, *args):
            QwtPlotItem.__init__(self, *args)
            self._y0 = y1
            self._dy = y1 - y0

        def setIntensityMap(self, imap):
            self.imap = imap

        def setColorMap(self, cmap):
            self.cmap = cmap

        def draw(self, painter, xmap, ymap, rect):
            """Implements QwtPlotItem.draw(), to render the colorbar on the given painter."""
            xp1, xp2, xdp, xs1, xs2, xds = xinfo = xmap.p1(), xmap.p2(), xmap.pDist(), xmap.s1(), xmap.s2(), xmap.sDist()
            yp1, yp2, ydp, ys1, ys2, yds = yinfo = ymap.p1(), ymap.p2(), ymap.pDist(), ymap.s1(), ymap.s2(), ymap.sDist()
            # xp: coordinates of pixels xp1...xp2 in data units
            xp = xs1 + (xds / xdp) * (0.5 + numpy.arange(int(xdp)))
            # convert y0 and y1 into pixel coordinates
            y0 = yp1 - (self._y0 - ys1) * (ydp / yds)
            dy = self._dy * (ydp / yds)
            # remap into an Nx1 image
            qimg = self.cmap.colorize(self.imap.remap(xp.reshape((len(xp), 1))))
            # plot image
            painter.drawImage(QRect(xp1, y0, xdp, dy), qimg)

    class HistogramLineMarker(object):
        """Helper class implementing a line marker for a histogram plot"""

        def __init__(self, plot, color="black", linestyle=Qt.DotLine, align=Qt.AlignBottom | Qt.AlignRight, z=90,
                     label="", zlabel=None, linewidth=1, spacing=2,
                     yaxis=QwtPlot.yRight):
            self.line = TiggerPlotCurve()
            self.color = color = color if isinstance(color, QColor) else QColor(color)
            self.line.setPen(QPen(color, linewidth, linestyle))
            self.marker = TiggerPlotMarker()
            self.marker.setLabelAlignment(align)
            try:
                self.marker.setSpacing(spacing)
            except AttributeError:
                pass
            self.setText(label)
            self.line.setZ(z)
            self.marker.setZ(zlabel if zlabel is not None else z)
            # set axes -- using yRight, since that is the "markup" z-axis
            self.line.setAxis(QwtPlot.xBottom, yaxis)
            self.marker.setAxis(QwtPlot.xBottom, yaxis)
            # attach to plot
            self.line.attach(plot)
            self.marker.attach(plot)

        def show(self):
            self.line.show()
            self.marker.show()

        def hide(self):
            self.line.hide()
            self.marker.hide()

        def setText(self, text):
            label = QwtText(text)
            label.setColor(self.color)
            self.marker.setLabel(label)

    def _setupHistogramPlot(self):
        self._histplot.setCanvasBackground(QColor("lightgray"))
        self._histplot.setAxisFont(QwtPlot.yLeft, QApplication.font())
        self._histplot.setAxisFont(QwtPlot.xBottom, QApplication.font())
        # add histogram curves
        self._histcurve1 = TiggerPlotCurve()
        self._histcurve2 = TiggerPlotCurve()
        self._histcurve1.setStyle(QwtPlotCurve.Steps)
        self._histcurve2.setStyle(QwtPlotCurve.Steps)
        self._histcurve1.setPen(QPen(Qt.NoPen))
        self._histcurve1.setBrush(QBrush(QColor("slategrey")))
        pen = QPen(QColor("red"))
        pen.setWidth(1)
        self._histcurve2.setPen(pen)
        self._histcurve1.setZ(0)
        self._histcurve2.setZ(100)
        #    self._histcurve1.attach(self._histplot)
        self._histcurve2.attach(self._histplot)
        # add maxbin and half-max curves
        self._line_0 = self.HistogramLineMarker(self._histplot, color="grey50", linestyle=Qt.SolidLine,
                                                align=Qt.AlignTop | Qt.AlignLeft, z=90)
        self._line_mean = self.HistogramLineMarker(self._histplot, color="black", linestyle=Qt.SolidLine,
                                                   align=Qt.AlignBottom | Qt.AlignRight, z=91,
                                                   label="mean", zlabel=151)
        self._line_std = self.HistogramLineMarker(self._histplot, color="black", linestyle=Qt.SolidLine,
                                                  align=Qt.AlignTop | Qt.AlignRight, z=91,
                                                  label="std", zlabel=151)
        sym = QwtSymbol()
        sym.setStyle(QwtSymbol.VLine)
        sym.setSize(8)
        self._line_std.line.setSymbol(sym)
        self._line_maxbin = self.HistogramLineMarker(self._histplot, color="green", linestyle=Qt.DotLine,
                                                     align=Qt.AlignTop | Qt.AlignRight, z=92,
                                                     label="max bin", zlabel=150)
        self._line_halfmax = self.HistogramLineMarker(self._histplot, color="green", linestyle=Qt.DotLine,
                                                      align=Qt.AlignBottom | Qt.AlignRight, z=90,
                                                      label="half-max", yaxis=QwtPlot.yLeft)
        # add current range
        self._rangebox = TiggerPlotCurve()
        self._rangebox.setStyle(QwtPlotCurve.Steps)
        self._rangebox.setYAxis(QwtPlot.yRight)
        self._rangebox.setPen(QPen(Qt.NoPen))
        self._rangebox.setBrush(QBrush(QColor("darkgray")))
        self._rangebox.setZ(50)
        self._rangebox.attach(self._histplot)
        self._rangebox2 = TiggerPlotCurve()
        self._rangebox2.setStyle(QwtPlotCurve.Sticks)
        self._rangebox2.setYAxis(QwtPlot.yRight)
        self._rangebox2.setZ(60)
        #  self._rangebox2.attach(self._histplot)
        # add intensity transfer function
        self._itfcurve = TiggerPlotCurve()
        self._itfcurve.setStyle(QwtPlotCurve.Lines)
        self._itfcurve.setPen(QPen(QColor("blue")))
        self._itfcurve.setYAxis(QwtPlot.yRight)
        self._itfcurve.setZ(120)
        self._itfcurve.attach(self._histplot)
        self._itfmarker = TiggerPlotMarker()
        label = QwtText("ITF")
        label.setColor(QColor("blue"))
        self._itfmarker.setLabel(label)
        try:
            self._itfmarker.setSpacing(0)
        except AttributeError:
            pass
        self._itfmarker.setLabelAlignment(Qt.AlignTop | Qt.AlignRight)
        self._itfmarker.setZ(120)
        self._itfmarker.attach(self._histplot)
        # add colorbar
        self._cb_item = self.ColorBarPlotItem(1, 1 + self.ColorBarHeight)
        self._cb_item.setYAxis(QwtPlot.yRight)
        self._cb_item.attach(self._histplot)
        # add pickers
        self._hist_minpicker = self.HistLimitPicker(self._histplot, "low: %(x).4g")
        self._hist_minpicker.setMousePattern(QwtEventPattern.MouseSelect1, Qt.LeftButton)
        QObject.connect(self._hist_minpicker, SIGNAL("selected(const QwtDoublePoint &)"), self._selectLowLimit)
        self._hist_maxpicker = self.HistLimitPicker(self._histplot, "high: %(x).4g")
        self._hist_maxpicker.setMousePattern(QwtEventPattern.MouseSelect1, Qt.RightButton)
        QObject.connect(self._hist_maxpicker, SIGNAL("selected(const QwtDoublePoint &)"), self._selectHighLimit)
        self._hist_maxpicker1 = self.HistLimitPicker(self._histplot, "high: %(x).4g")
        self._hist_maxpicker1.setMousePattern(QwtEventPattern.MouseSelect1, Qt.LeftButton, Qt.CTRL)
        QObject.connect(self._hist_maxpicker1, SIGNAL("selected(const QwtDoublePoint &)"), self._selectHighLimit)
        self._hist_zoompicker = self.HistLimitPicker(self._histplot, label="zoom",
                                                     tracker_mode=QwtPicker.AlwaysOn, track=self._trackHistCoordinates,
                                                     color="black",
                                                     mode=QwtPicker.RectSelection,
                                                     rubber_band=QwtPicker.RectRubberBand)
        self._hist_zoompicker.setMousePattern(QwtEventPattern.MouseSelect1, Qt.LeftButton, Qt.SHIFT)
        QObject.connect(self._hist_zoompicker, SIGNAL("selected(const QwtDoubleRect &)"), self._zoomHistogramIntoRect)

    def _trackHistCoordinates(self, x, y):
        self._wlab_histpos.setText((DataValueFormat + " %d") % (x, y) if x is not None else self._wlab_histpos_text)
        return QwtText()

    def _updateITF(self):
        """Updates current ITF array."""
        # do nothing if no histogram -- means we're not visible
        if self._hist is not None:
            xdata = self._itf_bins
            ydata = self.image.intensityMap().remap(xdata)
            self._rangebox.setData(self._rc.displayRange(), [1, 1])
            self._rangebox2.setData(self._rc.displayRange(), [1, 1])
            self._itfcurve.setData(xdata, ydata)
            self._itfmarker.setValue(xdata[0], 1)

    def _updateHistogram(self, hmin=None, hmax=None):
        """Recomputes histogram. If no arguments, computes full histogram for
        data subset. If hmin/hmax is specified, computes zoomed-in histogram."""
        busy = BusyIndicator()
        self._prev_range = self._display_range
        dmin, dmax = self._subset_range
        hmin0, hmax0 = dmin, dmax
        if hmin0 >= hmax0:
            hmax0 = hmin0 + 1
        subset, mask = self.image.optimalRavel(self._subset)
        # compute full-subset hi-res histogram, if we don't have one (for percentile stats)
        if self._hist_hires is None:
            dprint(1, "computing histogram for full subset range", hmin0, hmax0)
            self._hist_hires = measurements.histogram(subset, hmin0, hmax0, self.NumHistBinsHi, labels=mask,
                                                      index=None if mask is None else False)
            self._hist_bins_hires = hmin0 + (hmax0 - hmin0) * (numpy.arange(self.NumHistBinsHi) + 0.5) / float(
                self.NumHistBinsHi)
            self._hist_binsize_hires = (hmax0 - hmin0) / self.NumHistBins
        # if hist limits not specified, then compute lo-res histogram based on the hi-res one
        if hmin is None:
            hmin, hmax = hmin0, hmax0
            # downsample to low-res histogram
            self._hist = self._hist_hires.reshape((self.NumHistBins, self.NumHistBinsHi / self.NumHistBins)).sum(1)
        else:
            # zoomed-in low-res histogram
            # bracket limits at subset range
            hmin, hmax = max(hmin, dmin), min(hmax, dmax)
            if hmin >= hmax:
                hmax = hmin + 1
            dprint(1, "computing histogram for", self._subset.shape, self._subset.dtype, hmin, hmax)
            self._hist = measurements.histogram(subset, hmin, hmax, self.NumHistBins, labels=mask,
                                                index=None if mask is None else False)
        dprint(1, "histogram computed")
        # compute bins
        self._itf_bins = hmin + (hmax - hmin) * (numpy.arange(self.NumItfBins)) / (float(self.NumItfBins) - 1)
        self._hist_bins = hmin + (hmax - hmin) * (numpy.arange(self.NumHistBins) + 0.5) / float(self.NumHistBins)
        # histogram range and position of peak
        self._hist_range = hmin, hmax
        self._hist_min, self._hist_max, self._hist_imin, self._hist_imax = measurements.extrema(self._hist)
        self._hist_peak = self._hist_bins[self._hist_imax]
        # set controls accordingly
        if dmin >= dmax:
            dmax = dmin + 1
        zoom = math.log10((dmax - dmin) / (hmax - hmin))
        self._whistzoom.setValue(zoom)
        self._whistunzoom.setEnabled(zoom > 0)
        self._whistzoomout.setEnabled(zoom > 0)
        # reset scales
        self._histplot.setAxisScale(QwtPlot.xBottom, hmin, hmax)
        self._histplot.setAxisScale(QwtPlot.yRight, 0, 1 + self.ColorBarHeight)
        # update curves
        # call _setHistLogScale() (with current setting) to update axis scales and set data
        self._setHistLogScale(self._ylogscale, replot=False)
        # set plot lines
        self._line_0.line.setData([0, 0], [0, 1])
        self._line_0.marker.setValue(0, 0)
        self._line_maxbin.line.setData([self._hist_peak, self._hist_peak], [0, 1])
        self._line_maxbin.marker.setValue(self._hist_peak, 0)
        self._line_maxbin.setText(("max bin:" + DataValueFormat) % self._hist_peak)
        # set half-max line
        self._line_halfmax.line.setData(self._hist_range, [self._hist_max / 2, self._hist_max / 2])
        self._line_halfmax.marker.setValue(hmin, self._hist_max / 2)
        # update ITF
        self._updateITF()

    def _updateStats(self, subset, minmax):
        """Recomputes subset statistics."""
        if subset.size <= (2048 * 2048):
            self._showMeanStd(busy=False)
        else:
            self._wlab_stats.setText(
                ("min: %s  max: %s  np: %d" % (DataValueFormat, DataValueFormat, self._subset.size)) % minmax)
            self._wmore_stats.show()

    def _updateDataSubset(self, subset, minmax, desc, subset_type):
        """Called when the displayed data subset is changed. Updates the histogram."""
        self._subset = subset
        self._subset_range = minmax
        self._wlab_subset.setText("Subset: %s" % desc)
        self._hist = self._hist_hires = None
        self._wreset_full.setVisible(subset_type is not RenderControl.SUBSET_FULL)
        self._wreset_slice and self._wreset_slice.setVisible(subset_type is not RenderControl.SUBSET_SLICE)
        # hide the mean/std markers, they will only be shown when _showMeanStd() is called
        self._line_mean.hide()
        self._line_std.hide()
        # if we're visibile, recompute histograms and stats
        if self.isVisible():
            # if subset is sufficiently small, compute extended stats on-the-fly. Else show the "more" button to compute them later
            self._updateHistogram()
            self._updateStats(subset, minmax)
            self._histplot.replot()

    def _showMeanStd(self, busy=True):
        if busy:
            busy = BusyIndicator()
        dmin, dmax = self._subset_range
        subset, mask = self.image.optimalRavel(self._subset)
        dprint(5, "computing mean")
        mean = measurements.mean(subset, labels=mask, index=None if mask is None else False)
        dprint(5, "computing std")
        std = measurements.standard_deviation(subset, labels=mask, index=None if mask is None else False)
        dprint(5, "done")
        text = "  ".join([("%s: " + DataValueFormat) % (name, value) for name, value in
                          ("min", dmin), ("max", dmax), ("mean", mean), ("std", std)] + ["np: %d" % self._subset.size])
        self._wlab_stats.setText(text)
        self._wmore_stats.hide()
        # update markers
        ypos = 0.3
        self._line_mean.line.setData([mean, mean], [0, 1])
        self._line_mean.marker.setValue(mean, ypos)
        self._line_mean.setText(("\u03BC=" + DataValueFormat) % mean)
        self._line_mean.show()
        self._line_std.line.setData([mean - std, mean + std], [ypos, ypos])
        self._line_std.marker.setValue(mean, ypos)
        self._line_std.setText(("\u03C3=" + DataValueFormat) % std)
        self._line_std.show()
        self._histplot.replot()

    def _setIntensityLogCyclesLabel(self, value):
        self._wlogcycles_label.setText("Log cycles: %4.1f" % value)

    def _previewIntensityLogCycles(self, value):
        self._setIntensityLogCycles(value, notify_image=False, write_config=False)
        self._wlogcycles_timer.start(500)

    def _setIntensityLogCycles(self, value=None, notify_image=True, write_config=True):
        if value is None:
            value = self._wlogcycles.value()
        # stop timer if being called to finalize the change in value
        if notify_image:
            self._wlogcycles_timer.stop()
        if not self._updating_imap:
            self._setIntensityLogCyclesLabel(value)
            self._rc.setIntensityMapLogCycles(value, notify_image=notify_image, write_config=write_config)
            self._updateITF()
            self._histplot.replot()

    def _updateDisplayRange(self, dmin, dmax):
        self._rangebox.setData([dmin, dmax], [.9, .9])
        self._wrange[0].setText(DataValueFormat % dmin)
        self._wrange[1].setText(DataValueFormat % dmax)
        self._wrangeleft0.setEnabled(dmin != 0)
        self._display_range = dmin, dmax
        # if auto-zoom is on, zoom the histogram
        # try to be a little clever about this. Zoom only if (a) both limits have changed (so that adjusting one end of the range
        # does not cause endless rezooms), or (b) display range is < 1/10 of the histogram range
        if self._wautozoom.isChecked() and self._hist is not None:
            if (dmax - dmin) / (self._hist_range[1] - self._hist_range[0]) < .1 or (
                    dmin != self._prev_range[0] and dmax != self._prev_range[1]):
                margin = (dmax - dmin) / 8
                self._updateHistogram(dmin - margin, dmax + margin)
        self._updateITF()
        self._histplot.replot()

    def _updateIntensityMap(self, imap, index):
        self._updating_imap = True
        try:
            self._cb_item.setIntensityMap(imap)
            self._updateITF()
            self._histplot.replot()
            self._wimap.setCurrentIndex(index)
            if isinstance(imap, Colormaps.LogIntensityMap):
                self._wlogcycles.setValue(imap.log_cycles)
                self._setIntensityLogCyclesLabel(imap.log_cycles)
                self._wlogcycles.show()
                self._wlogcycles_label.show()
            else:
                self._wlogcycles.hide()
                self._wlogcycles_label.hide()
        finally:
            self._updating_imap = False

    def _updateColorMap(self, cmap):
        self._cb_item.setColorMap(cmap)
        self._histplot.replot()
        try:
            index = self._rc.getColormapList().index(cmap)
        except:
            return
        self._setCurrentColormapNumber(index, cmap)

    def _previewColormapParameters(self, index, cmap):
        """Called to preview a new colormap parameter value"""
        self._histplot.replot()
        self._wcolmaps.setItemIcon(index, QIcon(cmap.makeQPixmap(128, 16)))

    def _setCurrentColormapNumber(self, index, cmap):
        self._wcolmaps.setCurrentIndex(index)
        # show controls for colormap
        self._wcolmap_control_stack.setCurrentWidget(self._colmap_controls[index])

    def _changeDisplayRange(self):
        """Gets display range from widgets and updates the image with it."""
        try:
            newrange = [float(str(w.text())) for w in self._wrange]
        except ValueError:
            return
        self._rc.setDisplayRange(*newrange)

    def _setHistDisplayRange(self):
        self._rc.setDisplayRange(*self._hist_range)

    def _updateImageSlice(self, slice):
        for i, (iextra, name, labels) in enumerate(self._rc.slicedAxes()):
            self._wslicers[i].setCurrentIndex(slice[iextra])

    def _changeDisplayRangeToPercent(self, percent):
        busy = BusyIndicator()
        if self._hist is None:
            self._updateHistogram()
            self._updateStats(self._subset, self._subset_range)
        # delta: we need the [delta,100-delta] interval of the total distribution
        delta = self._subset.size * ((100. - percent) / 200.)
        # get F(x): cumulative sum
        cumsum = numpy.zeros(len(self._hist_hires) + 1, dtype=int)
        cumsum[1:] = numpy.cumsum(self._hist_hires)
        bins = numpy.zeros(len(self._hist_hires) + 1, dtype=float)
        bins[0] = self._subset_range[0]
        bins[1:] = self._hist_bins_hires + self._hist_binsize_hires / 2
        # use interpolation to find value interval corresponding to [delta,100-delta] of the distribution
        dprint(2, self._subset.size, delta, self._subset.size - delta)
        dprint(2, cumsum, self._hist_bins_hires)
        # if first bin is already > delta, then set colour range to first bin
        x0, x1 = numpy.interp([delta, self._subset.size - delta], cumsum, bins)
        # and change the display range (this will also cause a histplot.replot() via _updateDisplayRange above)
        self._rc.setDisplayRange(x0, x1)

    def _setZeroLeftLimit(self):
        self._rc.setDisplayRange(0., self._rc.displayRange()[1])

    def _selectLowLimit(self, pos):
        self._rc.setDisplayRange(pos.x(), self._rc.displayRange()[1])

    def _selectHighLimit(self, pos):
        self._rc.setDisplayRange(self._rc.displayRange()[0], pos.x())

    def _unzoomHistogram(self):
        self._updateHistogram()
        self._histplot.replot()

    def _zoomHistogramByFactor(self, factor):
        """Changes histogram limits by specified factor"""
        # get max distance of plot limit from peak
        dprint(1, "zooming histogram by", factor)
        halfdist = (self._hist_range[1] - self._hist_range[0]) / (factor * 2)
        self._updateHistogram(self._hist_peak - halfdist, self._hist_peak + halfdist)
        self._histplot.replot()

    def _zoomHistogramIntoRect(self, rect):
        hmin, hmax = rect.bottomLeft().x(), rect.bottomRight().x()
        if hmax > hmin:
            self._updateHistogram(rect.bottomLeft().x(), rect.bottomRight().x())
            self._histplot.replot()

    def _zoomHistogramPreview(self, value):
        dprint(2, "wheel moved to", value)
        self._zoomHistogramFinalize(value, preview=True)
        self._whistzoom_timer.start()

    def _zoomHistogramFinalize(self, value=None, preview=False):
        if self._zooming_histogram:
            return
        self._zooming_histogram = True
        try:
            if value is not None:
                dmin, dmax = self._subset_range
                dist = max(dmax - self._hist_peak, self._hist_peak - dmin) / 10 ** value
                self._preview_hist_range = max(self._hist_peak - dist, dmin), min(self._hist_peak + dist, dmax)
            if preview:
                self._histplot.setAxisScale(QwtPlot.xBottom, *self._preview_hist_range)
            else:
                dprint(2, "wheel finalized at", value)
                self._whistzoom_timer.stop()
                self._updateHistogram(*self._preview_hist_range)
            self._histplot.replot()
        finally:
            self._zooming_histogram = False

    def _setHistLogScale(self, logscale, replot=True):
        self._ylogscale = logscale
        if logscale:
            self._histplot.setAxisScaleEngine(QwtPlot.yLeft, QwtLog10ScaleEngine())
            ymax = max(1, self._hist_max)
            self._histplot.setAxisScale(QwtPlot.yLeft, 1, 10 ** (math.log10(ymax) * (1 + self.ColorBarHeight)))
            y = self._hist.copy()
            y[y == 0] = 1
            self._histcurve1.setData(self._hist_bins, y)
            self._histcurve2.setData(self._hist_bins, y)
        else:
            self._histplot.setAxisScaleEngine(QwtPlot.yLeft, QwtLinearScaleEngine())
            self._histplot.setAxisScale(QwtPlot.yLeft, 0, self._hist_max * (1 + self.ColorBarHeight))
            self._histcurve1.setData(self._hist_bins, self._hist)
            self._histcurve2.setData(self._hist_bins, self._hist)
        if replot:
            self._histplot.replot()
Exemple #21
0
class PM_FileChooser(QWidget):
    """
    The PM_FileChooser widget provides a file chooser widget for a
    Property Manager group box. The PM_FileChooser widget is a composite widget
    made from 3 other Qt widgets:
    - a QLabel 
    - a QLineEdit and
    - a QToolButton (with a "..." text label). 
    
    IMAGE(http://www.nanoengineer-1.net/mediawiki/images/e/e2/PM_FileChooser1.jpg)
    
    The user can type the path name of a file into the line edit widget or 
    select a file using Qt's file (chooser) dialog by clicking the "..."
    button. The path name of the selected file will be inserted into the
    line edit widget.
    
    The parent must make the following signal-slot connection to be
    notified when the user has selected a new file via the file chooser dialog:
    
    self.connect(pmFileChooser.lineEdit, SIGNAL("editingFinished()"), self.mySlotMethod)
    
    @cvar defaultText: The default text (path) of the line edit widget.
    @type defaultText: string
    
    @cvar setAsDefault: Determines whether to reset the value of the
                        lineedit to I{defaultText} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: boolean
    
    @cvar labelWidget: The Qt label widget of this PM widget.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    
    @cvar lineEdit: The Qt line edit widget for this PM widget.
    @type lineEdit: U{B{QLineEdit}<http://doc.trolltech.com/4/qlineedit.html>}
    
    @cvar browseButton: The Qt tool button widget for this PM widget.
    @type browseButton: U{B{QToolButton}<http://doc.trolltech.com/4/qtoolbutton.html>}
    """

    defaultText = ""
    setAsDefault = True
    hidden = False
    lineEdit = None
    browseButton = None

    def __init__(self,
                 parentWidget,
                 label='',
                 labelColumn=0,
                 text='',
                 setAsDefault=True,
                 spanWidth=False,
                 caption="Choose file",
                 directory='',
                 filter="All Files (*.*)"):
        """
        Appends a file chooser widget to <parentWidget>, a property manager 
        group box.
        
        @param parentWidget: the parent group box containing this widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: The label that appears to the left or right of the 
                      file chooser lineedit (and "Browse" button). 
                      
                      If spanWidth is True, the label will be displayed on
                      its own row directly above the lineedit (and button).
                      
                      To suppress the label, set I{label} to an 
                      empty string.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
        
        @param text: initial value of LineEdit widget.
        @type  text: string
        
        @param setAsDefault: if True, will restore <val> when the
                    "Restore Defaults" button is clicked.
        @type  setAsDefault: boolean
        
        @param spanWidth: if True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left justified.
        @type  spanWidth: boolean
        
        @param caption: The caption used as the title of the file chooser 
                        dialog. "Choose file" is the default.
        @type  caption: string
        
        @param directory: The directory that the file chooser dialog should 
                          open in when the "..." button is clicked. 
                          If blank or if directory does not exist,
                          the current working directory is used.
        @type  directory: string
        
        @param filter: The file type filters to use for the file chooser dialog.
        @type  filter: string (a semicolon-separated list of file types)
        
        @see: U{B{QLineEdit}<http://doc.trolltech.com/4/qlineedit.html>}
        """

        QWidget.__init__(self)

        self.parentWidget = parentWidget
        self.label = label
        self.labelColumn = labelColumn
        self.text = text
        self.setAsDefault = setAsDefault
        self.spanWidth = spanWidth
        self.caption = caption
        self.directory = directory
        self.filter = filter

        if label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)
        else:
            # Create a dummy attribute for PM_GroupBox to see.  This might have
            # needed to be fixed in PM_GroupBox, but it was done here to try to
            # avoid causing errors in other PM widgets. -Derrick 20080916
            self.labelWidget = None

        self.lineEdit = QLineEdit()
        self.browseButton = QToolButton()

        # Create vertical box layout.
        self.hBoxLayout = QHBoxLayout(self)
        self.hBoxLayout.setMargin(0)
        self.hBoxLayout.setSpacing(2)
        self.hBoxLayout.insertWidget(-1, self.lineEdit)
        self.hBoxLayout.insertWidget(-1, self.browseButton)

        # Set (QLineEdit) text
        self.setText(text)

        # Set browse button text and make signal-slot connection.
        self.browseButton.setText("...")
        self.connect(self.browseButton, SIGNAL("clicked()"),
                     self.openFileChooserDialog)

        # Set default value
        self.defaultText = text
        self.setAsDefault = setAsDefault

        parentWidget.addPmWidget(self)
        return

    def setText(self, text):
        """
        Set the line edit text.
        
        @param text: The text.
        @type  text: string
        """
        self.lineEdit.setText(text)
        self.text = text
        return

    def openFileChooserDialog(self):
        """
        Prompts the user to choose a file from disk and inserts the full path
        into the lineEdit widget.
        """

        _dir = getDefaultWorkingDirectory()

        if self.directory:
            if os.path.isdir(self.directory):
                _dir = self.directory

        fname = QFileDialog.getOpenFileName(self, self.caption, _dir,
                                            self.filter)

        if fname:
            self.setText(fname)
            self.lineEdit.emit(SIGNAL("editingFinished()"))

        return

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setText(self.defaultText)
        return

    def hide(self):
        """
        Hides the lineedit and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()
        return

    def show(self):
        """
        Unhides the lineedit and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()
        return


# End of PM_FileChooser ############################
Exemple #22
0
class PM_TextEdit(QTextEdit):
    """
    The PM_TextEdit widget provides a QTextEdit with a
    QLabel for a Property Manager groupbox.

    @cvar defaultText: The default text of the textedit.
    @type defaultText: str

    @cvar setAsDefault: Determines whether to reset the value of the
                        textedit to I{defaultText} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: bool

    @cvar labelWidget: The Qt label widget of this textedit.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    """

    defaultText = ""
    setAsDefault = True
    labelWidget = None

    def __init__(self,
                 parentWidget,
                 label='',
                 labelColumn=0,
                 spanWidth=False,
                 addToParent=True,
                 permit_enter_keystroke=True):
        """
        Appends a QTextEdit (Qt) widget to the bottom of I{parentWidget},
        a Property Manager group box.

        The QTextEdit is empty (has no text) by default. Use insertHtml()
        to insert HTML text into the TextEdit.

        @param parentWidget: the parent group box containing this widget.
        @type  parentWidget: PM_GroupBox

        @param label: The label that appears to the left of (or above) the
                      spin box. To suppress the label, set I{label} to an
                      empty string.
        @type  label: str

        @param spanWidth: If True, the spin box and its label will span the width
                          of the group box. The label will appear directly above
                          the spin box and is left justified.
        @type  spanWidth: bool

        @param addToParent: If True (the default), self will be added to
                            parentWidget by passing it to
                            parentWidget.addPmWidget. If False, self will not
                            be added to parentWidget. Typically, when this is
                            False, the caller will add self to parent in some
                            other way.
        @type  addToParent: bool

        @param permit_enter_keystroke: If set to True, this PM_textEdit can have multiple
                               lines. Otherwise, it will block the 'Enter' keypress
                               within the text editor. Note that caller needs
                               to make sure that linewrapping option is
                               appropriately set, (in addition to this flag)
                               so as to permit/ not permit multiple lines
                               in the text edit.


        @see: U{B{QTextEdit}<http://doc.trolltech.com/4/qtextedit.html>}
        """

        if 0:  # Debugging code
            print "QTextEdit.__init__():"
            print "  label       =", label
            print "  labelColumn =", labelColumn
            print "  spanWidth   =", spanWidth

        QTextEdit.__init__(self)

        self.parentWidget = parentWidget
        self.label = label
        self.labelColumn = labelColumn
        self.spanWidth = spanWidth
        self._permit_enter_keystroke = permit_enter_keystroke

        if label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        self._setHeight()  # Default height is 4 lines high.

        ##        from PM.PM_MessageGroupBox import PM_MessageGroupBox
        ##        if isinstance(parentWidget, PM_MessageGroupBox):
        ##            # Add to parentWidget's vBoxLayout if <parentWidget> is a MessageGroupBox.
        ##            parentWidget.vBoxLayout.addWidget(self)
        ##            # We should be calling the PM's getMessageTextEditPalette() method,
        ##            # but that will take some extra work which I will do soon. Mark 2007-06-21
        ##            self.setPalette(getPalette( None,
        ##                                        QPalette.Base,
        ##                                        pmMessageBoxColor))
        ##            self.setReadOnly(True)
        ##            #@self.labelWidget = None # Never has one. Mark 2007-05-31
        ##            parentWidget._widgetList.append(self)
        ##            parentWidget._rowCount += 1
        ##        else:
        ##            parentWidget.addPmWidget(self)
        # bruce 071103 refactored the above into the new addToParent option and
        # code added to PM_MessageGroupBox.__init__ after it calls this method.

        if addToParent:
            parentWidget.addPmWidget(self)

        return

    def keyPressEvent(self, event):
        """
        Overrides the superclass method.
        """
        #If user hits 'Enter' key (return key), don't do anything.
        if event.key() == Qt.Key_Return:
            #Urmi 20080724: emit a signal to indicate end of processing
            self.emit(SIGNAL("editingFinished()"))
            #there is no obvious way to allow only a single line in a
            #QTextEdit (we can use some methods that restrict the columnt width
            #, line wrapping etc but this is untested when the line contains
            # huge umber of characters. Anyway, the following always works
            #and fixes bug 2713
            if not self._permit_enter_keystroke:
                return

        QTextEdit.keyPressEvent(self, event)

    def insertHtml(self,
                   text,
                   setAsDefault=False,
                   minLines=4,
                   maxLines=6,
                   replace=True):
        """
        Insert <text> (HTML) into the Prop Mgr's message groupbox.
        <minLines> is the minimum number of lines to
        display, even if the text takes up fewer lines. The default
        number of lines is 4.
        <maxLines> is the maximum number of lines to
        diplay before adding a vertical scrollbar.
        <replace> should be set to False if you do not wish
        to replace the current text. It will append <text> instead.
        """
        if setAsDefault:
            self.defaultText = text
            self.setAsDefault = True

        if replace:
            # Replace the text by selecting effectively all text and
            # insert the new text 'over' it (overwrite). :jbirac: 20070629
            cursor = self.textCursor()
            cursor.setPosition(0, QTextCursor.MoveAnchor)
            cursor.setPosition(len(self.toPlainText()), QTextCursor.KeepAnchor)
            self.setTextCursor(cursor)

        QTextEdit.insertHtml(self, text)

        if replace:
            # Restore the previous cursor position/selection and mode.
            cursor.setPosition(len(self.toPlainText()), QTextCursor.MoveAnchor)
            self.setTextCursor(cursor)

        #Don't call _setHeight after insertHtml, it increases the height of the
        #text widget and thus gives an undesirable visual effect.
        #This was seen in DnaSequenceEditor. Also tried using 'setSizePolicy' like
        #done in PM_MessagegroupBox but that didn't work.
        ##self._setHeight(minLines, maxLines)

    def _setHeight(self, minLines=4, maxLines=8):
        """
        Set the height just high enough to display
        the current text without a vertical scrollbar.
        <minLines> is the minimum number of lines to
        display, even if the text takes up fewer lines.
        <maxLines> is the maximum number of lines to
        diplay before adding a vertical scrollbar.
        """

        if minLines == 0:
            fitToHeight = True
        else:
            fitToHeight = False

        # Current width of PM_TextEdit widget.
        current_width = self.sizeHint().width()

        # Probably including Html tags.
        text = self.toPlainText()
        text_width = self.fontMetrics().width(text)

        num_lines = text_width / current_width + 1
        # + 1 may create an extra (empty) line on rare occasions.

        if fitToHeight:
            num_lines = min(num_lines, maxLines)

        else:
            num_lines = max(num_lines, minLines)

        #margin = self.fontMetrics().leading() * 2 # leading() returned 0. Mark 2007-05-28
        margin = 10  # Based on trial and error. Maybe it is pm?Spacing=5 (*2)? Mark 2007-05-28
        new_height = num_lines * self.fontMetrics().lineSpacing() + margin

        if 0:  # Debugging code for me. Mark 2007-05-24
            print "--------------------------------"
            print "Widget name = ", self.objectName()
            print "minLines =", minLines
            print "maxLines = ", maxLines
            print "num_lines = ", num_lines
            print "New height = ", new_height
            print "text = ", text
            print "Text width = ", text_width
            print "current_width (of PM_TextEdit)=", current_width

        # Reset height of PM_TextEdit.
        self.setMinimumSize(QSize(PM_MINIMUM_WIDTH * 0.5, new_height))
        self.setMaximumHeight(new_height)

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.insertHtml(self.defaultText, setAsDefault=True, replace=True)

    def hide(self):
        """
        Hides the tool button and its label (if it has one).

        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()

    def show(self):
        """
        Unhides the tool button and its label (if it has one).

        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()
Exemple #23
0
class PM_SpinBox( QSpinBox ):
    """    
    The PM_SpinBox widget provides a QSpinBox (with an 
    optional label) for a Property Manager group box.
    
    Detailed Description
    ====================
    The PM_SpinBox class provides a spin box widget (with an
    optional label) for a 
    U{B{Property Manager dialog}<http://www.nanoengineer-1.net/mediawiki/
    index.php?title=Property_Manager>}.
    
    PM_SpinBox is designed to handle integers and discrete sets of values 
    (e.g., month names); use PM_DoubleSpinBox for floating point values.
    
    PM_SpinBox allows the user to choose a value by clicking the up/down buttons
    or pressing up/down on the keyboard to increase/decrease the value currently
    displayed. The user can also type the value in manually. The spin box
    supports integer values but can be extended to use different strings with 
    validate(), textFromValue() and valueFromText().
    
    Every time the value changes PM_SpinBox emits the valueChanged() signals. 
    The current value can be fetched with value() and set with setValue().
    
    Clicking the up/down buttons or using the keyboard accelerator's up and down 
    arrows will increase or decrease the current value in steps of size 
    singleStep(). If you want to change this behaviour you can reimplement the 
    virtual function stepBy(). The minimum and maximum value and the step size 
    can be set using one of the constructors, and can be changed later with 
    setMinimum(), setMaximum() and setSingleStep().
    
    Most spin boxes are directional, but PM_SpinBox can also operate as a circular
    spin box, i.e. if the range is 0-99 and the current value is 99, clicking
    "up" will give 0 if wrapping() is set to true. Use setWrapping() if you want
    circular behavior.
    
    The displayed value can be prepended and appended with arbitrary strings
    indicating, for example, currency or the unit of measurement. See 
    setPrefix() and setSuffix(). The text in the spin box is retrieved with 
    text() (which includes any prefix() and suffix()), or with cleanText() 
    (which has no prefix(), no suffix() and no leading or trailing whitespace).
    
    It is often desirable to give the user a special (often default) choice 
    in addition to the range of numeric values. See setSpecialValueText() for 
    how to do this with QSpinBox.
    
    @see: U{B{QSpinBox}<http://doc.trolltech.com/4/qspinbox.html>}
    
    @cvar defaultValue: The default value of the spin box.
    @type defaultValue: int
    
    @cvar setAsDefault: Determines whether to reset the value of the
                        spin box to I{defaultValue} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: bool
    
    @cvar labelWidget: The Qt label widget of this spin box.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    """
    
    defaultValue = 0
    setAsDefault = True
    labelWidget  = None
    
    def __init__(self, 
                 parentWidget, 
                 label        = '', 
                 labelColumn  = 0,
                 value        = 0, 
                 setAsDefault = True,
                 minimum      = 0, 
                 maximum      = 99,
                 singleStep   = 1,
                 suffix       = '',
                 spanWidth    = False
                 ):
        """
        Appends a QSpinBox (Qt) widget to the bottom of I{parentWidget}, 
        a Property Manager group box.
        
        @param parentWidget: the parent group box containing this widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: The label that appears to the left of (or above) the 
                      spin box. If label contains the relative path to an 
                      icon (.png) file, that icon image will be used for the
                      label.
                      
                      If spanWidth is True, the label will be displayed on
                      its own row directly above the spin box. 
                      
                      To suppress the label, set I{label} to an empty string.
        @type  label: str
        
        @param value: The initial value of the spin box.
        @type  value: int
        
        @param setAsDefault: Determines if the spin box value is reset when 
                             the "Restore Defaults" button is clicked. If True,
                             (the default) I{value} will be used as the reset 
                             value.
        @type  setAsDefault: bool
        
        @param minimum: The minimum value of the spin box.
        @type  minimum: int
        
        @param maximum: The maximum value of the spin box.
        @type  maximum: int
        
        @param singleStep: When the user uses the arrows to change the 
                           spin box's value the value will be 
                           incremented/decremented by the amount of the 
                           singleStep. The default value is 1. 
                           Setting a singleStep value of less than 0 does 
                           nothing.
        @type  singleStep: int
        
        @param suffix: The suffix is appended to the end of the displayed value. 
                       Typical use is to display a unit of measurement. 
                       The default is no suffix. The suffix is not displayed
                       for the minimum value if specialValueText() is set.
        @type  suffix: str
        
        @param spanWidth: If True, the spin box and its label will span the 
                          width of the group box. The label will appear 
                          directly above the spin box and is left justified. 
        @type  spanWidth: bool
        
        @see: U{B{QSpinBox}<http://doc.trolltech.com/4/qspinbox.html>}
        """
        
        if 0: # Debugging code
            print "PM_SpinBox.__init__():"
            print "  label        = ", label
            print "  labelColumn  = ", labelColumn
            print "  value        = ", value
            print "  setAsDefault = ", setAsDefault
            print "  minimum      = ", minimum
            print "  maximum      = ", maximum
            print "  singleStep   = ", singleStep
            print "  suffix       = ", suffix
            print "  spanWidth    = ", spanWidth
        
        QSpinBox.__init__(self)
                
        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth
        
        self._suppress_valueChanged_signal = False
        
        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)
                
        # Set QSpinBox minimum, maximum and initial value
        self.setRange(minimum, maximum)
        self.setSingleStep(singleStep)
        self.setValue(value)
        
        # Set default value
        self.defaultValue = value
        self.setAsDefault = setAsDefault
        
        # Add suffix if supplied.
        if suffix:
            self.setSuffix(suffix)
            
        parentWidget.addPmWidget(self)
    
    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setValue(self.defaultValue)
            
    def setDefaultValue(self, value):
        """
        Sets the default value of the spin box to I{value}. The current spin box
        value is unchanged.
                      
        @param value: The new default value of the spin box.
        @type  value: int
        
        @see: L{setValue}
        """
        self.setAsDefault = True
        self.defaultValue = value
            
    def setValue(self, value, 
                 setAsDefault = True, 
                 blockSignals = False):
        """
        Sets the value of the spin box to I{value}. 
        
        setValue() will emit valueChanged() if the new value is different from 
        the old one.  (and if blockSignals flag is False)
        
        @param value: The new spin box value.
        @type  value: int
        
        @param setAsDefault: Determines if the spin box value is reset when 
                             the "Restore Defaults" button is clicked. If True,
                             (the default) I{value} will be used as the reset 
                             value.
        @type  setAsDefault: bool
        
        @param blockSignals: Many times, the caller just wants to setValue 
                             and don't want to send valueChanged signal. 
                             If this flag is set to True, the valueChanged 
                             signal won't be emitted.  The default value is 
                             False.
        @type  blockSignals: bool 
        
        @see: L{setDefaultValue}
        @see: QObject.blockSignals(bool block)
        
        @see: B{InsertNanotube_PropertyManager._chiralityFixup()} for an example
              use of blockSignals flag
        
        """
        #If blockSignals flag is True, the valueChanged signal won't be emitted 
        #This is done by self.blockSignals method below.  -- Ninad 2008-08-13
        self.blockSignals(blockSignals)
       
        if setAsDefault:
            self.setDefaultValue(value)
        QSpinBox.setValue(self, value)        
        
        #Make sure to always 'unblock' signals that might have been temporarily
        #blocked before calling superclass.setValue. 
        self.blockSignals(False)
        
        
    def hide(self):
        """
        Hides the spin box and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget: 
            self.labelWidget.hide()
            
    def show(self):
        """
        Unhides the spin box and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget: 
            self.labelWidget.show()
            
                 
        
# End of PM_SpinBox ############################
Exemple #24
0
class PM_LineEdit( QLineEdit ):
    """
    The PM_LineEdit widget provides a QLineEdit with a QLabel for a 
    Property Manager group box.
    
    @cvar defaultText: The default text of the lineedit.
    @type defaultText: str
    
    @cvar setAsDefault: Determines whether to reset the value of the
                        lineedit to I{defaultText} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: bool
    
    @cvar labelWidget: The Qt label widget of this lineedit.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    """
    
    defaultText = ""
    setAsDefault = True
    hidden       = False
    labelWidget  = None
    
    def __init__(self, 
                 parentWidget, 
                 label        = '', 
                 labelColumn  = 0,
                 text         = '', 
                 setAsDefault = True,
                 spanWidth    = False
                 ):
        """
        Appends a QLineEdit widget to <parentWidget>, a property manager group box.
        
        @param parentWidget: the parent group box containing this widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: The label that appears to the left or right of the 
                      checkbox. 
                      
                      If spanWidth is True, the label will be displayed on
                      its own row directly above the checkbox.
                      
                      To suppress the label, set I{label} to an 
                      empty string.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
        
        @param text: initial value of LineEdit widget.
        @type  text: str
        
        @param setAsDefault: if True, will restore <val> when the
                    "Restore Defaults" button is clicked.
        @type  setAsDefault: bool
        
        @param spanWidth: if True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left justified.
        @type  spanWidth: bool
        
        @see: U{B{QLineEdit}<http://doc.trolltech.com/4/qlineedit.html>}
        """
        
        if 0: # Debugging code
            print "PM_LineEdit.__init__():"
            print "  label        = ", label
            print "  labelColumn  = ", labelColumn
            print "  text         = ", text
            print "  setAsDefault = ", setAsDefault
            print "  spanWidth    = ", spanWidth
                
        QLineEdit.__init__(self)
        
        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth
        
        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)
                
        # Set QLineEdit text
        self.setText(text)
        
        # Set default value
        self.defaultText = text
        self.setAsDefault = setAsDefault
                    
        parentWidget.addPmWidget(self)
        
    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setText(self.defaultText)
        
    def hide(self):
        """
        Hides the lineedit and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget: 
            self.labelWidget.hide()
            
    def show(self):
        """
        Unhides the lineedit and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget: 
            self.labelWidget.show()
                
            
# End of PM_LineEdit ############################
class Ui_ProteinSequenceEditor(PM_DockWidget):
    """
    The Ui_DnaSequenceEditor class defines UI elements for the Sequence Editor
    object. The sequence editor is usually visible while in DNA edit mode.
    It is a DockWidget that is doced at the bottom of the MainWindow
    """
    _title         =  "Sequence Editor"
    _groupBoxCount = 0
    _lastGroupBox = None

    def __init__(self, win):
        """
        Constructor for the Ui_DnaSequenceEditor 
        @param win: The parentWidget (MainWindow) for the sequence editor 
        """
        
        self.win = win
        # Should parentWidget for a docwidget always be win? 
        #Not necessary but most likely it will be the case.        
        parentWidget = win 
        
        _superclass.__init__(self, parentWidget, title = self._title)
        
        #A flag used to restore the state of the Reports dock widget 
        #(which can be accessed through View  >  Reports) see self.show() and
        #self.closeEvent() for more details. 
        self._reportsDockWidget_closed_in_show_method = False
        self.setFixedHeight(90)

    def show(self):
        """
        Shows the sequence editor. While doing this, it also closes the reports
        dock widget (if visible) the state of the reports dockwidget will be
        restored when the sequence editor is closed. 
        @see:self.closeEvent()
        """
        self._reportsDockWidget_closed_in_show_method = False
        
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self.win.reportsDockWidget.isVisible():
                self.win.reportsDockWidget.close()
                self._reportsDockWidget_closed_in_show_method = True

        _superclass.show(self)  
        
    def closeEvent(self, event):
        """
        Overrides close event. Makes sure that the visible state of the reports
        widgetis restored when the sequence editor is closed. 
        @see: self.show()
        """
        _superclass.closeEvent(self, event)
       
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self._reportsDockWidget_closed_in_show_method:
                self.win.viewReportsAction.setChecked(True) 
                self._reportsDockWidget_closed_in_show_method = False

    def _loadWidgets(self):
        """
        Overrides PM.PM_DockWidget._loadWidgets. Loads the widget in this
        dockwidget.
        """
        self._loadMenuWidgets()
        self._loadTextEditWidget()


    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.loadSequenceButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Open.png")  

        self.saveSequenceButton = PM_ToolButton(
            self, 
            iconPath = "ui/actions/Properties Manager/Save_Strand_Sequence.png") 

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        
        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)


        self.replaceLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text = "Replace")

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        

        #Widgets to include in the widget row. 
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "     Find:", 4),
                      ('PM_LineEdit', self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8), 
                      ('QLabel', "     Replace:", 9),
                      ('PM_TextEdit', self.replaceLineEdit, 10), 
                      ('PM_PushButton', self.replacePushButton, 11),
                      ('PM_Label', self.warningSign, 12),
                      ('PM_Label', self.phraseNotFoundLabel, 13),
                      ('QSpacerItem', 5, 5, 14) ]

        widgetRow = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList,
                                 label = "",
                                 spanWidth = True )
        
        

    def _loadTextEditWidget(self):
        """
        Load the SequenceTexteditWidgets.         
        """        
        self.aaRulerTextEdit = \
            PM_TextEdit( self, 
                         label = "", 
                         spanWidth = False,
                         permit_enter_keystroke = False) 
        
        palette = getPalette(None, 
                             QPalette.Base, 
                             pmGrpBoxColor)
        self.aaRulerTextEdit.setPalette(palette)     
        self.aaRulerTextEdit.setWordWrapMode( QTextOption.WrapAnywhere )
        self.aaRulerTextEdit.setFixedHeight(20)
        self.aaRulerTextEdit.setReadOnly(True)
        
        self.sequenceTextEdit = \
            PM_TextEdit( self, 
                         label = " Sequence: ", 
                         spanWidth = False,
                         permit_enter_keystroke = False) 
        
        
        self.sequenceTextEdit.setCursorWidth(2)
        self.sequenceTextEdit.setWordWrapMode( QTextOption.WrapAnywhere )
        self.sequenceTextEdit.setFixedHeight(20)
        
        self.secStrucTextEdit = \
            PM_TextEdit( self, 
                         label = " Secondary structure: ", 
                         spanWidth = False,
                         permit_enter_keystroke = False) 
        
        palette = getPalette(None, 
                             QPalette.Base, 
                             sequenceEditStrandMateBaseColor)
        self.secStrucTextEdit.setPalette(palette)     
        self.secStrucTextEdit.setWordWrapMode( QTextOption.WrapAnywhere )
        self.secStrucTextEdit.setFixedHeight(20)
        self.secStrucTextEdit.setReadOnly(True)

        #Important to make sure that the horizontal and vertical scrollbars 
        #for these text edits are never displayed. 
        
        self.sequenceTextEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.sequenceTextEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.secStrucTextEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.secStrucTextEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.aaRulerTextEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.aaRulerTextEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def _getFindLineEditStyleSheet(self):
        """
        Return the style sheet for the findLineEdit. This sets the following 
        properties only:
         - background-color

        This style is set whenever the searchStrig can't be found (sets
        a light red color background to the lineedit when this happens)   

        @return: The line edit style sheet.
        @rtype:  str

        """
        styleSheet = \
                   "QLineEdit {\
                   background-color: rgb(255, 102, 102)\
                   }"
        #Not used:
        #  background-color: rgb(217, 255, 216)\       

        return styleSheet

    def _setFindOptionsToolButtonMenu(self):
        """
        Sets the menu for the findOptionstoolbutton that appears a small 
        menu button next to the findLineEdit.
        """
        self.findOptionsMenu = QMenu(self.findOptionsToolButton)

        self.caseSensitiveFindAction = QAction(self.findOptionsToolButton)
        self.caseSensitiveFindAction.setText('Match Case')
        self.caseSensitiveFindAction.setCheckable(True)
        self.caseSensitiveFindAction.setChecked(False)

        self.findOptionsMenu.addAction(self.caseSensitiveFindAction)
        self.findOptionsMenu.addSeparator()

        self.findOptionsToolButton.setMenu(self.findOptionsMenu)

    def _addToolTipText(self):
        """
            What's Tool Tip text for widgets in this Property Manager.  
            """ 
        pass

    def _addWhatsThisText(self):
        """
            What's This text for widgets in this Property Manager.  

            """
        pass
Exemple #26
0
class PM_TextEdit( QTextEdit ):
    """
    The PM_TextEdit widget provides a QTextEdit with a 
    QLabel for a Property Manager groupbox.
    
    @cvar defaultText: The default text of the textedit.
    @type defaultText: str
    
    @cvar setAsDefault: Determines whether to reset the value of the
                        textedit to I{defaultText} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: bool

    @cvar labelWidget: The Qt label widget of this textedit.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    """
    
    defaultText  = ""
    setAsDefault = True
    labelWidget  = None
    
    def __init__(self, 
                 parentWidget, 
                 label       = '', 
                 labelColumn = 0,
                 spanWidth   = False,
                 addToParent = True,
                 permit_enter_keystroke = True
                 ):
        """
        Appends a QTextEdit (Qt) widget to the bottom of I{parentWidget}, 
        a Property Manager group box.
        
        The QTextEdit is empty (has no text) by default. Use insertHtml() 
        to insert HTML text into the TextEdit.        
        
        @param parentWidget: the parent group box containing this widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: The label that appears to the left of (or above) the 
                      spin box. To suppress the label, set I{label} to an 
                      empty string.
        @type  label: str
        
        @param spanWidth: If True, the spin box and its label will span the width
                          of the group box. The label will appear directly above
                          the spin box and is left justified. 
        @type  spanWidth: bool
        
        @param addToParent: If True (the default), self will be added to
                            parentWidget by passing it to
                            parentWidget.addPmWidget. If False, self will not
                            be added to parentWidget. Typically, when this is
                            False, the caller will add self to parent in some
                            other way.
        @type  addToParent: bool
        
        @param permit_enter_keystroke: If set to True, this PM_textEdit can have multiple
                               lines. Otherwise, it will block the 'Enter' keypress
                               within the text editor. Note that caller needs 
                               to make sure that linewrapping option is a
                               propriately set, (in addition to this flag)
                               so as to permit/ not permit multiple lines 
                               in the text edit.
                               
        
        @see: U{B{QTextEdit}<http://doc.trolltech.com/4/qtextedit.html>}
        """
        
        if 0: # Debugging code
            print "QTextEdit.__init__():"
            print "  label       =", label
            print "  labelColumn =", labelColumn
            print "  spanWidth   =", spanWidth
        
        QTextEdit.__init__(self)
        
        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.spanWidth    = spanWidth
        self._permit_enter_keystroke = permit_enter_keystroke
        
        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)
        
        self._setHeight() # Default height is 4 lines high.
        
        
        
##        from PM.PM_MessageGroupBox import PM_MessageGroupBox
##        if isinstance(parentWidget, PM_MessageGroupBox):
##            # Add to parentWidget's vBoxLayout if <parentWidget> is a MessageGroupBox.
##            parentWidget.vBoxLayout.addWidget(self)
##            # We should be calling the PM's getMessageTextEditPalette() method,
##            # but that will take some extra work which I will do soon. Mark 2007-06-21
##            self.setPalette(getPalette( None, 
##                                        QPalette.Base,
##                                        pmMessageBoxColor))
##            self.setReadOnly(True)
##            #@self.labelWidget = None # Never has one. Mark 2007-05-31
##            parentWidget._widgetList.append(self)
##            parentWidget._rowCount += 1
##        else:
##            parentWidget.addPmWidget(self)
        # bruce 071103 refactored the above into the new addToParent option and
        # code added to PM_MessageGroupBox.__init__ after it calls this method.
        
        if addToParent:
            parentWidget.addPmWidget(self)
        
       
        return
    
    def keyPressEvent(self, event):
        """
        Overrides the superclass method. 
        """
        #If user hits 'Enter' key (return key), don't do anything. 
        if event.key() == Qt.Key_Return:
            #there is no obvious way to allow only a single line in a 
            #QTextEdit (we can use some methods that restrict the columnt width
            #, line wrapping etc but this is untested when the line contains 
            # huge umber of characters. Anyway, the following always works 
            #and fixes bug 2713
            if not self._permit_enter_keystroke:
                return  
            
        QTextEdit.keyPressEvent(self, event)
                
        
    def insertHtml(self, 
                   text, 
                   setAsDefault = False, 
                   minLines     = 4, 
                   maxLines     = 6, 
                   replace      = True
                   ):
        """
        Insert <text> (HTML) into the Prop Mgr's message groupbox.
        <minLines> is the minimum number of lines to
        display, even if the text takes up fewer lines. The default
        number of lines is 4.
        <maxLines> is the maximum number of lines to
        diplay before adding a vertical scrollbar.
        <replace> should be set to False if you do not wish
        to replace the current text. It will append <text> instead.
        """
        if setAsDefault:
            self.defaultText = text
            self.setAsDefault = True
    
        if replace:
            # Replace the text by selecting effectively all text and
            # insert the new text 'over' it (overwrite). :jbirac: 20070629
            cursor  =  self.textCursor()
            cursor.setPosition( 0, 
                                QTextCursor.MoveAnchor )
            cursor.setPosition( len(self.toPlainText()), 
                                QTextCursor.KeepAnchor )
            self.setTextCursor( cursor )
        
        QTextEdit.insertHtml(self, text)

        if replace:
            # Restore the previous cursor position/selection and mode.
            cursor.setPosition( len(self.toPlainText()), 
                                QTextCursor.MoveAnchor )
            self.setTextCursor( cursor )
        
              
        #Don't call _setHeight after insertHtml, it increases the height of the
        #text widget and thus gives an undesirable visual effect.
        #This was seen in DnaSequenceEditor. Also tried using 'setSizePolicy' like 
        #done in PM_MessagegroupBox but that didn't work. 
        ##self._setHeight(minLines, maxLines)
        
    def _setHeight( self, 
                    minLines = 4, 
                    maxLines = 8 ):
        """
        Set the height just high enough to display
        the current text without a vertical scrollbar.
        <minLines> is the minimum number of lines to
        display, even if the text takes up fewer lines.
        <maxLines> is the maximum number of lines to
        diplay before adding a vertical scrollbar.
        """
        
        if minLines == 0:
            fitToHeight=True
        else:
            fitToHeight=False
        
        # Current width of PM_TextEdit widget.
        current_width = self.sizeHint().width()
        
        # Probably including Html tags.
        text = self.toPlainText()
        text_width = self.fontMetrics().width(text)
        
        num_lines = text_width/current_width + 1
            # + 1 may create an extra (empty) line on rare occasions.
                        
        if fitToHeight:
            num_lines = min(num_lines, maxLines)
                
        else:
            num_lines = max(num_lines, minLines)

        #margin = self.fontMetrics().leading() * 2 # leading() returned 0. Mark 2007-05-28
        margin = 10 # Based on trial and error. Maybe it is pm?Spacing=5 (*2)? Mark 2007-05-28
        new_height = num_lines * self.fontMetrics().lineSpacing() + margin
        
        if 0: # Debugging code for me. Mark 2007-05-24
            print "--------------------------------"
            print "Widget name = ", self.objectName()
            print "minLines =" , minLines
            print "maxLines = ", maxLines
            print "num_lines = ", num_lines
            print "New height = ", new_height
            print "text = ", text   
            print "Text width = ", text_width
            print "current_width (of PM_TextEdit)=", current_width
        
        # Reset height of PM_TextEdit.
        self.setMinimumSize(QSize(PM_MINIMUM_WIDTH * 0.5, new_height))
        self.setMaximumHeight(new_height)
    
    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.insertHtml(self.defaultText, 
                            setAsDefault = True,
                            replace = True)
        
    def hide(self):
        """
        Hides the tool button and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget: 
            self.labelWidget.hide()
            
    def show(self):
        """
        Unhides the tool button and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget: 
            self.labelWidget.show()
class PM_FileChooser( QWidget ):
    """
    The PM_FileChooser widget provides a file chooser widget for a
    Property Manager group box. The PM_FileChooser widget is a composite widget
    made from 3 other Qt widgets:
    - a QLabel 
    - a QLineEdit and
    - a QToolButton (with a "..." text label). 
    
    IMAGE(http://www.nanoengineer-1.net/mediawiki/images/e/e2/PM_FileChooser1.jpg)
    
    The user can type the path name of a file into the line edit widget or 
    select a file using Qt's file (chooser) dialog by clicking the "..."
    button. The path name of the selected file will be inserted into the
    line edit widget.
    
    The parent must make the following signal-slot connection to be
    notified when the user has selected a new file via the file chooser dialog:
    
    self.connect(pmFileChooser.lineEdit, SIGNAL("editingFinished()"), self.mySlotMethod)
    
    @cvar defaultText: The default text (path) of the line edit widget.
    @type defaultText: string
    
    @cvar setAsDefault: Determines whether to reset the value of the
                        lineedit to I{defaultText} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: boolean
    
    @cvar labelWidget: The Qt label widget of this PM widget.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    
    @cvar lineEdit: The Qt line edit widget for this PM widget.
    @type lineEdit: U{B{QLineEdit}<http://doc.trolltech.com/4/qlineedit.html>}
    
    @cvar browseButton: The Qt tool button widget for this PM widget.
    @type browseButton: U{B{QToolButton}<http://doc.trolltech.com/4/qtoolbutton.html>}
    """
    
    defaultText = ""
    setAsDefault = True
    hidden       = False
    lineEdit     = None
    browseButton = None
    
    def __init__(self, 
                 parentWidget, 
                 label        = '', 
                 labelColumn  = 0,
                 text         = '', 
                 setAsDefault = True,
                 spanWidth    = False,
                 caption      = "Choose file",
                 directory    = '',
                 filter       = "All Files (*.*)"
                 ):
        """
        Appends a file chooser widget to <parentWidget>, a property manager 
        group box.
        
        @param parentWidget: the parent group box containing this widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: The label that appears to the left or right of the 
                      file chooser lineedit (and "Browse" button). 
                      
                      If spanWidth is True, the label will be displayed on
                      its own row directly above the lineedit (and button).
                      
                      To suppress the label, set I{label} to an 
                      empty string.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
        
        @param text: initial value of LineEdit widget.
        @type  text: string
        
        @param setAsDefault: if True, will restore <val> when the
                    "Restore Defaults" button is clicked.
        @type  setAsDefault: boolean
        
        @param spanWidth: if True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left justified.
        @type  spanWidth: boolean
        
        @param caption: The caption used as the title of the file chooser 
                        dialog. "Choose file" is the default.
        @type  caption: string
        
        @param directory: The directory that the file chooser dialog should 
                          open in when the "..." button is clicked. 
                          If blank or if directory does not exist,
                          the current working directory is used.
        @type  directory: string
        
        @param filter: The file type filters to use for the file chooser dialog.
        @type  filter: string (a semicolon-separated list of file types)
        
        @see: U{B{QLineEdit}<http://doc.trolltech.com/4/qlineedit.html>}
        """
        
        QWidget.__init__(self)
        
        self.parentWidget = parentWidget
        self.label        = label
        self.labelColumn  = labelColumn
        self.text         = text
        self.setAsDefault = setAsDefault
        self.spanWidth    = spanWidth
        self.caption      = caption
        self.directory    = directory
        self.filter       = filter
        
        if label: # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)
        
        self.lineEdit = QLineEdit()
        self.browseButton = QToolButton()
        
        # Create vertical box layout.
        self.hBoxLayout = QHBoxLayout(self)
        self.hBoxLayout.setMargin(0)
        self.hBoxLayout.setSpacing(2)
        self.hBoxLayout.insertWidget(-1, self.lineEdit)
        self.hBoxLayout.insertWidget(-1, self.browseButton)
        
        # Set (QLineEdit) text
        self.setText(text)
        
        # Set browse button text and make signal-slot connection.
        self.browseButton.setText("...")
        self.connect(self.browseButton, SIGNAL("clicked()"), self.openFileChooserDialog)
        
        # Set default value
        self.defaultText = text
        self.setAsDefault = setAsDefault
        
        parentWidget.addPmWidget(self)
        return
        
    def setText(self, text):
        """
        Set the line edit text.
        
        @param text: The text.
        @type  text: string
        """
        self.lineEdit.setText(text)
        self.text = text
        return
        
    def openFileChooserDialog(self):
        """
        Prompts the user to choose a file from disk and inserts the full path
        into the lineEdit widget.
        """
        
        _dir = getDefaultWorkingDirectory()
        
        if self.directory:
            if os.path.isdir(self.directory):
                _dir = self.directory

        fname = QFileDialog.getOpenFileName(self,
                                   self.caption,
                                   _dir,
                                   self.filter)
        
        if fname:
            self.setText(fname)
            self.lineEdit.emit(SIGNAL("editingFinished()"))
            
        return
    
    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setText(self.defaultText)
        return
    
    def hide(self):
        """
        Hides the lineedit and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget: 
            self.labelWidget.hide()
        return
    
    def show(self):
        """
        Unhides the lineedit and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget: 
            self.labelWidget.show()
        return
            
# End of PM_FileChooser ############################
Exemple #28
0
class PM_ColorChooser(QWidget):
    """
    The PM_ColorChooser widget provides a color chooser widget for a
    Property Manager group box. The PM_ColorChooser widget is a composite widget
    made from 3 other Qt widgets:
    - a QLabel 
    - a QFrame and
    - a QToolButton (with a "..." text label). 
    
    IMAGE(http://www.nanoengineer-1.net/mediawiki/images/e/e2/PM_ColorChooser1.jpg)
    
    The user can color using Qt's color (chooser) dialog by clicking the "..."
    button. The selected color will be used as the color of the QFrame widget.
    
    The parent must make the following signal-slot connection to be
    notified when the user has selected a new color via the color chooser 
    dialog:
    
    self.connect(pmColorChooser.colorFrame, SIGNAL("editingFinished()"), self.mySlotMethod)
    
    @cvar setAsDefault: Determines whether to reset the value of the
                        color to I{defaultColor} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: boolean
    
    @cvar labelWidget: The Qt label widget of this PM widget.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    
    @cvar colorFrame: The Qt frame widget for this PM widget.
    @type colorFrame: U{B{QFrame}<http://doc.trolltech.com/4/qframe.html>}
    
    @cvar chooseButton: The Qt tool button widget for this PM widget.
    @type chooseButton: U{B{QToolButton}<http://doc.trolltech.com/4/qtoolbutton.html>}
    """

    defaultColor = None
    setAsDefault = True
    hidden = False
    chooseButton = None
    customColorCount = 0
    standardColorList = [white, black]

    def __init__(
        self,
        parentWidget,
        label='Color:',
        labelColumn=0,
        color=white,
        setAsDefault=True,
        spanWidth=False,
    ):
        """
        Appends a color chooser widget to <parentWidget>, a property manager 
        group box.
        
        @param parentWidget: the parent group box containing this widget.
        @type  parentWidget: PM_GroupBox
        
        @param label: The label that appears to the left or right of the 
                      color frame (and "Browse" button). 
                      
                      If spanWidth is True, the label will be displayed on
                      its own row directly above the lineedit (and button).
                      
                      To suppress the label, set I{label} to an 
                      empty string.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
        
        @param color: initial color. White is the default.
        @type  color: tuple of 3 floats (r, g, b)
        
        @param setAsDefault: if True, will restore L{color} when the
                    "Restore Defaults" button is clicked.
        @type  setAsDefault: boolean
        
        @param spanWidth: if True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left
                      justified.
        @type  spanWidth: boolean
        
        @see: U{B{QColorDialog}<http://doc.trolltech.com/4/qcolordialog.html>}
        """

        QWidget.__init__(self)

        self.parentWidget = parentWidget
        self.label = label
        self.labelColumn = labelColumn
        self.color = color
        self.setAsDefault = setAsDefault
        self.spanWidth = spanWidth

        if label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        # Create the color frame (color swath) and "..." button.
        self.colorFrame = QFrame()
        self.colorFrame.setFrameShape(QFrame.Box)
        self.colorFrame.setFrameShadow(QFrame.Plain)

        # Set browse button text and make signal-slot connection.
        self.chooseButton = QToolButton()
        self.chooseButton.setText("...")
        self.connect(self.chooseButton, SIGNAL("clicked()"),
                     self.openColorChooserDialog)

        # Add a horizontal spacer to keep the colorFrame and "..." squeezed
        # together, even when the PM width changes.
        self.hSpacer = QSpacerItem(10, 10, QSizePolicy.MinimumExpanding,
                                   QSizePolicy.Fixed)

        # Create vertical box layout.
        self.hBoxLayout = QHBoxLayout(self)
        self.hBoxLayout.setMargin(0)
        self.hBoxLayout.setSpacing(2)
        self.hBoxLayout.insertWidget(-1, self.colorFrame)
        self.hBoxLayout.insertWidget(-1, self.chooseButton)

        # Set this to False to make the colorFrame an expandable rectangle.
        COLORFRAME_IS_SQUARE = True
        if COLORFRAME_IS_SQUARE:
            squareSize = 20
            self.colorFrame.setMinimumSize(QSize(squareSize, squareSize))
            self.colorFrame.setMaximumSize(QSize(squareSize, squareSize))
            self.hBoxLayout.addItem(self.hSpacer)

        self.setColor(color, default=setAsDefault)

        parentWidget.addPmWidget(self)
        return

    def setColor(self, color, default=False):
        """
        Set the color.
        
        @param color: The color.
        @type  color: tuple of 3 floats (r, g, b)
        
        @param default: If True, make I{color} the default color. Default is
                        False.
        @type  default: boolean
        """
        if default:
            self.defaultColor = color
            self.setAsDefault = default
        self.color = color
        self._updateColorFrame()
        return

    def getColor(self):
        """
        Return the current color.
        
        @return: The current r, g, b color.
        @rtype:  Tuple of 3 floats (r, g, b)
        """
        return self.color

    def getQColor(self):
        """
        Return the current QColor.
        
        @return: The current color.
        @rtype:  QColor
        """
        return RGBf_to_QColor(self.color)

    def _updateColorFrame(self):
        """
        Updates the color frame with the current color.
        """
        colorframe = self.colorFrame
        try:
            qcolor = self.getQColor()
            palette = QPalette(
            )  # QPalette(qcolor) would have window color set from qcolor, but that doesn't help us here
            qcolorrole = QPalette.Window
            ## http://doc.trolltech.com/4.2/qpalette.html#ColorRole-enum says:
            ##   QPalette.Window    10    A general background color.
            palette.setColor(QPalette.Active, qcolorrole,
                             qcolor)  # used when window is in fg and has focus
            palette.setColor(
                QPalette.Inactive, qcolorrole,
                qcolor)  # used when window is in bg or does not have focus
            palette.setColor(QPalette.Disabled, qcolorrole,
                             qcolor)  # used when widget is disabled
            colorframe.setPalette(palette)
            colorframe.setAutoFillBackground(True)
        except:
            print "data for following exception: ",
            print "colorframe %r has palette %r" % (colorframe,
                                                    colorframe.palette())
        pass

    def openColorChooserDialog(self):
        """
        Prompts the user to choose a color and then updates colorFrame with
        the selected color.
        """
        qcolor = RGBf_to_QColor(self.color)
        if not self.color in self.standardColorList:
            QColorDialog.setCustomColor(self.customColorCount, qcolor.rgb())
            self.customColorCount += 1
        c = QColorDialog.getColor(qcolor, self)
        if c.isValid():
            self.setColor(QColor_to_RGBf(c))
            self.colorFrame.emit(SIGNAL("editingFinished()"))

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setColor(self.defaultColor)
        return

    def hide(self):
        """
        Hides the lineedit and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()
        return

    def show(self):
        """
        Unhides the lineedit and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()
        return
class PM_GroupBox( QGroupBox ):
    """
    The PM_GroupBox widget provides a group box container with a 
    collapse/expand button and a title button.
    
    PM group boxes can be nested by supplying an existing PM_GroupBox as the 
    parentWidget of a new PM_GroupBox (as an argument to its constructor).
    If the parentWidget is a PM_GroupBox, no title button will be created
    for the new group box.
    
    @cvar setAsDefault: Determines whether to reset the value of all
                        widgets in the group box when the user clicks
                        the "Restore Defaults" button. If set to False,
                        no widgets will be reset regardless thier own 
                        I{setAsDefault} value.
    @type setAsDefault: bool
       
    @cvar labelWidget: The Qt label widget of this group box.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    
    @cvar expanded: Expanded flag.
    @type expanded: bool
    
    @cvar _title: The group box title.
    @type _title: str
    
    @cvar _widgetList: List of widgets in the group box 
                      (except the title button).
    @type _widgetList: list
    
    @cvar _rowCount: Number of rows in the group box.
    @type _rowCount: int
    
    @cvar _groupBoxCount: Number of group boxes in this group box.
    @type _groupBoxCount: int
    
    @cvar _lastGroupBox: The last group box in this group box (i.e. the
                         most recent PM group box added).
    @type _lastGroupBox: PM_GroupBox
    """
    
    setAsDefault = True
    labelWidget  = None
    expanded     = True
    
    _title         = ""
    _widgetList    = []
    _rowCount      = 0
    _groupBoxCount = 0
    _lastGroupBox  = None
    titleButtonRequested = True
    
    def __init__(self, 
                 parentWidget, 
                 title          = '', 
                 connectTitleButton = True,
                 setAsDefault   = True
                 ):
        """
        Appends a PM_GroupBox widget to I{parentWidget}, a L{PM_Dialog} or a 
        L{PM_GroupBox}.
        
        If I{parentWidget} is a L{PM_Dialog}, the group box will have a title 
        button at the top for collapsing and expanding the group box. If 
        I{parentWidget} is a PM_GroupBox, the title will simply be a text 
        label at the top of the group box.
        
        @param parentWidget: The parent dialog or group box containing this
                             widget.
        @type  parentWidget: L{PM_Dialog} or L{PM_GroupBox}
        
        @param title: The title (button) text. If empty, no title is added.
        @type  title: str
        
        @param connectTitleButton: If True, this class will automatically 
                      connect the title button of the groupbox to send signal
                      to expand or collapse the groupbox. Otherwise, the caller
                      has to connect this signal by itself. See:
                      B{Ui_MovePropertyManager.addGroupBoxes} and 
                      B{MovePropertyManager.connect_or_disconnect_signals} for
                      examples where the client connects this slot. 
        @type  connectTitleButton: bool
               
        
        @param setAsDefault: If False, no widgets in this group box will have 
                             thier default values restored when the B{Restore 
                             Defaults} button is clicked, regardless thier own 
                             I{setAsDefault} value.
        @type  setAsDefault: bool
        
        @see: U{B{QGroupBox}<http://doc.trolltech.com/4/qgroupbox.html>}
        """
      
        QGroupBox.__init__(self)
        
        self.parentWidget = parentWidget
        
        # Calling addWidget() here is important. If done at the end,
        # the title button does not get assigned its palette for some 
        # unknown reason. Mark 2007-05-20.
        # Add self to PropMgr's vBoxLayout
        if parentWidget:
            parentWidget._groupBoxCount += 1
            parentWidget.vBoxLayout.addWidget(self)
            parentWidget._widgetList.append(self)
            
        _groupBoxCount = 0
        self._widgetList = []
        self._title = title
        self.setAsDefault = setAsDefault
        
        self.setAutoFillBackground(True)
        self.setStyleSheet(self._getStyleSheet())
        
        # Create vertical box layout which will contain two widgets:
        # - the group box title button (or title) on row 0.
        # - the container widget for all PM widgets on row 1.
        self._vBoxLayout = QVBoxLayout(self)
        self._vBoxLayout.setMargin(0)
        self._vBoxLayout.setSpacing(0)
        
        # _containerWidget contains all PM widgets in this group box.
        # Its sole purpose is to easily support the collapsing and
        # expanding of a group box by calling this widget's hide()
        # and show() methods.
        self._containerWidget = QWidget()
        
        self._vBoxLayout.insertWidget(0, self._containerWidget)
        
        # Create vertical box layout
        self.vBoxLayout = QVBoxLayout(self._containerWidget)
        self.vBoxLayout.setMargin(PM_GROUPBOX_VBOXLAYOUT_MARGIN)
        self.vBoxLayout.setSpacing(PM_GROUPBOX_VBOXLAYOUT_SPACING)
        
        # Create grid layout
        self.gridLayout = QGridLayout()
        self.gridLayout.setMargin(PM_GRIDLAYOUT_MARGIN)
        self.gridLayout.setSpacing(PM_GRIDLAYOUT_SPACING)
        
        # Insert grid layout in its own vBoxLayout
        self.vBoxLayout.addLayout(self.gridLayout)
        
        # Add title button (or just a title if the parent is not a PM_Dialog).
        if not parentWidget or isinstance(parentWidget, PM_GroupBox):
            self.setTitle(title)
        else: # Parent is a PM_Dialog, so add a title button.
            if not self.titleButtonRequested:
                self.setTitle(title)
            else:
                self.titleButton = self._getTitleButton(self, title)
                self._vBoxLayout.insertWidget(0, self.titleButton)
                if connectTitleButton:
                    self.connect( self.titleButton, 
                                  SIGNAL("clicked()"),
                                  self.toggleExpandCollapse)
                self._insertMacSpacer()
            
        # Fixes the height of the group box. Very important. Mark 2007-05-29
        self.setSizePolicy(
            QSizePolicy(QSizePolicy.Policy(QSizePolicy.Preferred),
                        QSizePolicy.Policy(QSizePolicy.Fixed)))
        
        self._addBottomSpacer()
        return
    
    def _insertMacSpacer(self, spacerHeight = 6):
        """
        This addresses a Qt 4.3.5 layout bug on Mac OS X in which the 
        title button will overlap with and cover the first widget in 
        the group box. This workaround inserts a I{spacerHeight} tall
        spacer between the group box's title button and container widget.
        """
        if sys.platform != "darwin":
            return
        
        self._macVerticalSpacer = QSpacerItem(10, spacerHeight, 
                                        QSizePolicy.MinimumExpanding,
                                        QSizePolicy.Fixed)
        self._vBoxLayout.insertItem(1, self._macVerticalSpacer)
        return
        
    def _addBottomSpacer(self):
        """
        Add a vertical spacer below this group box.
        
        Method: Assume this is going to be the last group box in the PM, so set
        its spacer's vertical sizePolicy to MinimumExpanding. We then set the 
        vertical sizePolicy of the last group box's spacer to Fixed and set its
        height to PM_GROUPBOX_SPACING.
        """
        # Spacers are only added to groupboxes in the PropMgr, not
        # nested groupboxes.
        
##        from PM.PM_Dialog import PM_Dialog
##        if not isinstance(self.parentWidget, PM_Dialog):
        
        if not self.parentWidget or isinstance(self.parentWidget, PM_GroupBox):
            #bruce 071103 revised test to remove import cycle; I assume that
            # self.parentWidget is either a PM_GroupBox or a PM_Dialog, since
            # comments about similar code in __init__ imply that.
            #
            # A cleaner fix would involve asking self.parentWidget whether to
            # do this, with instances of PM_GroupBox and PM_Dialog giving
            # different answers, and making them both inherit an API class
            # which documents the method or attr with which we ask them that
            # question; the API class would be inherited by any object to
            # which PM_GroupBox's such as self can be added.
            #
            # Or, an even cleaner fix would just call a method in
            # self.parentWidget to do what this code does now (implemented
            # differently in PM_GroupBox and PM_Dialog).
            self.verticalSpacer = None
            return
        
        if self.parentWidget._lastGroupBox:
            # _lastGroupBox is no longer the last one. <self> will be the
            # _lastGroupBox, so we must change the verticalSpacer height 
            # and sizePolicy of _lastGroupBox to be a fixed
            # spacer between it and <self>.
            defaultHeight = PM_GROUPBOX_SPACING
            self.parentWidget._lastGroupBox.verticalSpacer.changeSize(
                10, defaultHeight, 
                QSizePolicy.Fixed,
                QSizePolicy.Fixed)
            self.parentWidget._lastGroupBox.verticalSpacer.defaultHeight = \
                defaultHeight
            
        # Add a 1 pixel high, MinimumExpanding VSpacer below this group box.
        # This keeps all group boxes in the PM layout squeezed together as 
        # group boxes  are expanded, collapsed, hidden and shown again.
        defaultHeight = 1
        self.verticalSpacer = QSpacerItem(10, defaultHeight, 
                                        QSizePolicy.Fixed,
                                        QSizePolicy.MinimumExpanding)
        
        self.verticalSpacer.defaultHeight = defaultHeight
        
        self.parentWidget.vBoxLayout.addItem(self.verticalSpacer)
        
        # This groupbox is now the last one in the PropMgr.
        self.parentWidget._lastGroupBox = self
        return
        
    def restoreDefault (self):
        """
        Restores the default values for all widgets in this group box.
        """
        for widget in self._widgetList:
            if debug_flags.atom_debug:
                print "PM_GroupBox.restoreDefault(): widget =", \
                      widget.objectName()
            widget.restoreDefault()
            
        return
    
    def getTitle(self):
        """
        Returns the group box title.
        
        @return: The group box title.
        @rtype:  str
        """
        return self._title
    
    def setTitle(self, title):
        """
        Sets the groupbox title to <title>.
        
        @param title: The group box title.
        @type  title: str
        
        @attention: This overrides QGroupBox's setTitle() method.
        """
        
        if not title:
            return
        
        # Create QLabel widget.
        if not self.labelWidget:
            self.labelWidget = QLabel()
            self.vBoxLayout.insertWidget(0, self.labelWidget)
            labelAlignment = PM_LABEL_LEFT_ALIGNMENT
            self.labelWidget.setAlignment(labelAlignment)
        
        self._title = title
        self.labelWidget.setText(title)
        return
        
    def getPmWidgetPlacementParameters(self, pmWidget):
        """
        Returns all the layout parameters needed to place 
        a PM_Widget in the group box grid layout.
        
        @param pmWidget: The PM widget.
        @type  pmWidget: PM_Widget
        """
        
        row = self._rowCount
        
        #PM_CheckBox doesn't have a label. So do the following to decide the 
        #placement of the checkbox. (can be placed either in column 0 or 1 , 
        #This also needs to be implemented for PM_RadioButton, but at present 
        #the following code doesn't support PM_RadioButton. 
        if isinstance(pmWidget, PM_CheckBox):
            spanWidth = pmWidget.spanWidth
            
            if not spanWidth:
                # Set the widget's row and column parameters.
                widgetRow      = row
                widgetColumn   = pmWidget.widgetColumn
                widgetSpanCols = 1
                widgetAlignment = PM_LABEL_LEFT_ALIGNMENT
                rowIncrement   = 1
                #set a virtual label
                labelRow       = row
                labelSpanCols  = 1
                labelAlignment = PM_LABEL_RIGHT_ALIGNMENT
                            
                if widgetColumn == 0:
                    labelColumn   = 1                              
                elif widgetColumn == 1:
                    labelColumn   = 0
            else:                
                # Set the widget's row and column parameters.
                widgetRow      = row
                widgetColumn   = pmWidget.widgetColumn
                widgetSpanCols = 2
                widgetAlignment = PM_LABEL_LEFT_ALIGNMENT
                rowIncrement   = 1
                #no label 
                labelRow       = 0
                labelColumn    = 0
                labelSpanCols  = 0
                labelAlignment = PM_LABEL_RIGHT_ALIGNMENT
                
            
            return widgetRow, \
               widgetColumn, \
               widgetSpanCols, \
               widgetAlignment, \
               rowIncrement, \
               labelRow, \
               labelColumn, \
               labelSpanCols, \
               labelAlignment
        
       
        label       = pmWidget.label            
        labelColumn = pmWidget.labelColumn
        spanWidth   = pmWidget.spanWidth
        
        if not spanWidth: 
            # This widget and its label are on the same row
            labelRow       = row
            labelSpanCols  = 1
            labelAlignment = PM_LABEL_RIGHT_ALIGNMENT
            # Set the widget's row and column parameters.
            widgetRow      = row
            widgetColumn   = 1
            widgetSpanCols = 1
            widgetAlignment = PM_LABEL_LEFT_ALIGNMENT
            rowIncrement   = 1
            
            if labelColumn == 1:
                widgetColumn   = 0
                labelAlignment = PM_LABEL_LEFT_ALIGNMENT
                widgetAlignment = PM_LABEL_RIGHT_ALIGNMENT
                        
        else: 
                      
            # This widget spans the full width of the groupbox           
            if label: 
                # The label and widget are on separate rows.
                # Set the label's row, column and alignment.
                labelRow       = row
                labelColumn    = 0
                labelSpanCols  = 2
                    
                # Set this widget's row and column parameters.
                widgetRow      = row + 1 # Widget is below the label.
                widgetColumn   = 0
                widgetSpanCols = 2
                
                rowIncrement   = 2
            else:  # No label. Just the widget.
                labelRow       = 0
                labelColumn    = 0
                labelSpanCols  = 0

                # Set the widget's row and column parameters.
                widgetRow      = row
                widgetColumn   = 0
                widgetSpanCols = 2
                rowIncrement   = 1
                
            labelAlignment = PM_LABEL_LEFT_ALIGNMENT
            widgetAlignment = PM_LABEL_LEFT_ALIGNMENT
                
        return widgetRow, \
               widgetColumn, \
               widgetSpanCols, \
               widgetAlignment, \
               rowIncrement, \
               labelRow, \
               labelColumn, \
               labelSpanCols, \
               labelAlignment
    
    def addPmWidget(self, pmWidget):
        """
        Add a PM widget and its label to this group box.
        
        @param pmWidget: The PM widget to add.
        @type  pmWidget: PM_Widget
        """
        
        # Get all the widget and label layout parameters.
        widgetRow, \
        widgetColumn, \
        widgetSpanCols, \
        widgetAlignment, \
        rowIncrement, \
        labelRow, \
        labelColumn, \
        labelSpanCols, \
        labelAlignment = \
            self.getPmWidgetPlacementParameters(pmWidget)
        
        if pmWidget.labelWidget: 
            #Create Label as a pixmap (instead of text) if a valid icon path 
            #is provided
            labelPath = str(pmWidget.label)
            if labelPath and labelPath.startswith("ui/"): #bruce 080325 revised
                labelPixmap = getpixmap(labelPath)
                if not labelPixmap.isNull():
                    pmWidget.labelWidget.setPixmap(labelPixmap)
                    pmWidget.labelWidget.setText('')
               
            self.gridLayout.addWidget( pmWidget.labelWidget,
                                       labelRow, 
                                       labelColumn,
                                       1, 
                                       labelSpanCols,
                                       labelAlignment )
            
        
        # The following is a workaround for a Qt bug. If addWidth()'s 
        # <alignment> argument is not supplied, the widget spans the full 
        # column width of the grid cell containing it. If <alignment> 
        # is supplied, this desired behavior is lost and there is no 
        # value that can be supplied to maintain the behavior (0 doesn't 
        # work). The workaround is to call addWidget() without the <alignment>
        # argument. Mark 2007-07-27.

        if widgetAlignment == PM_LABEL_LEFT_ALIGNMENT:
            self.gridLayout.addWidget( pmWidget,
                                       widgetRow, 
                                       widgetColumn,
                                       1, 
                                       widgetSpanCols) 
                                       # aligment = 0 doesn't work.
        else:
            self.gridLayout.addWidget( pmWidget,
                                       widgetRow, 
                                       widgetColumn,
                                       1, 
                                       widgetSpanCols,
                                       widgetAlignment
                                     )

        self._widgetList.append(pmWidget)
        
        self._rowCount += rowIncrement
        return
    
    def getRowCount(self):
        """
        Return the row count of gridlayout of L{PM_Groupbox}
        
        @return: The row count of L{self.gridlayout}
        @rtype: int
        """
        return self._rowCount
    
    def incrementRowCount(self, increment = 1):
        """
        Increment the row count of the gridlayout of L{PM_groupBox}
        @param increment: The incremental value
        @type  increment: int
        """
        self._rowCount += increment
        return
        
    def addQtWidget(self, qtWidget, column = 0, spanWidth = False):
        """
        Add a Qt widget to this group box.
        
        @param qtWidget: The Qt widget to add.
        @type  qtWidget: QWidget
        
        @warning: this method has not been tested yet.
        """
        # Set the widget's row and column parameters.
        widgetRow      = self._rowCount
        widgetColumn   = column
        if spanWidth:
            widgetSpanCols = 2
        else:
            widgetSpanCols = 1
        
        self.gridLayout.addWidget( qtWidget,
                                   widgetRow, 
                                   widgetColumn,
                                   1, 
                                   widgetSpanCols )
        
        self._rowCount += 1
        return
    
    def collapse(self):
        """
        Collapse this group box i.e. hide all its contents and change the look 
        and feel of the groupbox button. 
        """
        self.vBoxLayout.setMargin(0)
        self.vBoxLayout.setSpacing(0)
        self.gridLayout.setMargin(0)
        self.gridLayout.setSpacing(0)
        # The styleSheet contains the expand/collapse.
        styleSheet = self._getTitleButtonStyleSheet(showExpanded = False)
        self.titleButton.setStyleSheet(styleSheet)
        self._containerWidget.hide()
        self.expanded = False 
        return
    
    def expand(self):
        """
        Expand this group box i.e. show all its contents and change the look 
        and feel of the groupbox button. 
        """       
        self.vBoxLayout.setMargin(PM_GROUPBOX_VBOXLAYOUT_MARGIN)
        self.vBoxLayout.setSpacing(PM_GROUPBOX_VBOXLAYOUT_SPACING)
        self.gridLayout.setMargin(PM_GROUPBOX_GRIDLAYOUT_MARGIN)
        self.gridLayout.setSpacing(PM_GROUPBOX_GRIDLAYOUT_SPACING)
            
        # The styleSheet contains the expand/collapse.
        styleSheet = self._getTitleButtonStyleSheet(showExpanded = True)
        self.titleButton.setStyleSheet(styleSheet)
        self._containerWidget.show()
        self.expanded = True
        return

    def hide(self):
        """
        Hides the group box .
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide() 
        
        # Change the spacer height to zero to "hide" it unless
        # self is the last GroupBox in the Property Manager.
        if self.parentWidget._lastGroupBox is self:
            return
        
        if self.verticalSpacer:
            self.verticalSpacer.changeSize(10, 0)
        return
            
    def show(self):
        """
        Unhides the group box.
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show() 
        
        if self.parentWidget._lastGroupBox is self:
            return
        
        if self.verticalSpacer:
            self.verticalSpacer.changeSize(10, 
                                           self.verticalSpacer.defaultHeight)
        return

    # Title Button Methods #####################################
    
    def _getTitleButton(self, 
                        parentWidget = None,
                        title        = '', 
                        showExpanded = True ):
        """
        Return the group box title push button. The push button is customized 
        such that it appears as a title bar at the top of the group box. 
        If the user clicks on this 'title bar' it sends a signal to open or 
        close the group box.
        
        @param parentWidget: The parent dialog or group box containing this 
                             widget.
        @type  parentWidget: PM_Dialog or PM_GroupBox
        
        @param title: The button title.
        @type  title: str 
        
        @param showExpanded: dDetermines whether the expand or collapse image is 
                             displayed on the title button.
        @type  showExpanded: bool
                             
        @see: L{_getTitleButtonStyleSheet()}
        
        @Note: Including a title button should only be legal if the parentWidget
               is a PM_Dialog.
        """
        
        button  = QPushButton(title, parentWidget)
        button.setFlat(False)
        button.setAutoFillBackground(True)
        
        button.setStyleSheet(self._getTitleButtonStyleSheet(showExpanded))     
        
        return button
    
    def _getTitleButtonStyleSheet(self, showExpanded = True):
        """
        Returns the style sheet for a group box title button (or checkbox).
        
        @param showExpanded: Determines whether to include an expand or
                             collapse icon.
        @type  showExpanded: bool
        
        @return: The title button style sheet.
        @rtype:  str
        """
        
        # Need to move border color and text color to top 
        # (make global constants).
        if showExpanded:        
            styleSheet = \
                       "QPushButton {"\
                       "border-style: outset; "\
                       "border-width: 2px; "\
                       "border-color: #%s; "\
                       "border-radius: 2px; "\
                       "background-color: #%s; "\
                       "font: bold 12px 'Arial'; "\
                       "color: #%s; "\
                       "min-width: 10em; "\
                       "background-image: url(%s); "\
                       "background-position: right; "\
                       "background-repeat: no-repeat; "\
                       "text-align: left; "\
                       "}" % (QColor_to_Hex(pmGrpBoxButtonBorderColor),
                              QColor_to_Hex(pmGrpBoxButtonColor),
                              QColor_to_Hex(pmGrpBoxButtonTextColor),
                              pmGrpBoxExpandedIconPath
                              )
                              
        else:
            # Collapsed.
            styleSheet = \
                       "QPushButton {"\
                       "border-style: outset; "\
                       "border-width: 2px; "\
                       "border-color: #%s; "\
                       "border-radius: 2px; "\
                       "background-color: #%s; "\
                       "font: bold 12px 'Arial'; "\
                       "color: #%s; "\
                       "min-width: 10em; "\
                       "background-image: url(%s); "\
                       "background-position: right; "\
                       "background-repeat: no-repeat; "\
                       "text-align: left; "\
                       "}" % (QColor_to_Hex(pmGrpBoxButtonBorderColor),
                              QColor_to_Hex(pmGrpBoxButtonColor),
                              QColor_to_Hex(pmGrpBoxButtonTextColor),
                              pmGrpBoxCollapsedIconPath
                              )
        return styleSheet
            
    def toggleExpandCollapse(self):
        """
        Slot method for the title button to expand/collapse the group box.
        """
        if self._widgetList:
            if self.expanded:
                self.collapse()
            else: # Expand groupbox by showing all widgets in groupbox.
                self.expand()         
        else:
            print "Clicking on the group box button has no effect "\
                   "since it has no widgets."
        return
    
    # GroupBox stylesheet methods. ##############################
    
    def _getStyleSheet(self):
        """
        Return the style sheet for the groupbox. This sets the following 
        properties only:
         - border style
         - border width
         - border color
         - border radius (on corners)
         - background color
        
        @return: The group box style sheet.
        @rtype:  str
        """
        
        styleSheet = \
                   "QGroupBox {"\
                   "border-style: solid; "\
                   "border-width: 1px; "\
                   "border-color: #%s; "\
                   "border-radius: 0px; "\
                   "background-color: #%s; "\
                   "min-width: 10em; "\
                   "}" % ( QColor_to_Hex(pmGrpBoxBorderColor), 
                           QColor_to_Hex(pmGrpBoxColor)
                           )
        return styleSheet
class Ui_DnaSequenceEditor(PM_DockWidget):
    """
    The Ui_DnaSequenceEditor class defines UI elements for the Sequence Editor
    object. The sequence editor is usually visible while in while editing
    a DnaStrand.
    
    It is a DockWidget that is docked at the bottom of the MainWindow.
    """
    _title = "Sequence Editor"
    _groupBoxCount = 0
    _lastGroupBox = None

    def __init__(self, win):
        """
        Constructor for the Ui_DnaSequenceEditor 
        @param win: The parentWidget (MainWindow) for the sequence editor 
        """

        self.win = win
        # Should parentWidget for a docwidget always be win?
        #Not necessary but most likely it will be the case.
        parentWidget = win

        _superclass.__init__(self, parentWidget, title=self._title)

        #A flag used to restore the state of the Reports dock widget
        #(which can be accessed through View  >  Reports) see self.show() and
        #self.closeEvent() for more details.
        self._reportsDockWidget_closed_in_show_method = False
        self.setFixedHeight(90)
        return

    def show(self):
        """
        Shows the sequence editor. While doing this, it also closes the reports
        dock widget (if visible) the state of the reports dockwidget will be
        restored when the sequence editor is closed. 
        @see:self.closeEvent()
        """
        self._reportsDockWidget_closed_in_show_method = False
        #hide the history widget first
        #(It will be shown back during self.close)
        #The history widget is hidden or shown only when both
        # 'View > Full Screen' and View > Semi Full Screen actions
        # are *unchecked*
        #Thus show or close methods won't do anything to history widget
        # if either of the above mentioned actions is checked.
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self.win.reportsDockWidget.isVisible():
                self.win.reportsDockWidget.close()
                self._reportsDockWidget_closed_in_show_method = True

        _superclass.show(self)
        return

    def closeEvent(self, event):
        """
        Overrides close event. Makes sure that the visible state of the reports
        widgetis restored when the sequence editor is closed. 
        @see: self.show()
        """
        _superclass.closeEvent(self, event)

        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self._reportsDockWidget_closed_in_show_method:
                self.win.viewReportsAction.setChecked(True)
                self._reportsDockWidget_closed_in_show_method = False
        return

    def _loadWidgets(self):
        """
        Overrides PM.PM_DockWidget._loadWidgets. Loads the widget in this
        dockwidget.
        """
        self._loadMenuWidgets()
        self._loadTextEditWidget()
        return

    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.loadSequenceButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Open.png")

        self.saveSequenceButton = PM_ToolButton(
            self,
            iconPath="ui/actions/Properties Manager/Save_Strand_Sequence.png")

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        # Only supporting 5' to 3' direction until bug 2956 is fixed.
        # Mark 2008-12-19
        editDirectionChoices = ["5' to 3'"]  # , "3' to 5'"]
        self.baseDirectionChoiceComboBox = \
            PM_ComboBox( self,
                         choices = editDirectionChoices,
                         index     = 0,
                         spanWidth = False )

        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)


        self.replaceLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text="Replace")

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        # NOTE: Following needs cleanup in the PM_WidgetRow/ PM_WidgetGrid
        # but this explanation is sufficient  until thats done --

        # When the widget type starts with the word 'PM_' , the
        # PM_WidgetRow treats it as a well defined widget and thus doesn't try
        # to create a QWidget object (or its subclasses)
        # This is the reason why qLabels such as self.warningSign and
        # self.phraseNotFoundLabel  are defined as PM_Labels and not 'QLabels'
        # If they were defined as 'QLabel'(s) then PM_WidgetRow would have
        # recreated the label. Since we want to show/hide the above mentioned
        # labels (and if they were recreated as mentioned above),
        # we would have needed to define  those something like this:
        # self.phraseNotFoundLabel = widgetRow._widgetList[-2]
        #Cleanup in PM_widgetGrid could be to check if the widget starts with
        #'Q'  instead of 'PM_'

        #Widgets to include in the widget row.
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "     Sequence direction:", 2),
                      ('PM_ComboBox', self.baseDirectionChoiceComboBox, 3),
                      ('QLabel', "     Find:", 4),
                      ('PM_LineEdit', self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8),
                      ('QLabel', "     Replace:", 9),
                      ('PM_TextEdit', self.replaceLineEdit, 10),
                      ('PM_PushButton', self.replacePushButton, 11),
                      ('PM_Label', self.warningSign, 12),
                      ('PM_Label', self.phraseNotFoundLabel, 13),
                      ('QSpacerItem', 5, 5, 14)]

        widgetRow = PM_WidgetRow(self,
                                 title='',
                                 widgetList=widgetList,
                                 label="",
                                 spanWidth=True)
        return

    def _loadTextEditWidget(self):
        """
        Load the SequenceTexteditWidgets.         
        """
        self.sequenceTextEdit = \
            PM_TextEdit( self,
                         label = " Sequence: ",
                         spanWidth = False,
                         permit_enter_keystroke = False)
        self.sequenceTextEdit.setCursorWidth(2)
        self.sequenceTextEdit.setWordWrapMode(QTextOption.WrapAnywhere)
        self.sequenceTextEdit.setFixedHeight(20)

        #The StrandSequence 'Mate' it is a read only etxtedit that shows
        #the complementary strand sequence.
        self.sequenceTextEdit_mate = \
            PM_TextEdit(self,
                        label = "",
                        spanWidth = False,
                        permit_enter_keystroke = False
                        )
        palette = getPalette(None, QPalette.Base,
                             sequenceEditStrandMateBaseColor)
        self.sequenceTextEdit_mate.setPalette(palette)
        self.sequenceTextEdit_mate.setFixedHeight(20)
        self.sequenceTextEdit_mate.setReadOnly(True)
        self.sequenceTextEdit_mate.setWordWrapMode(QTextOption.WrapAnywhere)

        #Important to make sure that the horizontal and vertical scrollbars
        #for these text edits are never displayed.
        for textEdit in (self.sequenceTextEdit, self.sequenceTextEdit_mate):
            textEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            textEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        return

    def _getFindLineEditStyleSheet(self):
        """
        Return the style sheet for the findLineEdit. This sets the following 
        properties only:
         - background-color

        This style is set whenever the searchStrig can't be found (sets
        a light red color background to the lineedit when this happens)   

        @return: The line edit style sheet.
        @rtype:  str

        """
        styleSheet = "QLineEdit {"\
                   "background-color: rgb(255, 102, 102)"\
                   "}"
        #Not used:
        #  background-color: rgb(217, 255, 216)\

        return styleSheet

    def _setFindOptionsToolButtonMenu(self):
        """
        Sets the menu for the findOptionstoolbutton that appears a small 
        menu button next to the findLineEdit.
        """
        self.findOptionsMenu = QMenu(self.findOptionsToolButton)

        self.caseSensitiveFindAction = QAction(self.findOptionsToolButton)
        self.caseSensitiveFindAction.setText('Match Case')
        self.caseSensitiveFindAction.setCheckable(True)
        self.caseSensitiveFindAction.setChecked(False)

        self.findOptionsMenu.addAction(self.caseSensitiveFindAction)
        self.findOptionsMenu.addSeparator()

        self.findOptionsToolButton.setMenu(self.findOptionsMenu)
        return

    def _addToolTipText(self):
        """
            What's Tool Tip text for widgets in this Property Manager.  
            """
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_DnaSequenceEditor
        ToolTip_DnaSequenceEditor(self)
        return

    def _addWhatsThisText(self):
        """
            What's This text for widgets in this Property Manager.  

            """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_DnaSequenceEditor
        whatsThis_DnaSequenceEditor(self)
        return
Exemple #31
0
class PM_Slider(QSlider):

    labelWidget = None

    def __init__(
            self,
            parentWidget,
            orientation=None,  ##Qt.Horizontal,
            currentValue=0,
            minimum=0,
            maximum=100,
            label='',
            labelColumn=0,
            setAsDefault=True,
            spanWidth=True):
        """
        
        Appends a Qslider widget (with a QLabel widget) to <parentWidget>
        a property manager group box.
        
        Arguments:
        
        @param parentWidget: the group box containing this PM widget.
        @type  parentWidget: PM_GroupBox
        
        @param currentValue: 
        @type  currentValue: int
        
        @param minimum: minimum value the slider can get.
              (used to set its 'range')
        @type  minimum: int
        
        @param maximum: maximum value the slider can get.
              (used to set its 'range')
        @type  maximum: int
                
        @param label: label that appears to the left of (or above) this widget.
        @type  label: str
        
        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left 
                            column) and 1 (right column). The default is 0 
                            (left column).
        @type  labelColumn: int
                
        
        @param setAsDefault: if True, will restore value when the 
               "Restore Defaults" button is clicked.
        @type  setAsDefault: bool (default True)
        
        @param spanWidth: If True, the widget and its label will span the width
                    of the group box. Its label will appear directly above
                    the widget (unless the label is empty)and is left justified.
        @type  spanWidth: bool (default True)
        
        @see: U{B{QSlider}<http://doc.trolltech.com/4/qslider.html>}
        
        """

        ##QSlider.__init__(self, orientation, parentWidget)
        QSlider.__init__(self, parentWidget)

        self.parentWidget = parentWidget
        self.label = label
        self.labelColumn = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth = spanWidth

        #Ideally, this should be simply self.setOrientation(orientation)  with the
        #default orientation = Qt.Horizontal in the init argument itself. But,
        #apparently pylint chokes up when init argument is a Qt enum.
        #This problem happened while running pylint 0.23 on the SEMBOT server
        #so comitting this temporary workaround. The pylint on my machine is
        #0.25 and it runs fine even before this workaround. Similar changes made
        #in PM_CheckBox. -- Ninad 2008-06-30
        if orientation is None:
            self.setOrientation(Qt.Horizontal)
        else:
            self.setOrientation(orientation)

        if label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        self.setValue(currentValue)
        self.setRange(minimum, maximum)

        self.setAsDefault = setAsDefault

        if self.setAsDefault:
            self.setDefaultValue(currentValue)

        parentWidget.addPmWidget(self)

    def hide(self):
        """
        Hides the slider and its label (if it has one).
        
        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()

    def show(self):
        """
        Unhides the slider and its label (if it has one).
        
        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setValue(self.defaultValue)

    def setDefaultValue(self, value):
        """
        Sets the Default value for the slider. This value will be set if 
        user hits Restore Defaults button in the Property Manager. 
        """
        self.defaultValue = value
        self.setAsDefault = True
Exemple #32
0
class PM_ToolButton(QToolButton):
    """
    The PM_ToolButton widget provides a QToolButton with a
    QLabel for a Property Manager group box.

    @cvar defaultText: The default text of the tool button.
    @type defaultText: str

    @cvar setAsDefault: Determines whether to reset the value of the
                        tool button to I{defaultText} when the user clicks
                        the "Restore Defaults" button.
    @type setAsDefault: bool

    @cvar labelWidget: The Qt label widget of this tool button.
    @type labelWidget: U{B{QLabel}<http://doc.trolltech.com/4/qlabel.html>}
    """

    defaultText = ""
    setAsDefault = True
    labelWidget = None

    def __init__(self,
                 parentWidget,
                 label='',
                 labelColumn=0,
                 text='',
                 iconPath='',
                 setAsDefault=True,
                 spanWidth=False):
        """
        Appends a QToolButton (Qt) widget to the bottom of I{parentWidget},
        a Property Manager group box.

        @param parentWidget: The parent group box containing this widget.
        @type  parentWidget: PM_GroupBox

        @param label: The label that appears to the left or right of the
                      checkbox.

                      If spanWidth is True, the label will be displayed on
                      its own row directly above the list widget.

                      To suppress the label, set I{label} to an
                      empty string.
        @type  label: str

        @param labelColumn: The column number of the label in the group box
                            grid layout. The only valid values are 0 (left
                            column) and 1 (right column). The default is 0
                            (left column).
        @type  labelColumn: int

        @param text: The button's text.
        @type  text: str

        @param iconPath: The relative path to the button's icon.
        @type  iconPath: str

        @param setAsDefault: If True, will restore <text> as the button's text
                         when the "Restore Defaults" button is clicked.
        @type  setAsDefault: bool

        @param spanWidth: If True, the widget and its label will span the width
                      of the group box. Its label will appear directly above
                      the widget (unless the label is empty) and is left
                      justified.
        @type  spanWidth: bool

        @see: U{B{QToolButton}<http://doc.trolltech.com/4/qtoolbutton.html>}
        """

        if 0:  # Debugging code
            print "PM_ToolButton.__init__():"
            print "  label        = ", label
            print "  labelColumn  = ", labelColumn
            print "  text         = ", text
            print "  iconPath     = ", iconPath
            print "  setAsDefault = ", setAsDefault
            print "  spanWidth    = ", spanWidth

        QToolButton.__init__(self)

        self.parentWidget = parentWidget
        self.label = label
        self.labelColumn = labelColumn
        self.setAsDefault = setAsDefault
        self.spanWidth = spanWidth

        if label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(label)

        # Set text
        self.setText(text)

        # Set icon
        self.setIcon(geticon(iconPath))
        self.setIconSize(QSize(22, 22))

        # Set default text
        self.defaultText = text
        self.setAsDefault = setAsDefault

        parentWidget.addPmWidget(self)

    def restoreDefault(self):
        """
        Restores the default value.
        """
        if self.setAsDefault:
            self.setText(self.defaultText)

    def hide(self):
        """
        Hides the tool button and its label (if it has one).

        @see: L{show}
        """
        QWidget.hide(self)
        if self.labelWidget:
            self.labelWidget.hide()

    def show(self):
        """
        Unhides the tool button and its label (if it has one).

        @see: L{hide}
        """
        QWidget.show(self)
        if self.labelWidget:
            self.labelWidget.show()
class SelectNodeByNameDockWidget(PM_DockWidget):
    """
    The Ui_DnaSequenceEditor class defines UI elements for the Sequence Editor
    object. The sequence editor is usually visible while in DNA edit mode.
    It is a DockWidget that is doced at the bottom of the MainWindow
    """
    _title         =  "Select Node by Name"
    _groupBoxCount = 0
    _lastGroupBox = None

    def __init__(self, win):
        """
        Constructor for the Ui_DnaSequenceEditor
        @param win: The parentWidget (MainWindow) for the sequence editor
        """

        self.win = win
        parentWidget = win

        _superclass.__init__(self, parentWidget, title = self._title)

        win.addDockWidget(Qt.BottomDockWidgetArea, self)
        self.setFixedHeight(120)
        ##self.setFixedWidth(90)
        self.connect_or_disconnect_signals(True)

        if not self.win.selectByNameAction.isChecked():
            self.close()

    def show(self):
        """
        Overrides superclass method.
        """
        _superclass.show(self)
        val = env.prefs[dnaSearchTypeLabelChoice_prefs_key]
        self.searchTypeComboBox_indexChanged(val)


    def connect_or_disconnect_signals(self, isConnect):
        """
        Connect or disconnect widget signals sent to their slot methods.
        This can be overridden in subclasses. By default it does nothing.
        @param isConnect: If True the widget will send the signals to the slot
                          method.
        @type  isConnect: boolean
        """


        if isConnect:
            change_connect = self.win.connect
        else:
            change_connect = self.win.disconnect

        self._listWidget.connect_or_disconnect_signals(isConnect)

        change_connect( self.searchToolButton,
                      SIGNAL("clicked()"),
                      self.searchNodes)

        prefs_key = dnaSearchTypeLabelChoice_prefs_key
        connect_comboBox_with_pref(self.searchTypeComboBox,
                                   prefs_key )

        change_connect( self.searchTypeComboBox,
                      SIGNAL("currentIndexChanged(int)"),
                      self.searchTypeComboBox_indexChanged)

    def searchTypeComboBox_indexChanged(self, val):
        if val == 0:
            ##self._widgetRow1.show()
            ##self._widgetRow2.hide()
            self._widgetRow1.setEnabled(True)
            self._widgetRow2.setEnabled(False)
        else:
            ##self._widgetRow2.show()
            ##self._widgetRow1.hide()
            self._widgetRow2.setEnabled(True)
            self._widgetRow1.setEnabled(False)

    def searchNodes(self):
        """
        ONLY implemented for DnaStrand or DnaSegments.
        """

        assy = self.win.assy

        topnode = assy.part.topnode
        lst = []
        def func(node):
            if isinstance(node, assy.DnaStrandOrSegment):
                lst.append(node)

        topnode.apply2all(func)

        choice = env.prefs[dnaSearchTypeLabelChoice_prefs_key]

        if choice == 0:
            nodes = self._searchNodesByName(lst)
        elif choice == 1:
            nodes = self._searchNodesByNucleotides(lst)

        self._listWidget.insertItems(
                row = 0,
                items = nodes)

    def _searchNodesByNucleotides(self, nodeList):
        lst = nodeList
        min_val = self._nucleotidesSpinBox_1.value()
        max_val = self._nucleotidesSpinBox_2.value()
        if min_val > max_val:
            print "Lower value for number of nucleotides exceeds max search value"
            return ()

        def func2(node):
            n = node.getNumberOfNucleotides()

            return (n >= min_val and n <= max_val)

        return filter(lambda m:func2(m), lst)


    def _searchNodesByName(self, nodeList):
        nodeNameString = self.findLineEdit.text()
        nodeNameString = str(nodeNameString)

        lst = nodeList
        def func2(node):
            n = len(nodeNameString)
            if len(node.name)< n:
                return False

            nameString = str(node.name[:n])

            if  nameString.lower() == nodeNameString.lower():
                return True

            return False

        return filter(lambda m:func2(m), lst)




    def closeEvent(self, event):
        self.win.selectByNameAction.setChecked(False)
        _superclass.closeEvent(self, event)


    def _loadWidgets(self):
        """
        Overrides PM.PM_DockWidget._loadWidgets. Loads the widget in this
        dockwidget.
        """
        self._loadMenuWidgets()
        self._loadTableWidget()

    def _loadTableWidget(self):
        self._listWidget = PM_DnaSearchResultTable(self, self.win)

    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options,
        Find and replace widgets etc.
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.searchTypeComboBox  = \
            PM_ComboBox( self,
                         label         =  "Search options:",
                         choices       =  ["By node name", "By # of bases (DNA only)"],
                         setAsDefault  =  True)


        #Find  widgets --
        self._nucleotidesSpinBox_1 = PM_SpinBox(self,
                        label         =  "",
                        value         =  10,
                        setAsDefault  =  False,
                        singleStep = 10,
                        minimum       =  1,
                        maximum       =  50000)

        self._nucleotidesSpinBox_2 = PM_SpinBox(self,
                        label         =  "",
                        value         =  50,
                        setAsDefault  =  False,
                        singleStep = 10,
                        minimum       =  1,
                        maximum       =  50000)


        self.findLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)

        self.findLineEdit.setMaximumWidth(80)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        ##self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        ##self._setFindOptionsToolButtonMenu()

        self.searchToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Next.png")
        self.searchToolButton.setAutoRaise(False)


        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Not Found")
        self.phraseNotFoundLabel.hide()

        # NOTE: Following needs cleanup in the PM_WidgetRow/ PM_WidgetGrid
        # but this explanation is sufficient  until thats done --

        # When the widget type starts with the word 'PM_' , the
        # PM_WidgetRow treats it as a well defined widget and thus doesn't try
        # to create a QWidget object (or its subclasses)
        # This is the reason why qLabels such as self.warningSign and
        # self.phraseNotFoundLabel  are defined as PM_Labels and not 'QLabels'
        # If they were defined as 'QLabel'(s) then PM_WidgetRow would have
        # recreated the label. Since we want to show/hide the above mentioned
        # labels (and if they were recreated as mentioned above),
        # we would have needed to define  those something like this:
        # self.phraseNotFoundLabel = widgetRow._widgetList[-2]
        #Cleanup in PM_widgetGrid could be to check if the widget starts with
        #'Q'  instead of 'PM_'


        #Widgets to include in the widget row.


        widgetList1 = [
                      ('QLabel', "     Search for name:", 1),
                      ('PM_LineEdit', self.findLineEdit, 2),
                      ('PM_ToolButton', self.findOptionsToolButton, 3),
                      ('PM_ToolButton', self.searchToolButton, 4),
                      ('PM_Label', self.warningSign, 5),
                      ('PM_Label', self.phraseNotFoundLabel, 6),
                      ('QSpacerItem', 5, 5, 7) ]

        widgetList2 = [
                      ('QLabel', "     Number of bases: >=", 1),
                      ('PM_SpinBox', self._nucleotidesSpinBox_1, 2),
                      ('QLabel', "     <=", 3),
                      ('PM_SpinBox', self._nucleotidesSpinBox_2, 4),
                      ('QSpacerItem', 5, 5, 5)]

        widgetList3 = [
                      ('QSpacerItem', 5, 5, 1),
                      ('PM_ToolButton', self.searchToolButton, 2),
                      ('PM_Label', self.warningSign, 3),
                      ('PM_Label', self.phraseNotFoundLabel, 4),
                      ('QSpacerItem', 5, 5, 5) ]


        self._widgetRow1 = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList1,
                                 label = "",
                                 spanWidth = True )

        self._widgetRow2 = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList2,
                                 label = "",
                                 spanWidth = True )

        self._widgetRow3 = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList3,
                                 label = "",
                                 spanWidth = True )