6 from PyQt4.QtGui import QTableWidget,QTableWidgetItem,QCheckBox,QWidget,QSpinBox,QHBoxLayout,QVBoxLayout,QLineEdit,QSizePolicy,QTextEdit,QTextOption,QFrame,QToolButton,QPalette,QComboBox, QFileDialog,QTextCursor,QInputDialog,QPushButton,QGridLayout,QIcon,QHeaderView,QMessageBox
17 QWidget.__init__(self)
18 self.setContentsMargins(0, 0, 0, 0)
19 self.setLayout(QHBoxLayout())
20 self.layout().setSpacing(0)
21 self.layout().setContentsMargins(0, 0, 0, 0)
22 self.layout().addWidget(property)
24 self._closeButton.setText(
"x")
25 self._closeButton.hide()
33 self._closeButton.show()
35 self._closeButton.hide()
39 QComboBox.keyPressEvent(self,event)
40 if event.key()==Qt.Key_Return:
41 self.emit(SIGNAL(
"returnPressed()"))
44 """ Shows properties of an object in a QTableWidget using the DataAccessor.
46 The view can be used readonly ('setReadOnly') or for editing.
47 On editing the signals 'valueChanged', 'propertyDeleted', 'propertyAdded' are emitted.
50 LABEL =
"&Property View"
55 AbstractView.__init__(self)
56 QTableWidget.__init__(self, parent)
63 self.setSortingEnabled(
False)
64 self.verticalHeader().hide()
65 self.setSelectionMode(QTableWidget.NoSelection)
71 self.connect(self.horizontalHeader(), SIGNAL(
"sectionResized(int,int,int)"), self.
sectionResized)
75 """ Stop all running operations.
80 """ Clear the table and set the header label.
82 QTableWidget.clear(self)
84 self.setColumnCount(2)
85 self.setHorizontalHeaderLabels([
'Property',
'Value'])
88 """ Return all property widgets in the right column.
90 Closable as well as normal properties are returned.
93 for i
in range(self.rowCount()):
94 widget=self.cellWidget(i,1)
95 if isinstance(widget,Property):
97 elif hasattr(widget,
"closableProperty"):
98 widgets+=[(widget.closableProperty(),i)]
102 """ Update the height of the column that holds a certain property.
107 self.verticalHeader().resizeSection(i, property.properyHeight())
111 """ Adds a property to the PropertyView and returns it.
113 property.setPropertyView(self)
115 property.setReadOnly(
True)
116 self.insertRow(self.
lastRow()+1)
120 self.connect(widget.closeButton(), SIGNAL(
'clicked(bool)'), self.
removeProperty)
121 self.setCellWidget(self.
lastRow(), 1, widget)
123 self.setCellWidget(self.
lastRow(), 1, property)
129 """ Return the last row holding a property.
131 The row with the add new property field is not counted.
134 return self.rowCount() - 2
136 return self.rowCount() - 1
139 """ Add a category row to the tabel which consists of two gray LabelItems.
141 self.insertRow(self.
lastRow()+1)
144 self.verticalHeader().resizeSection(self.rowCount() - 1, Property.DEFAULT_HEIGHT)
148 """ Sets all properties in the PropertyView to read-only.
150 After calling this function all properties that are added are set to read-only as well.
168 """ Resize columns when table size is changed.
171 QTableWidget.resizeEvent(self, event)
172 space = self.width() - 4
173 if self.verticalScrollBar().isVisible():
174 space -= self.verticalScrollBar().
width()
175 space -= self.columnWidth(0)
176 self.setColumnWidth(1, space)
181 space = self.width() - 4
182 if self.verticalScrollBar().isVisible():
183 space -= self.verticalScrollBar().
width()
184 space -= self.columnWidth(0)
185 self.setColumnWidth(1, space)
190 """ Sets the DataAccessor from which the object properties are read.
192 You need to call updateContent() in order to make the changes visible.
194 if not isinstance(accessor, BasicDataAccessor):
195 raise TypeError(__name__ +
" requires data accessor of type BasicDataAccessor.")
196 AbstractView.setDataAccessor(self, accessor)
199 """ Append a row with a field to add new properties.
201 self.insertRow(self.
lastRow()+1)
203 lineedit.setFrame(
False)
204 lineedit.setContentsMargins(0, 0, 0, 0)
205 self.setCellWidget(self.
lastRow(), 0, lineedit)
207 widget.setContentsMargins(0, 0, 0, 0)
208 widget.setLayout(QHBoxLayout())
209 widget.layout().setSpacing(0)
210 widget.layout().setContentsMargins(0, 0, 0, 0)
212 types=[
"String",
"Boolean",
"Integer",
"Double",
"File",
"FileVector"]
214 typelist.addItem(type)
215 widget.layout().addWidget(typelist)
216 addButton=QToolButton()
217 addButton.setText(
"+")
218 widget.layout().addWidget(addButton)
219 self.setCellWidget(self.
lastRow(), 1, widget)
220 self.verticalHeader().resizeSection(self.
lastRow(), Property.DEFAULT_HEIGHT)
221 self.connect(addButton, SIGNAL(
'clicked(bool)'), self.
addProperty)
222 self.connect(lineedit, SIGNAL(
'returnPressed()'), self.
addProperty)
223 self.connect(typelist, SIGNAL(
'returnPressed()'), self.
addProperty)
224 addButton._lineedit=lineedit
225 addButton._typelist=typelist
226 lineedit._lineedit=lineedit
227 lineedit._typelist=typelist
228 typelist._lineedit=lineedit
229 typelist._typelist=typelist
232 """ Fill the properties of an object in the PropertyView using the DataAccessor.
247 if property[0] ==
"Category":
252 self.
append(propertyWidget)
253 if isinstance(propertyWidget,(FileProperty,FileVectorProperty)):
255 if isinstance(propertyWidget,QCheckBox):
256 propertyWidget.setChecked(property[2],
False)
266 """ Create a property widget from a property tuple.
268 This function is static in order to be used by other view, e.g. TableView.
271 if property[0] ==
"String":
272 propertyWidget=
StringProperty(property[1], property[2], categoryName)
273 elif property[0] ==
"MultilineString":
274 propertyWidget=
StringProperty(property[1], property[2], categoryName,
True)
275 elif property[0] ==
"File":
276 propertyWidget=
FileProperty(property[1], property[2], categoryName)
277 elif property[0] ==
"FileVector":
279 elif property[0] ==
"Boolean":
280 propertyWidget =
BooleanProperty(property[1], property[2], categoryName)
281 elif property[0] ==
"Integer":
283 elif property[0] ==
"Double":
284 propertyWidget=
DoubleProperty(property[1], property[2], categoryName)
285 elif property[0] ==
"DropDown":
286 propertyWidget=
DropDownProperty(property[1], property[2], property[6], categoryName)
288 logging.error(__name__+
": propertyWidgetFromProperty() - Unknown property type "+str(property[0]))
290 if len(property) > 3
and property[3]:
291 propertyWidget.setUserInfo(property[3])
292 if len(property) > 4
and property[4]:
293 propertyWidget.setReadOnly(
True)
294 if len(property) > 5
and property[5]:
295 propertyWidget.setDeletable(
True)
296 return propertyWidget
297 propertyWidgetFromProperty = staticmethod(propertyWidgetFromProperty)
300 """ This function is called when a property a changed.
302 The DataAcessor is called to handle the property change.
306 newvalue = property.value()
308 if newvalue != oldValue:
309 if isinstance(newvalue,ValueError):
312 result=self.
dataAccessor().setProperty(self.
dataObject(), property.name(), newvalue, property.categoryName())
314 self.emit(SIGNAL(
'valueChanged'),property.name(), newvalue, oldValue, property.categoryName())
316 print "valueChanged() result = ", result, type(result)
317 property.setToolTip(result)
318 QMessageBox.critical(self.parent(),
'Error', result)
320 property.setHighlighted(bad)
323 """ This function deletes a property.
325 The DataAcessor is called to handle the property remove.
327 property=self.sender().
parent()._property
334 self.emit(SIGNAL(
'propertyDeleted'),name)
337 """ This function adds a property.
339 The DataAcessor is called to add the property.
341 type=str(self.sender()._typelist.currentText())
342 name=str(self.sender()._lineedit.text().toAscii())
343 if type
in [
"String",
"File"]:
345 elif type
in [
"Integer",
"Double"]:
347 elif type
in [
"FileVector"]:
349 elif type
in [
"Boolean"]:
351 if name==
None or name==
"":
352 QCoreApplication.instance().infoMessage(
"Please specify name of property.")
359 if isinstance(property,(FileProperty,FileVectorProperty)):
361 self.sender()._lineedit.setText(
"")
363 self.emit(SIGNAL(
'propertyAdded'),property.name())
366 """ Slot for itemClicked() signal.
368 Calls items's property's doubleClicked().
372 item.property().labelDoubleClicked()
378 """ A QTableWidgetItem with a convenient constructor.
383 Argument may be either a string or a Property object.
384 If argument is the latter the property's user info will be used for the label's tooltip.
386 if isinstance(argument, Property):
387 tooltip = argument.name() +
" (" + argument.userInfo() +
")"
388 name = argument.name()
395 QTableWidgetItem.__init__(self, name)
396 self.setToolTip(tooltip)
397 self.setFlags(Qt.ItemIsEnabled)
398 self.setBackgroundColor(color)
404 """ Mother of all properties which can be added to the PropertyView using its append() function.
407 USER_INFO =
"General property"
418 """ Sets the name of this property.
423 """ Return the name of this property.
437 """ Sets PropertyView object.
442 """ Returns property view.
447 """ Returns user info string containing information on type of property and what data may be insert.
452 """ Returns user info string containing information on type of property and what data may be insert.
457 """ Disables editing functionality.
462 """ Return the height of the property widget.
464 return self.DEFAULT_HEIGHT
467 """ Abstract function returning current value of this property.
469 Has to be implemented by properties which allow the user to change their value.
471 raise NotImplementedError
474 """ Abstract function returning current value of this property.
476 Has to be implemented by properties which allow the user to change their value.
478 raise NotImplementedError
481 """ Slot for change events.
483 The actual object which have changed should connect their value changed signal
484 (or similar) to this function to forward change to data accessor of PropertyView.
486 logging.debug(
'Property: valueChanged() ' + str(self.
name()))
491 """ Called by PropertyView itemDoubleClicked().
496 """ Highlight the property, e.g. change color.
500 class BooleanProperty(Property, QCheckBox):
501 """ Property holding a check box for boolean values.
504 USER_INFO =
"Enable / Disable"
506 def __init__(self, name, value, categoryName=None):
509 Property.__init__(self, name, categoryName)
510 QCheckBox.__init__(self)
511 self.connect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
515 self.disconnect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
516 QCheckBox.setChecked(self, check)
518 self.connect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
521 """ Disables editing functionality.
524 self.setEnabled(
False)
525 self.disconnect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
527 self.setEnabled(
True)
528 self.connect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
531 """ Returns True if check box is checked.
533 return self.isChecked()
536 """ Property holding a check box for boolean values.
539 USER_INFO =
"Drop down field"
541 def __init__(self, name, value, values, categoryName=None):
544 Property.__init__(self, name, categoryName)
545 QComboBox.__init__(self)
550 self.setCurrentIndex(values.index(value))
551 self.connect(self, SIGNAL(
'currentIndexChanged(int)'), self.
valueChanged)
554 """ Disables editing functionality.
557 self.setEnabled(
False)
558 self.disconnect(self, SIGNAL(
'currentIndexChanged(int)'), self.
valueChanged)
560 self.setEnabled(
True)
561 self.connect(self, SIGNAL(
'currentIndexChanged(int)'), self.
valueChanged)
564 """ Returns True if check box is checked.
566 return self.
_values[self.currentIndex()]
570 QTextEdit.focusOutEvent(self,event)
571 self.emit(SIGNAL(
"editingFinished()"))
574 """ This class provides a PropertyView property holding an editable text and a button.
576 It is possible to hide the button unless the mouse cursor is over the property. This feature is turned on by default. See setAutohideButton().
577 If the button is pressed nothing happens. This functionality should be implemented in sub-classes. See buttonClicked().
578 The text field can hold single or multiple lines. See setMultiline()
582 AUTOHIDE_BUTTON =
True
584 def __init__(self, name, value, categoryName=None, multiline=False):
585 """ The constructor creates a QHBoxLayout and calls createLineEdit(), createTextEdit() and createButton().
587 Property.__init__(self, name, categoryName)
588 QWidget.__init__(self)
594 self.setLayout(QHBoxLayout())
595 self.layout().setSpacing(0)
596 self.layout().setContentsMargins(0, 0, 0, 0)
608 """ Sets value of text edit.
612 strValue = str(value)
618 self._lineEdit.setText(strValue)
619 self._textEdit.setText(strValue)
629 self._lineEdit.setToolTip(text)
630 self._textEdit.setToolTip(text)
633 """ Switch between single and multi line mode.
638 self._textEdit.show()
639 self._lineEdit.hide()
642 self._lineEdit.show()
643 self._textEdit.hide()
647 """ This function creates the signle line text field and adds it to the property's layout.
650 self._lineEdit.setFrame(
False)
652 self._lineEdit.setContentsMargins(0, 0, 0, 0)
653 self._lineEdit.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding))
657 """ This function creates the multi line text field and adds it to the property's layout.
660 self._textEdit.setWordWrapMode(QTextOption.NoWrap)
661 self._textEdit.setFrameStyle(QFrame.NoFrame)
663 self._textEdit.setContentsMargins(0, 0, 0, 0)
664 self._textEdit.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding))
668 """ Return the estimated height of the property.
670 The returned height covers the whole text, even if multiline.
673 self._textEdit.document().adjustSize()
674 height=self._textEdit.document().
size().height()+3
675 if self._textEdit.horizontalScrollBar().isVisible():
676 height+=self._textEdit.horizontalScrollBar().height()+3
682 """ Returns line edit.
687 """ Returns text edit.
692 """ Creates a button and adds it to the property's layout.
694 self.
_button = QToolButton(self)
696 self._button.setContentsMargins(0, 0, 0, 0)
698 self.layout().addWidget(self.
_button)
709 """ Returns True if the button has been created, otherwise False is returned.
714 """ Switch between readonly and editable.
717 if not self.
lineEdit().isReadOnly()
and readOnly:
722 if self.
lineEdit().isReadOnly()
and not readOnly:
734 """ Returns value of text edit.
737 return str(self._lineEdit.text().toAscii())
739 return str(self._textEdit.toPlainText().toAscii())
743 """ Returns the value of correct type (in case its not a string).
748 """ If hide is True the button will only be visible while the cursor is over the property.
754 This function is called if the button was clicked. For information on the checked argument see documentation of QPushButton::clicked().
755 This function should be overwritten by sub-classes.
760 """ If autohideButtonFlag is set this function makes the button visible. See setAutohideButton().
766 """ If autohideButtonFlag is set this function makes the button invisible. See setAutohideButton().
772 """ Update tooltip and height when text is changed.
775 self.emit(SIGNAL(
'updatePropertyHeight'),self)
779 Property.valueChanged(self)
782 """ Highlight the property by changing the background color of the textfield.
786 p.setColor(QPalette.Active, QPalette.ColorRole(9),Qt.red)
788 p.setColor(QPalette.Active, QPalette.ColorRole(9),Qt.white)
789 self._lineEdit.setPalette(p)
790 self._textEdit.viewport().setPalette(p)
793 """ Switch back to the original value on ESC.
795 QWidget.keyPressEvent(self,event)
796 if event.key()==Qt.Key_Escape:
801 """ Property which holds an editable text.
803 A button is provided to switch between single and multi line mode.
807 USER_INFO =
"Text field"
809 AUTHIDE_BUTTON =
False
811 def __init__(self, name, value, categoryName=None, multiline=None):
813 TextEditWithButtonProperty.__init__(self, name, value, categoryName, (multiline
or str(value).
count(
"\n")>0))
816 TextEditWithButtonProperty.setMultiline(self,multiline)
817 icon = QIcon(
":/resources/editor.svg")
819 self._button.setIcon(icon)
820 self._button.setIconSize(QSize(15,15))
823 """ Switch to multiline mode if button is clicked.
829 textEdit=dialog.getText()
835 """ Property which hold editable integer numbers.
837 A Spinbox is provided when the property is editable.
840 USER_INFO =
"Integer field"
842 def __init__(self, name, value, categoryName=None):
845 Property.__init__(self, name, categoryName)
846 QWidget.__init__(self)
847 self.setLayout(QHBoxLayout())
848 self.layout().setSpacing(0)
849 self.layout().setContentsMargins(0, 0, 0, 0)
855 self._spinbox.setFrame(
False)
856 self.layout().addWidget(self.
_spinbox)
859 self._lineedit.setReadOnly(
True)
860 self._lineedit.setFrame(
False)
861 self._lineedit.setContentsMargins(0, 0, 0, 0)
863 self._lineedit.hide()
869 """ Switches between lineedit and spinbox.
873 self._lineedit.show()
877 self._lineedit.hide()
881 """ Returns integer value.
883 return self._spinbox.value()
887 self._spinbox.setValue(value % self.
maxint)
889 self._lineedit.setText(str(value))
892 """ TextEditWithButtonProperty which holds float numbers.
895 USER_INFO =
"Double field"
897 AUTHIDE_BUTTON =
False
899 def __init__(self, name, value, categoryName=None):
902 TextEditWithButtonProperty.__init__(self, name, value, categoryName=
None)
905 """ Do not create a button."""
909 if isinstance(object, float):
910 return "%.10g" % object
915 TextEditWithButtonProperty.setValue(self, self.
_toString(value))
918 """ Transform text to float and return.
921 return float(TextEditWithButtonProperty.value(self))
924 return float.fromhex(TextEditWithButtonProperty.value(self))
926 return ValueError(
"Entered value is not of type double.")
929 """ TextEditWithButtonProperty which holds file names.
931 A button for opening a dialog allowing to choose a file is provided.
934 USER_INFO =
"Select a file. Double click on label to open file."
937 def __init__(self, name, value, categoryName=None):
938 TextEditWithButtonProperty.__init__(self, name, value, categoryName)
942 """ Shows the file selection dialog. """
949 dir=QCoreApplication.instance().getLastOpenLocation()
950 filename = QFileDialog.getSaveFileName(
956 QFileDialog.DontConfirmOverwrite)
957 if not filename.isEmpty():
958 filename=str(filename)
963 self.
textEdit().emit(SIGNAL(
'editingFinished()'))
966 """ Open selected file in default application.
976 """ TextEditWithButtonProperty which holds file names.
978 A button for opening a dialog allowing to choose a list of files is provided.
981 USER_INFO =
"Edit list of files."
984 def __init__(self, name, value, categoryName=None):
985 TextEditWithButtonProperty.__init__(self, name, value, categoryName)
989 """ Shows the file selection dialog. """
995 dir=QCoreApplication.instance().getLastOpenLocation()
996 fileList = QFileDialog.getOpenFileNames(
998 'Select a list of files',
1002 QFileDialog.DontConfirmOverwrite)
1003 fileNames=[str(f)
for f
in fileList]
1011 fileNames=nfileNames
1012 if len(fileNames)>0:
1014 self.
textEdit().emit(SIGNAL(
'editingFinished()'))
1017 return self._updatingFlag>0
def setShowAddDeleteButton
tuple propertyWidgetFromProperty
def itemDoubleClickedSlot
tuple size
Write out results.