summaryrefslogtreecommitdiff
path: root/lib/taurus/qt/qtgui/table/taurusvaluestable.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/taurus/qt/qtgui/table/taurusvaluestable.py')
-rw-r--r--lib/taurus/qt/qtgui/table/taurusvaluestable.py785
1 files changed, 785 insertions, 0 deletions
diff --git a/lib/taurus/qt/qtgui/table/taurusvaluestable.py b/lib/taurus/qt/qtgui/table/taurusvaluestable.py
new file mode 100644
index 00000000..ffcea9bd
--- /dev/null
+++ b/lib/taurus/qt/qtgui/table/taurusvaluestable.py
@@ -0,0 +1,785 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus
+##
+## http://taurus-scada.org
+##
+## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
+##
+## Taurus is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Taurus is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with Taurus. If not, see <http://www.gnu.org/licenses/>.
+##
+#############################################################################
+
+__all__ = ["TaurusValuesTable"]
+
+__docformat__ = 'restructuredtext'
+
+from taurus.external.qt import Qt
+import numpy
+
+import sys
+
+import taurus.core
+from taurus.core.taurusbasetypes import DataFormat
+from taurus.qt.qtgui.base import TaurusBaseWidget, TaurusBaseWritableWidget
+from taurus.qt.qtgui.display import TaurusLabel
+from taurus.qt.qtgui.resource import getThemeIcon, getThemePixmap
+from taurus.qt.qtgui.container import TaurusWidget
+from taurus.core.util.enumeration import Enumeration
+
+TableRWState = Enumeration("TableRWState", ("Read", "Write"))
+
+class TaurusValuesIOTableModel(Qt.QAbstractTableModel):
+ typeCastingMap = {'f':float, 'b':bool, 'u':int, 'i':int, 'S':str, 'U':unicode}
+ # Need to have an array
+ def __init__(self, size, parent=None):
+ Qt.QAbstractTableModel.__init__(self, parent)
+ self._rtabledata = []
+ self._wtabledata = []
+ self._rowCount = size [0]
+ self._columnCount = size[1]
+ self._modifiedDict = {}
+ self._attr = None
+ self._attrConfig = None
+ self.editedIndex = None
+ self._editable = False
+ self._writeMode = False
+
+ def isDirty(self):
+ '''returns True if there are user changes. False Otherwise'''
+ return bool(self._modifiedDict)
+
+ #To be implemented -----
+ def rowCount(self, index = Qt.QModelIndex()):
+ '''see :meth:`Qt.QAbstractTableModel.rowCount`'''
+ if self._rowCount == 0:
+ self._rowCount = 1
+ return self._rowCount
+
+ def columnCount(self, index = Qt.QModelIndex()):
+ '''see :meth:`Qt.QAbstractTableModel.columnCount`'''
+ if self._columnCount == 0:
+ self._columnCount = 1
+ return self._columnCount
+
+ def data(self, index, role = Qt.Qt.DisplayRole):
+ '''see :meth:`Qt.QAbstractTableModel.data`'''
+ if self._writeMode == False:
+ tabledata = self._rtabledata
+ else:
+ tabledata = self._wtabledata
+ if not index.isValid() or not (0 <=index.row() < len(tabledata)):
+ return Qt.QVariant()
+ elif role == Qt.Qt.DisplayRole:
+ value = None
+ rc = (index.row(), index.column())
+ if self._writeMode and rc in self._modifiedDict:
+ return self._modifiedDict[rc]
+ else:
+ value = tabledata[rc]
+ #cast the value to a standard python type
+ value = self.typeCastingMap[tabledata.dtype.kind](value)
+ return Qt.QVariant(value)
+ elif role == Qt.Qt.DecorationRole:
+ status = self.getStatus(index)
+ if (self._modifiedDict.has_key((index.row(), index.column()))) and (self._writeMode):
+ float_data = Qt.from_qvariant(self._modifiedDict[(index.row(), index.column())], float)
+ if self.inAlarmRange(float_data):
+ icon = getThemeIcon('document-save')
+ #return Qt.QVariant(Qt.QColor('blue'))
+ else:
+ icon = getThemeIcon('emblem-important')
+ #return Qt.QVariant(Qt.QColor('orange'))
+ return Qt.QVariant(icon)
+ elif role == Qt.Qt.EditRole:
+ value = None
+ if self._modifiedDict.has_key((index.row(), index.column())) and (self._writeMode):
+ value = self._modifiedDict[(index.row(), index.column())]
+ else:
+ value = tabledata[index.row(),index.column()]
+ if tabledata.dtype == bool:
+ value = bool(value)
+ return Qt.QVariant(value)
+ elif role == Qt.Qt.BackgroundRole:
+ if self._writeMode:
+ return Qt.QVariant(Qt.QColor(22,223,21,50))
+ else:
+ return Qt.QVariant(Qt.QColor('white'))
+ elif role == Qt.Qt.ForegroundRole:
+ if self._modifiedDict.has_key((index.row(), index.column())) and (self._writeMode):
+ float_data = Qt.from_qvariant(self._modifiedDict[(index.row(), index.column())], float)
+ if self.inAlarmRange(float_data):
+ return Qt.QVariant(Qt.QColor('blue'))
+ else:
+ return Qt.QVariant(Qt.QColor('orange'))
+ return Qt.QVariant(Qt.QColor('black'))
+ elif role == Qt.Qt.FontRole:
+ if self._modifiedDict.has_key((index.row(), index.column())) and (self._writeMode):
+ return Qt.QVariant(Qt.QFont("Arial", 10, Qt.QFont.Bold))
+ elif role == Qt.Qt.ToolTipRole:
+ if self._modifiedDict.has_key((index.row(), index.column())) and (self._writeMode):
+ float_data = Qt.from_qvariant(self._modifiedDict[index.row(), index.column()], float)
+ return Qt.QVariant('Original value: %d.\nNew value that will be saved: %d'
+ %(tabledata[index.row(), index.column()], float_data))
+ return Qt.QVariant()
+
+ def setAttr(self, attr):
+ '''
+ Updated the internal table data from an attribute value
+
+ :param attr: (DeviceAttribute)
+ '''
+ self._attr = attr
+ values = numpy.array(attr.value)
+ wvalues = numpy.array(attr.w_value)
+ #reshape the table
+ if attr.data_format == DataFormat._1D:
+ rows, columns = values.size, 1
+ elif attr.data_format == DataFormat._2D:
+ rows, columns = values.shape
+ else:
+ raise TypeError('Unsupported data format "%s"'%repr(attr.data_format))
+
+ if (self._rowCount != rows) or (self._columnCount != columns):
+ self.reset()
+
+ self._rowCount = rows
+ self._columnCount = columns
+ values = values.reshape(rows,columns) #make sure it is in matrix form (not a vector)
+ self._rtabledata = values
+ self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),self.createIndex(0,0), self.createIndex(rows-1,columns-1))
+
+ def setConfig(self, config):
+ '''
+ Handles configuration events
+
+ :param attr: (TaurusConfiguration)
+ '''
+ self._attrConfig = config
+ self._editable = config.isWritable()
+
+ def getConfig(self):
+ '''
+ Returns the configuration object for the data
+
+ :returns: (taurus.core.taurusconfiguration.TaurusConfiguration)
+ '''
+ return self._attrConfig
+
+ def getStatus(self, index):
+ '''
+ Returns Status of the variable
+
+ :returns: (taurus.core.taurusbasetypes.AttrQuality)
+ '''
+ return self._attr.quality
+
+ def getType(self):
+ '''
+ Returns the table data type.
+
+ :returns: (numpy.dtype)
+ '''
+ return self._rtabledata.dtype
+
+ def addValue(self, index, value):
+ '''adds a value to the dictionary of modified cell values
+
+ :param index: (QModelIndex) table index
+ :param value: (object)
+ '''
+ self._modifiedDict[(index.row(), index.column())] = value
+
+ def removeValue(self, index):
+ '''
+ Removes index from dictionary
+
+ :param index: (QModelIndex) table index
+ '''
+ if self._modifiedDict.has_key((index.row(), index.column())):
+ self._modifiedDict.pop((index.row(), index.column()))
+
+ def flags(self, index):
+ '''see :meth:`Qt.QAbstractTableModel`'''
+ if not index.isValid():
+ return Qt.Qt.ItemIsEnabled
+ if self._editable:
+ return Qt.Qt.ItemFlags(Qt.Qt.ItemIsEnabled | Qt.Qt.ItemIsEditable | Qt.Qt.ItemIsSelectable)
+ else:
+ return Qt.Qt.ItemFlags(Qt.Qt.ItemIsEnabled | Qt.Qt.ItemIsSelectable)
+
+ def getModifiedWriteData(self):
+ '''
+ returns an array for the write data that includes the user modifications
+
+ :return: (numpy.array) The write values including user modifications.
+ '''
+ table = self._wtabledata
+ kind = table.dtype.kind
+ if kind in 'SU':
+ table = table.tolist() #we want to allow the strings to be larger than the original ones
+ for (r,c),v in self._modifiedDict.items():
+ table[r][c] = Qt.from_qvariant(v, str)
+ table = numpy.array(table)
+ else:
+ for k,v in self._modifiedDict.items():
+ if kind == 'f':
+ table[k] = Qt.from_qvariant(v, float)
+ elif kind in 'iu':
+ table[k] = Qt.from_qvariant(v, int)
+ elif kind == 'b':
+ table[k] = Qt.from_qvariant(v, bool)
+ else:
+ raise TypeError('Unknown data type "%s"'%kind)
+ #reshape if needed
+ if self._attr.data_format == DataFormat._1D:
+ table = table.flatten()
+ return table
+
+ def clearChanges(self):
+ '''clears the dictionary of changed values'''
+ self._modifiedDict.clear()
+ self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),self.createIndex(0,0), self.createIndex(self.rowCount()-1,self.columnCount()-1))
+
+ def inAlarmRange(self, value):
+ '''
+ Checkes if value is in alarm range.
+
+ :param value: (float/int) user entered value
+ :returns: (bool) True if value in alarm range, False if valid
+
+ '''
+ try:
+ if float(self._attrConfig.getMinAlarm()) <= value <= float(self._attrConfig.getMaxAlarm()):
+ return True
+ else:
+ return False
+ except:
+ return True
+
+ def inRange(self, value):
+ '''
+ Checks if value is in range.
+
+ :param value: (float/int) user entered value
+ :returns: (bool) True if value in range, False if valid
+
+ '''
+ try:
+ if float(self._attrConfig.getMinValue()) <= value <= float(self._attrConfig.getMaxValue()):
+ return True
+ else:
+ return False
+ except:
+ return True
+
+ def setWriteMode(self, isWrite):
+ '''Changes the write state
+
+ :param isWrite: (bool)
+ '''
+ self._writeMode = isWrite
+ if isWrite and not self.isDirty():
+ #refresh the write data (unless it is dirty)
+ wvalues=numpy.array(self._attr.w_value)
+ #reshape the table
+ if self._attr.data_format == DataFormat._1D:
+ rows, columns = wvalues.size, 1
+ elif self._attr.data_format == DataFormat._2D:
+ rows, columns = wvalues.shape
+ else:
+ self.warning('unsupported data format %s'%str(val.data_format))
+ wvalues = wvalues.reshape(rows,columns)
+ #In version 4.6 of Qt when whole table is updated it is recommended to use beginReset()
+ self._wtabledata = wvalues
+ self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),self.createIndex(0,0), self.createIndex(self.rowCount()-1,self.columnCount()-1))
+
+ def getModifiedDict(self):
+ '''
+ Returns dictionary.
+
+ :returns: (dictionary) dictionary containing modified indexes and values
+ '''
+ return self._modifiedDict
+
+ def getReadValue(self, index):
+ '''
+ Returns read value for a given index.
+
+ :param index: (QModelIndex) table model index
+ :returns: (string/int/float/bool) read table value for a given cell
+ '''
+ return self._rtabledata[index.row(), index.column()]
+
+
+class TaurusValuesIOTable(Qt.QTableView):
+ def __init__(self, parent = None):
+ name = self.__class__.__name__
+ Qt.QTableView.__init__(self, parent)
+ self._showQuality = True
+ self._attr = None
+ self._value = None
+ self.setSelectionMode(Qt.QAbstractItemView.SingleSelection)
+ self.horizontalHeader().setResizeMode(Qt.QHeaderView.Stretch)
+ itemDelegate = TaurusValuesIOTableDelegate(self)
+ self.setItemDelegate(itemDelegate)
+ #self.setStyleSheet('TaurusValuesIOTable { selection-background-color: violet;} ')
+
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # TaurusBaseWidget overwriting
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+
+ def setModel(self,shape):
+ '''
+ Creates an instance of QTableModel and sets a QT model.
+
+ :param shape: (tuple<int>) shape of the model table to be set
+
+ '''
+ qmodel = TaurusValuesIOTableModel(shape, parent=self)
+ Qt.QTableView.setModel(self, qmodel)
+
+ def cancelChanges(self):
+ '''
+ Cancels all table modifications.
+ '''
+ self.model().clearChanges()
+
+ def removeChange(self):
+ '''
+ If the cell was modified it restores the original write value.
+ '''
+ self.model().removeValue(self.selectedIndexes()[0])
+
+ def showHelp(self):
+ '''
+ Shows QMessageBox help window. It contains explanations of used icons.
+ '''
+ buttonBox = Qt.QMessageBox(self)
+ buttonBox.setLayout(Qt.QGridLayout(self))
+ icon = getThemePixmap('document-save')
+ l = Qt.QLabel()
+ l.setPixmap(icon)
+ buttonBox.layout().addWidget(l,0,0)
+ buttonBox.layout().addWidget(Qt.QLabel('- value is valid and will be saved if changes will be accepted'),0,1)
+ icon = getThemePixmap('dialog-warning')
+ l = Qt.QLabel()
+ l.setPixmap(icon)
+ buttonBox.layout().addWidget(l,1,0)
+ buttonBox.layout().addWidget(Qt.QLabel('- value is in alarm range and will be saved if changes will be accepted'),1,1)
+ buttonBox.exec_()
+
+
+class TaurusValuesIOTableDelegate(Qt.QStyledItemDelegate):
+
+ def __init__(self, parent = None):
+ Qt.QStyledItemDelegate.__init__(self, parent)
+ self._initialText = ""
+
+ def createEditor(self,parent,option,index):
+ '''
+ Creates a custom editor for a table delagate.
+
+ see :meth:`Qt.QStyledItemDelegate.createEditor`
+ '''
+ if index.model().getType() == bool:
+ #editor = Qt.QStyledItemDelegate.createEditor(self,parent,option,index)
+ editor = Qt.QComboBox(parent)
+ else:
+ editor = TableInlineEdit(parent)
+ editor._updateValidator(index.model().getConfig(), index.model().getType())
+ self.emit(Qt.SIGNAL('editorCreated'))
+ return editor
+
+ def setEditorData(self, editor, index):
+ '''
+ see :meth:`Qt.QStyledItemDelegate.setEditorData`
+ '''
+ if index.model().editedIndex == (index.row(), index.column()):
+ return
+ index.model().editedIndex = (index.row(), index.column())
+ self._initialText = None
+ if index.model().getType() == bool:
+ editor.addItems(['true', 'false'])
+ a = str(Qt.from_qvariant(index.data(), bool)).lower()
+ self._initialText = a
+ editor.setCurrentIndex(editor.findText(a))
+ else:
+ data = index.model().data(index, Qt.Qt.EditRole)
+ self._initialText = Qt.from_qvariant(data, str)
+ editor.setText(str(self._initialText))
+
+ def setModelData(self, editor, model,index):
+ '''
+ see :meth:`Qt.QStyledItemDelegate.setModelData`
+ '''
+ #if editor text changed, then don't mark as updated.
+ try:
+ if not model.inRange(float(editor.text())):
+ return
+ except:
+ pass
+ if index.model().getType() == bool:
+ text = editor.currentText()
+ else:
+ text = editor.text()
+
+ if(text != self._initialText) & (text != ""):
+ model.addValue(index, Qt.QVariant(text))
+ self.parent().resizeColumnsToContents()
+ index.model().editedIndex = None
+
+
+class TableInlineEdit(Qt.QLineEdit):
+ '''
+ TableInLineEdit is used to validate the content of the new value, also to paint the text: blue - valid, orange - in alarm, grey - invalid.
+ '''
+ def __init__(self, parent = None):
+ super(Qt.QLineEdit, self).__init__(parent)
+ self.connect(self, Qt.SIGNAL('textEdited(const QString &)'), self.textEdited)
+ self.connect(self, Qt.SIGNAL('focusLost(const QString &)'), self.textEdited)
+ self._attrConfig = None
+ self.__minAlarm = -float("inf")
+ self.__maxAlarm = float("inf")
+ self.__minLimit = -float("inf")
+ self.__maxLimit = float("inf")
+ self.setValidator(None)
+
+ def textEdited (self):
+ '''
+ Paints the text while typing.
+
+ see :meth:`Qt.QLineEdit.textEdited`
+ '''
+ color, weight = 'gray', 'normal' #default case: the value is in normal range with no pending changes
+ try:
+ v = float(self.displayText())
+ except:
+ v = 0.0
+ try:
+ if float(self._attrConfig.getMinAlarm()) <= v <= float(self._attrConfig.getMaxAlarm()): #the value is invalid and can't be applied
+ color = 'blue'
+ elif float(self._attrConfig.getMinValue()) <= v <= float(self._attrConfig.getMaxValue()): #the value is valid but in alarm range...
+ color = 'orange'
+ except:
+ color = 'orange'
+
+ weight = 'bold'
+ self.setStyleSheet('TableInlineEdit {color: %s; font-weight: %s}'%(color,weight))
+
+
+ def _updateValidator(self, attrinfo, datatype):
+ '''This method sets a validator depending on the data type
+
+ :param attrinfo: (AttributeInfoEx)
+ :datatype: (numpy.dtype) type of the data being edited
+ '''
+ self._attrConfig = attrinfo
+ if numpy.issubdtype(datatype, int):
+ validator = Qt.QIntValidator(self) #initial range is -2147483648 to 2147483647 (and cannot be set larger)
+ if validator.bottom() < self.__minLimit < validator.top():
+ validator.setBottom(int(self.__minLimit))
+ if validator.bottom() < self.__maxLimit < validator.top():
+ validator.setTop(int(self.__maxLimit))
+ self.setValidator(validator)
+ elif numpy.issubdtype(datatype, float):
+ validator= Qt.QDoubleValidator(self)
+ validator.setBottom(self.__minLimit)
+ validator.setTop(self.__maxLimit)
+ self.setValidator(validator)
+ else:
+ self.setValidator(None)
+
+ def __decimalDigits(self, fmt):
+ '''returns the number of decimal digits from a format string
+ (or None if they are not defined)'''
+ try:
+ if fmt[-1].lower() in ['f','g'] and '.' in fmt:
+ return int(fmt[:-1].split('.')[-1])
+ else:
+ return None
+ except:
+ return None
+
+
+class TaurusValuesTable(TaurusWidget):
+ '''
+ A table for displaying and/or editing 1D/2D Taurus attributes
+ '''
+ _showQuality = False
+ _writeMode = False
+
+ def __init__(self, parent = None, designMode = False, defaultWriteMode=None):
+ TaurusWidget.__init__(self, parent = parent, designMode = designMode)
+ self._tableView = TaurusValuesIOTable()
+ l = Qt.QGridLayout()
+ l.addWidget(self._tableView,1,0)
+ self.connect(self._tableView.itemDelegate(), Qt.SIGNAL("editorCreated"), self._onEditorCreated)
+
+ if defaultWriteMode is None:
+ self.defaultWriteMode = self._writeMode
+ else:
+ self.defaultWriteMode = defaultWriteMode
+
+ self._label = TaurusLabel()
+ self._label.setBgRole('quality')
+ self._label.setFgRole('quality')
+
+ self._applyBT = Qt.QPushButton('Apply')
+ self._cancelBT = Qt.QPushButton('Cancel')
+ self.connect(self._applyBT,Qt.SIGNAL("clicked()"),self.okClicked)
+ self.connect(self._cancelBT,Qt.SIGNAL("clicked()"),self.cancelClicked)
+
+ self._rwModeCB = Qt.QCheckBox()
+ self._rwModeCB.setText('Write mode')
+ self.connect(self._rwModeCB, Qt.SIGNAL("toggled(bool)"),self.setWriteMode)
+
+ l.addWidget(self._label,2,0)
+ l.addWidget(self._rwModeCB,0,0)
+ lv = Qt.QHBoxLayout()
+ lv.addWidget(self._applyBT)
+ lv.addWidget(self._cancelBT)
+ l.addLayout(lv,3,0)
+ self.setLayout(l)
+ self._initActions()
+
+ def _initActions(self):
+ """Initializes the actions for this widget (currently, the pause action.)
+ """
+ self._pauseAction = Qt.QAction("&Pause", self)
+ self._pauseAction.setShortcuts([Qt.Qt.Key_P,Qt.Qt.Key_Pause])
+ self._pauseAction.setCheckable(True)
+ self._pauseAction.setChecked(False)
+ self.addAction(self._pauseAction)
+ self.connect(self._pauseAction, Qt.SIGNAL("toggled(bool)"), self.setPaused)
+ self.chooseModelAction = Qt.QAction("Choose &Model", self)
+ self.chooseModelAction.setEnabled(self.isModifiableByUser())
+ self.addAction(self.chooseModelAction)
+ self.connect(self.chooseModelAction, Qt.SIGNAL("triggered()"), self.chooseModel)
+
+ def getModelClass(self):
+ '''see :meth:`TaurusWidget.getModelClass`'''
+ return taurus.core.taurusattribute.TaurusAttribute
+
+ def setModel(self, model):
+ '''Reimplemented from :meth:`TaurusWidget.setModel`'''
+ TaurusWidget.setModel(self, model)
+ value = self.getModelValueObj()
+ if value is not None:
+ try:
+ dim_x,dim_y = value.dim_x, value.dim_y #@this is tango-centric. dim_x and dim_y attribute is not present in TaurusConfiguration
+ except:
+ v = numpy.array(value.value)
+ if v.ndim == 1:
+ dim_x, dim_y = v.shape[0], 1
+ elif v.ndim == 2:
+ dim_x,dim_y = v.shape
+ else:
+ self.error('Cannot display %i-dimensional data', v.ndim)
+ return
+ self._tableView.setModel([dim_x, dim_y])
+ self._label.setModel(model)
+
+ def handleEvent(self, evt_src, evt_type, evt_value):
+ '''see :meth:`TaurusWidget.handleEvent`'''
+ #@fixme: in some situations, we may miss some config event because of the qmodel not being set. The whole handleEvent Method and setModel method should be re-thought
+ model = self._tableView.model()
+ if model is None:
+ return
+ if evt_type in (taurus.core.taurusbasetypes.TaurusEventType.Change, taurus.core.taurusbasetypes.TaurusEventType.Periodic) and evt_value is not None:
+ model.setAttr(evt_value)
+ self._tableView.resizeColumnsToContents()
+ elif evt_type == taurus.core.taurusbasetypes.TaurusEventType.Config:
+ #force a read to set an attr
+ model.setAttr(self.getModelValueObj())
+ model.setConfig(evt_src)
+ writable = bool(evt_value.writable)
+ self.resetWriteMode()
+ self._rwModeCB.setVisible(writable)
+
+ def contextMenuEvent(self, event):
+ '''Reimplemented from :meth:`QWidget.contextMenuEvent`'''
+ menu = Qt.QMenu()
+ globalPos = event.globalPos()
+ menu.addAction(self.chooseModelAction)
+ menu.addAction(self._pauseAction)
+ if self._writeMode:
+ index = self._tableView.selectedIndexes()[0]
+ if index.isValid():
+ val = self._tableView.model().getReadValue(index)
+ if self._tableView.model().getModifiedDict().has_key((index.row(), index.column())):
+ menu.addAction(getThemeIcon('edit-undo'),"Reset to original value (%s) "%repr(val), self._tableView.removeChange)
+ menu.addSeparator()
+ menu.addAction(getThemeIcon('process-stop'), "Reset all table", self.askCancel)
+ menu.addSeparator()
+ menu.addAction(getThemeIcon('help-browser') ,"Help", self._tableView.showHelp)
+ menu.exec_(globalPos)
+ event.accept()
+
+ def applyChanges(self):
+ '''
+ Writes table modifications to the device server.
+ '''
+ tab = self._tableView.model().getModifiedWriteData()
+ attr = self.getModelObj()
+ #attr.write(tab)
+ attr.write(tab.tolist()) #@fixme If I don't convert this to a list it segfaults when writing arrays of strings
+ self._tableView.model().clearChanges()
+
+
+ def okClicked(self):
+ """This is a SLOT that is being triggered when ACCEPT button is clicked.
+
+ .. note:: This SLOT is called, when user wants to apply table modifications.
+ When no cell was modified it will not be called. When
+ modifications have been done, they will be writen to w_value
+ of an attribute.
+ """
+ if self._tableView.model().isDirty():
+ self.applyChanges()
+ self.resetWriteMode()
+
+ def cancelClicked(self):
+ """This is a SLOT that is being triggered when CANCEL button is clicked.
+
+ .. note:: This SLOT is called, when user does not want to apply table
+ modifications. When no cell was modified it will not be called.
+ """
+ if self._tableView.model().isDirty():
+ self.askCancel()
+
+ def askCancel(self):
+ '''
+ Shows a QMessageBox, asking if user wants to cancel all changes. Triggered when user clicks Cancel button.
+ '''
+ result = Qt.QMessageBox.warning(self,'Your changes will be lost!',
+ 'Do you want to cancel changes done to the whole table?',
+ Qt.QMessageBox.Ok | Qt.QMessageBox.Cancel)
+ if result == Qt.QMessageBox.Ok:
+ self._tableView.cancelChanges()
+ self.resetWriteMode()
+
+ def _onEditorCreated(self):
+ '''slot called when an editor has been created'''
+ self.setWriteMode(True)
+
+ def getWriteMode(self):
+ '''whether the widget is showing the read or write values
+
+ :return: (bool)'''
+ return self._writeMode
+
+ def setWriteMode(self, isWrite):
+ '''
+ Triggered when the read mode is changed to write mode.
+
+ :param isWrite: (bool)
+ '''
+ if isWrite == self._writeMode: return
+ self._writeMode = isWrite
+
+ if isWrite:
+ valueObj = self.getModelValueObj()
+ w_value = valueObj.w_value
+ value = valueObj.value
+ if numpy.array(w_value).shape != numpy.array(value).shape:
+ ta = self.getModelObj()
+ v = ta.read()
+ ta.write(v.value) #@fixme: this is ugly! we should not be writing into the attribute without asking first...
+
+ self._tableView.model().setWriteMode(isWrite)
+ self._label.setVisible(isWrite)
+ self._applyBT.setVisible(isWrite)
+ self._cancelBT.setVisible(isWrite)
+ self._rwModeCB.setChecked(isWrite)
+
+ def resetWriteMode(self):
+ '''equivalent to self.setWriteMode(self.defaultWriteMode)'''
+ self.setWriteMode(self.defaultWriteMode)
+
+ @classmethod
+ def getQtDesignerPluginInfo(cls):
+ '''Reimplemented from :meth:`TaurusWidget.getQtDesignerPluginInfo`'''
+ ret = TaurusWidget.getQtDesignerPluginInfo()
+ ret['module'] = 'taurus.qt.qtgui.table'
+ ret['group'] = 'Taurus Views'
+ ret['icon'] = ":/designer/table.png"
+ return ret
+
+ def chooseModel(self):
+ '''shows a model chooser'''
+ from taurus.qt.qtgui.panel import TaurusModelChooser
+ selectables=[taurus.core.taurusbasetypes.TaurusElementType.Attribute]
+ models, ok = TaurusModelChooser.modelChooserDlg(selectables=selectables, singleModel=True)
+ if ok and len(models)==1:
+ self.setModel(models[0])
+
+ def setModifiableByUser(self, modifiable):
+ '''Reimplemented from :meth:`TaurusWidget.setModifiableByUser`'''
+ self.chooseModelAction.setEnabled(modifiable)
+ TaurusWidget.setModifiableByUser(self, modifiable)
+
+ def isReadOnly(self):
+ '''Reimplemented from :meth:`TaurusWidget.isReadOnly`'''
+ return False
+
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # QT property definition
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+
+ model = Qt.pyqtProperty("QString", TaurusWidget.getModel,
+ setModel,
+ TaurusWidget.resetModel)
+ writeMode = Qt.pyqtProperty("bool", getWriteMode, setWriteMode, resetWriteMode)
+
+
+def taurusTableMain():
+ '''A launcher for TaurusValuesTable.'''
+
+ from taurus.qt.qtgui.application import TaurusApplication
+ from taurus.core.util import argparse
+ import sys, os
+
+ parser = argparse.get_taurus_parser()
+ parser.set_usage("%prog [options] [model]]")
+ parser.set_description("A table for viewing and editing 1D and 2D attribute values")
+ app = TaurusApplication(cmd_line_parser=parser,
+ app_name="TaurusValuesTable",
+ app_version=taurus.Release.version)
+ args = app.get_command_line_args()
+
+ dialog = TaurusValuesTable()
+ dialog.setModifiableByUser(True)
+ dialog.setWindowTitle(app.applicationName())
+
+ #set a model list from the command line or launch the chooser
+ if len(args)==1:
+ model=args[0]
+ dialog.setModel(model)
+ else:
+ dialog.chooseModel()
+ #model = 'sys/tg_test/1/string_spectrum'
+ #model = 'sys/tg_test/1/wave'
+ #dialog.setModel(model)
+
+ dialog.show()
+ sys.exit(app.exec_())
+
+if __name__ == '__main__':
+ taurusTableMain()
+
+
+