1 from __future__
import print_function
7 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
18 QWidget.__init__(self)
19 self.setContentsMargins(0, 0, 0, 0)
20 self.setLayout(QHBoxLayout())
21 self.layout().setSpacing(0)
22 self.layout().setContentsMargins(0, 0, 0, 0)
23 self.layout().addWidget(property)
25 self._closeButton.setText(
"x")
26 self._closeButton.hide()
34 self._closeButton.show()
36 self._closeButton.hide()
40 QComboBox.keyPressEvent(self,event)
41 if event.key()==Qt.Key_Return:
42 self.emit(SIGNAL(
"returnPressed()"))
45 """ Shows properties of an object in a QTableWidget using the DataAccessor. 47 The view can be used readonly ('setReadOnly') or for editing. 48 On editing the signals 'valueChanged', 'propertyDeleted', 'propertyAdded' are emitted. 51 LABEL =
"&Property View" 56 AbstractView.__init__(self)
57 QTableWidget.__init__(self, parent)
64 self.setSortingEnabled(
False)
65 self.verticalHeader().hide()
66 self.setSelectionMode(QTableWidget.NoSelection)
72 self.connect(self.horizontalHeader(), SIGNAL(
"sectionResized(int,int,int)"), self.
sectionResized)
76 """ Stop all running operations. 81 """ Clear the table and set the header label. 83 QTableWidget.clear(self)
85 self.setColumnCount(2)
86 self.setHorizontalHeaderLabels([
'Property',
'Value'])
89 """ Return all property widgets in the right column. 91 Closable as well as normal properties are returned. 94 for i
in range(self.rowCount()):
95 widget=self.cellWidget(i,1)
96 if isinstance(widget,Property):
98 elif hasattr(widget,
"closableProperty"):
99 widgets+=[(widget.closableProperty(),i)]
103 """ Update the height of the column that holds a certain property. 108 self.verticalHeader().resizeSection(i, property.properyHeight())
112 """ Adds a property to the PropertyView and returns it. 114 property.setPropertyView(self)
116 property.setReadOnly(
True)
117 self.insertRow(self.
lastRow()+1)
121 self.connect(widget.closeButton(), SIGNAL(
'clicked(bool)'), self.
removeProperty)
122 self.setCellWidget(self.
lastRow(), 1, widget)
124 self.setCellWidget(self.
lastRow(), 1, property)
130 """ Return the last row holding a property. 132 The row with the add new property field is not counted. 135 return self.rowCount() - 2
137 return self.rowCount() - 1
140 """ Add a category row to the tabel which consists of two gray LabelItems. 142 self.insertRow(self.
lastRow()+1)
145 self.verticalHeader().resizeSection(self.rowCount() - 1, Property.DEFAULT_HEIGHT)
149 """ Sets all properties in the PropertyView to read-only. 151 After calling this function all properties that are added are set to read-only as well. 169 """ Resize columns when table size is changed. 172 QTableWidget.resizeEvent(self, event)
173 space = self.width() - 4
174 if self.verticalScrollBar().isVisible():
175 space -= self.verticalScrollBar().
width()
176 space -= self.columnWidth(0)
177 self.setColumnWidth(1, space)
182 space = self.width() - 4
183 if self.verticalScrollBar().isVisible():
184 space -= self.verticalScrollBar().
width()
185 space -= self.columnWidth(0)
186 self.setColumnWidth(1, space)
191 """ Sets the DataAccessor from which the object properties are read. 193 You need to call updateContent() in order to make the changes visible. 195 if not isinstance(accessor, BasicDataAccessor):
196 raise TypeError(__name__ +
" requires data accessor of type BasicDataAccessor.")
197 AbstractView.setDataAccessor(self, accessor)
200 """ Append a row with a field to add new properties. 202 self.insertRow(self.
lastRow()+1)
204 lineedit.setFrame(
False)
205 lineedit.setContentsMargins(0, 0, 0, 0)
206 self.setCellWidget(self.
lastRow(), 0, lineedit)
208 widget.setContentsMargins(0, 0, 0, 0)
209 widget.setLayout(QHBoxLayout())
210 widget.layout().setSpacing(0)
211 widget.layout().setContentsMargins(0, 0, 0, 0)
213 types=[
"String",
"Boolean",
"Integer",
"Double",
"File",
"FileVector"]
215 typelist.addItem(type)
216 widget.layout().addWidget(typelist)
217 addButton=QToolButton()
218 addButton.setText(
"+")
219 widget.layout().addWidget(addButton)
220 self.setCellWidget(self.
lastRow(), 1, widget)
221 self.verticalHeader().resizeSection(self.
lastRow(), Property.DEFAULT_HEIGHT)
222 self.connect(addButton, SIGNAL(
'clicked(bool)'), self.
addProperty)
223 self.connect(lineedit, SIGNAL(
'returnPressed()'), self.
addProperty)
224 self.connect(typelist, SIGNAL(
'returnPressed()'), self.
addProperty)
225 addButton._lineedit=lineedit
226 addButton._typelist=typelist
227 lineedit._lineedit=lineedit
228 lineedit._typelist=typelist
229 typelist._lineedit=lineedit
230 typelist._typelist=typelist
233 """ Fill the properties of an object in the PropertyView using the DataAccessor. 248 if property[0] ==
"Category":
253 self.
append(propertyWidget)
254 if isinstance(propertyWidget,(FileProperty,FileVectorProperty)):
256 if isinstance(propertyWidget,QCheckBox):
257 propertyWidget.setChecked(property[2],
False)
267 """ Create a property widget from a property tuple. 269 This function is static in order to be used by other view, e.g. TableView. 272 if property[0] ==
"String":
273 propertyWidget=
StringProperty(property[1], property[2], categoryName)
274 elif property[0] ==
"MultilineString":
275 propertyWidget=
StringProperty(property[1], property[2], categoryName,
True)
276 elif property[0] ==
"File":
277 propertyWidget=
FileProperty(property[1], property[2], categoryName)
278 elif property[0] ==
"FileVector":
280 elif property[0] ==
"Boolean":
281 propertyWidget =
BooleanProperty(property[1], property[2], categoryName)
282 elif property[0] ==
"Integer":
284 elif property[0] ==
"Double":
285 propertyWidget=
DoubleProperty(property[1], property[2], categoryName)
286 elif property[0] ==
"DropDown":
287 propertyWidget=
DropDownProperty(property[1], property[2], property[6], categoryName)
289 logging.error(__name__+
": propertyWidgetFromProperty() - Unknown property type "+
str(property[0]))
291 if len(property) > 3
and property[3]:
292 propertyWidget.setUserInfo(property[3])
293 if len(property) > 4
and property[4]:
294 propertyWidget.setReadOnly(
True)
295 if len(property) > 5
and property[5]:
296 propertyWidget.setDeletable(
True)
297 return propertyWidget
298 propertyWidgetFromProperty = staticmethod(propertyWidgetFromProperty)
301 """ This function is called when a property a changed. 303 The DataAcessor is called to handle the property change. 307 newvalue = property.value()
309 if newvalue != oldValue:
310 if isinstance(newvalue,ValueError):
313 result=self.
dataAccessor().setProperty(self.
dataObject(), property.name(), newvalue, property.categoryName())
315 self.emit(SIGNAL(
'valueChanged'),property.name(), newvalue, oldValue, property.categoryName())
317 print(
"valueChanged() result = ", result, type(result))
318 property.setToolTip(result)
319 QMessageBox.critical(self.parent(),
'Error', result)
321 property.setHighlighted(bad)
324 """ This function deletes a property. 326 The DataAcessor is called to handle the property remove. 328 property=self.sender().
parent()._property
335 self.emit(SIGNAL(
'propertyDeleted'),name)
338 """ This function adds a property. 340 The DataAcessor is called to add the property. 342 type=
str(self.sender()._typelist.currentText())
343 name=
str(self.sender()._lineedit.text().toAscii())
344 if type
in [
"String",
"File"]:
346 elif type
in [
"Integer",
"Double"]:
348 elif type
in [
"FileVector"]:
350 elif type
in [
"Boolean"]:
352 if name==
None or name==
"":
353 QCoreApplication.instance().infoMessage(
"Please specify name of property.")
360 if isinstance(property,(FileProperty,FileVectorProperty)):
362 self.sender()._lineedit.setText(
"")
364 self.emit(SIGNAL(
'propertyAdded'),property.name())
367 """ Slot for itemClicked() signal. 369 Calls items's property's doubleClicked(). 373 item.property().labelDoubleClicked()
379 """ A QTableWidgetItem with a convenient constructor. 384 Argument may be either a string or a Property object. 385 If argument is the latter the property's user info will be used for the label's tooltip. 387 if isinstance(argument, Property):
388 tooltip = argument.name() +
" (" + argument.userInfo() +
")" 389 name = argument.name()
396 QTableWidgetItem.__init__(self, name)
397 self.setToolTip(tooltip)
398 self.setFlags(Qt.ItemIsEnabled)
399 self.setBackgroundColor(color)
405 """ Mother of all properties which can be added to the PropertyView using its append() function. 408 USER_INFO =
"General property" 419 """ Sets the name of this property. 424 """ Return the name of this property. 438 """ Sets PropertyView object. 443 """ Returns property view. 448 """ Returns user info string containing information on type of property and what data may be insert. 453 """ Returns user info string containing information on type of property and what data may be insert. 458 """ Disables editing functionality. 463 """ Return the height of the property widget. 465 return self.DEFAULT_HEIGHT
468 """ Abstract function returning current value of this property. 470 Has to be implemented by properties which allow the user to change their value. 472 raise NotImplementedError
475 """ Abstract function returning current value of this property. 477 Has to be implemented by properties which allow the user to change their value. 479 raise NotImplementedError
482 """ Slot for change events. 484 The actual object which have changed should connect their value changed signal 485 (or similar) to this function to forward change to data accessor of PropertyView. 487 logging.debug(
'Property: valueChanged() ' +
str(self.
name()))
492 """ Called by PropertyView itemDoubleClicked(). 497 """ Highlight the property, e.g. change color. 501 class BooleanProperty(Property, QCheckBox):
502 """ Property holding a check box for boolean values. 505 USER_INFO =
"Enable / Disable" 507 def __init__(self, name, value, categoryName=None):
510 Property.__init__(self, name, categoryName)
511 QCheckBox.__init__(self)
512 self.connect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
516 self.disconnect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
517 QCheckBox.setChecked(self, check)
519 self.connect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
522 """ Disables editing functionality. 525 self.setEnabled(
False)
526 self.disconnect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
528 self.setEnabled(
True)
529 self.connect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
532 """ Returns True if check box is checked. 534 return self.isChecked()
537 """ Property holding a check box for boolean values. 540 USER_INFO =
"Drop down field" 542 def __init__(self, name, value, values, categoryName=None):
545 Property.__init__(self, name, categoryName)
546 QComboBox.__init__(self)
551 self.setCurrentIndex(values.index(value))
552 self.connect(self, SIGNAL(
'currentIndexChanged(int)'), self.
valueChanged)
555 """ Disables editing functionality. 558 self.setEnabled(
False)
559 self.disconnect(self, SIGNAL(
'currentIndexChanged(int)'), self.
valueChanged)
561 self.setEnabled(
True)
562 self.connect(self, SIGNAL(
'currentIndexChanged(int)'), self.
valueChanged)
565 """ Returns True if check box is checked. 567 return self.
_values[self.currentIndex()]
571 QTextEdit.focusOutEvent(self,event)
572 self.emit(SIGNAL(
"editingFinished()"))
575 """ This class provides a PropertyView property holding an editable text and a button. 577 It is possible to hide the button unless the mouse cursor is over the property. This feature is turned on by default. See setAutohideButton(). 578 If the button is pressed nothing happens. This functionality should be implemented in sub-classes. See buttonClicked(). 579 The text field can hold single or multiple lines. See setMultiline() 583 AUTOHIDE_BUTTON =
True 585 def __init__(self, name, value, categoryName=None, multiline=False):
586 """ The constructor creates a QHBoxLayout and calls createLineEdit(), createTextEdit() and createButton(). 588 Property.__init__(self, name, categoryName)
589 QWidget.__init__(self)
595 self.setLayout(QHBoxLayout())
596 self.layout().setSpacing(0)
597 self.layout().setContentsMargins(0, 0, 0, 0)
609 """ Sets value of text edit. 613 strValue =
str(value)
619 self._lineEdit.setText(strValue)
620 self._textEdit.setText(strValue)
630 self._lineEdit.setToolTip(text)
631 self._textEdit.setToolTip(text)
634 """ Switch between single and multi line mode. 639 self._textEdit.show()
640 self._lineEdit.hide()
643 self._lineEdit.show()
644 self._textEdit.hide()
648 """ This function creates the signle line text field and adds it to the property's layout. 651 self._lineEdit.setFrame(
False)
653 self._lineEdit.setContentsMargins(0, 0, 0, 0)
654 self._lineEdit.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding))
658 """ This function creates the multi line text field and adds it to the property's layout. 661 self._textEdit.setWordWrapMode(QTextOption.NoWrap)
662 self._textEdit.setFrameStyle(QFrame.NoFrame)
664 self._textEdit.setContentsMargins(0, 0, 0, 0)
665 self._textEdit.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding))
669 """ Return the estimated height of the property. 671 The returned height covers the whole text, even if multiline. 674 self._textEdit.document().adjustSize()
675 height=self._textEdit.document().
size().height()+3
676 if self._textEdit.horizontalScrollBar().isVisible():
677 height+=self._textEdit.horizontalScrollBar().height()+3
680 return self.DEFAULT_HEIGHT
683 """ Returns line edit. 688 """ Returns text edit. 693 """ Creates a button and adds it to the property's layout. 695 self.
_button = QToolButton(self)
696 self._button.setText(self.BUTTON_LABEL)
697 self._button.setContentsMargins(0, 0, 0, 0)
699 self.layout().addWidget(self.
_button)
710 """ Returns True if the button has been created, otherwise False is returned. 715 """ Switch between readonly and editable. 735 """ Returns value of text edit. 738 return str(self._lineEdit.text().toAscii())
740 return str(self._textEdit.toPlainText().toAscii())
744 """ Returns the value of correct type (in case its not a string). 749 """ If hide is True the button will only be visible while the cursor is over the property. 755 This function is called if the button was clicked. For information on the checked argument see documentation of QPushButton::clicked(). 756 This function should be overwritten by sub-classes. 761 """ If autohideButtonFlag is set this function makes the button visible. See setAutohideButton(). 767 """ If autohideButtonFlag is set this function makes the button invisible. See setAutohideButton(). 773 """ Update tooltip and height when text is changed. 776 self.emit(SIGNAL(
'updatePropertyHeight'),self)
780 Property.valueChanged(self)
783 """ Highlight the property by changing the background color of the textfield. 787 p.setColor(QPalette.Active, QPalette.ColorRole(9),Qt.red)
789 p.setColor(QPalette.Active, QPalette.ColorRole(9),Qt.white)
790 self._lineEdit.setPalette(p)
791 self._textEdit.viewport().setPalette(p)
794 """ Switch back to the original value on ESC. 796 QWidget.keyPressEvent(self,event)
797 if event.key()==Qt.Key_Escape:
802 """ Property which holds an editable text. 804 A button is provided to switch between single and multi line mode. 808 USER_INFO =
"Text field" 810 AUTHIDE_BUTTON =
False 812 def __init__(self, name, value, categoryName=None, multiline=None):
814 TextEditWithButtonProperty.__init__(self, name, value, categoryName, (multiline
or str(value).
count(
"\n")>0))
817 TextEditWithButtonProperty.setMultiline(self,multiline)
818 icon = QIcon(
":/resources/editor.svg")
820 self._button.setIcon(icon)
821 self._button.setIconSize(QSize(15,15))
824 """ Switch to multiline mode if button is clicked. 830 textEdit=dialog.getText()
836 """ Property which hold editable integer numbers. 838 A Spinbox is provided when the property is editable. 841 USER_INFO =
"Integer field" 843 def __init__(self, name, value, categoryName=None):
846 Property.__init__(self, name, categoryName)
847 QWidget.__init__(self)
848 self.setLayout(QHBoxLayout())
849 self.layout().setSpacing(0)
850 self.layout().setContentsMargins(0, 0, 0, 0)
856 self._spinbox.setFrame(
False)
857 self.layout().addWidget(self.
_spinbox)
860 self._lineedit.setReadOnly(
True)
861 self._lineedit.setFrame(
False)
862 self._lineedit.setContentsMargins(0, 0, 0, 0)
864 self._lineedit.hide()
870 """ Switches between lineedit and spinbox. 874 self._lineedit.show()
878 self._lineedit.hide()
882 """ Returns integer value. 884 return self._spinbox.value()
888 self._spinbox.setValue(value % self.
maxint)
890 self._lineedit.setText(
str(value))
893 """ TextEditWithButtonProperty which holds float numbers. 896 USER_INFO =
"Double field" 898 AUTHIDE_BUTTON =
False 900 def __init__(self, name, value, categoryName=None):
903 TextEditWithButtonProperty.__init__(self, name, value, categoryName=
None)
906 """ Do not create a button.""" 910 if isinstance(object, float):
911 return "%.10g" % object
916 TextEditWithButtonProperty.setValue(self, self.
_toString(value))
919 """ Transform text to float and return. 922 return float(TextEditWithButtonProperty.value(self))
925 return float.fromhex(TextEditWithButtonProperty.value(self))
927 return ValueError(
"Entered value is not of type double.")
930 """ TextEditWithButtonProperty which holds file names. 932 A button for opening a dialog allowing to choose a file is provided. 935 USER_INFO =
"Select a file. Double click on label to open file." 938 def __init__(self, name, value, categoryName=None):
939 TextEditWithButtonProperty.__init__(self, name, value, categoryName)
943 """ Shows the file selection dialog. """ 950 dir=QCoreApplication.instance().getLastOpenLocation()
951 filename = QFileDialog.getSaveFileName(
957 QFileDialog.DontConfirmOverwrite)
958 if not filename.isEmpty():
959 filename=
str(filename)
964 self.
textEdit().emit(SIGNAL(
'editingFinished()'))
967 """ Open selected file in default application. 977 """ TextEditWithButtonProperty which holds file names. 979 A button for opening a dialog allowing to choose a list of files is provided. 982 USER_INFO =
"Edit list of files." 985 def __init__(self, name, value, categoryName=None):
986 TextEditWithButtonProperty.__init__(self, name, value, categoryName)
990 """ Shows the file selection dialog. """ 996 dir=QCoreApplication.instance().getLastOpenLocation()
997 fileList = QFileDialog.getOpenFileNames(
999 'Select a list of files',
1003 QFileDialog.DontConfirmOverwrite)
1004 fileNames=[
str(f)
for f
in fileList]
1012 fileNames=nfileNames
1013 if len(fileNames)>0:
1015 self.
textEdit().emit(SIGNAL(
'editingFinished()'))
1018 return self._updatingFlag>0
def __init__(self, name, value, categoryName=None)
def setShowAddDeleteButton(self, show)
def keyPressEvent(self, event)
def _toString(self, object)
def __init__(self, name, categoryName=None)
def __init__(self, argument, color=Qt.white)
def showAddDeleteButton(self)
def __init__(self, name, value, categoryName=None)
def itemDoubleClickedSlot(self, item)
def enterEvent(self, event)
def setChecked(self, check, report=True)
def setToolTip(self, text)
def setDeletable(self, deletable)
def resizeEvent(self, event)
def __init__(self, name, value, categoryName=None, multiline=None)
def updatePropertyHeight(self, property)
def sectionResized(self, index, old, new)
def leaveEvent(self, event)
def useRelativePaths(self, path)
S & print(S &os, JobReport::InputFile const &f)
def setMultiline(self, multi)
def setReadOnly(self, readOnly)
def closableProperty(self)
def __init__(self, name, value, categoryName=None, multiline=False)
def setReadOnly(self, readOnly)
def removeProperty(self, bool=False)
def labelDoubleClicked(self)
def buttonClicked(self, checked=False)
def enterEvent(self, event)
def useRelativePaths(self, path)
def addProperty(self, bool=False)
def setMultiline(self, multiline)
def valueChanged(self, property)
def leaveEvent(self, event)
def focusOutEvent(self, event)
def buttonClicked(self, checked=False)
def __init__(self, parent=None, name=None)
def setReadOnly(self, readOnly)
def setReadOnly(self, readOnly)
def setValue(self, value)
def setReadOnly(self, readOnly)
def setAutohideButton(self, hide)
def __init__(self, name, value, categoryName=None)
def propertyWidgetFromProperty(property, categoryName=None)
def setReadOnly(self, readOnly)
def createLineEdit(self, value=None)
def setValue(self, value)
def __init__(self, property)
def append(self, property)
def addCategory(self, name)
def __init__(self, name, value, values, categoryName=None)
def keyPressEvent(self, event)
def __init__(self, name, value, categoryName=None)
def setValue(self, value)
def createTextEdit(self, value=None)
def buttonClicked(self, checked=False)
def propertyWidgets(self)
def __init__(self, name, value, categoryName=None)
def setValue(self, value)
def setHighlighted(self, highlight)
def labelDoubleClicked(self)
def useRelativePaths(self, path)
def setPropertyView(self, propertyView)
def setHighlighted(self, highlight)
def setDataAccessor(self, accessor)
def setUserInfo(self, info)