Logo Search packages:      
Sourcecode: veusz version File versions  Download package

controls.py

#    Copyright (C) 2005 Jeremy S. Sanders
#    Email: Jeremy Sanders <jeremy@jeremysanders.net>
#
#    This program is 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 2 of the License, or
#    (at your option) any later version.
#
#    This program 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.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
###############################################################################

# $Id: controls.py 1469 2010-12-08 22:15:00Z jeremysanders $

"""Module for creating QWidgets for the settings, to enable their values
   to be changed.

    These widgets emit settingChanged(control, setting, val) when the setting is
    changed. The creator should use this to change the setting.
"""

from itertools import izip
import re

import veusz.qtall as qt4

import setting
import veusz.utils as utils

def styleClear(widget):
    """Return widget to default"""
    widget.setStyleSheet("")

def styleError(widget):
    """Show error state on widget."""
    widget.setStyleSheet("background-color: " +
                         setting.settingdb.color('error').name() )

00045 class Edit(qt4.QLineEdit):
    """Main control for editing settings which are text."""

00048     def __init__(self, setting, parent):
        """Initialise the setting widget."""

        qt4.QLineEdit.__init__(self, parent)
        self.setting = setting

        # set the text of the widget to the 
        self.setText( setting.toText() )

        self.connect(self, qt4.SIGNAL('editingFinished()'),
                     self.validateAndSet)

        self.setting.setOnModified(self.onModified)

        if setting.readonly:
            self.setReadOnly(True)

00065     def validateAndSet(self):
        """Check the text is a valid setting and update it."""

        text = unicode(self.text())
        try:
            val = self.setting.fromText(text)
            styleClear(self)

            # value has changed
            if self.setting.val != val:
                self.emit( qt4.SIGNAL('settingChanged'),
                           self, self.setting, val )

        except setting.InvalidType:
            styleError(self)

00081     def onModified(self, mod):
        """called when the setting is changed remotely"""
        self.setText( self.setting.toText() )

00085 class _EditBox(qt4.QTextEdit):
    """A popup edit box to support editing long text sections.

    Emits closing(text) when the box closes
    """

00091     def __init__(self, origtext, readonly, parent):
        """Make a popup, framed widget containing a text editor."""

        qt4.QTextEdit.__init__(self, parent)
        self.setWindowFlags(qt4.Qt.Popup)
        self.setAttribute(qt4.Qt.WA_DeleteOnClose)

        self.spacing = self.fontMetrics().height()

        self.origtext = origtext
        self.setPlainText(origtext)

        cursor = self.textCursor()
        cursor.movePosition(qt4.QTextCursor.End)
        self.setTextCursor(cursor)

        if readonly:
            self.setReadOnly(True)

        self.positionSelf(parent)

        self.installEventFilter(self)

00114     def eventFilter(self, obj, event):
        """Grab clicks outside this window to close it."""
        if ( isinstance(event, qt4.QMouseEvent) and
             event.buttons() != qt4.Qt.NoButton ):
            frame = qt4.QRect(0, 0, self.width(), self.height())
            if not frame.contains(event.pos()):
                self.close()
                return True
        return qt4.QTextEdit.eventFilter(self, obj, event)

00124     def keyPressEvent(self, event):
        """Close if escape or return is pressed."""
        qt4.QTextEdit.keyPressEvent(self, event)

        key = event.key()
        if key == qt4.Qt.Key_Escape:
            # restore original content
            self.setPlainText(self.origtext)
            self.close()
        elif key == qt4.Qt.Key_Return:
            # keep changes
            self.close()

00137     def sizeHint(self):
        """A reasonable size for the text editor."""
        return qt4.QSize(self.spacing*40, self.spacing*3)

00141     def positionSelf(self, widget):
        """Open the edit box below the widget."""

        pos = widget.parentWidget().mapToGlobal( widget.pos() )
        desktop = qt4.QApplication.desktop()

        # recalculates out position so that size is correct below
        self.adjustSize()

        # is there room to put this widget besides the widget?
        if pos.y() + self.height() + 1 < desktop.height():
            # put below
            y = pos.y() + 1
        else:
            # put above
            y = pos.y() - self.height() - 1
        
        # is there room to the left for us?
        if ( (pos.x() + widget.width() + self.width() < desktop.width()) or
             (pos.x() + widget.width() < desktop.width()/2) ):
            # put left justified with widget
            x = pos.x() + widget.width()
        else:
            # put extending to left
            x = pos.x() - self.width() - 1

        self.move(x, y)
        self.setFocus()

00170     def closeEvent(self, event):
        """Tell the calling widget that we are closing, and provide
        the new text."""

        text = unicode(self.toPlainText())
        text = text.replace('\n', '')
        self.emit( qt4.SIGNAL('closing'), text)
        event.accept()

00179 class String(qt4.QWidget):
    """A line editor which allows editting in a larger popup window."""

    def __init__(self, setting, parent):
        qt4.QWidget.__init__(self, parent)
        self.setting = setting

        layout = qt4.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        self.setLayout(layout)

        self.edit = qt4.QLineEdit()
        layout.addWidget(self.edit)

        b = self.button = qt4.QPushButton('..')
        b.setFlat(True)
        b.setSizePolicy(qt4.QSizePolicy.Maximum, qt4.QSizePolicy.Maximum)
        b.setMaximumWidth(16)
        b.setCheckable(True)
        layout.addWidget(b)

        # set the text of the widget to the 
        self.edit.setText( setting.toText() )

        self.connect(self.edit, qt4.SIGNAL('editingFinished()'),
                     self.validateAndSet)
        self.connect(b, qt4.SIGNAL('toggled(bool)'),
                     self.buttonToggled)

        self.setting.setOnModified(self.onModified)

        if setting.readonly:
            self.edit.setReadOnly(True)

00214     def buttonToggled(self, on):
        """Button is pressed to bring popup up / down."""

        # if button is down and there's no existing popup, bring up a new one
        if on:
            e = _EditBox( unicode(self.edit.text()),
                          self.setting.readonly, self.button)

            # we get notified with text when the popup closes
            self.connect(e, qt4.SIGNAL('closing'), self.boxClosing)
            e.show()

00226     def boxClosing(self, text):
        """Called when the popup edit box closes."""

        # update the text if we can
        if not self.setting.readonly:
            self.edit.setText(text)
            self.edit.setFocus()
            self.parentWidget().setFocus()
            self.edit.setFocus()

        self.button.setChecked(False)

00238     def validateAndSet(self):
        """Check the text is a valid setting and update it."""

        text = unicode(self.edit.text())
        try:
            val = self.setting.fromText(text)
            styleClear(self.edit)

            # value has changed
            if self.setting.val != val:
                self.emit( qt4.SIGNAL('settingChanged'), self, self.setting, val)

        except setting.InvalidType:
            styleError(self.edit)

00253     def onModified(self, mod):
        """called when the setting is changed remotely"""
        self.edit.setText( self.setting.toText() )

00257 class Int(qt4.QSpinBox):
    """A control for changing an integer."""

    def __init__(self, setting, parent):
        qt4.QSpinBox.__init__(self, parent)

        self.setting = setting
        self.setMinimum(setting.minval)
        self.setMaximum(setting.maxval)
        self.setValue(setting.val)

        self.connect(self, qt4.SIGNAL('valueChanged(int)'), self.slotChanged)
        self.setting.setOnModified(self.onModified)

        if setting.readonly:
            self.setEnabled(False)            

00274     def slotChanged(self, value):
        """If check box changes."""
        self.emit(qt4.SIGNAL('settingChanged'), self, self.setting, value)

00278     def onModified(self, mod):
        """called when the setting is changed remotely"""
        self.setValue( self.setting.val )

00282 class Bool(qt4.QCheckBox):
    """A check box for changing a bool setting."""
    
    def __init__(self, setting, parent):
        qt4.QCheckBox.__init__(self, parent)

        self.setting = setting
        self.setChecked(setting.val)

        # we get a signal when the button is toggled
        self.connect( self, qt4.SIGNAL('toggled(bool)'),
                      self.slotToggled )

        self.setting.setOnModified(self.onModified)

        if setting.readonly:
            self.setEnabled(False)

00300     def slotToggled(self, state):
        """Emitted when checkbox toggled."""
        self.emit( qt4.SIGNAL('settingChanged'), self, self.setting, state )
        
00304     def onModified(self, mod):
        """called when the setting is changed remotely"""
        self.setChecked( self.setting.val )

00308 class BoolSwitch(Bool):
    """Bool for switching off/on other settings."""

    def showEvent(self, event):
        Bool.showEvent(self, event)
        self.updateState()

00315     def slotToggled(self, state):
        Bool.slotToggled(self, state)
        self.updateState()

00319     def updateState(self):
        """Set hidden state of settings."""
        s1, s2 = self.setting.strue, self.setting.sfalse
        if self.setting.val:
            show, hide = s1, s2
        else:
            show, hide = s2, s1

        if hasattr(self.parent(), 'showHideSettings'):
            self.parent().showHideSettings(show, hide)

00330 class Choice(qt4.QComboBox):
    """For choosing between a set of values."""

    def __init__(self, setting, iseditable, vallist, parent, icons=None,
                 descriptions=None):
        qt4.QComboBox.__init__(self, parent)

        self.setting = setting

        self.setEditable(iseditable)

        # stops combobox readjusting in size to fit contents
        self.setSizeAdjustPolicy(
            qt4.QComboBox.AdjustToMinimumContentsLengthWithIcon)

        if icons is None:
            # add items to list (text only)
            self.addItems( list(vallist) )
        else:
            # add pixmaps and text to list
            for icon, text in izip(icons, vallist):
                self.addItem(icon, text)

        # use tooltip descriptions if requested
        if descriptions is not None:
            for i, descr in enumerate(descriptions):
                self.setItemData(i, qt4.QVariant(descr), qt4.Qt.ToolTipRole)

        # choose the correct setting
        try:
            index = list(vallist).index(setting.toText())
            self.setCurrentIndex(index)
        except ValueError:
            # for cases when this is editable
            # set the text of the widget to the setting
            assert iseditable
            self.setEditText( setting.toText() )

        # if a different item is selected
        self.connect( self, qt4.SIGNAL('activated(const QString&)'),
                      self.slotActivated )

        self.setting.setOnModified(self.onModified)

        if setting.readonly:
            self.setEnabled(False)

00377     def focusOutEvent(self, *args):
        """Allows us to check the contents of the widget."""
        qt4.QComboBox.focusOutEvent(self, *args)
        self.slotActivated('')

00382     def slotActivated(self, val):
        """If a different item is chosen."""

        text = unicode(self.currentText())
        try:
            val = self.setting.fromText(text)
            styleClear(self)
            
            # value has changed
            if self.setting.val != val:
                self.emit( qt4.SIGNAL('settingChanged'), self, self.setting, val )

        except setting.InvalidType:
            styleError(self)

00397     def onModified(self, mod):
        """called when the setting is changed remotely"""
        text = self.setting.toText()
        index = self.findText(text)
        if index >= 0:
            self.setCurrentIndex(index)
        if self.isEditable():
            self.setEditText(text)

00406 class MultiLine(qt4.QTextEdit):
    """For editting multi-line settings."""

00409     def __init__(self, setting, parent):
        """Initialise the widget."""

        qt4.QTextEdit.__init__(self, parent)
        self.setting = setting

        self.setWordWrapMode(qt4.QTextOption.NoWrap)
        self.setTabChangesFocus(True)
        
        # set the text of the widget to the 
        self.setPlainText( setting.toText() )

        self.setting.setOnModified(self.onModified)

        if setting.readonly:
            self.setReadOnly(True)

00426     def focusOutEvent(self, *args):
        """Allows us to check the contents of the widget."""
        qt4.QTextEdit.focusOutEvent(self, *args)

        text = unicode(self.toPlainText())
        try:
            val = self.setting.fromText(text)
            styleClear(self)
            
            # value has changed
            if self.setting.val != val:
                self.emit( qt4.SIGNAL('settingChanged'), self, self.setting, val )

        except setting.InvalidType:
            styleError(self)

00442     def onModified(self, mod):
        """called when the setting is changed remotely"""
        self.setPlainText( self.setting.toText() )

00446 class Distance(Choice):
    """For editing distance settings."""

    # used to remove non-numerics from the string
    # we also remove X/ from X/num
    stripnumre = re.compile(r"[0-9]*/|[^0-9.]")

    # remove spaces
    stripspcre = re.compile(r"\s")

00456     def __init__(self, setting, parent, allowauto=False):
        '''Initialise with blank list, then populate with sensible units.'''
        Choice.__init__(self, setting, True, [], parent)
        self.allowauto = allowauto
        self.updateComboList()
        
00462     def updateComboList(self):
        '''Populates combo list with sensible list of other possible units.'''

        # turn off signals, so our modifications don't create more signals
        self.blockSignals(True)

        # get current text
        text = unicode(self.currentText())

        # get rid of non-numeric things from the string
        num = self.stripnumre.sub('', text)

        # here are a list of possible different units the user can choose
        # between. should this be in utils?
        newitems = [ num+'pt', num+'cm', num+'mm',
                     num+'in', num+'%', '1/'+num ]

        if self.allowauto:
            newitems.insert(0, 'Auto')

        # if we're already in this list, we position the current selection
        # to the correct item (up and down keys work properly then)
        # spaces are removed to make sure we get sensible matches
        spcfree = self.stripspcre.sub('', text)
        try:
            index = newitems.index(spcfree)
        except ValueError:
            index = 0
            newitems.insert(0, text)

        # get rid of existing items in list (clear doesn't work here)
        for i in range(self.count()):
            self.removeItem(0)

        # put new items in and select the correct option
        self.addItems(newitems)
        self.setCurrentIndex(index)

        # must remember to do this!
        self.blockSignals(False)

00503     def slotActivated(self, val):
        '''Populate the drop down list before activation.'''
        self.updateComboList()
        Choice.slotActivated(self, val)

00508 class DistancePt(Choice):
    """For editing distances with defaults in points."""

    points = (
        '0pt', '0.25pt', '0.5pt', '1pt', '1.5pt', '2pt', '3pt',
        '4pt', '5pt', '6pt', '8pt', '10pt', '12pt', '14pt', '16pt',
        '18pt', '20pt', '22pt', '24pt', '26pt', '28pt', '30pt',
        '34pt', '40pt', '44pt', '50pt', '60pt', '70pt'
        )
    
00518     def __init__(self, setting, parent, allowauto=False):
        '''Initialise with blank list, then populate with sensible units.'''
        Choice.__init__(self, setting, True, DistancePt.points, parent)
        
00522 class Dataset(Choice):
    """Allow the user to choose between the possible datasets."""

00525     def __init__(self, setting, document, dimensions, datatype, parent):
        """Initialise the combobox. The list is populated with datasets.

        dimensions specifies the dimension of the dataset to list

        Changes on the document refresh the list of datasets."""
        
        Choice.__init__(self, setting, True, [], parent)
        self.document = document
        self.dimensions = dimensions
        self.datatype = datatype
        self.lastdatasets = None
        self._populateEntries()
        self.connect(document, qt4.SIGNAL('sigModified'), self.slotModified)

00540     def _populateEntries(self):
        """Put the list of datasets into the combobox."""

        # get datasets of the correct dimension
        datasets = []
        for name, ds in self.document.data.iteritems():
            if ds.dimensions == self.dimensions and ds.datatype == self.datatype:
                datasets.append(name)
        datasets.sort()

        if datasets != self.lastdatasets:
            utils.populateCombo(self, datasets)
            self.lastdatasets = datasets

00554     def slotModified(self, modified):
        """Update the list of datasets if the document is modified."""
        self._populateEntries()

00558 class DatasetOrString(qt4.QWidget):
    """Allow use to choose a dataset or enter some text."""

    def __init__(self, setting, document, dimensions, datatype, parent):
        qt4.QWidget.__init__(self, parent)
        self.datachoose = Dataset(setting, document, dimensions, datatype,
                                  None)
        
        b = self.button = qt4.QPushButton('..')
        b.setFlat(True)
        b.setSizePolicy(qt4.QSizePolicy.Maximum, qt4.QSizePolicy.Maximum)
        b.setMaximumHeight(self.datachoose.height())
        b.setMaximumWidth(16)
        b.setCheckable(True)

        layout = qt4.QHBoxLayout()
        self.setLayout(layout)
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.datachoose)
        layout.addWidget(b)

        self.connect(b, qt4.SIGNAL('toggled(bool)'),
                     self.buttonToggled)
        self.connect(self.datachoose, qt4.SIGNAL('settingChanged'),
                     self.slotSettingChanged)

00585     def slotSettingChanged(self, *args):
        """When datachoose changes, inform any listeners."""
        self.emit( qt4.SIGNAL('settingChanged'), *args )
        
00589     def buttonToggled(self, on):
        """Button is pressed to bring popup up / down."""

        # if button is down and there's no existing popup, bring up a new one
        if on:
            e = _EditBox( unicode(self.datachoose.currentText()),
                          self.datachoose.setting.readonly, self.button)

            # we get notified with text when the popup closes
            self.connect(e, qt4.SIGNAL('closing'), self.boxClosing)
            e.show()

00601     def boxClosing(self, text):
        """Called when the popup edit box closes."""

        # update the text if we can
        if not self.datachoose.setting.readonly:
            self.datachoose.setEditText(text)
            self.datachoose.setFocus()
            self.parentWidget().setFocus()
            self.datachoose.setFocus()

        self.button.setChecked(False)

00613 class FillStyle(Choice):
    """For choosing between fill styles."""

    _icons = None
    _fills = None
    _fillcnvt = None

    def __init__(self, setting, parent):
        if self._icons is None:
            self._generateIcons()

        Choice.__init__(self, setting, False,
                        self._fills, parent,
                        icons=self._icons)

    @classmethod
00629     def _generateIcons(cls):
        """Generate a list of pixmaps for drop down menu."""

        size = 12
        icons = []
        c = qt4.QColor('grey')
        for f in cls._fills:
            pix = qt4.QPixmap(size, size)
            pix.fill()
            painter = qt4.QPainter(pix)
            painter.setRenderHint(qt4.QPainter.Antialiasing)
            brush = qt4.QBrush(c, cls._fillcnvt[f])
            painter.fillRect(0, 0, size, size, brush)
            painter.end()
            icons.append( qt4.QIcon(pix) )

        cls._icons = icons

00647 class Marker(Choice):
    """A control to let the user choose a marker."""

    _icons = None

    def __init__(self, setting, parent):
        if self._icons is None:
            self._generateIcons()

        Choice.__init__(self, setting, False,
                        utils.MarkerCodes, parent,
                        icons=self._icons)

    @classmethod
    def _generateIcons(cls):
        size = 16
        icons = []
        brush = qt4.QBrush( qt4.QColor('darkgrey') )
        pen = qt4.QPen( qt4.QBrush(qt4.Qt.black), 1. )
        for marker in utils.MarkerCodes:
            pix = qt4.QPixmap(size, size)
            pix.fill()
            painter = qt4.QPainter(pix)
            painter.setRenderHint(qt4.QPainter.Antialiasing)
            painter.setBrush(brush)
            painter.setPen(pen)
            utils.plotMarker(painter, size*0.5, size*0.5, marker, size*0.33)
            painter.end()
            icons.append( qt4.QIcon(pix) )

        cls._icons = icons

00679 class Arrow(Choice):
    """A control to let the user choose an arrowhead."""

    _icons = None

    def __init__(self, setting, parent):
        if self._icons is None:
            self._generateIcons()

        Choice.__init__(self, setting, False,
                        utils.ArrowCodes, parent,
                        icons=self._icons)

    @classmethod
    def _generateIcons(cls):
        size = 16
        icons = []
        brush = qt4.QBrush(qt4.Qt.black)
        pen = qt4.QPen( qt4.QBrush(qt4.Qt.black), 1. )
        for arrow in utils.ArrowCodes:
            pix = qt4.QPixmap(size, size)
            pix.fill()
            painter = qt4.QPainter(pix)
            painter.setRenderHint(qt4.QPainter.Antialiasing)
            painter.setBrush(brush)
            painter.setPen(pen)
            utils.plotLineArrow(painter, size*0.4, size*0.5,
                                size*2, 0.,
                                arrowsize=size*0.2,
                                arrowleft=arrow, arrowright=arrow)
            painter.end()
            icons.append( qt4.QIcon(pix) )

        cls._icons = icons

00714 class LineStyle(Choice):
    """For choosing between line styles."""

    _icons = None
    _lines = None
    _linecnvt = None

    size = (24, 8)

    def __init__(self, setting, parent):
        if self._icons is None:
            self._generateIcons()

        Choice.__init__(self, setting, False,
                        self._lines, parent,
                        icons=self._icons)
        self.setIconSize( qt4.QSize(*self.size) )

    @classmethod
00733     def _generateIcons(cls):
        """Generate a list of icons for drop down menu."""

        # import later for dependency issues
        import veusz.setting.collections

        icons = []
        size = cls.size
        setn = veusz.setting.collections.Line('temp')
        setn.get('color').set('black')
        setn.get('width').set('1pt')
        
        for lstyle in cls._lines:
            pix = qt4.QPixmap(*size)
            pix.fill()
            painter = qt4.QPainter(pix)
            painter.setRenderHint(qt4.QPainter.Antialiasing)

            setn.get('style').set(lstyle)
            
            painter.setPen( setn.makeQPen(painter) )
            painter.drawLine( int(size[0]*0.1), size[1]/2,
                              int(size[0]*0.9), size[1]/2 )
            painter.end()
            icons.append( qt4.QIcon(pix) )

        cls._icons = icons

00761 class Color(qt4.QWidget):
    """A control which lets the user choose a color.

    A drop down list and a button to bring up a dialog are used
    """

    _icons = None
    _colors = None
    _qobj = None

    def __init__(self, setting,  parent):
        qt4.QWidget.__init__(self, parent)

        if self._icons is None:
            self._generateIcons()

        self.setting = setting
 
        # combo box
        c = self.combo = qt4.QComboBox()
        c.setEditable(True)
        for color in self._colors:
            c.addItem(self._icons[color], color)
        self.connect(c, qt4.SIGNAL('activated(const QString&)'),
                     self.slotActivated )

        # add color if a color is added by a different combo box
        self.connect(Color._qobj, qt4.SIGNAL('addcolor'), self.addcolorSlot)

        # button for selecting colors
        b = self.button = qt4.QPushButton()
        b.setFlat(True)
        b.setSizePolicy(qt4.QSizePolicy.Maximum, qt4.QSizePolicy.Maximum)
        b.setMaximumHeight(24)
        b.setMaximumWidth(24)
        self.connect(b, qt4.SIGNAL('clicked()'), self.slotButtonClicked)

        if setting.readonly:
            c.setEnabled(False)
            b.setEnabled(False)
                     
        layout = qt4.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(c)
        layout.addWidget(b)

        self.setColor( setting.toText() )
        self.setLayout(layout)
        self.setting.setOnModified(self.onModified)

00812     def addcolorSlot(self, color):
        """When another Color combo adds a color, add one to this one"""
        self.combo.addItem(self._icons[color], color)

    @classmethod
00817     def _generateIcons(cls):
        """Generate a list of icons for drop down menu.
        Does not generate existing icons
        """

        size = 12
        if cls._icons is None:
            cls._icons = {}
        
        icons = cls._icons
        for c in cls._colors:
            if c not in icons:
                pix = qt4.QPixmap(size, size)
                pix.fill( qt4.QColor(c) )
                icons[c] = qt4.QIcon(pix)
                if cls._qobj is not None:
                    # tell other combo boxes a color has been added
                    cls._qobj.emit(qt4.SIGNAL('addcolor'), c)

        if cls._qobj is None:
            cls._qobj = qt4.QObject()
    
00839     def slotButtonClicked(self):
        """Open dialog to edit color."""

        col = qt4.QColorDialog.getColor(self.setting.color(), self)
        if col.isValid():
            # change setting
            val = unicode( col.name() )
            if self.setting.val != val:
                self.emit( qt4.SIGNAL('settingChanged'), self,
                           self.setting, val)

00850     def slotActivated(self, val):
        """A different value is selected."""
        
        text = unicode(self.combo.currentText())
        val = self.setting.fromText(text)
            
        # value has changed
        if self.setting.val != val:
            self.emit(qt4.SIGNAL('settingChanged'), self, self.setting, val)

00860     def setColor(self, color):
        """Update control with color given."""

        # construct color icon if not there
        if color not in Color._icons:
            Color._colors.append(color)
            Color._generateIcons()
        
        # add text to combo if not there
        index = self.combo.findText(color)

        # set correct index in combobox
        self.combo.setCurrentIndex(index)
        self.button.setIcon( self.combo.itemIcon(index) )

00875     def onModified(self, mod):
        """called when the setting is changed remotely"""
        self.setColor( self.setting.toText() )

00879 class WidgetSelector(Choice):
    """For choosing from a list of widgets."""

00882     def __init__(self, setting, document, parent):
        """Initialise and populate combobox."""

        Choice.__init__(self, setting, True, [], parent)
        self.document = document
        self.connect(document, qt4.SIGNAL('sigModified'),
                     self.slotModified)

    def _populateEntries(self):
        pass
    
00893     def slotModified(self, modified):
        """Update list of axes."""
        self._populateEntries()

00897 class Image(WidgetSelector):
    """Choose an image."""

00900     def __init__(self, setting, document, parent):
        """Initialise and populate combobox."""

        WidgetSelector.__init__(self, setting, document, parent)
        self._populateEntries()

00906     def _populateEntries(self):
        """Build up a list of images for combobox."""

        images = self.setting.getImageList()

        # we only need the list of names
        names = images.keys()
        names.sort()

        utils.populateCombo(self, names)

00917 class Axis(WidgetSelector):
    """Choose an axis to plot against."""

00920     def __init__(self, setting, document, direction, parent):
        """Initialise and populate combobox."""

        WidgetSelector.__init__(self, setting, document, parent)
        self.direction = direction
        self._populateEntries()

00927     def _populateEntries(self):
        """Build up a list of possible axes."""

        # get parent widget
        widget = self.setting.parent
        while not widget.isWidget() and widget is not None:
            widget = widget.parent

        # get list of axis widgets up the tree
        axes = {}
        while widget is not None:
            for w in widget.children:
                try:
                    # succeeds if axis
                    if w.settings.direction == self.direction:
                        axes[w.name] = True
                except AttributeError:
                    pass
            widget = widget.parent

        names = axes.keys()
        names.sort()

        utils.populateCombo(self, names)

00952 class ListSet(qt4.QFrame):
    """A widget for constructing settings which are lists of other
    properties.

    This code is pretty nasty and horrible, so we abstract it in this
    base widget
    """

    pixsize = 12

00962     def __init__(self, defaultval, setting, parent):
        """Initialise this base widget.

        defaultval is the default entry to add if add is clicked with
        no current entries

        setting is the setting this widget corresponds to

        parent is the parent widget.
        """
        
        qt4.QFrame.__init__(self, parent)
        self.setFrameStyle(qt4.QFrame.Box)
        self.defaultval = defaultval
        self.setting = setting
        self.controls = []
        self.layout = qt4.QGridLayout(self)
        self.layout.setMargin( self.layout.margin()/2 )
        self.layout.setSpacing( self.layout.spacing()/4 )

        # ignore changes if this set
        self.ignorechange = False

        self.populate()
        self.setting.setOnModified(self.onModified)
    
00988     def populateRow(self, row, val):
        """Populate the row in the control.

        Returns a list of the widgets created.
        """
        return None
    
00995     def populate(self):
        """Construct the list of controls."""

        # delete all children in case of refresh
        self.controls = []
        for c in self.children():
            if isinstance(c, qt4.QWidget):
                self.layout.removeWidget(c)
                c.deleteLater()
        c = None

        # iterate over each row
        row = -1
        for row, val in enumerate(self.setting.val):
            cntrls = self.populateRow(row, val)
            for i in cntrls:
                i.show()
            self.controls.append(cntrls)

        # buttons at end
        bbox = qt4.QWidget()
        h = qt4.QHBoxLayout(bbox)
        h.setMargin(0)
        bbox.setLayout(h)
        self.layout.addWidget(bbox, row+1, 0, 1, -1)
        
        # a button to add a new entry
        b = qt4.QPushButton('Add')
        h.addWidget(b)
        self.connect(b, qt4.SIGNAL('clicked()'), self.onAddClicked)
        b.show()

        # a button to delete the last entry
        b = qt4.QPushButton('Delete')
        h.addWidget(b)
        self.connect(b, qt4.SIGNAL('clicked()'), self.onDeleteClicked)
        b.setEnabled( len(self.setting.val) > 0 )
        b.show()

01034     def polish(self):
        """Remove tooltip from widget - avoid Qt bugs."""
        qt4.QVBox.polish(self)
        qt4.QToolTip.remove(self)

01039     def onAddClicked(self):
        """Add a line style to the list given."""

        rows = list(self.setting.val)
        if len(rows) != 0:
            rows.append(rows[-1])
        else:
            rows.append(self.defaultval)
        self.emit( qt4.SIGNAL('settingChanged'), self, self.setting, rows )

01049     def onDeleteClicked(self):
        """Remove final entry in settings list."""

        rows = list(self.setting.val)[:-1]
        self.emit( qt4.SIGNAL('settingChanged'), self, self.setting, rows )

01055     def onModified(self, mod):
        """called when the setting is changed remotely"""

        if not self.ignorechange:
            self.populate()
        else:
            self.ignorechange = False

01063     def identifyPosn(self, widget):
        """Identify the position this widget is in.

        Returns (row, col) or (None, None) if not found.
        """

        for row, cntrls in enumerate(self.controls):
            for col, cntrl in enumerate(cntrls):
                if cntrl == widget:
                    return (row, col)
        return (None, None)

01075     def addColorButton(self, row, col, tooltip):
        """Add a color button to the list at the position specified."""

        color = self.setting.val[row][col]
        wcolor = qt4.QPushButton()
        self.layout.addWidget(wcolor, row, col)
        wcolor.setMaximumWidth(wcolor.height())
        pix = qt4.QPixmap(self.pixsize, self.pixsize)
        pix.fill( utils.extendedColorToQColor(color) )
        wcolor.setIcon( qt4.QIcon(pix) )
        wcolor.setToolTip(tooltip)
        wcolor.setSizePolicy(qt4.QSizePolicy.Maximum, qt4.QSizePolicy.Maximum)

        self.connect(wcolor, qt4.SIGNAL('clicked()'), self.onColorClicked)
        return wcolor

01091     def addToggleButton(self, row, col, tooltip):
        """Make a toggle button."""

        toggle = self.setting.val[row][col]
        wtoggle = qt4.QCheckBox()
        self.layout.addWidget(wtoggle, row, col)
        wtoggle.setChecked(toggle)
        wtoggle.setToolTip(tooltip)
        self.connect(wtoggle, qt4.SIGNAL('toggled(bool)'), self.onToggled)
        return wtoggle

01102     def addCombo(self, row, col, tooltip, values, icons, texts):
        """Make an enumeration combo - choose from a set of icons."""
        
        val = self.setting.val[row][col]

        wcombo = qt4.QComboBox()
        self.layout.addWidget(wcombo, row, col)

        if texts is None:
            for icon in icons:
                wcombo.addItem(icon, "")
        else:
            for text, icon in izip(texts, icons):
                wcombo.addItem(icon, text)

        wcombo.setCurrentIndex(values.index(val))
        wcombo.setToolTip(tooltip)
        self.connect(wcombo, qt4.SIGNAL('activated(int)'),
                     self.onComboChanged)
        wcombo._vz_values = values
        return wcombo

01124     def _updateRowCol(self, row, col, val):
        """Update value on row and column."""
        rows = list(self.setting.val)
        items = list(rows[row])
        items[col] = val
        rows[row] = tuple(items)
        self.ignorechange = True
        self.emit( qt4.SIGNAL('settingChanged'), self, self.setting, rows )
        
01133     def onToggled(self, on):
        """Checkbox toggled."""
        row, col = self.identifyPosn(self.sender())
        self._updateRowCol(row, col, on)

01138     def onComboChanged(self, val):
        """Update the setting if the combo changes."""
        sender = self.sender()
        row, col = self.identifyPosn(sender)
        self._updateRowCol(row, col, sender._vz_values[val])

01144     def onColorClicked(self):
        """Color button clicked for line."""
        sender = self.sender()
        row, col = self.identifyPosn(sender)

        rows = self.setting.val
        color = qt4.QColorDialog.getColor(
            utils.extendedColorToQColor(rows[row][col]),
            self,
            "Choose color",
            qt4.QColorDialog.ShowAlphaChannel )
        if color.isValid():
            # change setting
            # this is a bit irritating, as have to do lots of
            # tedious conversions
            color = utils.extendedColorFromQColor(color)
            self._updateRowCol(row, col, color)

            # change the color
            pix = qt4.QPixmap(self.pixsize, self.pixsize)
            pix.fill( utils.extendedColorToQColor(color) )
            sender.setIcon( qt4.QIcon(pix) )
            
01167 class LineSet(ListSet):
    """A list of line styles.
    """

    def __init__(self, setting, parent):
        ListSet.__init__(self, ('solid', '1pt', 'black', False),
                         setting, parent)

01175     def populateRow(self, row, val):
        """Add the widgets for the row given."""

        # create line icons if not already created
        if LineStyle._icons is None:
            LineStyle._generateIcons()

        # make line style selector
        wlinestyle = self.addCombo(row, 0, 'Line style',
                                   LineStyle._lines,
                                   LineStyle._icons, None)
        
        # make line width edit box
        wwidth = qt4.QLineEdit()
        self.layout.addWidget(wwidth, row, 1)
        wwidth.setText(self.setting.val[row][1])
        wwidth.setToolTip('Line width')
        self.connect(wwidth, qt4.SIGNAL('editingFinished()'),
                     self.onWidthChanged)

        # make color selector button
        wcolor = self.addColorButton(row, 2, 'Line color')

        # make hide checkbox
        whide = self.addToggleButton(row, 3, 'Hide line')

        # return created controls
        return [wlinestyle, wwidth, wcolor, whide]

01204     def onWidthChanged(self):
        """Width has changed - validate."""

        sender = self.sender()
        row, col = self.identifyPosn(sender)

        text = unicode(sender.text())
        if setting.Distance.isDist(text):
            # valid distance
            styleClear(sender)
            self._updateRowCol(row, col, text)
        else:
            # invalid distance
            styleError(sender)

01219 class FillSet(ListSet):
    """A list of fill settings."""

    def __init__(self, setting, parent):
        ListSet.__init__(self, ('solid', 'black', False),
                         setting, parent)

01226     def populateRow(self, row, val):
        """Add the widgets for the row given."""

        # construct fill icons if not already done
        if FillStyle._icons is None:
            FillStyle._generateIcons()
    
        # make fill style selector
        wfillstyle = self.addCombo(row, 0, 'Fill style',
                                   FillStyle._fills,
                                   FillStyle._icons,
                                   FillStyle._fills)
        wfillstyle.setMinimumWidth(self.pixsize)

        # make color selector button
        wcolor = self.addColorButton(row, 1, 'Fill color')

        # make hide checkbox
        whide = self.addToggleButton(row, 2, 'Hide fill')

        # return widgets
        return [wfillstyle, wcolor, whide]

01249 class MultiSettingWidget(qt4.QWidget):
    """A widget for storing multiple values in a tuple,
    with + and - signs by each entry."""

01253     def __init__(self, setting, doc, parent):
        """Construct widget as combination of LineEdit and PushButton
        for browsing."""

        qt4.QWidget.__init__(self, parent)
        self.setting = setting
        self.document = doc

        self.grid = layout = qt4.QGridLayout()
        layout.setHorizontalSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.last = ()
        self.controls = []
        self.setting.setOnModified(self.onModified)
        
01270     def makeRow(self):
        """Make new row at end"""
        row = len(self.controls)
        cntrl = self.makeControl(row)
        cntrl.installEventFilter(self)
        addbutton = qt4.QPushButton('+')
        addbutton.setFixedWidth(24)
        addbutton.setFlat(True)
        addbutton.setToolTip('Add another item')
        subbutton = qt4.QPushButton('-')
        subbutton.setToolTip('Remove item')
        subbutton.setFixedWidth(24)
        subbutton.setFlat(True)

        self.controls.append((cntrl, addbutton, subbutton))

        self.grid.addWidget(cntrl, row, 0)
        self.grid.addWidget(addbutton, row, 1)
        self.grid.addWidget(subbutton, row, 2)

        self.connect(addbutton, qt4.SIGNAL('clicked()'),
                     lambda: self.addPressed(row))
        self.connect(subbutton, qt4.SIGNAL('clicked()'),
                     lambda: self.subPressed(row))

        if len(self.controls) == 2:
            # enable first subtraction button
            self.controls[0][2].setEnabled(True)
        elif len(self.controls) == 1:
            # or disable
            self.controls[0][2].setEnabled(False)

01302     def eventFilter(self, obj, event):
        """Capture loss of focus by controls."""
        if event.type() == qt4.QEvent.FocusOut:
            for row, c in enumerate(self.controls):
                if c[0] is obj:
                    self.dataChanged(row)
                    break
        return qt4.QWidget.eventFilter(self, obj, event)

01311     def deleteRow(self):
        """Remove last row"""
        for w in self.controls[-1]:
            self.grid.removeWidget(w)
            w.deleteLater()
        self.controls.pop(-1)

        # disable first subtraction button
        if len(self.controls) == 1:
            self.controls[0][2].setEnabled(False)

01322     def addPressed(self, row):
        """User adds a new row."""
        val = list(self.setting.val)
        val.insert(row+1, '')
        self.emit( qt4.SIGNAL('settingChanged'), self, self.setting,
                   tuple(val) )

01329     def subPressed(self, row):
        """User deletes a row."""
        val = list(self.setting.val)
        val.pop(row)
        self.emit( qt4.SIGNAL('settingChanged'), self, self.setting,
                   tuple(val) )

01336     def onModified(self, mod):
        """Called when the setting is changed remotely,
        or when control is opened"""

        s = self.setting

        if self.last == s.val:
            return
        self.last = s.val

        # update number of rows
        while len(self.setting.val) > len(self.controls):
            self.makeRow()
        while len(self.setting.val) < len(self.controls):
            self.deleteRow()

        # update values
        self.updateControls()

01355     def makeControl(self, row):
        """Override this to make an editing widget."""
        return None

01359     def updateControls():
        """Override this to update values in controls."""
        pass

01363     def readControl(self, cntrl):
        """Read value from control."""
        return None

01367     def dataChanged(self, row):
        """Update row of setitng with new data"""
        val = list(self.setting.val)
        val[row] = self.readControl( self.controls[row][0] )
        self.emit( qt4.SIGNAL('settingChanged'), self, self.setting,
                   tuple(val) )

01374 class Datasets(MultiSettingWidget):
    """A control for editing a list of datasets."""

01377     def __init__(self, setting, doc, dimensions, datatype, parent):
        """Contruct set of comboboxes"""

        MultiSettingWidget.__init__(self, setting, doc, parent)
        self.dimensions = dimensions
        self.datatype = datatype

        self.lastdatasets = []
        # force updating to initialise
        self.onModified(True)

01388     def makeControl(self, row):
        """Make QComboBox edit widget."""
        combo = qt4.QComboBox()
        combo.setEditable(True)
        self.connect(combo.lineEdit(), qt4.SIGNAL('editingFinished()'), 
                     lambda: self.dataChanged(row))
        # if a different item is selected
        self.connect(combo, qt4.SIGNAL('activated(const QString&)'),
                     lambda x: self.dataChanged(row))
        utils.populateCombo(combo, self.getDatasets())
        return combo

01400     def readControl(self, control):
        """Get text for control."""
        return unicode( control.lineEdit().text() )

01404     def getDatasets(self):
        """Get applicable datasets (sorted)."""
        datasets = []
        for name, ds in self.document.data.iteritems():
            if (ds.dimensions == self.dimensions and
                ds.datatype == self.datatype):
                datasets.append(name)
        datasets.sort()
        return datasets

01414     def updateControls(self):
        """Set values of controls."""
        for cntrls, val in izip(self.controls, self.setting.val):
            cntrls[0].lineEdit().setText(val)

01419     def onModified(self, mod):
        """Called when the setting is changed remotely,
        or when control is opened"""

        MultiSettingWidget.onModified(self, mod)

        s = self.setting
        datasets = self.getDatasets()

        if self.lastdatasets == datasets:
            return
        self.lastdatasets = datasets

        # update list of datasets
        for cntrls in self.controls:
            utils.populateCombo(cntrls[0], datasets)

01436 class Strings(MultiSettingWidget):
    """A list of strings."""

01439     def __init__(self, setting, doc, parent):
        """Construct widget as combination of LineEdit and PushButton
        for browsing."""

        MultiSettingWidget.__init__(self, setting, doc, parent)
        self.onModified(True)

01446     def makeControl(self, row):
        """Make edit widget."""
        lineedit = qt4.QLineEdit()
        self.connect(lineedit, qt4.SIGNAL('editingFinished()'), 
                     lambda: self.dataChanged(row))
        return lineedit

01453     def readControl(self, control):
        """Get text for control."""
        return unicode( control.text() )

01457     def updateControls(self):
        """Set values of controls."""
        for cntrls, val in izip(self.controls, self.setting.val):
            cntrls[0].setText(val)        

01462 class Filename(qt4.QWidget):
    """A widget for selecting a filename with a browse button."""

01465     def __init__(self, setting, mode, parent):
        """Construct widget as combination of LineEdit and PushButton
        for browsing.

        mode is 'image' or 'file'
        """

        qt4.QWidget.__init__(self, parent)
        self.mode = mode
        self.setting = setting

        layout = qt4.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        self.setLayout(layout)

        # the actual edit control
        self.edit = qt4.QLineEdit()
        self.edit.setText( setting.toText() )
        layout.addWidget(self.edit)
        
        # get a sensible shape for the button - yawn
        b = self.button = qt4.QPushButton('..')
        b.setFlat(True)
        layout.addWidget(b)
        b.setSizePolicy(qt4.QSizePolicy.Maximum, qt4.QSizePolicy.Maximum)
        b.setMaximumWidth(16)

        # connect up signals
        self.connect(self.edit, qt4.SIGNAL('editingFinished()'),
                     self.validateAndSet)
        self.connect(b, qt4.SIGNAL('clicked()'),
                     self.buttonClicked)

        # add completion if we have support (qt >= 4.3)
        if hasattr(qt4, 'QDirModel'):
            c = self.filenamecompleter = qt4.QCompleter(self)
            model = qt4.QDirModel(c)
            c.setModel(model)
            self.edit.setCompleter(c)

        # for read only filenames
        if setting.readonly:
            self.edit.setReadOnly(True)

        self.setting.setOnModified(self.onModified)

01512     def buttonClicked(self):
        """Button clicked - show file open dialog."""

        title = 'Choose file'
        filefilter = "All files (*)"
        if self.mode == 'image':
            title = 'Choose image'
            filefilter = ("Images (*.png *.jpg *.jpeg *.bmp *.svg *.tiff *.tif "
                          "*.gif *.xbm *.xpm);;" + filefilter)

        filename = qt4.QFileDialog.getOpenFileName(
            self, title, self.edit.text(), filefilter)

        if filename:
            val = unicode(filename)
            if self.setting.val != val:
                self.emit( qt4.SIGNAL('settingChanged'), self, self.setting,
                           val )

01531     def validateAndSet(self):
        """Check the text is a valid setting and update it."""

        text = unicode(self.edit.text())
        try:
            val = self.setting.fromText(text)
            styleClear(self.edit)

            # value has changed
            if self.setting.val != val:
                self.emit( qt4.SIGNAL('settingChanged'), self, self.setting,
                           val )

        except setting.InvalidType:
            styleError(self.edit)

01547     def onModified(self, mod):
        """called when the setting is changed remotely"""
        self.edit.setText( self.setting.toText() )

01551 class FontFamily(qt4.QFontComboBox):
    """List the font families, showing each font."""

01554     def __init__(self, setting, parent):
        """Create the combobox."""

        qt4.QFontComboBox.__init__(self, parent)
        self.setting = setting
        self.setFontFilters( qt4.QFontComboBox.ScalableFonts )
        
        # set initial value
        self.onModified(True)

        # stops combobox readjusting in size to fit contents
        self.setSizeAdjustPolicy(
            qt4.QComboBox.AdjustToMinimumContentsLengthWithIcon)

        self.setting.setOnModified(self.onModified)

        # if a different item is selected
        self.connect( self, qt4.SIGNAL('activated(const QString&)'),
                      self.slotActivated )

01574     def focusOutEvent(self, *args):
        """Allows us to check the contents of the widget."""
        qt4.QFontComboBox.focusOutEvent(self, *args)
        self.slotActivated('')

01579     def slotActivated(self, val):
        """Update setting if a different item is chosen."""
        newval = unicode(self.currentText())
        if self.setting.val != newval:
            self.emit(qt4.SIGNAL('settingChanged'), self, self.setting, newval)

01585     def onModified(self, mod):
        """Make control reflect chosen setting."""
        self.setCurrentFont( qt4.QFont(self.setting.toText()) )

01589 class ErrorStyle(Choice):
    """Choose different error bar styles."""
    
    _icons = None         # generated icons
    _errorstyles = None   # copied in by setting.py
    
    def __init__(self, setting, parent):
        if self._icons is None:
            self._generateIcons()

        Choice.__init__(self, setting, False,
                        self._errorstyles, parent,
                        icons=self._icons)

01603     def _generateIcons(cls):
        """Generate a list of pixmaps for drop down menu."""
        cls._icons = []
        for errstyle in cls._errorstyles:
            cls._icons.append( utils.getIcon('error_%s' % errstyle) )

Generated by  Doxygen 1.6.0   Back to index