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):
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