1 from __future__
import print_function
2 from builtins
import range
8 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
19 QWidget.__init__(self)
20 self.setContentsMargins(0, 0, 0, 0)
21 self.setLayout(QHBoxLayout())
22 self.layout().setSpacing(0)
23 self.layout().setContentsMargins(0, 0, 0, 0)
24 self.layout().addWidget(property)
26 self._closeButton.setText(
"x")
27 self._closeButton.hide()
35 self._closeButton.show()
37 self._closeButton.hide()
41 QComboBox.keyPressEvent(self,event)
42 if event.key()==Qt.Key_Return:
43 self.emit(SIGNAL(
"returnPressed()"))
46 """ Shows properties of an object in a QTableWidget using the DataAccessor. 48 The view can be used readonly ('setReadOnly') or for editing. 49 On editing the signals 'valueChanged', 'propertyDeleted', 'propertyAdded' are emitted. 52 LABEL =
"&Property View" 57 AbstractView.__init__(self)
58 QTableWidget.__init__(self, parent)
65 self.setSortingEnabled(
False)
66 self.verticalHeader().hide()
67 self.setSelectionMode(QTableWidget.NoSelection)
73 self.connect(self.horizontalHeader(), SIGNAL(
"sectionResized(int,int,int)"), self.
sectionResized)
77 """ Stop all running operations. 82 """ Clear the table and set the header label. 84 QTableWidget.clear(self)
86 self.setColumnCount(2)
87 self.setHorizontalHeaderLabels([
'Property',
'Value'])
90 """ Return all property widgets in the right column. 92 Closable as well as normal properties are returned. 95 for i
in range(self.rowCount()):
96 widget=self.cellWidget(i,1)
97 if isinstance(widget,Property):
99 elif hasattr(widget,
"closableProperty"):
100 widgets+=[(widget.closableProperty(),i)]
104 """ Update the height of the column that holds a certain property. 109 self.verticalHeader().resizeSection(i, property.properyHeight())
113 """ Adds a property to the PropertyView and returns it. 115 property.setPropertyView(self)
117 property.setReadOnly(
True)
118 self.insertRow(self.
lastRow()+1)
122 self.connect(widget.closeButton(), SIGNAL(
'clicked(bool)'), self.
removeProperty)
123 self.setCellWidget(self.
lastRow(), 1, widget)
125 self.setCellWidget(self.
lastRow(), 1, property)
131 """ Return the last row holding a property. 133 The row with the add new property field is not counted. 136 return self.rowCount() - 2
138 return self.rowCount() - 1
141 """ Add a category row to the tabel which consists of two gray LabelItems. 143 self.insertRow(self.
lastRow()+1)
146 self.verticalHeader().resizeSection(self.rowCount() - 1, Property.DEFAULT_HEIGHT)
150 """ Sets all properties in the PropertyView to read-only. 152 After calling this function all properties that are added are set to read-only as well. 170 """ Resize columns when table size is changed. 173 QTableWidget.resizeEvent(self, event)
174 space = self.width() - 4
175 if self.verticalScrollBar().isVisible():
176 space -= self.verticalScrollBar().
width()
177 space -= self.columnWidth(0)
178 self.setColumnWidth(1, space)
183 space = self.width() - 4
184 if self.verticalScrollBar().isVisible():
185 space -= self.verticalScrollBar().
width()
186 space -= self.columnWidth(0)
187 self.setColumnWidth(1, space)
192 """ Sets the DataAccessor from which the object properties are read. 194 You need to call updateContent() in order to make the changes visible. 196 if not isinstance(accessor, BasicDataAccessor):
197 raise TypeError(__name__ +
" requires data accessor of type BasicDataAccessor.")
198 AbstractView.setDataAccessor(self, accessor)
201 """ Append a row with a field to add new properties. 203 self.insertRow(self.
lastRow()+1)
205 lineedit.setFrame(
False)
206 lineedit.setContentsMargins(0, 0, 0, 0)
207 self.setCellWidget(self.
lastRow(), 0, lineedit)
209 widget.setContentsMargins(0, 0, 0, 0)
210 widget.setLayout(QHBoxLayout())
211 widget.layout().setSpacing(0)
212 widget.layout().setContentsMargins(0, 0, 0, 0)
214 types=[
"String",
"Boolean",
"Integer",
"Double",
"File",
"FileVector"]
216 typelist.addItem(type)
217 widget.layout().addWidget(typelist)
218 addButton=QToolButton()
219 addButton.setText(
"+")
220 widget.layout().addWidget(addButton)
221 self.setCellWidget(self.
lastRow(), 1, widget)
222 self.verticalHeader().resizeSection(self.
lastRow(), Property.DEFAULT_HEIGHT)
223 self.connect(addButton, SIGNAL(
'clicked(bool)'), self.
addProperty)
224 self.connect(lineedit, SIGNAL(
'returnPressed()'), self.
addProperty)
225 self.connect(typelist, SIGNAL(
'returnPressed()'), self.
addProperty)
226 addButton._lineedit=lineedit
227 addButton._typelist=typelist
228 lineedit._lineedit=lineedit
229 lineedit._typelist=typelist
230 typelist._lineedit=lineedit
231 typelist._typelist=typelist
234 """ Fill the properties of an object in the PropertyView using the DataAccessor. 249 if property[0] ==
"Category":
254 self.
append(propertyWidget)
255 if isinstance(propertyWidget,(FileProperty,FileVectorProperty)):
257 if isinstance(propertyWidget,QCheckBox):
258 propertyWidget.setChecked(property[2],
False)
268 """ Create a property widget from a property tuple. 270 This function is static in order to be used by other view, e.g. TableView. 273 if property[0] ==
"String":
274 propertyWidget=
StringProperty(property[1], property[2], categoryName)
275 elif property[0] ==
"MultilineString":
276 propertyWidget=
StringProperty(property[1], property[2], categoryName,
True)
277 elif property[0] ==
"File":
278 propertyWidget=
FileProperty(property[1], property[2], categoryName)
279 elif property[0] ==
"FileVector":
281 elif property[0] ==
"Boolean":
282 propertyWidget =
BooleanProperty(property[1], property[2], categoryName)
283 elif property[0] ==
"Integer":
285 elif property[0] ==
"Double":
286 propertyWidget=
DoubleProperty(property[1], property[2], categoryName)
287 elif property[0] ==
"DropDown":
288 propertyWidget=
DropDownProperty(property[1], property[2], property[6], categoryName)
290 logging.error(__name__+
": propertyWidgetFromProperty() - Unknown property type "+
str(property[0]))
292 if len(property) > 3
and property[3]:
293 propertyWidget.setUserInfo(property[3])
294 if len(property) > 4
and property[4]:
295 propertyWidget.setReadOnly(
True)
296 if len(property) > 5
and property[5]:
297 propertyWidget.setDeletable(
True)
298 return propertyWidget
299 propertyWidgetFromProperty = staticmethod(propertyWidgetFromProperty)
302 """ This function is called when a property a changed. 304 The DataAcessor is called to handle the property change. 308 newvalue = property.value()
310 if newvalue != oldValue:
311 if isinstance(newvalue,ValueError):
314 result=self.
dataAccessor().setProperty(self.
dataObject(), property.name(), newvalue, property.categoryName())
316 self.emit(SIGNAL(
'valueChanged'),property.name(), newvalue, oldValue, property.categoryName())
318 print(
"valueChanged() result = ", result, type(result))
319 property.setToolTip(result)
320 QMessageBox.critical(self.parent(),
'Error', result)
322 property.setHighlighted(bad)
325 """ This function deletes a property. 327 The DataAcessor is called to handle the property remove. 329 property=self.sender().
parent()._property
336 self.emit(SIGNAL(
'propertyDeleted'),name)
339 """ This function adds a property. 341 The DataAcessor is called to add the property. 343 type=
str(self.sender()._typelist.currentText())
344 name=
str(self.sender()._lineedit.text().toAscii())
345 if type
in [
"String",
"File"]:
347 elif type
in [
"Integer",
"Double"]:
349 elif type
in [
"FileVector"]:
351 elif type
in [
"Boolean"]:
353 if name==
None or name==
"":
354 QCoreApplication.instance().infoMessage(
"Please specify name of property.")
361 if isinstance(property,(FileProperty,FileVectorProperty)):
363 self.sender()._lineedit.setText(
"")
365 self.emit(SIGNAL(
'propertyAdded'),property.name())
368 """ Slot for itemClicked() signal. 370 Calls items's property's doubleClicked(). 374 item.property().labelDoubleClicked()
380 """ A QTableWidgetItem with a convenient constructor. 385 Argument may be either a string or a Property object. 386 If argument is the latter the property's user info will be used for the label's tooltip. 388 if isinstance(argument, Property):
389 tooltip = argument.name() +
" (" + argument.userInfo() +
")" 390 name = argument.name()
397 QTableWidgetItem.__init__(self, name)
398 self.setToolTip(tooltip)
399 self.setFlags(Qt.ItemIsEnabled)
400 self.setBackgroundColor(color)
406 """ Mother of all properties which can be added to the PropertyView using its append() function. 409 USER_INFO =
"General property" 420 """ Sets the name of this property. 425 """ Return the name of this property. 439 """ Sets PropertyView object. 444 """ Returns property view. 449 """ Returns user info string containing information on type of property and what data may be insert. 454 """ Returns user info string containing information on type of property and what data may be insert. 459 """ Disables editing functionality. 464 """ Return the height of the property widget. 466 return self.DEFAULT_HEIGHT
469 """ Abstract function returning current value of this property. 471 Has to be implemented by properties which allow the user to change their value. 473 raise NotImplementedError
476 """ Abstract function returning current value of this property. 478 Has to be implemented by properties which allow the user to change their value. 480 raise NotImplementedError
483 """ Slot for change events. 485 The actual object which have changed should connect their value changed signal 486 (or similar) to this function to forward change to data accessor of PropertyView. 488 logging.debug(
'Property: valueChanged() ' +
str(self.
name()))
493 """ Called by PropertyView itemDoubleClicked(). 498 """ Highlight the property, e.g. change color. 502 class BooleanProperty(Property, QCheckBox):
503 """ Property holding a check box for boolean values. 506 USER_INFO =
"Enable / Disable" 508 def __init__(self, name, value, categoryName=None):
511 Property.__init__(self, name, categoryName)
512 QCheckBox.__init__(self)
513 self.connect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
517 self.disconnect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
518 QCheckBox.setChecked(self, check)
520 self.connect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
523 """ Disables editing functionality. 526 self.setEnabled(
False)
527 self.disconnect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
529 self.setEnabled(
True)
530 self.connect(self, SIGNAL(
'stateChanged(int)'), self.
valueChanged)
533 """ Returns True if check box is checked. 535 return self.isChecked()
538 """ Property holding a check box for boolean values. 541 USER_INFO =
"Drop down field" 543 def __init__(self, name, value, values, categoryName=None):
546 Property.__init__(self, name, categoryName)
547 QComboBox.__init__(self)
552 self.setCurrentIndex(values.index(value))
553 self.connect(self, SIGNAL(
'currentIndexChanged(int)'), self.
valueChanged)
556 """ Disables editing functionality. 559 self.setEnabled(
False)
560 self.disconnect(self, SIGNAL(
'currentIndexChanged(int)'), self.
valueChanged)
562 self.setEnabled(
True)
563 self.connect(self, SIGNAL(
'currentIndexChanged(int)'), self.
valueChanged)
566 """ Returns True if check box is checked. 568 return self.
_values[self.currentIndex()]
572 QTextEdit.focusOutEvent(self,event)
573 self.emit(SIGNAL(
"editingFinished()"))
576 """ This class provides a PropertyView property holding an editable text and a button. 578 It is possible to hide the button unless the mouse cursor is over the property. This feature is turned on by default. See setAutohideButton(). 579 If the button is pressed nothing happens. This functionality should be implemented in sub-classes. See buttonClicked(). 580 The text field can hold single or multiple lines. See setMultiline() 584 AUTOHIDE_BUTTON =
True 586 def __init__(self, name, value, categoryName=None, multiline=False):
587 """ The constructor creates a QHBoxLayout and calls createLineEdit(), createTextEdit() and createButton(). 589 Property.__init__(self, name, categoryName)
590 QWidget.__init__(self)
596 self.setLayout(QHBoxLayout())
597 self.layout().setSpacing(0)
598 self.layout().setContentsMargins(0, 0, 0, 0)
610 """ Sets value of text edit. 614 strValue =
str(value)
620 self._lineEdit.setText(strValue)
621 self._textEdit.setText(strValue)
631 self._lineEdit.setToolTip(text)
632 self._textEdit.setToolTip(text)
635 """ Switch between single and multi line mode. 640 self._textEdit.show()
641 self._lineEdit.hide()
644 self._lineEdit.show()
645 self._textEdit.hide()
649 """ This function creates the signle line text field and adds it to the property's layout. 652 self._lineEdit.setFrame(
False)
654 self._lineEdit.setContentsMargins(0, 0, 0, 0)
655 self._lineEdit.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding))
659 """ This function creates the multi line text field and adds it to the property's layout. 662 self._textEdit.setWordWrapMode(QTextOption.NoWrap)
663 self._textEdit.setFrameStyle(QFrame.NoFrame)
665 self._textEdit.setContentsMargins(0, 0, 0, 0)
666 self._textEdit.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding))
670 """ Return the estimated height of the property. 672 The returned height covers the whole text, even if multiline. 675 self._textEdit.document().adjustSize()
676 height=self._textEdit.document().
size().height()+3
677 if self._textEdit.horizontalScrollBar().isVisible():
678 height+=self._textEdit.horizontalScrollBar().height()+3
681 return self.DEFAULT_HEIGHT
684 """ Returns line edit. 689 """ Returns text edit. 694 """ Creates a button and adds it to the property's layout. 696 self.
_button = QToolButton(self)
697 self._button.setText(self.BUTTON_LABEL)
698 self._button.setContentsMargins(0, 0, 0, 0)
700 self.layout().addWidget(self.
_button)
711 """ Returns True if the button has been created, otherwise False is returned. 716 """ Switch between readonly and editable. 736 """ Returns value of text edit. 739 return str(self._lineEdit.text().toAscii())
741 return str(self._textEdit.toPlainText().toAscii())
745 """ Returns the value of correct type (in case its not a string). 750 """ If hide is True the button will only be visible while the cursor is over the property. 756 This function is called if the button was clicked. For information on the checked argument see documentation of QPushButton::clicked(). 757 This function should be overwritten by sub-classes. 762 """ If autohideButtonFlag is set this function makes the button visible. See setAutohideButton(). 768 """ If autohideButtonFlag is set this function makes the button invisible. See setAutohideButton(). 774 """ Update tooltip and height when text is changed. 777 self.emit(SIGNAL(
'updatePropertyHeight'),self)
781 Property.valueChanged(self)
784 """ Highlight the property by changing the background color of the textfield. 788 p.setColor(QPalette.Active, QPalette.ColorRole(9),Qt.red)
790 p.setColor(QPalette.Active, QPalette.ColorRole(9),Qt.white)
791 self._lineEdit.setPalette(p)
792 self._textEdit.viewport().setPalette(p)
795 """ Switch back to the original value on ESC. 797 QWidget.keyPressEvent(self,event)
798 if event.key()==Qt.Key_Escape:
803 """ Property which holds an editable text. 805 A button is provided to switch between single and multi line mode. 809 USER_INFO =
"Text field" 811 AUTHIDE_BUTTON =
False 813 def __init__(self, name, value, categoryName=None, multiline=None):
815 TextEditWithButtonProperty.__init__(self, name, value, categoryName, (multiline
or str(value).
count(
"\n")>0))
818 TextEditWithButtonProperty.setMultiline(self,multiline)
819 icon = QIcon(
":/resources/editor.svg")
821 self._button.setIcon(icon)
822 self._button.setIconSize(QSize(15,15))
825 """ Switch to multiline mode if button is clicked. 831 textEdit=dialog.getText()
837 """ Property which hold editable integer numbers. 839 A Spinbox is provided when the property is editable. 842 USER_INFO =
"Integer field" 844 def __init__(self, name, value, categoryName=None):
847 Property.__init__(self, name, categoryName)
848 QWidget.__init__(self)
849 self.setLayout(QHBoxLayout())
850 self.layout().setSpacing(0)
851 self.layout().setContentsMargins(0, 0, 0, 0)
857 self._spinbox.setFrame(
False)
858 self.layout().addWidget(self.
_spinbox)
861 self._lineedit.setReadOnly(
True)
862 self._lineedit.setFrame(
False)
863 self._lineedit.setContentsMargins(0, 0, 0, 0)
865 self._lineedit.hide()
871 """ Switches between lineedit and spinbox. 875 self._lineedit.show()
879 self._lineedit.hide()
883 """ Returns integer value. 885 return self._spinbox.value()
889 self._spinbox.setValue(value % self.
maxint)
891 self._lineedit.setText(
str(value))
894 """ TextEditWithButtonProperty which holds float numbers. 897 USER_INFO =
"Double field" 899 AUTHIDE_BUTTON =
False 901 def __init__(self, name, value, categoryName=None):
904 TextEditWithButtonProperty.__init__(self, name, value, categoryName=
None)
907 """ Do not create a button.""" 911 if isinstance(object, float):
912 return "%.10g" % object
917 TextEditWithButtonProperty.setValue(self, self.
_toString(value))
920 """ Transform text to float and return. 923 return float(TextEditWithButtonProperty.value(self))
926 return float.fromhex(TextEditWithButtonProperty.value(self))
928 return ValueError(
"Entered value is not of type double.")
931 """ TextEditWithButtonProperty which holds file names. 933 A button for opening a dialog allowing to choose a file is provided. 936 USER_INFO =
"Select a file. Double click on label to open file." 939 def __init__(self, name, value, categoryName=None):
940 TextEditWithButtonProperty.__init__(self, name, value, categoryName)
944 """ Shows the file selection dialog. """ 951 dir=QCoreApplication.instance().getLastOpenLocation()
952 filename = QFileDialog.getSaveFileName(
958 QFileDialog.DontConfirmOverwrite)
959 if not filename.isEmpty():
960 filename=
str(filename)
965 self.
textEdit().emit(SIGNAL(
'editingFinished()'))
968 """ Open selected file in default application. 978 """ TextEditWithButtonProperty which holds file names. 980 A button for opening a dialog allowing to choose a list of files is provided. 983 USER_INFO =
"Edit list of files." 986 def __init__(self, name, value, categoryName=None):
987 TextEditWithButtonProperty.__init__(self, name, value, categoryName)
991 """ Shows the file selection dialog. """ 997 dir=QCoreApplication.instance().getLastOpenLocation()
998 fileList = QFileDialog.getOpenFileNames(
1000 'Select a list of files',
1004 QFileDialog.DontConfirmOverwrite)
1005 fileNames=[
str(f)
for f
in fileList]
1013 fileNames=nfileNames
1014 if len(fileNames)>0:
1016 self.
textEdit().emit(SIGNAL(
'editingFinished()'))
1019 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)