camelot.camelot.view.controls.delegates

Package camelot :: Package camelot :: Package view :: Package controls :: Module delegates |
[hide private]
[frames] | no frames]

# Source Code for Module camelot.camelot.view.controls.delegates

      1  #  ============================================================================
      2  #
      3  #  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved.
      4  #  www.conceptive.be / project-camelot@conceptive.be
      5  #
      6  #  This file is part of the Camelot Library.
      7  #
      8  #  This file may be used under the terms of the GNU General Public
      9  #  License version 2.0 as published by the Free Software Foundation
     10  #  and appearing in the file LICENSE.GPL included in the packaging of
     11  #  this file.  Please review the following information to ensure GNU
     12  #  General Public Licensing requirements will be met:
     13  #  http://www.trolltech.com/products/qt/opensource.html
     14  #
     15  #  If you are unsure which license is appropriate for your use, please
     16  #  review the following information:
     17  #  http://www.trolltech.com/products/qt/licensing.html or contact
     18  #  project-camelot@conceptive.be.
     19  #
     20  #  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
     21  #  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     22  #
     23  #  For use of this library in commercial applications, please contact
     24  #  project-camelot@conceptive.be
     25  #
     26  #  ============================================================================
     27
     28  """Contains classes for using custom delegates"""
     29
     30  import logging
     31
     32  logger = logging.getLogger('camelot.view.controls.delegates')
     33
     34  from PyQt4 import QtGui
     35  from PyQt4 import QtCore
     36  from PyQt4.QtCore import Qt
     37
     38  import datetime
     39  import StringIO
     40
     41  import camelot.types
     42  from camelot.view.controls import editors
     43  from camelot.view.model_thread import get_model_thread
     44
     45
     46  """Dictionary mapping widget types to an associated delegate"""
     47
     48  _registered_delegates_ = {}
     49  verbose = False
     50
     51 -def _paint_not_editable(painter, option, index):
     52    text = index.model().data(index, Qt.DisplayRole).toString()
     53    color = QtGui.QColor(235, 233, 237)
     54    painter.save()
     55
     56    painter.fillRect(option.rect, color)
     57    painter.setPen(QtGui.QColor(Qt.darkGray))
     58    painter.drawText(option.rect.x()+2,
     59                     option.rect.y(),
     60                     option.rect.width()-4,
     61                     option.rect.height(),
     62                     Qt.AlignVCenter,
     63                     text)
     64
     65    painter.restore()
     66
     67 -def create_constant_function(constant):
     68    return lambda:constant
     69
     70
     71 -class GenericDelegate(QtGui.QItemDelegate):
     72    """Manages custom delegates"""
     73
     74 -  def __init__(self, parent=None):
     75      super(GenericDelegate, self).__init__(parent)
     76      self.delegates = {}
     77
     78 -  def set_columns_desc(self, columnsdesc):
     79      self.columnsdesc = columnsdesc
     80
     81 -  def insertColumnDelegate(self, column, delegate):
     82      """Inserts a custom column delegate"""
     83      if verbose:
     84        logger.debug('inserting delegate for column %s' % column)
     85      delegate.setParent(self)
     86      self.delegates[column] = delegate
     87      self.connect(delegate, QtCore.SIGNAL('commitData(QWidget*)'), self.commitData)
     88      self.connect(delegate, QtCore.SIGNAL('closeEditor(QWidget*)'), self.closeEditor)
     89
     90 -  def commitData(self, editor):
     91      self.emit(QtCore.SIGNAL('commitData(QWidget*)'), editor)
     92
     93 -  def closeEditor(self, editor):
     94      self.emit(QtCore.SIGNAL('closeEditor(QWidget*)'), editor)
     95
     96 -  def removeColumnDelegate(self, column):
     97      """Removes custom column delegate"""
     98      logger.debug('removing a new custom column delegate')
     99      if column in self.delegates:
    100        del self.delegates[column]
    101
    102 -  def paint(self, painter, option, index):
    103      """Use a custom delegate paint method if it exists"""
    104      delegate = self.delegates.get(index.column())
    105      if delegate is not None:
    106        delegate.paint(painter, option, index)
    107      else:
    108        QtGui.QItemDelegate.paint(self, painter, option, index)
    109
    110 -  def createEditor(self, parent, option, index):
    111      """Use a custom delegate createEditor method if it exists"""
    112      delegate = self.delegates.get(index.column())
    113      if delegate is not None:
    114        return delegate.createEditor(parent, option, index)
    115      else:
    116        QtGui.QItemDelegate.createEditor(self, parent, option, index)
    117
    118 -  def setEditorData(self, editor, index):
    119      """Use a custom delegate setEditorData method if it exists"""
    120      if verbose:
    121        logger.debug('setting editor data for column %s' % index.column())
    122      delegate = self.delegates.get(index.column())
    123      if delegate is not None:
    124        if verbose:
    125          logger.debug('got delegate')
    126        delegate.setEditorData(editor, index)
    127      else:
    128        QtGui.QItemDelegate.setEditorData(self, editor, index)
    129      if verbose:
    130        logger.debug('data set')
    131
    132 -  def setModelData(self, editor, model, index):
    133      """Use a custom delegate setModelData method if it exists"""
    134      logger.debug('setting model data for column %s' % index.column())
    135      delegate = self.delegates.get(index.column())
    136      if delegate is not None:
    137        delegate.setModelData(editor, model, index)
    138      else:
    139        QtGui.QItemDelegate.setModelData(self, editor, model, index)
    140
    141 -  def sizeHint(self, option, index):
    142      option = QtGui.QStyleOptionViewItem()
    143      delegate = self.delegates.get(index.column())
    144      if delegate is not None:
    145        return delegate.sizeHint(option, index)
    146      else:
    147        return QtGui.QItemDelegate.sizeHint(option, index)
    148
    149
    150 -class IntegerColumnDelegate(QtGui.QItemDelegate):
    151    """Custom delegate for integer values"""
    152
    153 -  def __init__(self, minimum=0, maximum=100, parent=None):
    154      super(IntegerColumnDelegate, self).__init__(parent)
    155      self.minimum = minimum
    156      self.maximum = maximum
    157
    158 -  def createEditor(self, parent, option, index):
    159      editor = QtGui.QSpinBox(parent)
    160      editor.setRange(self.minimum, self.maximum)
    161      editor.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
    162      return editor
    163
    164 -  def setEditorData(self, editor, index):
    165      value = index.model().data(index, Qt.EditRole).toInt()[0]
    166      editor.setValue(value)
    167
    168 -  def setModelData(self, editor, model, index):
    169      editor.interpretText()
    170      model.setData(index, create_constant_function(editor.value()))
    171
    172  _registered_delegates_[QtGui.QSpinBox] = IntegerColumnDelegate
    173
    174
    175 -class PlainTextColumnDelegate(QtGui.QItemDelegate):
    176    """Custom delegate for simple string values"""
    177
    178 -  def __init__(self, maxlength=None, parent=None):
    179      super(PlainTextColumnDelegate, self).__init__(parent)
    180      self.maxlength = maxlength
    181
    182 -  def paint(self, painter, option, index):
    183      if (option.state & QtGui.QStyle.State_Selected):
    184        QtGui.QItemDelegate.paint(self, painter, option, index)
    185      elif not self.parent().columnsdesc[index.column()][1]['editable']:
    186        _paint_not_editable(painter, option, index)
    187      else:
    188        QtGui.QItemDelegate.paint(self, painter, option, index)
    189
    190 -  def createEditor(self, parent, option, index):
    191      editor = QtGui.QLineEdit(parent)
    192      editor.setMaxLength(self.maxlength)
    193      if not self.parent().columnsdesc[index.column()][1]['editable']:
    194        editor.setEnabled(False)
    195      return editor
    196
    197 -  def setEditorData(self, editor, index):
    198      value = index.model().data(index, Qt.EditRole).toString()
    199      editor.setText(value)
    200
    201 -  def setModelData(self, editor, model, index):
    202      model.setData(index, create_constant_function(unicode(editor.text())))
    203
    204  _registered_delegates_[QtGui.QLineEdit] = PlainTextColumnDelegate
    205
    206
    207 -class TimeColumnDelegate(QtGui.QItemDelegate):
    208 -  def __init__(self, format, default, nullable, parent=None):
    209      super(TimeColumnDelegate, self).__init__(parent)
    210      self.nullable = nullable
    211      self.format = format
    212      self.default = default
    213
    214 -  def createEditor(self, parent, option, index):
    215      editor = QtGui.QTimeEdit(parent)
    216      editor.setDisplayFormat(self.format)
    217      return editor
    218
    219 -  def setEditorData(self, editor, index):
    220      value = index.model().data(index, Qt.EditRole).toTime()
    221      editor.index = index
    222      if value:
    223        editor.setTime(value)
    224      else:
    225        editor.setTime(editor.minimumTime())
    226
    227 -  def setModelData(self, editor, model, index):
    228      value = editor.time()
    229      t = datetime.time(hour=value.hour(), minute=value.minute(), second=value.second())
    230      model.setData(index, create_constant_function(t))
    231
    232
    233 -class DateTimeColumnDelegate(QtGui.QItemDelegate):
    234 -  def __init__(self, parent, format, nullable, **kwargs):
    235      super(DateTimeColumnDelegate, self).__init__(parent)
    236      self.format = format
    237
    238 -  def createEditor(self, parent, option, index):
    239      editor = QtGui.QDateTimeEdit(parent)
    240      editor.setDisplayFormat(self.format)
    241      return editor
    242
    243 -  def setEditorData(self, editor, index):
    244      value = index.model().data(index, Qt.EditRole).toDateTime()
    245      if value:
    246        editor.setDateTime(value)
    247      else:
    248        editor.setDateTime(editor.minimumDateTime())
    249
    250
    251 -class DateColumnDelegate(QtGui.QItemDelegate):
    252    """Custom delegate for date values"""
    253
    254 -  def __init__(self,
    255                 format='dd/MM/yyyy',
    256                 default=None,
    257                 nullable=True,
    258                 parent=None):
    259
    260      super(DateColumnDelegate, self).__init__(parent)
    261      self.format = format
    262      self.default = default
    263      self.nullable = nullable
    264
    265 -  def createEditor(self, parent, option, index):
    266      editor = editors.DateEditor(self.nullable, self.format, parent)
    267      self.connect(editor, QtCore.SIGNAL('editingFinished()'), self.commitAndCloseEditor)
    268      return editor
    269
    270 -  def commitAndCloseEditor(self):
    271      editor = self.sender()
    272      self.emit(QtCore.SIGNAL('commitData(QWidget*)'), editor)
    273      #self.emit(QtCore.SIGNAL('closeEditor(QWidget*)'), editor)
    274
    275 -  def setEditorData(self, editor, index):
    276      value = index.model().data(index, Qt.EditRole).toDate()
    277      editor._index = index
    278      if value:
    279        editor.setDate(value)
    280      else:
    281        editor.setDate(editor.minimumDate())
    282
    283 -  def setModelData(self, editor, model, index):
    284      logger.debug('date delegate set model data')
    285      value = editor.date()
    286      logger.debug('date delegate got value')
    287      if value == editor.minimumDate():
    288        model.setData(index, create_constant_function(None))
    289      else:
    290        d = datetime.date(value.year(), value.month(), value.day())
    291        model.setData(index, create_constant_function(d))
    292      logger.debug('date delegate data set')
    293
    294  _registered_delegates_[editors.DateEditor] = DateColumnDelegate
    295
    296
    297 -class CodeColumnDelegate(QtGui.QItemDelegate):
    298 -  def __init__(self, parts, parent=None):
    299      super(CodeColumnDelegate, self).__init__(parent)
    300      self.parts = parts
    301      self._dummy_editor = editors.CodeEditor(self.parts, None)
    302
    303 -  def createEditor(self, parent, option, index):
    304      editor = editors.CodeEditor(self.parts, parent)
    305      self.connect(editor, QtCore.SIGNAL('editingFinished()'), self.commitAndCloseEditor)
    306      return editor
    307
    308 -  def commitAndCloseEditor(self):
    309      editor = self.sender()
    310      self.emit(QtCore.SIGNAL('commitData(QWidget*)'), editor)
    311      #self.emit(QtCore.SIGNAL('closeEditor(QWidget*)'), editor)
    312
    313 -  def setEditorData(self, editor, index):
    314      value = index.data(Qt.EditRole).toPyObject()
    315      if value:
    316        for part_editor, part in zip(editor.part_editors, value):
    317          part_editor.setText(unicode(part))
    318
    319 -  def sizeHint(self, option, index):
    320      return self._dummy_editor.sizeHint()
    321
    322 -  def setModelData(self, editor, model, index):
    323      value = []
    324      for part in editor.part_editors:
    325        value.append(unicode(part.text()))
    326      model.setData(index, create_constant_function(value))
    327
    328  _registered_delegates_[editors.CodeEditor] = CodeColumnDelegate
    329
    330
    331 -class VirtualAddressColumnDelegate(QtGui.QItemDelegate):
    332 -  def __init__(self, parent=None):
    333      super(VirtualAddressColumnDelegate, self).__init__(parent)
    334
    335 -  def commitAndCloseEditor(self):
    336      editor = self.sender()
    337      self.emit(QtCore.SIGNAL('commitData(QWidget*)'), editor)
    338      #self.emit(QtCore.SIGNAL('closeEditor(QWidget*)'), editor)
    339
    340 -  def createEditor(self, parent, option, index):
    341      editor = editors.VirtualAddressEditor(parent)
    342      self.connect(editor, QtCore.SIGNAL('editingFinished()'), self.commitAndCloseEditor)
    343      return editor
    344
    345 -  def setEditorData(self, editor, index):
    346      value = index.data(Qt.EditRole).toPyObject()
    347      if value:
    348        editor.editor.setText(value[1])
    349        editor.combo.setCurrentIndex(camelot.types.VirtualAddress.virtual_address_types.index(value[0]))
    350
    351 -  def setModelData(self, editor, model, index):
    352      value = (unicode(editor.combo.currentText()), unicode(editor.editor.text()))
    353      model.setData(index, create_constant_function(value))
    354
    355  _registered_delegates_[editors.VirtualAddressEditor] = \
    356      VirtualAddressColumnDelegate
    357
    358
    359 -class FloatColumnDelegate(QtGui.QItemDelegate):
    360    """Custom delegate for float values"""
    361
    362 -  def __init__(self, minimum=-100.0, maximum=100.0, precision=3,
    363                 editable=True, parent=None, **kwargs):
    364      super(FloatColumnDelegate, self).__init__(parent)
    365      self.minimum = minimum
    366      self.maximum = maximum
    367      self.precision = precision
    368      self.editable = editable
    369
    370 -  def createEditor(self, parent, option, index):
    371      editor = QtGui.QDoubleSpinBox(parent)
    372      editor.setReadOnly(self.editable==False)
    373      editor.setRange(self.minimum, self.maximum)
    374      editor.setDecimals(self.precision)
    375      editor.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
    376      editor.setSingleStep(1.0)
    377      return editor
    378
    379 -  def setEditorData(self, editor, index):
    380      value = index.model().data(index, Qt.EditRole).toDouble()[0]
    381      editor.setValue(value)
    382
    383 -  def setModelData(self, editor, model, index):
    384      editor.interpretText()
    385      model.setData(index, create_constant_function(editor.value()))
    386
    387  _registered_delegates_[QtGui.QDoubleSpinBox] = FloatColumnDelegate
    388
    389
    390 -class Many2OneColumnDelegate(QtGui.QItemDelegate):
    391    """Custom delegate for many 2 one relations"""
    392
    393 -  def __init__(self, admin, embedded=False, parent=None, **kwargs):
    394      logger.debug('create many2onecolumn delegate')
    395      assert admin != None
    396      super(Many2OneColumnDelegate, self).__init__(parent)
    397      self.admin = admin
    398      self._embedded = embedded
    399      self._kwargs = kwargs
    400      self._dummy_editor = editors.Many2OneEditor(self.admin, None)
    401
    402 -  def createEditor(self, parent, option, index):
    403      if self._embedded:
    404        editor = editors.EmbeddedMany2OneEditor(self.admin, parent)
    405      else:
    406        editor = editors.Many2OneEditor(self.admin, parent)
    407      self.connect(editor, QtCore.SIGNAL('editingFinished()'), self.commitAndCloseEditor)
    408      return editor
    409
    410 -  def setEditorData(self, editor, index):
    411      editor.setEntity(create_constant_function(index.data(Qt.EditRole).toPyObject()), propagate=False)
    412
    413 -  def setModelData(self, editor, model, index):
    414      if editor.entity_instance_getter:
    415        model.setData(index, editor.entity_instance_getter)
    416
    417 -  def commitAndCloseEditor(self):
    418      editor = self.sender()
    419      self.emit(QtCore.SIGNAL('commitData(QWidget*)'), editor)
    420
    421 -  def sizeHint(self, option, index):
    422      return self._dummy_editor.sizeHint()
    423
    424  _registered_delegates_[editors.Many2OneEditor] = Many2OneColumnDelegate
    425
    426
    427 -class One2ManyColumnDelegate(QtGui.QItemDelegate):
    428    """Custom delegate for many 2 one relations"""
    429
    430 -  def __init__(self, parent=None, **kwargs):
    431      logger.debug('create one2manycolumn delegate')
    432      assert 'admin' in kwargs
    433      super(One2ManyColumnDelegate, self).__init__(parent)
    434      self.kwargs = kwargs
    435
    436 -  def createEditor(self, parent, option, index):
    437      logger.debug('create a one2many editor')
    438      editor = editors.One2ManyEditor(parent=parent, **self.kwargs)
    439      self.setEditorData(editor, index)
    440      return editor
    441
    442 -  def setEditorData(self, editor, index):
    443      logger.debug('set one2many editor data')
    444      model = index.data(Qt.EditRole).toPyObject()
    445      if model:
    446        editor.setModel(model)
    447
    448 -  def setModelData(self, editor, model, index):
    449      pass
    450
    451  _registered_delegates_[editors.One2ManyEditor] = One2ManyColumnDelegate
    452
    453
    454 -class BoolColumnDelegate(QtGui.QItemDelegate):
    455    """Custom delegate for boolean values"""
    456
    457 -  def __init__(self, parent=None):
    458      super(BoolColumnDelegate, self).__init__(parent)
    459
    460 -  def createEditor(self, parent, option, index):
    461      editor = QtGui.QCheckBox(parent)
    462      return editor
    463
    464 -  def setEditorData(self, editor, index):
    465      checked = index.model().data(index, Qt.EditRole).toBool()
    466      editor.setChecked(checked)
    467
    468 -  def setModelData(self, editor, model, index):
    469      model.setData(index, create_constant_function(editor.isChecked()))
    470
    471 -  def paint(self, painter, option, index):
    472      painter.save()
    473      self.drawBackground(painter, option, index)
    474      checked = index.model().data(index, Qt.EditRole).toBool()
    475      check_option = QtGui.QStyleOptionButton()
    476      check_option.rect = option.rect
    477      check_option.palette = option.palette
    478      if checked:
    479        check_option.state = option.state | QtGui.QStyle.State_On
    480      else:
    481        check_option.state = option.state | QtGui.QStyle.State_Off
    482      QtGui.QApplication.style().drawControl(QtGui.QStyle.CE_CheckBox, check_option, painter)
    483      painter.restore()
    484
    485  _registered_delegates_[QtGui.QCheckBox] = BoolColumnDelegate
    486
    487
    488 -class ImageColumnDelegate(QtGui.QItemDelegate):
    489 -  def createEditor(self, parent, option, index):
    490      editor = editors.ImageEditor(parent)
    491      self.connect(editor, QtCore.SIGNAL('editingFinished()'), self.commitAndCloseEditor)
    492      return editor
    493
    494 -  def setEditorData(self, editor, index):
    495      s = StringIO.StringIO()
    496      data = index.data(Qt.EditRole).toPyObject()
    497      if data:
    498        editor.image = data.image
    499        data = data.image.copy()
    500        data.thumbnail((100, 100))
    501        data.save(s, 'png')
    502        s.seek(0)
    503        pixmap = QtGui.QPixmap()
    504        pixmap.loadFromData(s.read())
    505        s.close()
    506        editor.setPixmap(pixmap)
    507      else:
    508        #@todo: clear pixmap
    509        editor.clearFirstImage()
    510
    511 -  def commitAndCloseEditor(self):
    512      editor = self.sender()
    513      self.emit(QtCore.SIGNAL('commitData(QWidget*)'), editor)
    514      #self.emit(QtCore.SIGNAL('closeEditor(QWidget*)'), editor)
    515
    516 -  def setModelData(self, editor, model, index):
    517      model.setData(index, create_constant_function(camelot.types.StoredImage(editor.image)))
    518
    519  _registered_delegates_[editors.ImageEditor] = ImageColumnDelegate
    520
    521
    522 -class RichTextColumnDelegate(QtGui.QItemDelegate):
    523 -  def __init__(self, parent = None, **kwargs):
    524      super(RichTextColumnDelegate, self).__init__(parent)
    525      self.kwargs = kwargs
    526
    527 -  def createEditor(self, parent, option, index):
    528      editor = editors.RichTextEditor(parent, **self.kwargs)
    529      self.connect(editor, QtCore.SIGNAL('editingFinished()'), self.commitAndCloseEditor)
    530      return editor
    531
    532 -  def commitAndCloseEditor(self):
    533      editor = self.sender()
    534      self.emit(QtCore.SIGNAL('commitData(QWidget*)'), editor)
    535      #self.emit(QtCore.SIGNAL('closeEditor(QWidget*)'), editor)
    536
    537 -  def setEditorData(self, editor, index):
    538      html = index.model().data(index, Qt.EditRole).toString()
    539      if html:
    540        editor.setHtml(html)
    541      else:
    542        editor.clear()
    543
    544 -  def setModelData(self, editor, model, index):
    545      model.setData(index, create_constant_function(unicode(editor.toHtml())))
    546
    547  _registered_delegates_[editors.RichTextEditor] = RichTextColumnDelegate
    548
    549
    550 -class ComboBoxColumnDelegate(QtGui.QItemDelegate):
    551 -  def __init__(self, choices, parent=None, **kwargs):
    552      super(ComboBoxColumnDelegate, self).__init__(parent)
    553      self.choices = choices
    554
    555 -  def qvariantToPython(self, variant):
    556      if variant.canConvert(QtCore.QVariant.String):
    557        return unicode(variant.toString())
    558      else:
    559        return variant.toPyObject()
    560
    561 -  def createEditor(self, parent, option, index):
    562      editor = QtGui.QComboBox(parent)
    563
    564      def create_choices_getter(model, row):
    565
    566        def getChoices():
    567          return list(self.choices(model._get_object(row)))
    568
    569        return getChoices
    570
    571      def create_choices_setter(editor):
    572
    573        def setChoices(choices):
    574          allready_in_combobox = dict((self.qvariantToPython(editor.itemData(i)),i) for i in range(editor.count()))
    575          for i,(value,name) in enumerate(choices):
    576            if value not in allready_in_combobox:
    577              editor.insertItem(i, unicode(name), QtCore.QVariant(value))
    578            else:
    579              # the editor data might allready have been set, but its name is still ...,
    580              # therefor we set the name now correct
    581              editor.setItemText(i, unicode(name))
    582
    583        return setChoices
    584
    585      get_model_thread().post(create_choices_getter(index.model(), index.row()), create_choices_setter(editor))
    586      return editor
    587
    588 -  def setEditorData(self, editor, index):
    589      data = self.qvariantToPython(index.model().data(index, Qt.EditRole))
    590      if data!=None:
    591        for i in range(editor.count()):
    592          if data == self.qvariantToPython(editor.itemData(i)):
    593            editor.setCurrentIndex(i)
    594            return
    595        # it might happen, that when we set the editor data, the setChoices method has
    596        # not happened yes, therefore, we temporary set ... in the text while setting the
    597        # correct data to the editor
    598        editor.insertItem(editor.count(), '...', QtCore.QVariant(data))
    599        editor.setCurrentIndex(editor.count()-1)
    600
    601 -  def setModelData(self, editor, model, index):
    602      editor_data = self.qvariantToPython(editor.itemData(editor.currentIndex()))
    603      model.setData(index, create_constant_function(editor_data))
    604
    605  _registered_delegates_[QtGui.QComboBox] = ComboBoxColumnDelegate
    606