[taurus] 01/01: Imported Upstream version 3.0.1~beta1
Frédéric-Emmanuel Picca
picca at moszumanska.debian.org
Wed Aug 6 12:47:17 UTC 2014
This is an automated email from the git hooks/post-receive script.
picca pushed a commit to annotated tag upstream/3.0.1_beta1
in repository taurus.
commit 5612216e2c4a35b9fe078283ea196e1379c46195
Author: Frédéric-Emmanuel PICCA <picca at synchrotron-soleil.fr>
Date: Thu Jan 17 12:01:30 2013 +0100
Imported Upstream version 3.0.1~beta1
---
LICENSE.txt | 2 +-
PKG-INFO | 2 +-
doc/source/devel/coding_guide.rst | 82 +-
doc/source/devel/color_guide.rst | 4 +-
doc/source/devel/examples.rst | 10 +-
doc/source/devel/examples/edit01.py | 2 +-
doc/source/devel/examples/edit02.py | 4 +-
doc/source/devel/examples/edit03.py | 4 +-
doc/source/devel/examples/forms01.py | 4 +-
doc/source/devel/examples/forms02.py | 11 +-
doc/source/devel/examples/label01.py | 2 +-
doc/source/devel/examples/label02.py | 2 +-
doc/source/devel/examples/label03.py | 2 +-
doc/source/devel/examples/label04.py | 4 +-
doc/source/devel/examples/label05.py | 4 +-
doc/source/devel/examples/label06.py | 4 +-
.../devel/examples/parentmodel_issue_demo.py | 2 +-
doc/source/devel/examples/pyqwt_issue_test.py | 8 +-
doc/source/devel/examples/taurusplot01.py | 4 +-
doc/source/devel/examples/taurusplot02.py | 4 +-
doc/source/devel/examples/taurusplot03.py | 6 +-
doc/source/devel/examples/taurustrend01.py | 4 +-
doc/source/devel/examples/taurusvalue01.py | 2 +-
doc/source/devel/icon_example.py | 2 +-
doc/source/users/ui/experimentconfiguration.rst | 15 +
doc/source/users/ui/sardanaeditor.rst | 15 +
doc/source/users/ui/taurusgui.rst | 11 +-
lib/taurus/core/epics/__init__.py | 69 +
lib/taurus/core/epics/epicsfactory.py | 725 ++++++++
lib/taurus/core/evaluation/evalfactory.py | 96 +-
lib/taurus/core/evaluation/ipap_example.py | 2 +-
lib/taurus/core/release.py | 2 +-
lib/taurus/core/tango/sardana/macroserver.py | 417 +++--
lib/taurus/core/tango/sardana/motion.py | 48 +-
lib/taurus/core/tango/sardana/pool.py | 713 +++++---
lib/taurus/core/tango/sardana/sardana.py | 278 +--
lib/taurus/core/tango/search.py | 123 ++
lib/taurus/core/tango/tangoattribute.py | 5 +-
lib/taurus/core/tango/tangodatabase.py | 11 +-
lib/taurus/core/tango/tangodevice.py | 30 +-
lib/taurus/core/taurusattribute.py | 11 +-
lib/taurus/core/taurusconfiguration.py | 16 +-
lib/taurus/core/taurusdevice.py | 73 +-
lib/taurus/core/taurusexception.py | 10 +-
lib/taurus/core/taurushelper.py | 30 +-
lib/taurus/core/taurusmanager.py | 2 +-
lib/taurus/core/taurusoperation.py | 36 +-
lib/taurus/core/util/argparse/taurusargparse.py | 112 +-
lib/taurus/core/util/codecs.py | 169 +-
lib/taurus/core/util/containers.py | 74 +-
.../qtable.py => core/util/decorator/memoize.py} | 62 +-
lib/taurus/core/util/log.py | 359 ++--
lib/taurus/core/util/remotelogmonitor.py | 226 +++
lib/taurus/core/util/safeeval.py | 4 +-
lib/taurus/core/util/wrap.py | 64 +
lib/taurus/qt/Qt.py | 111 ++
lib/taurus/qt/{QtSvg.py => QtDesigner.py} | 4 +-
lib/taurus/qt/{QtSvg.py => QtNetwork.py} | 14 +-
lib/taurus/qt/QtSvg.py | 10 +-
lib/taurus/qt/{QtSvg.py => QtWebKit.py} | 14 +-
lib/taurus/qt/{QtSvg.py => Qwt5.py} | 12 +-
.../qt/qtcore/communication/communication.py | 52 +-
.../qt/qtcore/configuration/configuration.py | 28 +-
lib/taurus/qt/qtcore/model/taurusdatabasemodel.py | 180 +-
lib/taurus/qt/qtcore/model/taurusmodel.py | 4 +-
lib/taurus/qt/qtcore/tango/sardana/macroserver.py | 18 +-
lib/taurus/qt/qtcore/taurusqlistener.py | 201 +++
lib/taurus/qt/qtcore/util/emitter.py | 77 +-
lib/taurus/qt/qtcore/util/properties.py | 129 ++
lib/taurus/qt/qtdesigner/tauruspluginplugin.py | 6 +-
.../qt/qtgui/application/taurusapplication.py | 149 +-
lib/taurus/qt/qtgui/base/tauruscontroller.py | 25 +-
lib/taurus/qt/qtgui/button/taurusbutton.py | 48 +-
lib/taurus/qt/qtgui/console/__init__.py | 11 +-
lib/taurus/qt/qtgui/console/taurusconsole.py | 76 +-
.../taurusconsoleapplication.py} | 45 +-
.../qt/qtgui/console/taurusconsoleextensions.py | 186 ++
.../qt/qtgui/console/taurusconsolefactory.py | 153 ++
.../{taurusconsole.py => taurusconsolewidget.py} | 67 +-
lib/taurus/qt/qtgui/console/taurusconsolewindow.py | 94 +
lib/taurus/qt/qtgui/container/__init__.py | 4 +-
lib/taurus/qt/qtgui/container/taurusmainwindow.py | 264 ++-
lib/taurus/qt/qtgui/dialog/__init__.py | 1 +
.../qt/qtgui/dialog/taurusconfigurationdialog.py | 3 +-
lib/taurus/qt/qtgui/dialog/taurusinputdialog.py | 203 +++
lib/taurus/qt/qtgui/dialog/taurusmessagebox.py | 47 +-
.../qt/qtgui/display/demo/qpixmapwidgetdemo.py | 46 +-
.../qt/qtgui/display/demo/tauruslabeldemo.py | 1 -
lib/taurus/qt/qtgui/display/demo/tauruslcddemo.py | 3 -
lib/taurus/qt/qtgui/display/demo/taurusleddemo.py | 1 -
lib/taurus/qt/qtgui/display/qfallback.py | 6 +-
lib/taurus/qt/qtgui/display/qled.py | 52 +
lib/taurus/qt/qtgui/display/qlogo.py | 5 +-
lib/taurus/qt/qtgui/display/qpixmapwidget.py | 2 -
lib/taurus/qt/qtgui/display/qsevensegment.py | 7 +-
lib/taurus/qt/qtgui/display/tauruslabel.py | 19 +-
lib/taurus/qt/qtgui/display/tauruslcd.py | 21 +-
lib/taurus/qt/qtgui/display/tauruslcdvalue.py | 17 +-
lib/taurus/qt/qtgui/display/taurusled.py | 4 +-
lib/taurus/qt/qtgui/display/taurusstateled.py | 12 +-
lib/taurus/qt/qtgui/display/taurusvaluelabel.py | 16 +-
lib/taurus/qt/qtgui/extra_guiqwt/builder.py | 62 +-
lib/taurus/qt/qtgui/extra_guiqwt/curvesmodel.py | 13 +-
lib/taurus/qt/qtgui/extra_guiqwt/image.py | 57 +-
lib/taurus/qt/qtgui/extra_guiqwt/tools.py | 45 +-
.../qt/qtgui/extra_macroexecutor/__init__.py | 2 +-
lib/taurus/qt/qtgui/extra_macroexecutor/common.py | 6 +-
.../qt/qtgui/extra_macroexecutor/macrobutton.py | 69 +-
.../qt/qtgui/extra_macroexecutor/macroexecutor.py | 27 +-
.../macroparameterseditor/customeditors/senv.py | 14 +-
.../macroparameterseditor/delegate.py | 6 +-
.../macroparameterseditor/model.py | 94 +-
.../qt/qtgui/extra_macroexecutor/scanplotter.py | 6 +-
.../extra_macroexecutor/sequenceeditor/delegate.py | 6 +-
.../extra_macroexecutor/sequenceeditor/model.py | 9 +-
lib/taurus/qt/qtgui/extra_pool/poolchannel.py | 33 +-
lib/taurus/qt/qtgui/extra_pool/poolmotor.py | 87 +-
lib/taurus/qt/qtgui/extra_sardana/cmdline.py | 2 +-
.../qt/qtgui/extra_sardana/expdescription.py | 64 +-
.../qt/qtgui/extra_sardana/measurementgroup.py | 577 ++++---
lib/taurus/qt/qtgui/graphic/jdraw/jdraw.py | 135 +-
lib/taurus/qt/qtgui/graphic/jdraw/jdraw_parser.py | 14 +-
lib/taurus/qt/qtgui/graphic/jdraw/jdraw_view.py | 163 +-
lib/taurus/qt/qtgui/graphic/taurusgraphic.py | 264 ++-
lib/taurus/qt/qtgui/input/qwheel.py | 3 +-
lib/taurus/qt/qtgui/input/tauruscheckbox.py | 40 +-
lib/taurus/qt/qtgui/input/tauruscombobox.py | 24 +-
lib/taurus/qt/qtgui/input/tauruslineedit.py | 4 +
lib/taurus/qt/qtgui/model/qbasemodel.py | 274 +--
lib/taurus/qt/qtgui/panel/__init__.py | 6 +-
lib/taurus/qt/qtgui/panel/qdoublelist.py | 142 ++
.../qtable.py => panel/report/albareport.py} | 56 +-
lib/taurus/qt/qtgui/panel/report/basicreport.py | 178 +-
.../qt/qtgui/panel/report/ui/SendMailForm.ui | 69 +
lib/taurus/qt/qtgui/panel/report/ui/__init__.py | 0
.../qt/qtgui/panel/report/ui/ui_SendMailForm.py | 65 +
lib/taurus/qt/qtgui/panel/taurusconfigbrowser.py | 2 +-
lib/taurus/qt/qtgui/panel/taurusconfigeditor.py | 29 +-
lib/taurus/qt/qtgui/panel/taurusdevicepanel.py | 422 ++++-
lib/taurus/qt/qtgui/panel/taurusfilterpanel.py | 4 +-
lib/taurus/qt/qtgui/panel/taurusform.py | 107 +-
lib/taurus/qt/qtgui/panel/taurusinputpanel.py | 413 +++++
lib/taurus/qt/qtgui/panel/taurusmessagepanel.py | 174 +-
lib/taurus/qt/qtgui/panel/taurusmodellist.py | 18 +-
lib/taurus/qt/qtgui/panel/taurusvalue.py | 80 +-
.../ui/DoubleListDlg.ui} | 131 +-
lib/taurus/qt/qtgui/panel/ui/TaurusInputPanel.ui | 71 +
lib/taurus/qt/qtgui/panel/ui/ui_DoubleListDlg.py | 99 ++
.../qt/qtgui/panel/ui/ui_TaurusInputPanel.py | 69 +
lib/taurus/qt/qtgui/plot/__init__.py | 3 +-
lib/taurus/qt/qtgui/plot/arrayedit.py | 3 +-
lib/taurus/qt/qtgui/plot/curveStatsDlg.py | 316 ++++
lib/taurus/qt/qtgui/plot/curveprops.py | 13 +-
.../qt/qtgui/plot/curvesAppearanceChooserDlg.py | 7 +-
lib/taurus/qt/qtgui/plot/monitor.py | 3 +-
lib/taurus/qt/qtgui/plot/qwtdialog.py | 60 +-
lib/taurus/qt/qtgui/plot/scales.py | 3 +-
lib/taurus/qt/qtgui/plot/taurusplot.py | 446 ++++-
lib/taurus/qt/qtgui/plot/taurusplotconf.py | 3 +-
lib/taurus/qt/qtgui/plot/taurustrend.py | 359 +++-
lib/taurus/qt/qtgui/plot/ui/CurveStatsDialog.ui | 341 ++++
lib/taurus/qt/qtgui/plot/ui/ui_CurveStatsDialog.py | 174 ++
.../qt/qtgui/resource/large/TaurusSplash.png | Bin 0 -> 41538 bytes
.../resource/large/snapshot/TaurusDevicePanel.png | Bin 0 -> 107528 bytes
.../large/snapshot/TaurusJDrawSynopticsView.png | Bin 0 -> 39486 bytes
.../qt/qtgui/resource/taurus_resource_utils.py | 50 +-
lib/taurus/qt/qtgui/shell/spockshell.py | 98 --
lib/taurus/qt/qtgui/shell/taurusshell.py | 376 ----
lib/taurus/qt/qtgui/table/__init__.py | 3 +-
lib/taurus/qt/qtgui/table/qdictionary.py | 328 ++++
lib/taurus/qt/qtgui/table/qlogtable.py | 551 ++++--
lib/taurus/qt/qtgui/table/qtable.py | 18 +-
.../qt/qtgui/table/taurusdevicepropertytable.py | 6 +-
lib/taurus/qt/qtgui/table/taurusgrid.py | 375 ++--
lib/taurus/qt/qtgui/table/taurustable.py | 20 +-
lib/taurus/qt/qtgui/table/taurusvaluestable.py | 30 +-
.../qt/qtgui/taurusgui/PermanentCustomPanelsDlg.py | 62 +-
.../taurusgui/conf/tgconf_example01/config.py | 17 +
.../qtgui/taurusgui/conf/tgconf_macrogui/config.py | 8 +-
lib/taurus/qt/qtgui/taurusgui/macrolistener.py | 19 +-
.../qt/qtgui/taurusgui/paneldescriptionwizard.py | 85 +-
lib/taurus/qt/qtgui/taurusgui/taurusgui.py | 392 +++--
.../taurusgui/ui/ui_PermanentCustomPanelsDlg.py | 113 --
lib/taurus/qt/qtgui/taurusgui/utils.py | 33 +-
lib/taurus/qt/qtgui/tree/qtree.py | 134 +-
lib/taurus/qt/qtgui/tree/taurusdevicetree.py | 1819 ++++++++++++--------
lib/taurus/qt/qtgui/tree/taurustree.py | 6 +-
lib/taurus/qt/qtgui/util/__init__.py | 3 +-
lib/taurus/qt/qtgui/util/qdraganddropdebug.py | 64 +
lib/taurus/qt/qtgui/util/taurusaction.py | 33 +-
lib/taurus/qt/qtgui/util/tauruswidgetfactory.py | 2 +-
lib/taurus/qt/taurusqtoptions.py | 31 +-
scripts/taurusdemo | 35 +-
scripts/taurusdesigner | 10 +-
scripts/taurusremotelogmonitor | 93 +
setup.py | 76 +-
196 files changed, 12302 insertions(+), 4985 deletions(-)
diff --git a/LICENSE.txt b/LICENSE.txt
index 6f0c048..cd2afc9 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -13,7 +13,7 @@ SECTION 2: EXCEPTIONS
=====================
Some files (e.g., those authored by 3rd parties or the documentation
-sources) are distributed under Free Software / docmentation licenses
+sources) are distributed under Free Software / documentation licenses
that may differ from the the general one defined in SECTION 1.
The following is a list of these exceptions:
diff --git a/PKG-INFO b/PKG-INFO
index abe429a..5f15af1 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: taurus
-Version: 3.0.0
+Version: 3.0.1
Summary: A library designed to provide an abstraction layer over PyTango.
Home-page: http://packages.python.org/taurus
Author: Tiago Coutinho
diff --git a/doc/source/devel/coding_guide.rst b/doc/source/devel/coding_guide.rst
index 8f887fd..2bfd2b1 100644
--- a/doc/source/devel/coding_guide.rst
+++ b/doc/source/devel/coding_guide.rst
@@ -113,8 +113,88 @@ taurus::
if __name__ == "__main__":
main()
+Special notes about Qt programming
+-----------------------------------
+
+The following Qt guidelines are intended to ensure compatibility between all
+PyQt4/PySide versions.
+
+1. Avoid importing PyQt4/PySide directly.
+ Imports like::
+
+ from PyQt4 import Qt
+ from PyQt4 import QtCore
+ from PyQt4 import QtGui
+ from PyQt4 import QtNetwork
+ from PyQt4 import QtWebKit
+ from PyQt4 import Qwt5
+
+ Should be replaced by::
+
+ from taurus.qt import Qt
+ from taurus.qt import QtCore
+ from taurus.qt import QtGui
+ from taurus.qt import QtNetwork
+ from taurus.qt import QtWebKit
+ from taurus.qt import Qwt5
+
+2. Usage of :class:`~PyQt4.QString` is **discouraged**. You should always use
+ :class:`str`. QString objects don't exist in PySide or in the new PyQt4
+ API 2. Code like::
+
+ my_string = Qt.QString(" hello ")
+ my_string2 = my_string.trimmed()
+ label.setText(my_string2)
+ print label.text()
+
+ Should be replaced by::
+
+ my_string = " hello "
+ my_string2 = my_string.strip()
+ label.setText(my_string2)
+ print str(label.text()) # never assume Qt objects return str.
+
+ For compatibility reasons, QString and QStringList are always available
+ (even when using PySide or PyQt4 with API >=2) from :mod:`taurus.qt.Qt`.
+ Note that if you are using PySide or PyQt4 with API >=2 then QString is
+ actually :class:`str` and QStringList is actually :class:`list`!
+
+3. Usage of :class:`~PyQt4.QVariant` is **discouraged**. QVariant objects
+ don't exist in PySide or in the new PyQt4 API 2. Code like::
+
+ def setData(self, index, qvalue, role=Qt.Qt.EditRole):
+ value = qvalue.toString()
+ self.buffer[index.column()] = value
+
+ def data(self, index, role=Qt.Qt.DisplayRole):
+ value = self.buffer[index.column()]
+
+ if role == Qt.Qt.DisplayRole:
+ return Qt.QVariant(value)
+ else:
+ return Qt.QVariant()
+
+ Should be replaced by::
+
+ def setData(self, index, qvalue, role=Qt.Qt.EditRole):
+ value = Qt.from_qvariant(qvalue, str)
+ self.buffer[index.column()] = value
+
+ def data(self, index, role=Qt.Qt.DisplayRole):
+ value = self.buffer[index.column()]
+
+ if role == Qt.Qt.DisplayRole:
+ return Qt.to_qvariant(value)
+ else:
+ return Qt.from_qvariant()
+
+ For compatibility reasons, QVariant are always available
+ (even when using PySide or PyQt4 with API >=2) from :mod:`taurus.qt.Qt`.
+ Note that if you are using PySide or PyQt4 with API >=2 then QVariant(pyobj)
+ if function that returns actually pyobj (exactly the same as
+ :func:`~taurus.qt.Qt.from_qvariant`.)
.. _Tango: http://www.tango-controls.org/
.. _tango_cs: https://sourceforge.net/projects/tango-cs/
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
-.. _Sphinx: http://sphinx.pocoo.org/
\ No newline at end of file
+.. _Sphinx: http://sphinx.pocoo.org/
diff --git a/doc/source/devel/color_guide.rst b/doc/source/devel/color_guide.rst
index 93b24cd..061c723 100644
--- a/doc/source/devel/color_guide.rst
+++ b/doc/source/devel/color_guide.rst
@@ -73,7 +73,7 @@ Attribute quality
.ATTR_INVALID { background: rgb(128, 128, 128); color: rgb(255, 255, 255); text-align: right; }
.ATTR_VALID { background: rgb(0, 255, 0); color: rgb( 0, 0, 0); text-align: right; }
.ATTR_ALARM { background: rgb(255, 140, 0); color: rgb(255, 255, 255); text-align: right; }
- .ATTR_WARNING { background: rgb(255, 140, 0); color: rgb(255, 255, 255); text-align: right; }
+ .ATTR_WARNING { background: rgb(255, 140, 0); color: rgb( 0, 0, 0); text-align: right; }
.ATTR_CHANGING { background: rgb(128, 160, 255); color: rgb( 0, 0, 0); text-align: right; }
.ATTR_UNKNOWN { background: rgb(128, 128, 128); color: rgb( 0, 0, 0); text-align: right; }
.ATTR_NONE { background: rgb(128, 128, 128); color: rgb( 0, 0, 0); text-align: right; }
@@ -87,7 +87,7 @@ Attribute quality
<tr class="quality_row"><td>ATTR_INVALID</td><td>Gray (128,128,128)</td><td>White (255,255,255)</td><td class="ATTR_INVALID">-----</td></tr>
<tr class="quality_row"><td>ATTR_VALID</td><td>Dead Frog Green (0,255,0)</td><td>Black (0,0,0)</td><td class="ATTR_VALID">10.89 mV</td></tr>
<tr class="quality_row"><td>ATTR_ALARM</td><td>Orange (255,140,0)</td><td>White (255,255,255)</td><td class="ATTR_ALARM">76.54 mV</td></tr>
- <tr class="quality_row"><td>ATTR_WARNING</td><td>Orange (255,140,0)</td><td>White (255,255,255)</td><td class="ATTR_WARNING">64.23 mV</td></tr>
+ <tr class="quality_row"><td>ATTR_WARNING</td><td>Orange (255,140,0)</td><td>Black (0,0,0)</td><td class="ATTR_WARNING">64.23 mV</td></tr>
<tr class="quality_row"><td>ATTR_CHANGING</td><td>Light Blue (128,160,255)</td><td>Black (0,0,0)</td><td class="ATTR_CHANGING">20.45 mV</td></tr>
<tr class="quality_row"><td>None</td><td>Gray (128,128,128)</td><td>Black (0,0,0)</td><td class="ATTR_NONE">-----</td></tr>
</table>
diff --git a/doc/source/devel/examples.rst b/doc/source/devel/examples.rst
index 9ee337b..8334316 100644
--- a/doc/source/devel/examples.rst
+++ b/doc/source/devel/examples.rst
@@ -46,7 +46,7 @@ does not include the following header and footer code lines:
header::
import sys
- from PyQt4 import Qt
+ from taurus.qt import Qt
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(sys.argv)
@@ -76,7 +76,7 @@ Displaying a tango attribute value in a GUI is easy with taurus and
code::
import sys
- from PyQt4 import Qt
+ from taurus.qt import Qt
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(sys.argv)
@@ -224,8 +224,8 @@ code::
props = [ 'state', 'status', 'position', 'velocity', 'acceleration' ]
model = [ 'sys/taurustest/1/%s' % p for p in props ]
panel.setModel(model)
- panel.getItemByIndex(0).setReadWidgetClass(TaurusLabel) # you can provide an arbitrary class...
- panel.getItemByIndex(2).setWriteWidgetClass('TaurusWheelEdit') # ...or, if Taurus knows it, just give its name
+ panel[0].readWidgetClass = TaurusLabel # you can provide an arbitrary class...
+ panel[2].writeWidgetClass = 'TaurusWheelEdit' # ...or, if it is a Taurus class you can just give its name
*A little configuration goes a long way!*
@@ -300,7 +300,7 @@ Oh, and you can can change the display properties of any curve:
code::
import numpy
- from PyQt4 import Qwt5
+ from taurus.qt import Qwt5
from taurus.qt.qtgui.plot import TaurusPlot, CurveAppearanceProperties
panel = TaurusPlot()
diff --git a/doc/source/devel/examples/edit01.py b/doc/source/devel/examples/edit01.py
index 2248fa1..5fc9544 100644
--- a/doc/source/devel/examples/edit01.py
+++ b/doc/source/devel/examples/edit01.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(sys.argv)
diff --git a/doc/source/devel/examples/edit02.py b/doc/source/devel/examples/edit02.py
index f6cdc18..c3e89df 100644
--- a/doc/source/devel/examples/edit02.py
+++ b/doc/source/devel/examples/edit02.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.container import TaurusWidget
from taurus.qt.qtgui.display import TaurusValueLabel, TaurusConfigLabel
from taurus.qt.qtgui.input import TaurusValueSpinBox
@@ -28,5 +28,5 @@ w2.setModel('/position')
w3.setModel('/position')
w4.setModel('/position?configuration=unit')
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
\ No newline at end of file
diff --git a/doc/source/devel/examples/edit03.py b/doc/source/devel/examples/edit03.py
index 2b37e9a..109dcb9 100644
--- a/doc/source/devel/examples/edit03.py
+++ b/doc/source/devel/examples/edit03.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.container import TaurusWidget
from taurus.qt.qtgui.display import TaurusValueLabel, TaurusConfigLabel
from taurus.qt.qtgui.input import TaurusWheelEdit
@@ -28,5 +28,5 @@ w2.setModel('/position')
w3.setModel('/position')
w4.setModel('/position?configuration=unit')
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
\ No newline at end of file
diff --git a/doc/source/devel/examples/forms01.py b/doc/source/devel/examples/forms01.py
index 3e9ccbb..1beb1ad 100644
--- a/doc/source/devel/examples/forms01.py
+++ b/doc/source/devel/examples/forms01.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.panel import TaurusForm
app = Qt.QApplication(sys.argv)
@@ -8,5 +8,5 @@ panel = TaurusForm()
props = [ 'state', 'status', 'position', 'velocity', 'acceleration' ]
model = [ 'sys/taurustest/1/%s' % p for p in props ]
panel.setModel(model)
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
diff --git a/doc/source/devel/examples/forms02.py b/doc/source/devel/examples/forms02.py
index d98db09..136a826 100644
--- a/doc/source/devel/examples/forms02.py
+++ b/doc/source/devel/examples/forms02.py
@@ -1,17 +1,16 @@
import sys
-from PyQt4 import Qt
from taurus.qt.qtgui.panel import TaurusForm
from taurus.qt.qtgui.display import TaurusValueLabel
-from taurus.qt.qtgui.input import TaurusWheelEdit
+from taurus.qt.qtgui.application import TaurusApplication
-app = Qt.QApplication(sys.argv)
+app = TaurusApplication(sys.argv)
panel = TaurusForm()
props = [ 'state', 'status', 'position', 'velocity', 'acceleration' ]
model = [ 'sys/taurustest/1/%s' % p for p in props ]
panel.setModel(model)
-panel.getItemByIndex(0).setReadWidgetClass(TaurusValueLabel)
-panel.getItemByIndex(2).setWriteWidgetClass(TaurusWheelEdit)
+panel[0].readWidgetClass = TaurusValueLabel
+panel[2].writeWidgetClass='TaurusWheelEdit'
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
diff --git a/doc/source/devel/examples/label01.py b/doc/source/devel/examples/label01.py
index 807b1e7..cb143d8 100644
--- a/doc/source/devel/examples/label01.py
+++ b/doc/source/devel/examples/label01.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(sys.argv)
diff --git a/doc/source/devel/examples/label02.py b/doc/source/devel/examples/label02.py
index 0f5ef3c..744df71 100644
--- a/doc/source/devel/examples/label02.py
+++ b/doc/source/devel/examples/label02.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(sys.argv)
diff --git a/doc/source/devel/examples/label03.py b/doc/source/devel/examples/label03.py
index 19e29c9..7b1a308 100644
--- a/doc/source/devel/examples/label03.py
+++ b/doc/source/devel/examples/label03.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(sys.argv)
diff --git a/doc/source/devel/examples/label04.py b/doc/source/devel/examples/label04.py
index 5bc0c4f..f3a998e 100644
--- a/doc/source/devel/examples/label04.py
+++ b/doc/source/devel/examples/label04.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.container import TaurusWidget
from taurus.qt.qtgui.display import TaurusValueLabel, TaurusConfigLabel
@@ -22,6 +22,6 @@ w1.setShowQuality(False)
layout.addWidget(w1)
layout.addWidget(w2)
layout.addWidget(w3)
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
diff --git a/doc/source/devel/examples/label05.py b/doc/source/devel/examples/label05.py
index 8da19af..43e3402 100644
--- a/doc/source/devel/examples/label05.py
+++ b/doc/source/devel/examples/label05.py
@@ -1,11 +1,11 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.display import TaurusStateLabel
app = Qt.QApplication(sys.argv)
panel = Qt.QWidget()
w = TaurusStateLabel(panel)
w.setModel('sys/taurustest/1/state')
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
diff --git a/doc/source/devel/examples/label06.py b/doc/source/devel/examples/label06.py
index bfa4f21..0474d34 100644
--- a/doc/source/devel/examples/label06.py
+++ b/doc/source/devel/examples/label06.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.display import TaurusStateLabel
app = Qt.QApplication(sys.argv)
@@ -13,6 +13,6 @@ for y in range(4):
w.setShowText(False)
#w.setShowQualityForeground(False)
layout.addWidget(w,x,y)
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
diff --git a/doc/source/devel/examples/parentmodel_issue_demo.py b/doc/source/devel/examples/parentmodel_issue_demo.py
index 9e71eed..ae016b0 100644
--- a/doc/source/devel/examples/parentmodel_issue_demo.py
+++ b/doc/source/devel/examples/parentmodel_issue_demo.py
@@ -9,7 +9,7 @@ call recheckTaurusParent for all designer created widgets that use TaurusParentM
You can do it right after calling the setupUi method.
'''
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.container import TaurusWidget
from taurus.qt.qtgui.display import TaurusValueLabel
import sys
diff --git a/doc/source/devel/examples/pyqwt_issue_test.py b/doc/source/devel/examples/pyqwt_issue_test.py
index 5239f41..3db0d41 100644
--- a/doc/source/devel/examples/pyqwt_issue_test.py
+++ b/doc/source/devel/examples/pyqwt_issue_test.py
@@ -5,7 +5,7 @@ Simply run this script and see the diagnosis in the std output.
If you are affected, the solution is to install/compile a newer version of PyQwt/SIP
Notably, the python-qwt5-qt4 package originally shipped with
-Ubuntu 10.10 is affected
+Ubuntu 10.10 and Debian 6 is affected
In some systems, the bug produces a segfault while in some others it
produces an Assertion error similar to the following:
@@ -19,11 +19,11 @@ http://www.esrf.eu/mail_archives/tango/archive/msg04025.html
from PyQt4 import Qt, Qwt5
class MyScaleDrawSafe(Qwt5.QwtScaleDraw):
- def __init__(self, format = None, palette = None):
+ def __init__(self):
Qwt5.QwtScaleDraw.__init__(self)
class MyScaleDrawDanger(Qwt5.QwtScaleDraw):
- def __init__(self, format = None, palette = None):
+ def __init__(self):
Qwt5.QwtScaleDraw.__init__(self)
def label(self, val):
@@ -31,7 +31,7 @@ class MyScaleDrawDanger(Qwt5.QwtScaleDraw):
class MyPlot(Qwt5.QwtPlot):
- def __init__(self, parent = None, designMode = False):
+ def __init__(self, parent = None):
Qwt5.QwtPlot.__init__(self, parent)
self.setAxisScaleDraw(Qwt5.QwtPlot.xBottom, MyScaleDrawSafe())
print "Replotting with MyScaleDrawSafe:..."
diff --git a/doc/source/devel/examples/taurusplot01.py b/doc/source/devel/examples/taurusplot01.py
index 03466d1..9c04715 100644
--- a/doc/source/devel/examples/taurusplot01.py
+++ b/doc/source/devel/examples/taurusplot01.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.plot import TaurusPlot
app = Qt.QApplication(sys.argv)
@@ -16,5 +16,5 @@ panel.setModel(model)
########################
#END EXAMPLE CODE
########################
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
diff --git a/doc/source/devel/examples/taurusplot02.py b/doc/source/devel/examples/taurusplot02.py
index e6ed55d..7a84ca0 100644
--- a/doc/source/devel/examples/taurusplot02.py
+++ b/doc/source/devel/examples/taurusplot02.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.plot import TaurusPlot
app = Qt.QApplication(sys.argv)
@@ -16,5 +16,5 @@ panel.setModel(model)
########################
#END EXAMPLE CODE
########################
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
diff --git a/doc/source/devel/examples/taurusplot03.py b/doc/source/devel/examples/taurusplot03.py
index d398653..908db9f 100644
--- a/doc/source/devel/examples/taurusplot03.py
+++ b/doc/source/devel/examples/taurusplot03.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.plot import TaurusPlot, CurveAppearanceProperties
app = Qt.QApplication(sys.argv)
@@ -10,7 +10,7 @@ app = Qt.QApplication(sys.argv)
##########################
import numpy
-from PyQt4 import Qwt5
+from taurus.qt import Qwt5
panel = TaurusPlot()
@@ -40,5 +40,5 @@ panel.attachRawData(rawdata3)
########################
#END EXAMPLE CODE
########################
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
diff --git a/doc/source/devel/examples/taurustrend01.py b/doc/source/devel/examples/taurustrend01.py
index 2b7d6d9..1ab7b66 100644
--- a/doc/source/devel/examples/taurustrend01.py
+++ b/doc/source/devel/examples/taurustrend01.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.plot import TaurusTrend
app = Qt.QApplication(sys.argv)
@@ -17,5 +17,5 @@ panel.setModel(model)
########################
#END EXAMPLE CODE
########################
-panel.setVisible(True)
+panel.show()
sys.exit(app.exec_())
diff --git a/doc/source/devel/examples/taurusvalue01.py b/doc/source/devel/examples/taurusvalue01.py
index b271041..955b25b 100644
--- a/doc/source/devel/examples/taurusvalue01.py
+++ b/doc/source/devel/examples/taurusvalue01.py
@@ -1,5 +1,5 @@
import sys
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.panel import TaurusValue
diff --git a/doc/source/devel/icon_example.py b/doc/source/devel/icon_example.py
index bc3fc63..9828d97 100644
--- a/doc/source/devel/icon_example.py
+++ b/doc/source/devel/icon_example.py
@@ -1,4 +1,4 @@
-from PyQt4 import Qt
+from taurus.qt import Qt
from taurus.qt.qtgui.resource import getThemeIcon
class MyGUI(Qt.QMainWindow):
diff --git a/doc/source/users/ui/experimentconfiguration.rst b/doc/source/users/ui/experimentconfiguration.rst
new file mode 100644
index 0000000..9e48153
--- /dev/null
+++ b/doc/source/users/ui/experimentconfiguration.rst
@@ -0,0 +1,15 @@
+.. currentmodule:: taurus.qt.qtgui.extra_sardana
+
+.. _expconf_ui:
+
+
+=======================================
+Experiment Configuration user interface
+=======================================
+
+.. contents::
+
+.. todo::
+ Documentation to be written
+
+
\ No newline at end of file
diff --git a/doc/source/users/ui/sardanaeditor.rst b/doc/source/users/ui/sardanaeditor.rst
new file mode 100644
index 0000000..26094b9
--- /dev/null
+++ b/doc/source/users/ui/sardanaeditor.rst
@@ -0,0 +1,15 @@
+.. currentmodule:: taurus.qt.qtgui.extra_sardana
+
+.. _sardanaeditor_ui:
+
+
+==========================
+Sardana Editor's interface
+==========================
+
+.. contents::
+
+.. todo::
+ Documentation to be written
+
+
\ No newline at end of file
diff --git a/doc/source/users/ui/taurusgui.rst b/doc/source/users/ui/taurusgui.rst
index 1c369c8..4bc6332 100644
--- a/doc/source/users/ui/taurusgui.rst
+++ b/doc/source/users/ui/taurusgui.rst
@@ -207,8 +207,8 @@ Switching to another perspective can be done using the `Load perspectives` drop-
down button in the perspectives toolbar (or using the `View->Load perspective`
option).
-Apart from the pre-defined perspectives, you can always alter the panel re-
-arrange panels and store your preferred arrangement as a perspective using the
+Apart from the pre-defined perspectives, you can always re-arrange panels and
+store your preferred arrangement as a perspective using the
`Save perspective` button in the perspectives toolbar (or using the `View->Save
perspective` option).
@@ -254,12 +254,17 @@ related panels will be available:
- `Macros`, :ref:`a macro executor <macroexecutor_ui>` widget. See :ref:`this <sequencer_ui>` for further information.
- `Sequences`, :ref:`a macro sequencer <sequencer_ui>` widget. See :ref:`this <sequencer_ui>` for further information.
-- `1D Scans`, :ref:`a trend <trend_ui>` that plots the results of scan macro executions.
- `MacroDescription`, a text panel which provides documentation on the selected macro.
- `DoorOutput`, a text panel that logs the output of macro executions (similar to what
you would see if launching the macro in a `spock` console)
- `DoorDebug`, a text panel that logs the debug messages of macro executions.
- `DoorResult`, a text panel that logs the return value of the macro executions.
+- `Experiment Config`, `an experiment configuration <expconf_ui>` widget for configuring acquisition and display parameters
+- `Sardana Editor`, `a Sardana-aware code editor <sardanaeditor_ui>` for creating/modifying Sardana macros.
+
+Also, some other temporary panels may be dynamically created depending on the experiment configuration:
+
+- `1D Scans`, :ref:`a trend <trend_ui>` that plots the values of scalar attributes during some scan macro executions.
.. note:: You can run `taurusgui macrogui` for seeing an example of a TaurusGUI-
based application that provides the aforementioned panels
diff --git a/lib/taurus/core/epics/__init__.py b/lib/taurus/core/epics/__init__.py
new file mode 100644
index 0000000..2480adf
--- /dev/null
+++ b/lib/taurus/core/epics/__init__.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""
+.. currentmodule:: taurus.core.epics
+
+Epics extension for taurus core model.
+
+The epics extension provides access to Epics control system objects
+
+.. note:: The Epics scheme is only a proof of concept. The final syntax of the models is
+ not yet set in stone and only basic functionality is implemented.
+
+
+The Epics extension implements :mod:`taurus.core` objects that connect to Epics
+objects. The scheme prefix for epics objects is 'epics://'.
+
+You should never create objects of epics classes directly. Instead you
+should use the :class:`taurus.core.TaurusManager` and :class:`taurus.core.TaurusFactory` APIs
+to access all elements.
+
+For example, to get a reference to the epics process variable (PV) "my:example.RBV" you
+should do something like::
+
+ >>> import taurus
+ >>> myattr = taurus.Attribute('epics://my:example.RBV')
+
+Epics attributes (should) work just as other Taurus attributes and can be
+referred by their model name wherever a Taurus Attribute model is expected. For
+example, you can launch a `TaurusForm` with an epics attribute::
+
+ $> taurusform epics://my:example.RBV
+
+Similarly, you can combine epics attributes with attributes from other schemes::
+
+ $> taurusform 'epics://my:example.RBV' 'tango://sys/tg_test/1/float_scalar'\
+ 'eval://{epics://my:example.RBV}*{tango://sys/tg_test/1/float_scalar}'
+
+Currently, the taurus epics scheme just supports epics PVs, implementing them as
+taurus attributes (with configuration objects as well). Other taurus classes
+such as the Database, and Device classes are just convenience dummy objects in
+the epics scheme at this point. Epics records may eventually be mapped as
+Devices.
+
+"""
+
+from epicsfactory import *
diff --git a/lib/taurus/core/epics/epicsfactory.py b/lib/taurus/core/epics/epicsfactory.py
new file mode 100644
index 0000000..4f3864e
--- /dev/null
+++ b/lib/taurus/core/epics/epicsfactory.py
@@ -0,0 +1,725 @@
+#!/usr/bin/env python
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+'''
+Epics module. See __init__.py for more detailed documentation
+'''
+__all__ = ['EpicsFactory', 'EpicsDatabase', 'EpicsDevice',
+ 'EpicsAttribute','EpicsConfiguration',
+ 'EpicsConfigurationNameValidator', 'EpicsDeviceNameValidator',
+ 'EpicsAttributeNameValidator']
+
+
+
+import time, re, weakref
+import numpy
+import taurus.core
+from taurus.core.taurusexception import TaurusException
+import taurus.core.util
+from taurus.core import MatchLevel, TaurusSWDevState, SubscriptionState, TaurusEventType
+try:
+ import epics
+except ImportError: #note that if epics is not installed the factory will not be available
+ logger= taurus.core.util.info('cannot import epics module. Taurus will not support the "epics" scheme')
+ raise
+
+class AbstractEpicsNameValidator(taurus.core.util.Singleton):
+ #@todo: provide a mechanism to make base_sep configurable at installation time.
+ base_sep = ':' #the following characters need to be escaped with "\": ^$()<>[{\|.*+?
+ name_pattern = ''
+
+ def __init__(self):
+ """ Initialization. Nothing to be done here for now."""
+ pass
+
+ def init(self, *args, **kwargs):
+ """Singleton instance initialization."""
+ self.name_re = re.compile(self.name_pattern)
+
+ def isValid(self,s, matchLevel = MatchLevel.ANY):
+ return self.name_re.match(s) is not None
+
+ def getParams(self, s):
+ m = self.attrname_re.match(s)
+ if m is None:
+ return None
+ return m.groupdict()
+
+ def getNames(self, s, factory=None):
+ """Returns the full, normal and simple names for this object, or None if there is no match'''
+ """
+ raise RuntimeError('Not Allowed to call this method from subclasses')
+
+ def getDeviceName(self, s, full=True):
+ '''
+ returns the device name for the given attribute name.
+ The "full" argument is ignored since the DB is never included in the epics models
+ '''
+ m = self.name_re.match(s)
+ if m is None:
+ return None
+ devname = m.group('devname') or EpicsFactory.DEFAULT_DEVICE
+ return 'epics://%s%s'%(devname,m.group('base_sep') or self.base_sep )
+
+ def getDBName(self, s):
+ '''returns the full data base name for the given attribute name.
+ Note: the DB name is not a valid epics URI because the epics scheme does not implement a DB model'''
+ dbname = EpicsFactory.DEFAULT_DATABASE
+ return dbname
+
+
+class EpicsAttributeNameValidator(AbstractEpicsNameValidator):
+ #The groups in a match object using the regexp below are:
+ # 1: scheme; named as 'scheme'
+ # 2: EPICS PV name (in the case of attribute names) or same as $3 (in the case of device names)
+ # 3: device name including the trailing base_sep; optional
+ # 4: device name; optional; named as 'devname'
+ # 5: base separator if it appears on the URI; named as 'base_sep'
+ # 6: attribute name;optional; named as 'attrname'
+ #
+ # Reconstructing the names
+ # attrname= $6
+ # devname= $4 or EpicsFactory.DEFAULT_DEVICE
+ # fullname= "epics://%s"%($2)
+ #
+ # 1 2 34 5 6
+ name_pattern = '^(?P<scheme>epics)://(?P<epicsname>((?P<devname>[^?#]*)(?P<base_sep>%s))?(?P<attrname>[^?#%s]+))$'%(AbstractEpicsNameValidator.base_sep, AbstractEpicsNameValidator.base_sep)
+
+# def isValid(self,s, matchLevel = MatchLevel.ANY):
+# m = self.name_re.match(s)
+# return m is not None and m.group('attrname') #the model contains an attrname
+
+ def getNames(self, s, factory=None):
+ """Returns the complete, normal and short names.
+
+ For example::
+
+ >>> EpicsDeviceNameValidator.getNames("epics://foo:bar:baz")
+ >>> ("epics://foo:bar:baz", "foo:bar:baz", "baz")
+
+ """
+ m = self.name_re.match(s)
+ if m is None:
+ return None
+ #The following comments are for an example name like: "epics://foo:bar:baz"
+ attr_name = m.group('attrname') # attr_name = "baz"
+ normal_name = m.group('epicsname') #normal_name = "foo:bar:baz"
+ fullname = "%s://%s"%(m.group('scheme'),normal_name) #fullname = "epics://foo:bar:baz"
+ return fullname, normal_name, attr_name
+
+
+class EpicsDeviceNameValidator(AbstractEpicsNameValidator):
+ '''A validator of names for :class:`EpicsDevice`. By taurusconvention,
+ the model name for an epics device name *must* end with the base separator
+ (in order to distinguish device names from attribute names)'''
+
+ name_pattern = '^(?P<scheme>epics)://(?P<epicsname>((?P<devname>[^?#]*)(?P<base_sep>%s)))$'%(AbstractEpicsNameValidator.base_sep)
+
+# def isValid(self,s, matchLevel = MatchLevel.ANY):
+# m = self.name_re.match(s)
+# return m is not None and not m.group('attrname') #to be a device it must not contain an attribute
+
+ def getNames(self, s, factory=None):
+ """Returns the complete, normal and short names. (note: complete=normal)
+
+ :param s: (str) input string describing the device
+ :param factory: (TaurusFactory) [Unused]
+
+ :return: (tuple<str,str,str> or None) A tuple of complete, normal and
+ short names, or None if s is an invalid device name
+ """
+ m = self.name_re.match(s)
+ if m is None:
+ return None
+ #The following comments are for a name of the type: "epics://foo:bar:"
+ devname = m.group('devname') # foo:bar
+ normal_name = m.group('epicsname') #foo:bar:
+ full_name = self.getDeviceName(s, full=True) #epics://foo:bar:
+ return full_name, normal_name, devname
+
+
+class EpicsConfigurationNameValidator(AbstractEpicsNameValidator):
+ '''A validator of names for :class:`EpicsConfiguration`'''
+ # The groups in a match object using the regexp below are the
+ # same as for the AbstractEpicsNameValidator plus:
+ # +1: configuration extension
+ # +2: configuration key;optional; named as 'cfgkey'
+
+ name_pattern = '^(?P<scheme>epics)://(?P<epicsname>((?P<devname>[^?#]*)(?P<base_sep>%s))?(?P<attrname>[^?#%s]+)\?configuration=?(?P<cfgkey>[^#?]*))$'%(AbstractEpicsNameValidator.base_sep, AbstractEpicsNameValidator.base_sep)
+
+ def getNames(self, s, factory=None):
+ """Returns the complete, normal and short names"""
+ m = self.name_re.match(s)
+ if m is None:
+ return None
+ #The following comments are for an example name like: "epics://foo:bar:baz?configuration=label"
+ cfg_key = m.group('cfgkey') # cfg_key = "label"
+ full_name = s # "epics://foo:bar:baz?configuration=label"
+ normal_name = full_name # "epics://foo:bar:baz?configuration=label"
+ return full_name, normal_name, cfg_key
+
+ def getAttrName(self, s):
+ names = self.getNames(s)
+ if names is None: return None
+ return names[0].rsplit('?configuration')[0]#remove the "?configuration..." substring from the fullname
+
+
+class EpicsDatabase(taurus.core.TaurusDatabase):
+ '''
+ Dummy database class for Epics (the Database concept is not used in the Epics scheme)
+
+ .. warning:: In most cases this class should not be instantiated directly.
+ Instead it should be done via the :meth:`EpicsFactory.getDataBase`
+ '''
+ def factory(self):
+ return EpicsFactory()
+
+ def __getattr__(self, name):
+ return "EpicsDatabase object calling %s" % name
+
+
+class EpicsDevice(taurus.core.TaurusDevice):
+ '''
+ An Epics device object.
+ @todo: For the moment is a dummy object. Eventually we may map it to an epics record.
+
+ .. seealso:: :mod:`taurus.core.epics`
+
+ .. warning:: In most cases this class should not be instantiated directly.
+ Instead it should be done via the :meth:`EpicsFactory.getDevice`
+ '''
+
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # TaurusModel necessary overwrite
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # helper class property that stores a reference to the corresponding factory
+ _factory = None
+
+ @classmethod
+ def factory(cls):
+ if cls._factory is None:
+ cls._factory = taurus.Factory(scheme='epics')
+ return cls._factory
+
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # TaurusDevice necessary overwrite
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ def _createHWObject(self):
+ return 'Epics'
+
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ def getAttribute(self, attrname):
+ """Returns the attribute object given its name"""
+ full_attrname = "%s%s"%(self.getFullName(), attrname)
+ return self.factory().getAttribute(full_attrname)
+
+ @classmethod
+ def getNameValidator(cls):
+ return EpicsDeviceNameValidator()
+
+ def decode(self, event_value):
+ if isinstance(event_value, int): # TaurusSWDevState
+ new_sw_state = event_value
+ else:
+ self.info("Unexpected value to decode: %s" % str(event_value))
+ new_sw_state = TaurusSWDevState.Crash
+ value = taurus.core.TaurusAttrValue()
+ value.value = new_sw_state
+ return value
+
+
+class EpicsAttribute(taurus.core.TaurusAttribute):
+ '''
+ A :class:`TaurusAttribute` that gives access to an Epics Process Variable.
+
+ .. seealso:: :mod:`taurus.core.epics`
+
+ .. warning:: In most cases this class should not be instantiated directly.
+ Instead it should be done via the :meth:`EpicsFactory.getAttribute`
+ '''
+
+ def __init__(self, name, parent, storeCallback = None):
+ self.call__init__(taurus.core.TaurusAttribute, name, parent, storeCallback=storeCallback)
+
+ self.__attr_config = None
+ self.__pv = epics.PV(self.getNormalName(), callback=self.onEpicsEvent)
+ connected = self.__pv.wait_for_connection()
+ if connected:
+ self.info('successfully connected to epics PV')
+ self._value = self.decode_pv(self.__pv)
+ else:
+ self.info('connection to epics PV failed')
+ self._value = taurus.core.TaurusAttrValue()
+
+ #print "INIT",self.__pv, connected
+
+ def onEpicsEvent(self, **kw):
+ '''callback for PV changes'''
+ self._value = self.decode_epics_evt(kw)
+ self.fireEvent(TaurusEventType.Change, self._value)
+
+ def __getattr__(self,name):
+ return getattr(self._getRealConfig(), name)
+
+ def _getRealConfig(self):
+ """ Returns the current configuration of the attribute."""
+ if self.__attr_config is None:
+ cfg_name = "%s?configuration" % self.getFullName()
+ self.__attr_config = EpicsConfiguration(cfg_name, self)
+ return self.__attr_config
+
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # Necessary to overwrite from TaurusAttribute
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ def isNumeric(self):
+ return True
+
+ def isBoolean(self):
+ return isinstance(self._value.value, bool)
+
+ def isState(self):
+ return False
+
+ def getDisplayValue(self,cache=True):
+ return self.__pv.get(as_string=True, use_monitor=cache)
+
+ def encode(self, value):
+ '''encodes the value passed to the write method into
+ a representation that can be written in epics'''
+ try:
+ typeclass = numpy.dtype(self.__pv.type).type
+ return typeclass(value) #cast the value with the python type for this PV
+ except:
+ return value
+
+ def decode (self, obj):
+ if isinstance(obj, epics.PV):
+ return self.decode_pv(obj)
+ else:
+ return self.decode_epics_evt(obj)
+
+ def decode_pv(self, pv):
+ """Decodes an epics pv into the expected taurus representation"""
+ #@todo: This is a very basic implementation, and things like quality may not be correct
+ attr_value = taurus.core.TaurusAttrValue()
+ attr_value.value = pv.value
+ if pv.write_access:
+ attr_value.w_value = pv.value
+ if pv.timestamp is None:
+ attr_value.time = taurus.core.TaurusTimeVal.now()
+ else:
+ attr_value.time = taurus.core.TaurusTimeVal.fromtimestamp(pv.timestamp)
+ if pv.severity > 0:
+ attr_value.quality = taurus.core.AttrQuality.ATTR_ALARM
+ else:
+ attr_value.quality = taurus.core.AttrQuality.ATTR_VALID
+ attr_value.config.data_format = len(numpy.shape(attr_value.value))
+ return attr_value
+
+ def decode_epics_evt(self, evt):
+ """Decodes an epics event (a callback keywords dict) into the expected taurus representation"""
+ #@todo: This is a very basic implementation, and things like quality may not be correct
+ attr_value = taurus.core.TaurusAttrValue()
+ attr_value.value = evt.get('value')
+ if evt.get('write_access', False):
+ attr_value.w_value = attr_value.value
+ timestamp = evt.get('timestamp', None)
+ if timestamp is None:
+ attr_value.time = taurus.core.TaurusTimeVal.now()
+ else:
+ attr_value.time = taurus.core.TaurusTimeVal.fromtimestamp(timestamp)
+ if evt.get('severity', 1) > 0:
+ attr_value.quality = taurus.core.AttrQuality.ATTR_ALARM
+ else:
+ attr_value.quality = taurus.core.AttrQuality.ATTR_VALID
+ attr_value.config.data_format = len(numpy.shape(attr_value.value))
+ return attr_value
+
+ def write(self, value, with_read=True):
+ value = self.encode(value)
+ self.__pv.put(value)
+ if with_read:
+ return self.decode_pv(self.__pv)
+
+ def read(self, cache=True):
+ '''returns the value of the attribute.
+
+ :param cache: (bool) If True (default), the last calculated value will
+ be returned. If False, the referenced values will be re-
+ read and the transformation string will be re-evaluated
+
+ :return: attribute value
+ '''
+ if not cache:
+ self.__pv.get(use_monitor=False)
+ self._value = self.decode_pv(self.__pv)
+ return self._value
+
+ def poll(self):
+ v = self.read(cache=False)
+ self.fireEvent(TaurusEventType.Periodic, v)
+
+ def isUsingEvents(self):
+ return True #@todo
+
+#------------------------------------------------------------------------------
+
+ def isWritable(self, cache=True):
+ return self.__pv.write_access
+
+ def isWrite(self, cache=True):
+ return self.__pv.write_access
+
+ def isReadOnly(self, cache=True):
+ return self.__pv.read_access and not self.__pv.write_access
+
+ def isReadWrite(self, cache=True):
+ return self.__pv.read_access and self.__pv.write_access
+
+ def getWritable(self, cache=True):
+ return self.__pv.write_access
+
+
+ def factory(self):
+ return EpicsFactory()
+
+ @classmethod
+ def getNameValidator(cls):
+ return EpicsAttributeNameValidator()
+
+
+
+class EpicsConfiguration(taurus.core.TaurusConfiguration):
+ '''
+ A :class:`TaurusConfiguration`
+
+ .. seealso:: :mod:`taurus.core.epics`
+
+ .. warning:: In most cases this class should not be instantiated directly.
+ Instead it should be done via the :meth:`EpicsFactory.getConfig`
+ '''
+ def __init__(self, name, parent, storeCallback = None):
+ self.call__init__(taurus.core.TaurusConfiguration, name, parent, storeCallback=storeCallback)
+
+ #fill the attr info
+ i = parent.read().config
+ a=parent
+ d=self._getDev()
+ # add dev_name, dev_alias, attr_name, attr_full_name
+ i.dev_name = d.getNormalName()
+ i.dev_alias = d.getSimpleName()
+ i.attr_name = a.getSimpleName()
+ i.attr_fullname = a.getNormalName()
+ i.label = a.getSimpleName()
+ self._attr_info = i
+
+ def __getattr__(self, name):
+ try:
+ return getattr(self._attr_info,name)
+ except:
+ raise AttributeError("'%s'object has no attribute '%s'"%(self.__class__.__name__, name))
+ @classmethod
+ def getNameValidator(cls):
+ return EpicsConfigurationNameValidator()
+
+ def _subscribeEvents(self):
+ pass
+
+ def _unSubscribeEvents(self):
+ pass
+
+ def factory(self):
+ EpicsFactory()
+
+ def getValueObj(self, cache=True):
+ """ Returns the current configuration for the attribute."""
+ return self._attr_info
+
+
+class EpicsFactory(taurus.core.util.Singleton, taurus.core.TaurusFactory, taurus.core.util.Logger):
+ """
+ A Singleton class that provides Epics related objects.
+ """
+
+ schemes = ("epics",)
+ DEFAULT_DEVICE = '_DefaultEpicsDevice'
+ DEFAULT_DATABASE = '_DefaultEpicslDB'
+ def __init__(self):
+ """ Initialization. Nothing to be done here for now."""
+ pass
+
+ def init(self, *args, **kwargs):
+ """Singleton instance initialization."""
+ name = self.__class__.__name__
+ self.call__init__(taurus.core.util.Logger, name)
+ self.call__init__(taurus.core.TaurusFactory)
+ self.epics_attrs = weakref.WeakValueDictionary()
+ self.epics_devs = weakref.WeakValueDictionary()
+ self.epics_configs = weakref.WeakValueDictionary()
+
+ def findObjectClass(self, absolute_name):
+ """
+ Obtain the class object corresponding to the given name.
+ """
+ if EpicsConfiguration.isValid(absolute_name):
+ return EpicsConfiguration
+ elif EpicsDevice.isValid(absolute_name):
+ return EpicsDevice
+ elif EpicsAttribute.isValid(absolute_name):
+ return EpicsAttribute
+ else:
+ self.debug("Not able to find Object class for %s" % absolute_name)
+ self.traceback()
+ return None
+
+ def getDatabase(self, db_name=None):
+ """Obtain the EpicsDatabase object.
+
+ :param db_name: (str) this is ignored because only one dummy database is supported
+
+ :return: (EpicsDatabase)
+ """
+ if not hasattr(self, "_db"):
+ self._db = EpicsDatabase(self.DEFAULT_DATABASE)
+ return self._db
+
+ def getDevice(self, dev_name):
+ """Obtain the EpicsDevice object.
+
+ :param dev_name: (str) this is ignored because only one dummy device is supported
+
+ :return: (EpicsDevice)
+
+ .. todo:: Epics records may be implemented as taurus devices...
+ """
+ d = self.epics_devs.get(dev_name, None) #first try with the given name
+ if d is None: #if not, try with the full name
+ validator = EpicsDevice.getNameValidator()
+ names = validator.getNames(dev_name)
+ if names is None:
+ raise TaurusException("Invalid epics device name %s" % dev_name)
+ fullname = names[0]
+ d = self.epics_devs.get(fullname, None)
+ if d is None: #if the full name is not there, create one
+ db = self.getDatabase()
+ d = EpicsDevice(fullname, parent=db, storeCallback=self._storeDev) #use full name
+ return d
+
+ def getAttribute(self, attr_name):
+ """Obtain the object corresponding to the given attribute name. If the
+ corresponding attribute already exists, the existing instance is
+ returned. Otherwise a new instance is stored and returned. The
+ device associated to this attribute will also be created if necessary.
+
+ :param attr_name: (str) the attribute name string. See
+ :mod:`taurus.core.epics` for valid attribute names
+
+ :return: (EpicsAttribute)
+
+ @throws TaurusException if the given name is invalid.
+ """
+ a = self.epics_attrs.get(attr_name, None) #first try with the given name
+ if a is None: #if not, try with the full name
+ validator = EpicsAttribute.getNameValidator()
+ names = validator.getNames(attr_name)
+ if names is None:
+ raise TaurusException("Invalid epics attribute name %s" % attr_name)
+ fullname = names[0]
+ a = self.epics_attrs.get(fullname, None)
+ if a is None: #if the full name is not there, create one
+ dev = self.getDevice(validator.getDeviceName(attr_name))
+ a = EpicsAttribute(fullname, parent=dev, storeCallback=self._storeAttr) #use full name
+ return a
+
+ def getConfiguration(self, param):
+ """getConfiguration(param) -> taurus.core.TaurusConfiguration
+
+ Obtain the object corresponding to the given attribute or full name.
+ If the corresponding configuration already exists, the existing instance
+ is returned. Otherwise a new instance is stored and returned.
+
+ @param[in] param taurus.core.TaurusAttribute object or full configuration name
+
+ @return a taurus.core.TaurusAttribute object
+ @throws TaurusException if the given name is invalid.
+ """
+ if isinstance(param, str):
+ return self._getConfigurationFromName(param)
+ return self._getConfigurationFromAttribute(param)
+
+ def _getConfigurationFromName(self, cfg_name):
+ cfg = self.epics_configs.get(cfg_name, None) #first try with the given name
+ if cfg is None: #if not, try with the full name
+ validator = EpicsConfiguration.getNameValidator()
+ names = validator.getNames(cfg_name)
+ if names is None:
+ raise TaurusException("Invalid evaluation configuration name %s" % cfg_name)
+ fullname = names[0]
+ cfg = self.epics_configs.get(fullname, None)
+ if cfg is None: #if the full name is not there, create one
+ attr = self.getAttribute(validator.getAttrName(cfg_name))
+ cfg = EpicsConfiguration(fullname, parent=attr, storeCallback=self._storeConfig)
+ return cfg
+
+ def _getConfigurationFromAttribute(self, attr):
+ cfg = attr.getConfig()
+ cfg_name = attr.getFullName() + "?configuration"
+ self.epics_configs[cfg_name] = cfg
+ return cfg
+
+ def _storeDev(self, dev):
+ name = dev.getFullName()
+ exists = self.epics_devs.get(name)
+ if exists is not None:
+ if exists == dev:
+ self.debug("%s has already been registered before" % name)
+ raise taurus.core.DoubleRegistration
+ else:
+ self.debug("%s has already been registered before with a different object!" % name)
+ raise taurus.core.DoubleRegistration
+ self.epics_devs[name] = dev
+
+ def _storeAttr(self, attr):
+ name = attr.getFullName()
+ exists = self.epics_attrs.get(name)
+ if exists is not None:
+ if exists == attr:
+ self.debug("%s has already been registered before" % name)
+ raise taurus.core.DoubleRegistration
+ else:
+ self.debug("%s has already been registered before with a different object!" % name)
+ raise taurus.core.DoubleRegistration
+ self.epics_attrs[name] = attr
+
+ def _storeConfig(self, fullname, config):
+ #name = config.getFullName()
+ name = fullname
+ exists = self.epics_configs.get(name)
+ if exists is not None:
+ if exists == config:
+ self.debug("%s has already been registered before" % name)
+ raise taurus.core.DoubleRegistration
+ else:
+ self.debug("%s has already been registered before with a different object!" % name)
+ raise taurus.core.DoubleRegistration
+ self.epics_configs[name] = config
+
+ def addAttributeToPolling(self, attribute, period, unsubscribe_evts = False):
+ """Activates the polling (client side) for the given attribute with the
+ given period (seconds).
+
+ :param attribute: (taurus.core.tango.TangoAttribute) attribute name.
+ :param period: (float) polling period (in seconds)
+ :param unsubscribe_evts: (bool) whether or not to unsubscribe from events
+ """
+ tmr = self.polling_timers.get(period,taurus.core.TaurusPollingTimer(period))
+ self.polling_timers[period] = tmr
+ tmr.addAttribute(attribute, self.isPollingEnabled())
+
+ def removeAttributeFromPolling(self, attribute):
+ """Deactivate the polling (client side) for the given attribute. If the
+ polling of the attribute was not previously enabled, nothing happens.
+
+ :param attribute: (str) attribute name.
+ """
+ p = None
+ for period,timer in self.polling_timers.iteritems():
+ if timer.containsAttribute(attribute):
+ timer.removeAttribute(attribute)
+ if timer.getAttributeCount() == 0:
+ p = period
+ break
+ if p:
+ del self.polling_timers[period]
+
+
+
+
+#===============================================================================
+# Just for testing
+#===============================================================================
+def test1():
+ f = EpicsFactory()
+ d = f.getDevice('epics://foo:bar:')
+ a = f.getAttribute('epics://foo:bar:baz')
+ p = a.getParentObj()
+ c = f.getConfiguration('epics://foo:bar:baz?configuration=label')
+# cp = a.getConfig()
+ print "FACTORY:", f
+ print "DEVICE:", d, d.getSimpleName(), d.getNormalName(), d.getFullName()
+ print "ATTRIBUTE", a, a.getSimpleName(), a.getNormalName(), a.getFullName()
+ print "ATTRIBUTE PARENT", p, p.getSimpleName(), p.getNormalName(), p.getFullName(), p is d
+ print "CONFIGURATION", c, c.getSimpleName(), c.getNormalName(), c.getFullName()
+# print "CONFIGPROXY", cp, cp.getSimpleName()
+# print
+# print c.getValueObj()
+# print c.getUnit()
+
+
+def test2():
+ a=taurus.Attribute('epics://mp49t:sim1.RBV')
+ class Dummy:
+ n=0
+ def eventReceived(self, s,t,v):
+ print "DUMMY:",self.n, v.value
+ self.n += 1
+ kk = Dummy()
+ a.addListener(kk)
+ while kk.n <= 2:
+ time.sleep(1)
+ a.removeListener(kk)
+ while kk.n <= 20:
+ time.sleep(1)
+
+def test3():
+ import sys
+ from taurus.qt.qtgui.application import TaurusApplication
+ from taurus.qt.qtgui.panel import TaurusForm
+ from taurus.qt.qtgui.plot import TaurusTrend
+# from taurus.qt.qtgui.display import TaurusLabel
+ app = TaurusApplication()
+
+ w = TaurusForm()
+ w.modifiableByUser=True
+ w2=TaurusTrend()
+# w=TaurusLabel()
+
+ w.setModel(['epics://mp49t:sim1.RBV', 'epics://mp49t:sim1.VAL', 'epics://mp49t:sim1.HIGH'])
+ w2.setModel(['epics://mp49t:sim1.RBV', 'epics://mp49t:sim1.VAL'])
+
+# a=w.getModelObj()
+# print a, a.read().value
+
+# a=w.getModelObj()
+# a.setUnit('asd')
+# c=a.getConfig()
+
+ w.show()
+ w2.show()
+ sys.exit(app.exec_())
+
+if __name__ == "__main__":
+ test3()
+
+
diff --git a/lib/taurus/core/evaluation/evalfactory.py b/lib/taurus/core/evaluation/evalfactory.py
index 9a5e5fc..77844f6 100644
--- a/lib/taurus/core/evaluation/evalfactory.py
+++ b/lib/taurus/core/evaluation/evalfactory.py
@@ -32,15 +32,15 @@ __all__ = ['EvaluationFactory', 'EvaluationDatabase', 'EvaluationDevice',
-import os, time, re, weakref
+import time, re, weakref
import numpy
from taurus.core.taurusexception import TaurusException
import taurus.core
-from taurus.core import OperationMode, MatchLevel, TaurusSWDevState, SubscriptionState, TaurusEventType
-from taurus.core.util import SafeEvaluator
+from taurus.core import MatchLevel, TaurusSWDevState, SubscriptionState, TaurusEventType
+from taurus.core.util import SafeEvaluator, Singleton, Logger
-class AbstractEvaluationNameValidator(taurus.core.util.Singleton):
+class AbstractEvaluationNameValidator(Singleton):
#the object name class. *must* be implemented in subclasses
name_pattern = '' #must be implemented in children classes
# The following regexp pattern matches <variable>=<value> pairs
@@ -55,37 +55,37 @@ class AbstractEvaluationNameValidator(taurus.core.util.Singleton):
self.name_re = re.compile(self.name_pattern)
self.kvsymbols_re = re.compile(self.kvsymbols_pattern)
- def isValid(self,str, matchLevel = MatchLevel.ANY):
- return self.name_re.match(str) is not None
+ def isValid(self,s, matchLevel = MatchLevel.ANY):
+ return self.name_re.match(s) is not None
- def getParams(self, str):
- m = self.attrname_re.match(str)
+ def getParams(self, s):
+ m = self.attrname_re.match(s)
if m is None:
return None
return m.groupdict()
- def getNames(self, str, factory=None):
+ def getNames(self, s, factory=None):
"""Returns the full, normal and simple names for this object, or None if there is no match'''
"""
- raise RunTimeError('Not Allowed to call this method from subclasses')
+ raise RuntimeError('Not Allowed to call this method from subclasses')
- def getDeviceName(self, str, full=True):
+ def getDeviceName(self, s, full=True):
'''
returns the device name for the given attribute name.
If full=True, the returned name includes the DB name
'''
- m = self.name_re.match(str)
+ m = self.name_re.match(s)
if m is None:
return None
devname = m.group('devname') or EvaluationFactory.DEFAULT_DEVICE
if full:
- return '%s;dev=%s'%(self.getDBName(str),devname)
+ return '%s;dev=%s'%(self.getDBName(s),devname)
else:
return 'eval://dev=%s'%devname
- def getDBName(self,str):
+ def getDBName(self,s):
'''returns the full data base name for the given attribute name'''
- m = self.name_re.match(str)
+ m = self.name_re.match(s)
if m is None:
return None
dbname = m.group('dbname') or EvaluationFactory.DEFAULT_DATABASE
@@ -93,7 +93,7 @@ class AbstractEvaluationNameValidator(taurus.core.util.Singleton):
class EvaluationAttributeNameValidator(AbstractEvaluationNameValidator):
- # The groups in a match object using the regexp below are:
+ # The groups in a match object using the regexp below are:
# 1: scheme; named as 'scheme'
# 2:
# 3: database name; optional; named as 'dbname'
@@ -113,8 +113,8 @@ class EvaluationAttributeNameValidator(AbstractEvaluationNameValidator):
# 1 2 3 4 5 6 7 8 9 A
name_pattern = r'^(?P<scheme>eval|evaluation)://(db=(?P<dbname>[^?#;]+);)?(dev=(?P<devname>[^?#;]+);)?(?P<attrname>[^?#;]+)(\?(?!configuration=)(?P<subst>[^#?]*))?(#(?P<fragment>.*))?$'
- def isValid(self,str, matchLevel = MatchLevel.ANY):
- m = self.name_re.match(str)
+ def isValid(self,s, matchLevel = MatchLevel.ANY):
+ m = self.name_re.match(s)
if m is None:
return False
elif matchLevel == MatchLevel.COMPLETE:
@@ -122,7 +122,7 @@ class EvaluationAttributeNameValidator(AbstractEvaluationNameValidator):
else:
return True
- def getNames(self, str, factory=None):
+ def getNames(self, s, factory=None):
"""Returns the complete, normal and short names.
For example::
@@ -131,19 +131,19 @@ class EvaluationAttributeNameValidator(AbstractEvaluationNameValidator):
>>> ("eval://db=_DefaultEvalDB;dev=foo;123*{a/b/c/d}", "eval://dev=foo;bar*blah", "bar*blah")
"""
- m = self.name_re.match(str)
+ m = self.name_re.match(s)
if m is None:
return None
#The following comments are for an example name like: "eval://dev=foo;bar*blah?bar=123;blah={a/b/c/d}#[1:-3]"
attr_name = m.group('attrname') # attr_name = "bar*blah"
- normal_name = "%s;%s"%(self.getDeviceName(str, full=False),attr_name) #normal_name = "eval://dev=foo;bar*blah"
- expanded_attr_name = self.getExpandedTransformation(str)
- fullname = "%s;%s"%(self.getDeviceName(str, full=True),expanded_attr_name) #fullname = "eval://db=_DefaultEvalDB;dev=foo;123*{a/b/c/d}"
+ normal_name = "%s;%s"%(self.getDeviceName(s, full=False),attr_name) #normal_name = "eval://dev=foo;bar*blah"
+ expanded_attr_name = self.getExpandedTransformation(s)
+ fullname = "%s;%s"%(self.getDeviceName(s, full=True),expanded_attr_name) #fullname = "eval://db=_DefaultEvalDB;dev=foo;123*{a/b/c/d}"
return fullname, normal_name, attr_name
- def getExpandedTransformation(self, str):
+ def getExpandedTransformation(self, s):
'expands the attribute name by substituting all symbols'
- m = self.name_re.match(str)
+ m = self.name_re.match(s)
if m is None:
return None
transf = m.group('attrname')
@@ -167,23 +167,22 @@ class EvaluationDeviceNameValidator(AbstractEvaluationNameValidator):
# 1 2 3 4 5 6 7
name_pattern = r'^(?P<scheme>eval|evaluation)://(db=(?P<dbname>[^?#;]+);)?(dev=(?P<devname>[^?#;]+))(\?(?!configuration=)(?P<subst>[^#?]*))?$'
- def getNames(self, str, factory=None):
+ def getNames(self, s, factory=None):
"""Returns the complete, normal and short names. (note: complete=normal)
- :param str: (str) input string describing the device
+ :param s: (str) input string describing the device
:param factory: (TaurusFactory) [Unused]
:return: (tuple<str,str,str> or None) A tuple of complete, normal and
- short names, or None if str is an invalid device name
+ short names, or None if s is an invalid device name
"""
- m = self.name_re.match(str)
+ m = self.name_re.match(s)
if m is None:
return None
- gdict = m.groupdict()
#The following comments are for a name of the type: eval://dev=foo?bar=123;blah={a/b/c/d}
devname = m.group('devname') # foo
- normal_name = self.getDeviceName(str, full=False) #eval://dev=foo
- full_name = self.getDeviceName(str, full=True) #eval://db=_DefaultEvalDB;dev=foo
+ normal_name = self.getDeviceName(s, full=False) #eval://dev=foo
+ full_name = self.getDeviceName(s, full=True) #eval://db=_DefaultEvalDB;dev=foo
return full_name, normal_name, devname
@@ -205,8 +204,8 @@ class EvaluationConfigurationNameValidator(AbstractEvaluationNameValidator):
# 1 2 3 4 5 6 7 8 9 A
name_pattern = r'^(?P<scheme>eval|evaluation)://(db=(?P<dbname>[^?#;]+);)?(dev=(?P<devname>[^?#;]+);)?(?P<attrname>[^?#;]+)(\?(?!configuration=)(?P<subst>[^#?]*))?(\?configuration=?(?P<cfgkey>[^#?]*))$'
- def isValid(self,str, matchLevel = MatchLevel.ANY):
- m = self.name_re.match(str)
+ def isValid(self,s, matchLevel = MatchLevel.ANY):
+ m = self.name_re.match(s)
if m is None:
return False
elif matchLevel == MatchLevel.COMPLETE:
@@ -214,27 +213,27 @@ class EvaluationConfigurationNameValidator(AbstractEvaluationNameValidator):
else:
return True
- def getNames(self, str, factory=None):
+ def getNames(self, s, factory=None):
"""Returns the complete, normal and short names"""
- m = self.name_re.match(str)
+ m = self.name_re.match(s)
if m is None:
return None
#The following comments are for an example name like: "eval://dev=foo;bar*blah?bar=123;blah={a/b/c/d}?configuration=label"
cfg_key = m.group('cfgkey') # cfg_key = "label"
attr_name = m.group('attrname')
- normal_name = "%s;%s?configuration"%(self.getDeviceName(str, full=False),attr_name) #normal_name = "eval://dev=foo;bar*blah?configuration"
- expanded_attr_name = self.getExpandedTransformation(str)
- fullname = "%s;%s?configuration"%(self.getDeviceName(str, full=True),expanded_attr_name) #fullname = "eval://db=_DefaultEvalDB;dev=foo;123*{a/b/c/d}?configuration"
+ normal_name = "%s;%s?configuration"%(self.getDeviceName(s, full=False),attr_name) #normal_name = "eval://dev=foo;bar*blah?configuration"
+ expanded_attr_name = self.getExpandedTransformation(s)
+ fullname = "%s;%s?configuration"%(self.getDeviceName(s, full=True),expanded_attr_name) #fullname = "eval://db=_DefaultEvalDB;dev=foo;123*{a/b/c/d}?configuration"
return fullname, normal_name, cfg_key
- def getAttrName(self, str):
- names = self.getNames(str)
+ def getAttrName(self, s):
+ names = self.getNames(s)
if names is None: return None
return names[0][:-len('?configuration')] #remove the "?configuration" substring from the fullname
- def getExpandedTransformation(self, str):
+ def getExpandedTransformation(self, s):
'expands the attribute name by substituting all symbols'
- m = self.name_re.match(str)
+ m = self.name_re.match(s)
if m is None:
return None
transf = m.group('attrname')
@@ -644,7 +643,7 @@ class EvaluationConfiguration(taurus.core.TaurusConfiguration):
""" Returns the current configuration for the attribute."""
return self._attr_info
-class EvaluationFactory(taurus.core.util.Singleton, taurus.core.TaurusFactory, taurus.core.util.Logger):
+class EvaluationFactory(Singleton, taurus.core.TaurusFactory, Logger):
"""
A Singleton class that provides Evaluation related objects.
"""
@@ -659,7 +658,7 @@ class EvaluationFactory(taurus.core.util.Singleton, taurus.core.TaurusFactory, t
def init(self, *args, **kwargs):
"""Singleton instance initialization."""
name = self.__class__.__name__
- self.call__init__(taurus.core.util.Logger, name)
+ self.call__init__(Logger, name)
self.call__init__(taurus.core.TaurusFactory)
self.eval_attrs = weakref.WeakValueDictionary()
self.eval_devs = weakref.WeakValueDictionary()
@@ -784,7 +783,7 @@ class EvaluationFactory(taurus.core.util.Singleton, taurus.core.TaurusFactory, t
def _getConfigurationFromAttribute(self, attr):
cfg = attr.getConfig()
- cfg_name = attrObj.getFullName() + "?configuration"
+ cfg_name = attr.getFullName() + "?configuration"
self.eval_configs[cfg_name] = cfg
return cfg
@@ -876,7 +875,6 @@ def test1():
def test2():
- import taurus.core
a=taurus.Attribute('eval://[{sys/tg_test/1/short_scalar},{sys/tg_test/1/double_scalar}, {sys/tg_test/1/short_scalar}+{sys/tg_test/1/double_scalar}]')
#a=taurus.Attribute('eval://2*{sys/tg_test/1/short_scalar}+rand()')
class Dummy:
@@ -896,8 +894,8 @@ def test3():
import sys
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.panel import TaurusForm
- from taurus.qt.qtgui.plot import TaurusTrend
- from taurus.qt.qtgui.display import TaurusLabel
+ #from taurus.qt.qtgui.plot import TaurusTrend
+ #from taurus.qt.qtgui.display import TaurusLabel
app = TaurusApplication()
w = TaurusForm()
diff --git a/lib/taurus/core/evaluation/ipap_example.py b/lib/taurus/core/evaluation/ipap_example.py
index 9c07510..1299f4c 100644
--- a/lib/taurus/core/evaluation/ipap_example.py
+++ b/lib/taurus/core/evaluation/ipap_example.py
@@ -64,7 +64,7 @@ class IcepapDriverParam(EvaluationDevice):
try:
value = self.ipap.readParameter(axis, param)
- return double(value)
+ return float(value)
except:
return value
diff --git a/lib/taurus/core/release.py b/lib/taurus/core/release.py
index 98625f4..aa12ca3 100644
--- a/lib/taurus/core/release.py
+++ b/lib/taurus/core/release.py
@@ -53,7 +53,7 @@ name = 'taurus'
# bdist_deb does not accept underscores (a Debian convention).
-version_info = (3,0,0,'final',0)
+version_info = (3,0,1,'dev',0)
version = '.'.join(map(str, version_info[:3]))
revision = str(version_info[4])
diff --git a/lib/taurus/core/tango/sardana/macroserver.py b/lib/taurus/core/tango/sardana/macroserver.py
index c7aec57..25421a6 100644
--- a/lib/taurus/core/tango/sardana/macroserver.py
+++ b/lib/taurus/core/tango/sardana/macroserver.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -25,16 +25,13 @@
"""The macroserver submodule. It contains specific part of macroserver"""
-__all__ = [ 'BaseDoor', 'BaseMacroServer', 'registerExtensions' ]
+__all__ = [ 'BaseInputHandler', 'BaseDoor', 'BaseMacroServer',
+ 'registerExtensions' ]
__docformat__ = 'restructuredtext'
import sys
-import re
import weakref
-import copy
-import types
-import operator
import threading
import time
import uuid
@@ -43,14 +40,14 @@ import os.path as osp
import PyTango
from taurus import Device, Factory
-from taurus.core import TaurusEventType, TaurusSWDevState, \
+from taurus.core import TaurusManager, TaurusEventType, TaurusSWDevState, \
TaurusSerializationMode
from taurus.core.util import etree, CodecFactory, CaselessDict, Logger, \
EventGenerator, AttributeEventWait
-from taurus.core.util.console import NoColors, TermColors
from taurus.core.tango import TangoDevice
-from macro import MacroInfo, Macro, MacroNode, ParamFactory, RepeatNode, RepeatParamNode, SingleParamNode, ParamNode
+from macro import MacroInfo, Macro, MacroNode, ParamFactory, RepeatNode, \
+ RepeatParamNode, SingleParamNode, ParamNode
from sardana import BaseSardanaElementContainer, BaseSardanaElement
from pool import getChannelConfigs
CHANGE_EVT_TYPES = TaurusEventType.Change, TaurusEventType.Periodic
@@ -65,9 +62,9 @@ class Attr(Logger, EventGenerator):
self.call__init__(Logger, name)
event_name = '%s %s' % (dev.getNormalName(), name)
self.call__init__(EventGenerator, event_name)
-
+
self._attr.addListener(self)
-
+
def eventReceived(self, src, type, evt_value):
if type == TaurusEventType.Error:
self.fireEvent(None)
@@ -76,7 +73,7 @@ class Attr(Logger, EventGenerator):
self.fireEvent(evt_value.value)
else:
self.fireEvent(None)
-
+
def getTaurusAttribute(self):
return self._attr
@@ -85,18 +82,18 @@ class Attr(Logger, EventGenerator):
class LogAttr(Attr):
-
+
def __init__(self, dev, name, obj_class, attr, max_buff_size=4096):
self._log_buffer = []
self._max_buff_size = max_buff_size
self.call__init__(Attr, dev, name, obj_class, attr)
-
+
def getLogBuffer(self):
return self._log_buffer
-
+
def clearLogBuffer(self):
self._log_buffer = []
-
+
def eventReceived(self, src, type, evt_value):
if type == TaurusEventType.Change:
if evt_value is None or evt_value.value is None:
@@ -109,10 +106,36 @@ class LogAttr(Attr):
self.fireEvent(evt_value.value)
+class BaseInputHandler(object):
+
+ def __init__(self):
+ try:
+ self._input = raw_input
+ except NameError:
+ self._input = input
+
+ def input(self, input_data=None):
+ if input_data is None:
+ input_data = {}
+ prompt = input_data.get('prompt')
+ ret = dict(input=None, cancel=False)
+ try:
+ if prompt is None:
+ ret['input'] = self._input()
+ else:
+ ret['input'] = self._input(prompt)
+ except:
+ ret['cancel'] = True
+ return ret
+
+ def input_timeout(self, input_data):
+ print "input timeout"
+
+
class MacroServerDevice(TangoDevice):
- """A class encapsulating a generic macro server device (usually a
+ """A class encapsulating a generic macro server device (usually a
MacroServer or a Door"""
-
+
def _getEventWait(self):
if not hasattr(self, '_evt_wait'):
# create an object that waits for attribute events.
@@ -122,17 +145,17 @@ class MacroServerDevice(TangoDevice):
class ExperimentConfiguration(object):
-
+
def __init__(self, door):
self._door = door
-
+
def get(self, cache=False):
door = self._door
macro_server = door.macro_server
env = door.getEnvironment()
-
+
ret = dict(ScanDir=env.get('ScanDir'),
- DataCompressionRank=env.get('DataCompressionRank', -1),
+ DataCompressionRank=env.get('DataCompressionRank', 0),
PreScanSnapshot=env.get('PreScanSnapshot', []))
scan_file = env.get('ScanFile')
if scan_file is None:
@@ -141,21 +164,21 @@ class ExperimentConfiguration(object):
scan_file = [scan_file]
ret['ScanFile'] = scan_file
mnt_grps = macro_server.getElementNamesOfType("MeasurementGroup")
-
+
active_mnt_grp = env.get('ActiveMntGrp')
if active_mnt_grp is None and len(mnt_grps):
active_mnt_grp = mnt_grps[0]
door.putEnvironment('ActiveMntGrp', active_mnt_grp)
-
+
ret['ActiveMntGrp'] = active_mnt_grp
ret['MntGrpConfigs'] = mnt_grp_configs = CaselessDict()
-
+
if len(mnt_grps) == 0:
return ret
-
+
mnt_grp_grps = PyTango.Group("grp")
mnt_grp_grps.add(mnt_grps)
-
+
codec = CodecFactory().getCodec('json')
replies = mnt_grp_grps.read_attribute("configuration")
for mnt_grp, reply in zip(mnt_grps, replies):
@@ -163,7 +186,7 @@ class ExperimentConfiguration(object):
codec.decode(('json', reply.get_data().value),
ensure_ascii=True)[1]
return ret
-
+
def set(self, conf, mnt_grps=None):
"""Sets the ExperimentConfiguration dictionary."""
env = dict(ScanDir=conf.get('ScanDir'),
@@ -174,37 +197,48 @@ class ExperimentConfiguration(object):
if mnt_grps is None:
mnt_grps = conf['MntGrpConfigs'].keys()
self._door.putEnvironments(env)
-
+
codec = CodecFactory().getCodec('json')
for mnt_grp in mnt_grps:
- mnt_grp_cfg = conf['MntGrpConfigs'][mnt_grp]
- if mnt_grp_cfg is None: #a deleted mntGrp
- self._pool.DeleteElement(mnt_grp)
- else:
- try:
- mnt_grp_dev = Device(mnt_grp)
- except: #if the mnt_grp did not already exist, create it now
- chconfigs = getChannelConfigs(mnt_grp_cfg)
- chnames,chinfos = zip(*chconfigs) #unzipping
- self._pool.createMeasurementGroup([mnt_grp]+list(chnames))
- mnt_grp_dev = Device(mnt_grp)
-
- # TODO when we start using measurement group extension change the
- # code below with the following:
- # mnt_grp.setConfiguration(mnt_grp_cfg)
- data = codec.encode(('', mnt_grp_cfg))[1]
- mnt_grp_dev.write_attribute('configuration', data)
-
- @property
- def _pool(self):
- pooldict = self._door.macro_server.getElementsOfType('Pool')
- if len(pooldict)==0:
- raise ValueError('Cannot access the Pool')
- elif len(pooldict)>1:
- raise ValueError('Multiple pools are not supported')
- poolinfo = pooldict.values()[0]
- return poolinfo
-
+ try:
+ mnt_grp_cfg = conf['MntGrpConfigs'][mnt_grp]
+ if mnt_grp_cfg is None: #a mntGrp to be deleted
+ pool = self._getPoolOfElement(mnt_grp)
+ pool.DeleteElement(mnt_grp)
+ else:
+ try:
+ mnt_grp_dev = Device(mnt_grp)
+ except: #if the mnt_grp did not already exist, create it now
+ chconfigs = getChannelConfigs(mnt_grp_cfg)
+ chnames,chinfos = zip(*chconfigs) #unzipping
+ pool = self._getPoolOfElement(chnames[0]) #We assume that all the channels belong to the same pool!
+ pool.createMeasurementGroup([mnt_grp]+list(chnames))
+ mnt_grp_dev = Device(mnt_grp)
+
+ # TODO when we start using measurement group extension change the
+ # code below with the following:
+ # mnt_grp.setConfiguration(mnt_grp_cfg)
+ data = codec.encode(('', mnt_grp_cfg))[1]
+ mnt_grp_dev.write_attribute('configuration', data)
+ except Exception,e:
+ self.error('Could not create/delete/modify Measurement group "%s": %s',mnt_grp,repr(e))
+
+ def _getPoolOfElement(self, elementname):
+ ms = self._door.macro_server
+ einfo = ms.getElementInfo(elementname)
+ poolname = einfo.pool
+ return ms.getElementInfo(poolname)
+
+# @property
+# def _pool(self):
+# pooldict = self._door.macro_server.getElementsOfType('Pool')
+# if len(pooldict)==0:
+# raise ValueError('Cannot access the Pool')
+# elif len(pooldict)>1:
+# raise ValueError('Multiple pools are not supported')
+# poolinfo = pooldict.values()[0]
+# return poolinfo
+
class BaseDoor(MacroServerDevice):
""" Class encapsulating Door device functionality."""
@@ -212,7 +246,7 @@ class BaseDoor(MacroServerDevice):
On = PyTango.DevState.ON
Running = PyTango.DevState.RUNNING
Paused = PyTango.DevState.STANDBY
-
+
Critical = 'Critical'
Error = 'Error'
Warning = 'Warning'
@@ -229,13 +263,12 @@ class BaseDoor(MacroServerDevice):
# maximum execution time without user interruption
InteractiveTimeout = 0.1
-
-
+
+
def __init__(self, name, **kw):
self._log_attr = CaselessDict()
self._block_lines = 0
self._macro_server = None
-
self._running_macros = None
self._running_macro = None
self._last_running_macro = None
@@ -245,12 +278,13 @@ class BaseDoor(MacroServerDevice):
self._debug = kw.get("debug", False)
self._output_stream = kw.get("output", sys.stdout)
self._writeLock = threading.Lock()
+ self._input_handler = self.create_input_handler()
self.call__init__(MacroServerDevice, name, **kw)
-
+
self._old_door_state = PyTango.DevState.UNKNOWN
self._old_sw_door_state = TaurusSWDevState.Uninitialized
-
+
self.getStateObj().addListener(self.stateChanged)
for log_name in self.log_streams:
@@ -261,21 +295,30 @@ class BaseDoor(MacroServerDevice):
else:
attr.subscribeEvent(self.logReceived, log_name)
self._log_attr[log_name] = attr
-
+
+ input_attr = self.getAttribute("Input")
+ input_attr.addListener(self.inputReceived)
+
record_data_attr = self.getAttribute('RecordData')
record_data_attr.addListener(self.recordDataReceived)
-
+
macro_status_attr = self.getAttribute('MacroStatus')
macro_status_attr.addListener(self.macroStatusReceived)
-
+
self._experiment_configuration = ExperimentConfiguration(self)
+ def create_input_handler(self):
+ return BaseInputHandler()
+
+ def get_input_handler(self):
+ return self._input_handler
+
def get_color_mode(self):
return "NoColor"
-
+
#def macrosChanged(self, s, v, t):
# pass
-
+
@property
def log_start(self):
if not hasattr(self, "_log_start"):
@@ -292,7 +335,7 @@ class BaseDoor(MacroServerDevice):
BaseDoor.Debug : kls.DarkGray,
BaseDoor.Result : kls.LightGreen }
return self._log_start
-
+
@property
def log_stop(self):
if not hasattr(self, "_log_stop"):
@@ -309,10 +352,10 @@ class BaseDoor(MacroServerDevice):
BaseDoor.Debug : kls.Normal,
BaseDoor.Result : kls.Normal }
return self._log_stop
-
+
def getStateAttr(self):
return self._state_attr
-
+
@property
def macro_server(self):
if self._macro_server is None:
@@ -340,16 +383,16 @@ class BaseDoor(MacroServerDevice):
def setDebugMode(self, state):
self._debug = state
-
+
def getDebugMode(self):
return self._debug
def setSilent(self, yesno):
self._silent = yesno
-
+
def isSilent(self):
return self._silent
-
+
def getLogObj(self, log_name='Debug'):
return self._log_attr.get(log_name, None)
@@ -366,7 +409,7 @@ class BaseDoor(MacroServerDevice):
if not synch:
self.command_inout("AbortMacro")
return
-
+
evt_wait = AttributeEventWait(self.getAttribute("state"))
evt_wait.lock()
try:
@@ -382,7 +425,7 @@ class BaseDoor(MacroServerDevice):
if not synch:
self.command_inout("StopMacro")
return
-
+
evt_wait = AttributeEventWait(self.getAttribute("state"))
evt_wait.lock()
try:
@@ -404,9 +447,9 @@ class BaseDoor(MacroServerDevice):
def preRunMacro(self, obj, parameters):
self._clearRunMacro()
-
+
xml_root = None
- if type(obj) in types.StringTypes:
+ if isinstance(obj ,(str, unicode)):
if obj.startswith('<') and not parameters:
xml_root = etree.fromstring(obj)
else:
@@ -425,12 +468,12 @@ class BaseDoor(MacroServerDevice):
xml_macro.set('name', m[0])
xml_macro.set('id', str(uuid.uuid1()))
for p in m[1]:
- xml_param = etree.SubElement(xml_macro, 'param', value=p)
+ etree.SubElement(xml_macro, 'param', value=p)
elif etree.iselement(obj):
xml_root = obj
else:
raise TypeError('obj must be a string or a etree.Element')
-
+
self._running_macros = {}
for macro_xml in xml_root.xpath('//macro'):
id, name = macro_xml.get('id'), macro_xml.get('name')
@@ -439,7 +482,7 @@ class BaseDoor(MacroServerDevice):
def postRunMacro(self, result, synch):
pass
-
+
def runMacro(self, obj, parameters=[], synch=False):
self._user_xml = self.preRunMacro(obj, parameters)
result = self._runMacro(self._user_xml, synch=synch)
@@ -465,34 +508,56 @@ class BaseDoor(MacroServerDevice):
evt_wait.unlock()
evt_wait.disconnect()
return result
-
+
def stateChanged(self, s, t, v):
self._old_door_state = self.getState()
self._old_sw_door_state = self.getSWState()
def resultReceived(self, log_name, result):
"""Method invoked by the arrival of a change event on the Result attribute"""
- if self._ignore_logs or self._running_macro is None:
+ if self._ignore_logs or self._running_macro is None:
return
self._running_macro.setResult(result)
return result
-
+
def putEnvironment(self, name, value):
self.macro_server.putEnvironment(name, value)
def putEnvironments(self, obj):
self.macro_server.putEnvironments(obj)
-
+
setEnvironment = putEnvironment
setEnvironments = putEnvironments
-
+
def getEnvironment(self, name=None):
return self.macro_server.getEnvironment(name=name)
+ def inputReceived(self, s, t, v):
+ if t not in CHANGE_EVT_TYPES:
+ return
+ if v is None or self._running_macros is None:
+ return
+ input_data = CodecFactory().decode(('json', v.value))
+ self.processInput(input_data)
+
+ def processInput(self, input_data):
+ TaurusManager().addJob(self._processInput, None, input_data)
+
+ def _processInput(self, input_data):
+ input_type = input_data['type']
+ if input_type == 'input':
+ result = self._input_handler.input(input_data)
+ if result is '' and 'default_value' in input_data:
+ result = input_data['default_value']
+ result = CodecFactory().encode('json', ('', result))[1]
+ self.write_attribute('Input', result)
+ elif input_type == 'timeout':
+ self._input_handler.input_timeout(input_data)
+
def recordDataReceived(self, s, t, v):
if t not in CHANGE_EVT_TYPES: return
return self._processRecordData(v)
-
+
def _processRecordData(self, data):
if data is None: return
# make sure we get it as string since PyTango 7.1.4 returns a buffer
@@ -503,7 +568,11 @@ class BaseDoor(MacroServerDevice):
if size == 0: return
format = data[0]
codec = CodecFactory().getCodec(format)
- return codec.decode(data)
+ data = codec.decode(data)
+ return data
+
+ def processRecordData(self, data):
+ pass
def macroStatusReceived(self, s, t, v):
if v is None or self._running_macros is None:
@@ -517,7 +586,7 @@ class BaseDoor(MacroServerDevice):
return
format = v[0]
codec = CodecFactory().getCodec(format)
-
+
# make sure we get it as string since PyTango 7.1.4 returns a buffer
# object and json.loads doesn't support buffer objects (only str)
v[1] = str(v[1])
@@ -536,7 +605,7 @@ class BaseDoor(MacroServerDevice):
def logReceived(self, log_name, output):
if not output or self._silent or self._ignore_logs:
return
-
+
if log_name == self.Debug and not self._debug:
return
@@ -554,7 +623,7 @@ class BaseDoor(MacroServerDevice):
self._block_lines += 1
o += self.log_stop[log_name]
self.write(o)
-
+
def write(self, msg, stream=None):
if self.isSilent():
return
@@ -571,16 +640,16 @@ class BaseDoor(MacroServerDevice):
return
out.write(msg)
out.flush()
-
+
def writeln(self, msg='', stream=None):
self.write("%s\n" % msg, stream=stream)
-
+
def getExperimentConfigurationObj(self):
return self._experiment_configuration
-
+
def getExperimentConfiguration(self):
return self._experiment_configuration.get()
-
+
def setExperimentConfiguration(self, config, mnt_grps=None):
self._experiment_configuration.set(config, mnt_grps=mnt_grps)
@@ -590,7 +659,7 @@ class UnknownMacroServerElementFormat(Exception):
class MacroPath(object):
-
+
def __init__(self, ms):
self._ms = weakref.ref(ms)
self.refresh()
@@ -602,30 +671,30 @@ class MacroPath(object):
class Environment(dict):
-
+
def __init__(self, macro_server):
dict.__setattr__(self, "_macro_server_", weakref.ref(macro_server))
-
+
def __setattr__(self, key, value):
ms = self._macro_server_()
if ms is not None:
ms.putEnvironment(key, value)
-
+
def __getattr__(self, key):
return self[key]
-
+
def __delattr__(self, key):
ms = self._macro_server_()
if ms is not None:
ms.removeEnvironment(key)
-
+
def __dir__(self):
return [ key for key in self.keys() if not key.startswith("_") ]
class BaseMacroServer(MacroServerDevice):
"""Class encapsulating Macro Server device functionality."""
-
+
def __init__(self, name, **kw):
self._env = Environment(self)
self._elements = BaseSardanaElementContainer()
@@ -640,25 +709,25 @@ class BaseMacroServer(MacroServerDevice):
attr.setSerializationMode(TaurusSerializationMode.Serial)
attr.addListener(self.on_environment_changed)
attr.setSerializationMode(TaurusSerializationMode.Concurrent)
-
+
NO_CLASS_TYPES = 'ControllerClass', 'ControllerLibrary', \
'MacroLibrary', 'Instrument', 'Meta', 'ParameterType'
-
+
def on_environment_changed(self, evt_src, evt_type, evt_value):
try:
return self._on_environment_changed(evt_src, evt_type, evt_value)
- except Exception, e:
+ except Exception:
self.error("Exception occurred processing environment")
self.error("Details:", exc_info=1)
return set(), set(), set()
-
+
def _on_environment_changed(self, evt_src, evt_type, evt_value):
ret = added, removed, changed = set(), set(), set()
if evt_type not in CHANGE_EVT_TYPES:
return ret
-
+
env = CodecFactory().decode(evt_value.value)
-
+
for key, value in env.get('new', {}).items():
self._addEnvironment(key, value)
added.add(key)
@@ -670,42 +739,42 @@ class BaseMacroServer(MacroServerDevice):
self._addEnvironment(key, value)
changed.add(key)
return ret
-
+
def _addEnvironment(self, key, value):
self._env[key] = value
-
+
def _removeEnvironment(self, key):
try:
self._env.pop(key)
except KeyError:
pass
-
+
def putEnvironment(self, name, value):
self.putEnvironments({ name : value })
-
+
def putEnvironments(self, obj):
obj = dict(new=obj)
codec = CodecFactory().getCodec('pickle')
self.write_attribute('Environment', codec.encode(('', obj)))
-
+
setEnvironment = putEnvironment
setEnvironments = putEnvironments
-
+
def getEnvironment(self, name=None):
if name is None:
return self._env
else:
return self._env[name]
-
+
def removeEnvironment(self, key):
keys = key,
return self.removeEnvironments(keys)
-
+
def removeEnvironments(self, keys):
obj = { 'del' : keys }
codec = CodecFactory().getCodec('pickle')
self.write_attribute('Environment', codec.encode(('', obj)))
-
+
def getObject(self, element_info):
elem_type = element_info.getType()
data = element_info._data
@@ -716,21 +785,21 @@ class BaseMacroServer(MacroServerDevice):
else:
obj = self._createDeviceObject(element_info)
return obj
-
+
def _createMacroClassObject(self, element_info):
return MacroInfo(from_json=element_info._data)
-
+
def _createDeviceObject(self, element_info):
return Factory().getDevice(element_info.full_name)
-
+
def on_elements_changed(self, evt_src, evt_type, evt_value):
try:
return self._on_elements_changed(evt_src, evt_type, evt_value)
- except Exception, e:
+ except Exception:
self.error("Exception occurred processing elements")
self.error("Details:", exc_info=1)
return set(), set(), set()
-
+
def _on_elements_changed(self, evt_src, evt_type, evt_value):
ret = added, removed, changed = set(), set(), set()
if evt_type not in CHANGE_EVT_TYPES:
@@ -741,7 +810,7 @@ class BaseMacroServer(MacroServerDevice):
self.error("Could not decode element info format=%s len=%s",
evt_value.value[0], len(evt_value.value[1]))
return ret
-
+
for element_data in elems.get('new', ()):
element_data['manager'] = self
element = self._addElement(element_data)
@@ -755,51 +824,51 @@ class BaseMacroServer(MacroServerDevice):
element = self._addElement(element_data)
changed.add(element)
return ret
-
+
def _addElement(self, element_data):
element = BaseSardanaElement(**element_data)
self.getElementsInfo().addElement(element)
return element
-
+
def _removeElement(self, element_data):
name = element_data['name']
element = self.getElementInfo(name)
self.getElementsInfo().removeElement(element)
return element
-
+
def getElementsInfo(self):
return self._elements
-
+
def getElements(self):
return self.getElementsInfo().getElements()
-
+
def getElementInfo(self, name):
return self.getElementsInfo().getElement(name)
-
+
def getElementNamesOfType(self, elem_type):
return self.getElementsInfo().getElementNamesOfType(elem_type)
-
+
def getElementNamesWithInterface(self, interface):
return self.getElementsInfo().getElementNamesWithInterface(interface)
def getElementsWithInterface(self, interface):
return self.getElementsInfo().getElementsWithInterface(interface)
-
+
def getElementsWithInterfaces(self, interfaces):
return self.getElementsInfo().getElementsWithInterfaces(interfaces)
-
+
def getElementsOfType(self, elem_type):
return self.getElementsInfo().getElementsOfType(elem_type)
-
+
def getElementsOfTypes(self, elem_types):
elems = CaselessDict()
for elem_type in elem_types:
elems.update(self.getElementsOfType(elem_type))
return elems
-
+
def getInterfaces(self):
return self.getElementsInfo().getInterfaces()
-
+
def getExpChannelElements(self):
channel_types = "CTExpChannel", "ZeroDExpChannel", "OneDExpChannel", \
"PseudoCounter"
@@ -808,7 +877,7 @@ class BaseMacroServer(MacroServerDevice):
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Macro API
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-
+
def getMacros(self):
return dict(self.getElementsInfo().getElementsWithInterface('MacroCode'))
@@ -820,19 +889,19 @@ class BaseMacroServer(MacroServerDevice):
def getMacroStrList(self):
return self.getElementNamesWithInterface('MacroCode')
-
+
def getMacroNodeObj(self, macro_name):
"""
This method retrieves information about macro from MacroServer
and creates MacroNode object, filled with all information about parameters.
-
+
:param macro_name: (str) macro name
-
+
:return: (MacroNode)
-
+
See Also: fillMacroNodeAddidtionalInfos
"""
-
+
macroNode = MacroNode(name=macro_name)
macroInfoObj = self.getMacroInfoObj(macro_name)
if macroInfoObj is None: return
@@ -849,7 +918,7 @@ class BaseMacroServer(MacroServerDevice):
param = ParamFactory(paramInfo)
macroNode.addParam(param)
return macroNode
-
+
def validateMacroName(self, macroName):
macroInfo = self.getElementInfo(macroName)
if macroInfo is None:
@@ -857,13 +926,13 @@ class BaseMacroServer(MacroServerDevice):
elif macroInfo.type != 'MacroClass':
raise Exception("%s element is not a macro." % macroName)
return True
-
+
def validateMacroNode(self, macroNode):
paramNodes = macroNode.children()
for paramNode in paramNodes:
self.validateParamNode(paramNode)
return True
-
+
def validateParamNode(self, paramNode):
assert isinstance(paramNode, ParamNode)
if isinstance(paramNode, SingleParamNode):
@@ -871,12 +940,12 @@ class BaseMacroServer(MacroServerDevice):
else:
self.validateRepeatParam(paramNode)
return True
-
+
def validateSingleParam(self, singleParamNode):
name = singleParamNode.name()
type = singleParamNode.type()
value = singleParamNode.value()
-
+
if type == "Boolean":
pass
elif type == "Env":
@@ -890,7 +959,7 @@ class BaseMacroServer(MacroServerDevice):
elif type == "String":
pass
elif type == "User":
- pass
+ pass
elif type == "MotorParam":
pass
elif type == "Integer":
@@ -917,7 +986,7 @@ class BaseMacroServer(MacroServerDevice):
if not value in allowedValues:
raise Exception("%s element with %s interface does not exist in this sardana system." % (value, type))
return True
-
+
def validateRepeatParam(self, repeatParamNode):
paramName = repeatParamNode.name()
if repeatParamNode.isBelowMin():
@@ -933,15 +1002,15 @@ class BaseMacroServer(MacroServerDevice):
else:
self.validateRepeatParam(param)
return True
-
-
+
+
def fillMacroNodeAdditionalInfos(self, macroNode):
"""
- This method filles macroNode information which couldn't be stored
+ This method filles macroNode information which couldn't be stored
in XML file.
-
+
:param macroNode: (MacroNode) macro node obj populated from XML information
-
+
See Also: getMacroNodeObj
"""
macroName = macroNode.name()
@@ -958,10 +1027,10 @@ class BaseMacroServer(MacroServerDevice):
paramList = macroInfoObj.getParamList()
for paramNode, paramInfo in zip(macroNode.params(), paramList):
self.__fillParamNodeAdditionalInfos(paramNode, paramInfo)
-
+
def __fillParamNodeAdditionalInfos(self, paramNode, paramInfo):
"""
- This is a protected method foreseen to use only internally by
+ This is a protected method foreseen to use only internally by
fillMacroNodeAdditionaInfos, to be called for every param node obj."""
type = paramInfo.get('type')
paramNode.setDescription(str(paramInfo.get("description")))
@@ -977,14 +1046,14 @@ class BaseMacroServer(MacroServerDevice):
else:
paramNode.setType(str(type))
paramNode.setDefValue(str(paramInfo.get("default_value")))
-
+
def recreateMacroNodeAndFillAdditionalInfos(self, macroNode):
"""
- This method filles macroNode information which couldn't be stored
+ This method filles macroNode information which couldn't be stored
in plain text file.
-
+
:param macroNode: (MacroNode) macro node obj populated from plain text information
-
+
See Also: getMacroNodeObj
"""
macroName = macroNode.name()
@@ -1002,10 +1071,10 @@ class BaseMacroServer(MacroServerDevice):
return
paramInfosList = macroInfoObj.getParamList()
paramNodes = macroNode.params()
- paramIndex = 0
+ paramIndex = 0
for paramNode, paramInfo in zip(paramNodes, paramInfosList):
paramType = paramInfo.get('type')
- if isinstance(paramType,list):
+ if isinstance(paramType,list):
paramNode = self.__recreateParamRepeatNodes(macroNode, paramIndex, paramInfo)
else:
paramNode.setName(paramInfo.get("name"))
@@ -1020,15 +1089,15 @@ class BaseMacroServer(MacroServerDevice):
lastParam = macroNode.popParam()
paramNodes.append(lastParam)
paramNodes.reverse()
-
- nrOfSingleParams = len(paramNodes)
+
+ nrOfSingleParams = len(paramNodes)
paramName = repeatParamInfo.get("name")
min = repeatParamInfo.get("min")
max = repeatParamInfo.get("max")
repeatParamChildrenInfos = repeatParamInfo.get("type")
-
+
if nrOfSingleParams % len(repeatParamChildrenInfos):
- raise Exception("Param repeat %s doesn't have correct number of repetitions" % paramName)
+ raise Exception("Param repeat %s doesn't have correct number of repetitions" % paramName)
nrOfRepeats = nrOfSingleParams / len(repeatParamChildrenInfos)
repeatParamNode = RepeatParamNode(macroNode, repeatParamInfo)
for repeatIdx in range(nrOfRepeats):
@@ -1041,18 +1110,18 @@ class BaseMacroServer(MacroServerDevice):
repeatParamNode.insertChild(repeatNode)
macroNode.addParam(repeatParamNode)
return repeatParamNode
-
+
def __recreateParamNodeAdditionalInfos(self, paramNode, paramInfo):
"""
- This is a protected method foreseen to use only internally by
+ This is a protected method foreseen to use only internally by
fillMacroNodeAdditionaInfos, to be called for every param node obj."""
paramType = paramInfo.get('type')
min = paramInfo.get("min")
max = paramInfo.get("max")
paramNode.setMin(min)
- paramNode.setMax(max)
+ paramNode.setMax(max)
paramNode.setDescription(str(paramInfo.get("description")))
-
+
if type(paramType) == list:
paramNode.setParamsInfo(paramType)
for repeatNode in paramNode.children():
@@ -1061,8 +1130,8 @@ class BaseMacroServer(MacroServerDevice):
else:
paramNode.setType(paramType)
paramNode.setDefValue(str(paramInfo.get("default_value")))
-
-
+
+
def getMacroPathObj(self, cache=False):
if not hasattr(self, "_macro_path"):
self._macro_path = MacroPath(self)
diff --git a/lib/taurus/core/tango/sardana/motion.py b/lib/taurus/core/tango/sardana/motion.py
index e6c7d83..25c472e 100644
--- a/lib/taurus/core/tango/sardana/motion.py
+++ b/lib/taurus/core/tango/sardana/motion.py
@@ -67,6 +67,12 @@ class Moveable:
""" generator for motor positions"""
pass
+ def getLastMotionTime(self):
+ raise NotImplementedError
+
+ def getTotalLastMotionTime():
+ raise NotImplementedError
+
def abort(self, wait_ready=True, timeout=None):
pass
@@ -125,7 +131,14 @@ class BaseMotion(Moveable):
class MotionGroup(BaseMotion):
""" A virtual motion group object """
-
+
+ def __init__(self, elements, moveable_srcs, allow_repeat=False,
+ allow_unknown=False, read_only=False):
+ BaseMotion.__init__(self, elements, moveable_srcs,
+ allow_repeat=allow_repeat,
+ allow_unknown=allow_unknown, read_only=read_only)
+ self.__total_motion_time = 0
+
def init_by_movables(self, elements, moveable_srcs, allow_repeat, allow_unknown):
self.moveable_list = elements
@@ -133,6 +146,13 @@ class MotionGroup(BaseMotion):
moveables = [ self.getMoveable(moveable_srcs, name) for name in names ]
self.init_by_movables(moveables, moveable_srcs, allow_repeat,
allow_unknown)
+
+ def getLastMotionTime(self):
+ times = [ moveable.getLastMotionTime() for moveable in self.moveable_list ]
+ return max(times)
+
+ def getTotalLastMotionTime():
+ return self.__total_motion_time
def startMove(self, pos_list, timeout=None):
if self.read_only:
@@ -153,6 +173,7 @@ class MotionGroup(BaseMotion):
moveable.waitMove(timeout=timeout, id=id[i])
def move(self, new_pos, timeout=None):
+ start_time = time.time()
states, positions = [], []
for moveable, pos in zip(self.moveable_list, new_pos):
res = moveable.move(pos, timeout=timeout)
@@ -168,6 +189,7 @@ class MotionGroup(BaseMotion):
state = PyTango.DevState.UNKNOWN
elif PyTango.DevState.MOVING in states:
state = PyTango.DevState.MOVING
+ self.__total_motion_time = time.time() - start_time
return state, positions
def iterMove(self, new_pos, timeout=None):
@@ -212,13 +234,20 @@ class MotionGroup(BaseMotion):
class Motion(BaseMotion):
""" A motion object """
+
+ def __init__(self, elements, moveable_srcs, allow_repeat=False,
+ allow_unknown=False, read_only=False):
+ BaseMotion.__init__(self, elements, moveable_srcs,
+ allow_repeat=allow_repeat,
+ allow_unknown=allow_unknown, read_only=read_only)
+ self.__total_motion_time = 0
def init_by_movables(self, elements, moveable_srcs, allow_repeat, allow_unknown):
# TODO: Optimize this. Dont call init_by_names. It its possible to do it
# manually with some performance gain
names = [ elem.getName() for elem in elements]
self.init_by_names(names, moveable_srcs, allow_repeat, allow_unknown)
-
+
def init_by_names(self, names, moveable_srcs, allow_repeat, allow_unknown):
ms_elem_names = self.getElemNamesByMoveableSource(names, moveable_srcs,
@@ -306,7 +335,14 @@ class Motion(BaseMotion):
if moveable is None and not allow_unknown:
raise Exception("Moveable item %s not found" % name)
return ms_elems
+
+ def getLastMotionTime(self):
+ times = [ moveable.getLastMotionTime() for moveable in self.moveable_list ]
+ return max(times)
+ def getTotalLastMotionTime():
+ return self.__total_motion_time
+
def startMove(self, pos_list, timeout=None):
if self.read_only:
raise Exception("Trying to move read only motion")
@@ -331,10 +367,10 @@ class Motion(BaseMotion):
moveable.waitMove(timeout=timeout, id=id[i])
def move(self, new_pos, timeout=None):
- #assert len(self.moveable_list) == 1, "for now we support only 'simple' motions!!!!"
+ start_time = time.time()
if len(self.moveable_list) == 1:
moveable = self.moveable_list[0]
- return moveable.move(new_pos, timeout=timeout)
+ ret = moveable.move(new_pos, timeout=timeout)
else:
start, ids = 0, []
for moveable in self.moveable_list:
@@ -356,7 +392,9 @@ class Motion(BaseMotion):
state = PyTango.DevState.UNKNOWN
elif PyTango.DevState.MOVING in states:
state = PyTango.DevState.MOVING
- return state, positions
+ ret = state, positions
+ self.__total_motion_time = time.time()
+ return ret
def iterMove(self, new_pos, timeout=None):
""" generator for motor positions"""
diff --git a/lib/taurus/core/tango/sardana/pool.py b/lib/taurus/core/tango/sardana/pool.py
index 3ed72bc..b5bc124 100644
--- a/lib/taurus/core/tango/sardana/pool.py
+++ b/lib/taurus/core/tango/sardana/pool.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -38,16 +38,14 @@ __docformat__ = 'restructuredtext'
import sys
import os
import weakref
-import re
-import thread
import time
import operator
import traceback
from PyTango import DevState, AttrDataFormat, AttrQuality, DevFailed, \
- DeviceProxy, AttributeProxy
+ DeviceProxy
-from taurus import Factory
+from taurus import Factory, Device
from taurus.core import TaurusEventType, AttributeNameValidator
from taurus.core.util import Logger, CaselessDict, CodecFactory, \
EventGenerator, AttributeEventWait, AttributeEventIterator
@@ -84,58 +82,58 @@ class AbortException(InterruptException):
pass
class BaseElement(object):
- """ The base class for elements in the Pool (Pool itself, Motor,
+ """ The base class for elements in the Pool (Pool itself, Motor,
ControllerClass, ExpChannel all should inherit from this class directly or
- indirectly)
-
- - At the object level:
- self._name_lower - the lower case string name of the object (used for
- __cmp__)
- self._full_pool_name - the original string coming from the Pool
- self._pool - the pool object
+ indirectly)
"""
-
+
def __repr__(self):
- pd = self._pool_data
+ pd = self.getPoolData()
return "{0}({1})".format(pd['type'], pd['full_name'])
-
+
def __str__(self):
return self.getName()
-
+
def serialize(self):
- return self._pool_data
-
+ return self.getPoolData()
+
def str(self, n=0):
"""Returns a sequence of strings representing the object in 'consistent'
way. Default is to return <name>, <controller name>, <axis>
-
+
:param n: the number of elements in the tuple."""
if n == 0:
return CodecFactory.encode(('json'), self.serialize())
return self._str_tuple[:n]
-
+
def __cmp__(self,o):
- return cmp(self._name_lower, o._name_lower)
-
+ return cmp(self.getPoolData()['full_name'], o.getPoolData()['full_name'])
+
def getName(self):
- return self._pool_data['name']
+ return self.getPoolData()['name']
def getPoolObj(self):
- return self._pool
+ return self._pool_obj
+
+ def getPoolData(self):
+ try:
+ return self._pool_data
+ except AttributeError:
+ self._pool_data = self._find_pool_data()
+ return self._pool_data
class ControllerClass(BaseElement):
-
+
def __init__(self, **kw):
self.__dict__.update(kw)
- self._name_lower = self.name
self.path, self.f_name = os.path.split(self.file_name)
self.lib_name, self.ext = os.path.splitext(self.f_name)
-
+
def __repr__(self):
- pd = self._pool_data
+ pd = self.getPoolData()
return "ControllerClass({0})".format(pd['full_name'])
-
+
def getSimpleFileName(self):
return self.f_name
@@ -144,7 +142,7 @@ class ControllerClass(BaseElement):
def getClassName(self):
return self.getName()
-
+
def getType(self):
return self.getTypes()[0]
@@ -153,13 +151,13 @@ class ControllerClass(BaseElement):
def getLib(self):
return self.f_name
-
+
def getGender(self):
return self.gender
-
+
def getModel(self):
return self.model
-
+
def getOrganization(self):
return self.organization
@@ -172,10 +170,9 @@ class ControllerClass(BaseElement):
class ControllerLib(BaseElement):
-
+
def __init__(self, **kw):
self.__dict__.update(kw)
- self._name_lower = self.name
def getType(self):
return self.getTypes()[0]
@@ -186,7 +183,7 @@ class ControllerLib(BaseElement):
class TangoAttributeEG(Logger, EventGenerator):
"""An event generator for a 'State' attribute"""
-
+
def __init__(self, attr):
self._attr = attr
self.call__init__(Logger, 'EG', attr)
@@ -194,10 +191,10 @@ class TangoAttributeEG(Logger, EventGenerator):
self.call__init__(EventGenerator, event_name)
self._attr.addListener(self)
-
+
def getAttribute(self):
return self._attr
-
+
def eventReceived(self, evt_src, evt_type, evt_value):
"""Event handler from Taurus"""
if evt_type not in CHANGE_EVT_TYPES:
@@ -216,18 +213,21 @@ class TangoAttributeEG(Logger, EventGenerator):
self.debug("Details:", exc_info=1)
self.last_val = None
return EventGenerator.read(self)
-
+
def readValue(self, force=False):
r = self.read(force=force)
if r is None:
# do a retry
r = self.read(force=force)
return r
-
+
+ def write(self, value):
+ self._attr.write(value, with_read=False)
+
def __getattr__(self, name):
return getattr(self._attr, name)
-
-
+
+
def reservedOperation(fn):
def new_fn(*args, **kwargs):
self = args[0]
@@ -246,48 +246,62 @@ def reservedOperation(fn):
return new_fn
+def get_pool_for_device(db, device):
+ server_devs = db.get_device_class_list(device.info().server_id)
+ for dev_name, klass_name in zip(server_devs[0::2], server_devs[1::2]):
+ if klass_name == "Pool":
+ return Device(dev_name)
+
+
class PoolElement(BaseElement, TangoDevice):
"""Base class for a Pool element device."""
-
- def __init__(self, name, **kw):
+
+ def __init__(self, name, **kwargs):
"""PoolElement initialization."""
self._reserved = None
self._evt_wait = None
- self.call__init__(TangoDevice, name, **kw)
- self._name_lower = self.getName().lower()
-
+ self.__go_start_time = 0
+ self.__go_end_time = 0
+ self.__go_time = 0
+ self._total_go_time = 0
+ self.call__init__(TangoDevice, name, **kwargs)
+
# dict<string, TangoAttributeEG>
# key : the attribute name
- # value : the corresponding TangoAttributeEG
+ # value : the corresponding TangoAttributeEG
self._attrEG = CaselessDict()
-
+
# force the creation of a state attribute
self.getStateEG()
+ def _find_pool_data(self):
+ pool = get_pool_for_device(self.getParentObj(), self.getHWObj())
+ return pool.getElementInfo(self.getFullName())._data
+
def cleanUp(self):
TangoDevice.cleanUp(self)
self._reserved = None
f = self.factory()
-
+
attr_map = self._attrEG
for attr_name in attr_map.keys():
attrEG = attr_map.pop(attr_name)
attr = attrEG.getAttribute()
attrEG = None
f.removeExistingAttribute(attr)
-
+
def reserve(self, obj):
if obj is None:
self._reserved = None
return
self._reserved = weakref.ref(obj, self._unreserveCB)
-
+
def _unreserveCB(self, obj):
self.unreserve()
-
+
def unreserve(self):
self._reserved =None
-
+
def isReserved(self, obj=None):
if obj is None:
return self._reserved is not None
@@ -301,18 +315,23 @@ class PoolElement(BaseElement, TangoDevice):
def getReserved(self):
if self._reserved is None: return None
return self._reserved()
-
+
+ def dump_attributes(self):
+ attr_names = self.get_attribute_list()
+ req_id = self.read_attributes_asynch(attr_names)
+ return self.read_attributes_reply(req_id, 2000)
+
def _getAttrValue(self, name, force=False):
attrEG = self._getAttrEG(name)
if attrEG is None: return None
return attrEG.readValue(force=force)
-
+
def _getAttrEG(self, name):
attrEG = self.getAttrEG(name)
if attrEG is None:
attrEG = self._createAttribute(name)
return attrEG
-
+
def _createAttribute(self, name):
attrObj = self.getAttribute(name)
if attrObj is None:
@@ -321,34 +340,32 @@ class PoolElement(BaseElement, TangoDevice):
attrEG = TangoAttributeEG(attrObj)
self._attrEG[name] = attrEG
return attrEG
-
+
def _getEventWait(self):
if self._evt_wait is None:
# create an object that waits for attribute events.
# each time we use it we have to connect and disconnect to an attribute
self._evt_wait = AttributeEventWait()
return self._evt_wait
-
+
def _clearEventWait(self):
self._evt_wait = None
-
+
def getStateEG(self):
return self._getAttrEG('state')
-
- def __cmp__(self,o):
- return cmp(self._name_lower, o._name_lower)
def getControllerName(self):
- return self._pool_data['controller']
-
+ return self.getControllerObj().name
+
def getControllerObj(self):
- return self.getPoolObj().getObj("Controller", self.getControllerName())
-
+ full_ctrl_name = self.getPoolData()['controller']
+ return self.getPoolObj().getObj(full_ctrl_name, "Controller")
+
def getAxis(self):
- return self._pool_data['axis']
+ return self.getPoolData()['axis']
def getType(self):
- return self._pool_data['type']
+ return self.getPoolData()['type']
def getPoolObj(self):
return self._pool_obj
@@ -359,7 +376,7 @@ class PoolElement(BaseElement, TangoDevice):
def getAttrEG(self, name):
"""Returns the TangoAttributeEG object"""
return self._attrEG.get(name)
-
+
def getAttrObj(self, name):
"""Returns the taurus.core.TangoAttribute object"""
attrEG = self._attrEG.get(name)
@@ -369,18 +386,18 @@ class PoolElement(BaseElement, TangoDevice):
def getInstrumentObj(self):
return self._getAttrEG('instrument')
-
+
def getInstrumentName(self, force=False):
instr_name = self._getAttrValue('instrument', force=force)
if not instr_name: return ''
#instr_name = instr_name[:instr_name.index('(')]
return instr_name
-
+
def getInstrument(self):
instr_name = self.getInstrumentName()
if not instr_name: return None
return self.getPoolObj().getObj("Instrument", instr_name)
-
+
@reservedOperation
def start(self, *args, **kwargs):
evt_wait = self._getEventWait()
@@ -388,7 +405,8 @@ class PoolElement(BaseElement, TangoDevice):
evt_wait.lock()
try:
evt_wait.waitEvent(DevState.MOVING, equal=False)
- ts1 = time.time()
+ self.__go_time = 0
+ self.__go_start_time = ts1 = time.time()
self._start(*args, **kwargs)
ts2 = time.time()
evt_wait.waitEvent(DevState.MOVING, after=ts1)
@@ -410,14 +428,28 @@ class PoolElement(BaseElement, TangoDevice):
evt_wait.waitEvent(DevState.MOVING, after=id, equal=False,
timeout=timeout)
finally:
+ self.__go_end_time = time.time()
+ self.__go_time = self.__go_end_time - self.__go_start_time
evt_wait.unlock()
evt_wait.disconnect()
@reservedOperation
def go(self, *args, **kwargs):
- id = self.start(*args, **kwargs)
- self.waitFinish(id=id)
-
+ self._total_go_time = 0
+ start_time = time.time()
+ eid = self.start(*args, **kwargs)
+ self.waitFinish(id=eid)
+ self._total_go_time = time.time() - start_time
+
+ def getLastGoTime(self):
+ """Returns the time it took for last go operation"""
+ return self.__go_time
+
+ def getTotalLastGoTime(self):
+ """Returns the time it took for last go operation, including dead time
+ to prepare, wait for events, etc"""
+ return self._total_go_time
+
def abort(self, wait_ready=True, timeout=None):
state = self.getStateEG()
state.lock()
@@ -437,11 +469,11 @@ class PoolElement(BaseElement, TangoDevice):
self.waitReady(timeout=timeout)
finally:
state.unlock()
-
+
def information(self, tab=' '):
msg = self._information(tab=tab)
return "\n".join(msg)
-
+
def _information(self, tab=' '):
indent = "\n" + tab + 10*' '
msg = [ self.getName() + ":" ]
@@ -472,7 +504,7 @@ class PoolElement(BaseElement, TangoDevice):
e_info = sys.exc_info()[:2]
status = traceback.format_exception_only(*e_info)
msg.append(tab + " Status: " + status)
-
+
return msg
@@ -484,54 +516,54 @@ class Controller(PoolElement):
self.call__init__(PoolElement, name, **kw)
def getModuleName(self):
- return self._pool_data['module']
-
+ return self.getPoolData()['module']
+
def getClassName(self):
- return self._pool_data['klass']
-
+ return self.getPoolData()['klass']
+
def getTypes(self):
- return self._pool_data['types']
-
+ return self.getPoolData()['types']
+
def getMainType(self):
- return self._pool_data['main_type']
-
+ return self.getPoolData()['main_type']
+
def addElement(self, elem):
axis = elem.getAxis()
self._elems[axis] = elem
self._last_axis = max(self._last_axis, axis)
-
+
def removeElement(self, elem):
axis = elem.getAxis()
del self._elems[elem.getAxis()]
if axis == self._last_axis:
self._last_axis = max(self._elems)
-
+
def getElementByAxis(self, axis):
pool = self.getPoolObj()
- for name, elem in pool.getElementsOfType(self.getMainType()).items():
+ for _, elem in pool.getElementsOfType(self.getMainType()).items():
if elem.controller != self.getName() or elem.getAxis() != axis:
continue
return elem
-
+
def getElementByName(self, name):
pool = self.getPoolObj()
for name, elem in pool.getElementsOfType(self.getMainType()).items():
if elem.controller != self.getName() or elem.getName() != name:
continue
return elem
-
+
def getUsedAxis(self):
pool = self.getPoolObj()
axis = []
- for name, elem in pool.getElementsOfType(self.getMainType()).items():
+ for _, elem in pool.getElementsOfType(self.getMainType()).items():
if elem.controller != self.getName():
continue
axis.append(elem.getAxis())
return sorted(axis)
-
+
def getLastUsedAxis(self):
return max([1] + self.getUsedAxis())
-
+
def __cmp__(self, o):
return cmp(self.getName(), o.getName())
@@ -584,7 +616,7 @@ class Motor(PoolElement, Moveable):
def getDialPosition(self, force=False):
return self._getAttrValue('dialposition', force=force)
-
+
def getVelocity(self, force=False):
return self._getAttrValue('velocity', force=force)
@@ -593,7 +625,7 @@ class Motor(PoolElement, Moveable):
def getDeceleration(self, force=False):
return self._getAttrValue('deceleration', force=force)
-
+
def getBaseRate(self, force=False):
return self._getAttrValue('base_rate', force=force)
@@ -602,16 +634,16 @@ class Motor(PoolElement, Moveable):
def getLimitSwitches(self, force=False):
return self._getAttrValue('limit_switches', force=force)
-
+
def getOffset(self, force=False):
return self._getAttrValue('offset', force=force)
-
+
def getStepPerUnit(self, force=False):
return self._getAttrValue('step_per_unit', force=force)
-
+
def getSign(self, force=False):
return self._getAttrValue('Sign', force=force)
-
+
def getSimulationMode(self, force=False):
return self._getAttrValue('SimulationMode', force=force)
@@ -629,7 +661,7 @@ class Motor(PoolElement, Moveable):
def getDecelerationObj(self):
return self._getAttrEG('deceleration')
-
+
def getBaseRateObj(self):
return self._getAttrEG('base_rate')
@@ -638,20 +670,44 @@ class Motor(PoolElement, Moveable):
def getLimitSwitchesObj(self):
return self._getAttrEG('limit_switches')
-
+
def getOffsetObj(self):
return self._getAttrEG('offset')
-
+
def getStepPerUnitObj(self):
return self._getAttrEG('step_per_unit')
-
+
def getSimulationModeObj(self):
return self._getAttrEG('step_per_unit')
+ def setVelocity(self, value):
+ return self.getVelocityObj().write(value)
+
+ def setAcceleration(self, value):
+ return self.getAccelerationObj().write(value)
+
+ def setDeceleration(self, value):
+ return self.getDecelerationObj().write(value)
+
+ def setBaseRate(self, value):
+ return self.getBaseRateObj().write(value)
+
+ def setBacklash(self, value):
+ return self.getBacklashObj().write(value)
+
+ def setOffset(self, value):
+ return self.getOffsetObj().write(value)
+
+ def setStepPerUnit(self, value):
+ return self.getStepPerUnitObj().write(value)
+
+ def setSign(self, value):
+ return self.getSignObj().write(value)
+
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Moveable interface
#
-
+
def _start(self, *args, **kwargs):
new_pos = args[0]
if operator.isSequenceType(new_pos):
@@ -667,13 +723,18 @@ class Motor(PoolElement, Moveable):
self.final_pos = new_pos
def go(self, *args, **kwargs):
+ start_time = time.time()
PoolElement.go(self, *args, **kwargs)
- return self.getStateEG().readValue(), self.readPosition()
-
+ ret = self.getStateEG().readValue(), self.readPosition()
+ self._total_go_time = time.time() - start_time
+ return ret
+
startMove = PoolElement.start
waitMove = PoolElement.waitFinish
move = go
-
+ getLastMotionTime = PoolElement.getLastGoTime
+ getTotalLastMotionTime = PoolElement.getTotalLastGoTime
+
@reservedOperation
def iterMove(self, new_pos, timeout=None):
if operator.isSequenceType(new_pos):
@@ -701,7 +762,7 @@ class Motor(PoolElement, Moveable):
finally:
evt_wait.unlock()
evt_wait.disconnect()
-
+
evt_iter_wait = AttributeEventIterator(state, pos)
evt_iter_wait.lock()
try:
@@ -713,18 +774,18 @@ class Motor(PoolElement, Moveable):
finally:
evt_iter_wait.unlock()
evt_iter_wait.disconnect()
-
+
def readPosition(self, force=False):
return [ self.getPosition(force=force) ]
-
+
def getMoveableSource(self):
return self.getPoolObj()
def getSize(self):
return 1
-
+
def getIndex(self, name):
- if name.lower() == self._name_lower:
+ if name.lower() == self.getName().lower():
return 0
return -1
#
@@ -791,24 +852,29 @@ class PseudoMotor(PoolElement, Moveable):
self.final_pos = new_pos
def go(self, *args, **kwargs):
+ start_time = time.time()
PoolElement.go(self, *args, **kwargs)
- return self.getStateEG().readValue(), self.readPosition()
-
+ ret = self.getStateEG().readValue(), self.readPosition()
+ self._total_go_time = time.time() - start_time
+ return ret
+
startMove = PoolElement.start
waitMove = PoolElement.waitFinish
move = go
+ getLastMotionTime = PoolElement.getLastGoTime
+ getTotalLastMotionTime = PoolElement.getTotalLastGoTime
def readPosition(self, force=False):
return [ self.getPosition(force=force) ]
-
+
def getMoveableSource(self):
return self.getPoolObj()
def getSize(self):
return 1
-
+
def getIndex(self, name):
- if name.lower() == self._name_lower:
+ if name.lower() == self.getName().lower():
return 0
return -1
#
@@ -846,14 +912,14 @@ class MotorGroup(PoolElement, Moveable):
def _create_str_tuple(self):
return 3*["TODO"]
-
+
def getMotorNames(self):
- return self._pool_data['elements']
-
+ return self.getPoolData()['elements']
+
def hasMotor(self, name):
motor_names = map(str.lower, self.getMotorNames())
return name.lower() in motor_names
-
+
def getPosition(self, force=False):
return self._getAttrValue('position', force=force)
@@ -877,32 +943,37 @@ class MotorGroup(PoolElement, Moveable):
self.final_pos = new_pos
def go(self, *args, **kwargs):
+ start_time = time.time()
PoolElement.go(self, *args, **kwargs)
- return self.getStateEG().readValue(), self.readPosition()
-
+ ret = self.getStateEG().readValue(), self.readPosition()
+ self._total_go_time = time.time() - start_time
+ return ret
+
startMove = PoolElement.start
waitMove = PoolElement.waitFinish
move = go
-
+ getLastMotionTime = PoolElement.getLastGoTime
+ getTotalLastMotionTime = PoolElement.getTotalLastGoTime
+
def readPosition(self, force=False):
return self.getPosition(force=force)
-
+
def getMoveableSource(self):
return self.getPoolObj()
def getSize(self):
return len(self.getMotorNames())
-
+
def getIndex(self, name):
try:
motor_names = map(str.lower, self.getMotorNames())
return motor_names.index(name.lower())
except:
return -1
-
+
#
# End of Moveable interface
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
def _information(self, tab=' '):
msg = PoolElement._information(self, tab=tab)
@@ -920,13 +991,13 @@ class MotorGroup(PoolElement, Moveable):
except:
e_info = sys.exc_info()[:2]
pos = traceback.format_exception_only(*e_info)
-
+
msg.append(tab + "Position: " + str(pos))
return msg
class BaseChannelInfo(object):
-
+
def __init__(self, data):
# dict<str, obj>
# channel data
@@ -935,15 +1006,26 @@ class BaseChannelInfo(object):
class TangoChannelInfo(BaseChannelInfo):
-
+
def __init__(self, data, info):
BaseChannelInfo.__init__(self, data)
# PyTango.AttributeInfoEx
+ self.set_info(info)
+
+ def has_info(self):
+ return self.raw_info is not None
+
+ def set_info(self, info):
self.raw_info = info
+ if info is None:
+ return
+
+ data = self.raw_data
+
if 'data_type' not in data:
self.data_type = FROM_TANGO_TO_STR_TYPE[info.data_type]
-
+
if 'shape' not in data:
shape = ()
if info.data_format == AttrDataFormat.SPECTRUM:
@@ -951,9 +1033,15 @@ class TangoChannelInfo(BaseChannelInfo):
elif info.data_format == AttrDataFormat.IMAGE:
shape = (info.max_dim_x, info.max_dim_y)
self.shape = shape
+ else:
+ shape = self.shape
+ self.shape = list(shape)
def __getattr__(self, name):
- return getattr(self.raw_info, name)
+ if self.has_info():
+ return getattr(self.raw_info, name)
+ cls_name = self.__class__.__name__
+ raise AttributeError("'%s' has no attribute '%s'" % (cls_name, name))
def getChannelConfigs(mgconfig, ctrls=None, units=None, sort=True):
@@ -962,16 +1050,16 @@ def getChannelConfigs(mgconfig, ctrls=None, units=None, sort=True):
units levels of the given measurement group configuration. It optionally
filters to those channels matching given lists of controller and unit
names.
-
- :param ctrls: (seq<str> or None) a sequence of strings to filter the
+
+ :param ctrls: (seq<str> or None) a sequence of strings to filter the
controllers. If None given, all controllers will be used
- :param units: (seq<str>) a sequence of strings to filter the units. If
+ :param units: (seq<str>) a sequence of strings to filter the units. If
None given, all controllers will be used
:param sort: (bool) If True (default) the returned list will be sorted
according to channel index (if given in channeldata) and
then by channelname.
-
- :return: (list<tuple>) A list of channelname,channeldata pairs.
+
+ :return: (list<tuple>) A list of channelname,channeldata pairs.
'''
chconfigs = []
if not mgconfig: return []
@@ -983,42 +1071,41 @@ def getChannelConfigs(mgconfig, ctrls=None, units=None, sort=True):
ch_data.update({'_controller_name':ctrl_name, '_unit_id':unit_id}) #add controller and unit ids
chconfigs.append((ch_name,ch_data))
if sort:
- #sort the channel configs by index (primary sort) and then by channel name.
+ #sort the channel configs by index (primary sort) and then by channel name.
chconfigs = sorted(chconfigs, key=lambda c:c[0]) #sort by channel_name
chconfigs = sorted(chconfigs, key=lambda c:c[1].get('index',1e16)) #sort by index (give a very large index for those which don't have it)
return chconfigs
class MGConfiguration(object):
-
+
def __init__(self, mg, data):
self._mg = weakref.ref(mg)
if isinstance(data, (str, unicode)):
data = CodecFactory().decode(('json', data), ensure_ascii=True)
self.raw_data = data
self.__dict__.update(data)
-
+
# dict<str, dict>
# where key is the channel name and value is the channel data in form
- # of a dict as receveid by the MG configuration attribute
+ # of a dict as receveid by the MG configuration attribute
self.channels = channels = CaselessDict()
-
- for ctrl_name, ctrl_data in self.controllers.items():
- for unit_id, unit_data in ctrl_data['units'].items():
+
+ for _, ctrl_data in self.controllers.items():
+ for _, unit_data in ctrl_data['units'].items():
for channel_name, channel_data in unit_data['channels'].items():
- data_source = channel_data['source']
channels[channel_name] = channel_data
-
+
#####################
#@todo: the for-loops above could be replaced by something like:
- #self.channels = channels = CaselessDict(getChannelConfigs(data,sort=False))
+ #self.channels = channels = CaselessDict(getChannelConfigs(data,sort=False))
#####################
-
+
# seq<dict> each element is the channel data in form of a dict as
# receveid by the MG configuration attribute. This seq is just a cache
# ordered by channel index in the MG.
- self.channel_list = channel_list = len(channels)*[None]
-
+ self.channel_list = len(channels)*[None]
+
for channel in channels.values():
self.channel_list[channel['index']] = channel
@@ -1028,11 +1115,11 @@ class MGConfiguration(object):
# - A dict where keys are attribute names and value is a reference to
# a dict representing channel data as received in raw data
self.tango_dev_channels = None
-
- # Number of elements in tango_dev_channels in error (could not build
+
+ # Number of elements in tango_dev_channels in error (could not build
# DeviceProxy, probably)
self.tango_dev_channels_in_error = 0
-
+
# dict<str, tuple<str, str, TangoChannelInfo>>
# where key is a channel name and value is a tuple of three elements:
# - device name
@@ -1044,14 +1131,14 @@ class MGConfiguration(object):
# Number of elements in tango_channels_info_in_error in error
# (could not build attribute info, probably)
self.tango_channels_info_in_error = 0
-
+
# dict<str, dict>
# where key is a channel name and data is a reference to a dict
# representing channel data as received in raw data
self.non_tango_channels = None
-
+
self.initialized = False
-
+
def _build(self):
# internal channel structure that groups channels by tango device so
# they can be read as a group minimizing this way the network requests
@@ -1061,11 +1148,12 @@ class MGConfiguration(object):
self.tango_channels_info_in_error = 0
self.non_tango_channels = n_tg_chs = CaselessDict()
self.cache = cache = {}
-
+
tg_attr_validator = AttributeNameValidator()
for channel_name, channel_data in self.channels.items():
cache[channel_name] = None
data_source = channel_data['source']
+ #external = ctrl_name.startswith("__")
params = tg_attr_validator.getParams(data_source)
if params is None:
# Handle NON tango channel
@@ -1078,7 +1166,7 @@ class MGConfiguration(object):
if host is not None and port is not None:
dev_name = "{0}:{1}/{2}".format(host, port, dev_name)
dev_data = tg_dev_chs.get(dev_name)
-
+
if dev_data is None:
# Build tango device
dev = None
@@ -1089,7 +1177,7 @@ class MGConfiguration(object):
tg_dev_chs[dev_name] = dev_data = [ dev, CaselessDict() ]
dev, attr_data = dev_data
attr_data[attr_name] = channel_data
-
+
# get attribute configuration
attr_info = None
if dev is None:
@@ -1097,18 +1185,26 @@ class MGConfiguration(object):
else:
try:
tg_attr_info = dev.get_attribute_config_ex(attr_name)[0]
- attr_info = TangoChannelInfo(channel_data, tg_attr_info)
except:
- import traceback
- traceback.print_exc()
+ tg_attr_info = \
+ self._build_empty_tango_attr_info(channel_data)
self.tango_channels_info_in_error += 1
+ attr_info = TangoChannelInfo(channel_data, tg_attr_info)
+
tg_chs_info[channel_name] = dev_name, attr_name, attr_info
+ def _build_empty_tango_attr_info(self, channel_data):
+ import PyTango
+ ret = PyTango.AttributeInfoEx()
+ ret.name = channel_data['name']
+ ret.label = channel_data['label']
+ return ret
+
def prepare(self):
# first time? build everything
if self.tango_dev_channels is None:
return self._build()
-
+
# prepare missing tango devices
if self.tango_dev_channels_in_error > 0:
for dev_name, dev_data in self.tango_dev_channels.items():
@@ -1118,57 +1214,77 @@ class MGConfiguration(object):
self.tango_dev_channels_in_error -= 1
except:
pass
-
+
# prepare missing tango attribute configuration
if self.tango_channels_info_in_error > 0:
- for channel_name, attr_data in self.tango_channels_info.items():
+ for _, attr_data in self.tango_channels_info.items():
dev_name, attr_name, attr_info = attr_data
- if attr_info is not None:
+ if attr_info.has_info():
continue
dev = self.tango_dev_channels[dev_name]
if dev is None:
continue
try:
tg_attr_info = dev.get_attribute_config_ex(attr_name)[0]
- channel_data = self.channels[channel_name]
- attr_info = attr_info = TangoChannelInfo(channel_data, tg_attr_info)
- attr_data[2] = attr_info
+ attr_info.set_info(tg_attr_info)
self.tango_channels_info_in_error -= 1
except:
- continue
-
+ pass
+
def getChannelInfo(self, channel_name):
- return self.tango_channels_info[channel_name]
-
+ try:
+ return self.tango_channels_info[channel_name]
+ except:
+ channel_name = channel_name.lower()
+ for d_name, a_name, ch_info in self.tango_channels_info.values():
+ if ch_info.name.lower() == channel_name:
+ return d_name, a_name, ch_info
+
def getChannelsInfo(self):
self.prepare()
- return self.tango_channels_info
-
+ ret = CaselessDict(self.tango_channels_info)
+ ret.update(self.non_tango_channels)
+ return ret
+
def getChannelsInfoList(self):
- ch_info = self.getChannelsInfo()
- return [ ch_info[ch['name']][2] for ch in self.channel_list ]
-
- def getCountersInfoList(self):
- ch_info = self.getChannelsInfo()
- ret = []
- for ch in self.channel_list:
- ch_name = ch['name']
- if ch_name != self.timer:
- ret.append(ch_info[ch_name][2])
+ channels_info = self.getChannelsInfo()
+ ret = len(channels_info)*[None]
+ for _, (_,_,ch_info) in channels_info.items():
+ ret[ch_info.index] = ch_info
return ret
-
- def read_parallel(self):
+
+ def getCountersInfoList(self):
+ channels_info = self.getChannelsInfoList()
+ timer_name, idx = self.timer, -1
+ for i, ch in enumerate(channels_info):
+ if ch['full_name'] == timer_name:
+ idx = i
+ break
+ if idx >= 0:
+ channels_info.pop(idx)
+ return channels_info
+
+ def read(self, parallel=True):
+ if parallel:
+ return self._read_parallel()
+ return self._read()
+
+ def _read_parallel(self):
self.prepare()
ret = CaselessDict(self.cache)
dev_replies = {}
- for dev_name, dev_data in self.tango_dev_channels.items():
+
+ # deposit read requests
+ for _, dev_data in self.tango_dev_channels.items():
dev, attrs = dev_data
if dev is None:
continue
try:
dev_replies[dev] = dev.read_attributes_asynch(attrs.keys()), attrs
except:
- continue
+ dev_replies[dev] = None, attrs
+
+ # gather all replies
for dev, reply_data in dev_replies.items():
reply, attrs = reply_data
try:
@@ -1179,15 +1295,17 @@ class MGConfiguration(object):
value = None
else:
value = data_item.value
- ret[channel_data['name']] = value
+ ret[channel_data['full_name']] = value
except:
- continue
+ for _, channel_data in attrs.items():
+ ret[channel_data['full_name']] = None
+
return ret
- def read(self):
+ def _read(self):
self.prepare()
ret = CaselessDict(self.cache)
- for dev_name, dev_data in self.tango_dev_channels.items():
+ for _, dev_data in self.tango_dev_channels.items():
dev, attrs = dev_data
try:
data = dev.read_attributes(attrs.keys())
@@ -1197,51 +1315,53 @@ class MGConfiguration(object):
value = None
else:
value = data_item.value
- ret[channel_data['name']] = value
+ ret[channel_data['full_name']] = value
except:
- continue
+ for _, channel_data in attrs.items():
+ ret[channel_data['full_name']] = None
return ret
class MeasurementGroup(PoolElement):
""" Class encapsulating MeasurementGroup functionality."""
-
+
def __init__(self, name, **kw):
"""PoolElement initialization."""
self._configuration = None
self._channels = None
+ self._last_integ_time = None
self.call__init__(PoolElement, name, **kw)
-
+
cfg_attr = self.getAttribute('configuration')
cfg_attr.addListener(self.on_configuration_changed)
def _create_str_tuple(self):
return self.getName(), self.getTimerName(), ", ".join(self.getChannelNames())
-
+
def getConfigurationAttrEG(self):
return self._getAttrEG('Configuration')
-
+
def setConfiguration(self, configuration):
data = CodecFactory().encode(('json', configuration))
self.write_attribute('configuration', data)
-
+
def _setConfiguration(self, data):
self._configuration = MGConfiguration(self, data)
-
+
def getConfiguration(self, force=False):
if force or self._configuration is None:
data = self.getConfigurationAttrEG().readValue(force=True)
self._setConfiguration(data)
return self._configuration
-
+
def on_configuration_changed(self, evt_src, evt_type, evt_value):
if evt_type not in CHANGE_EVT_TYPES:
return
self.info("Configuration changed")
self._setConfiguration(evt_value.value)
-
+
def getTimerName(self):
- return self.getConfiguration().timer
-
+ return self.getTimer()['name']
+
def getTimer(self):
cfg = self.getConfiguration()
return cfg.channels[cfg.timer]
@@ -1250,7 +1370,7 @@ class MeasurementGroup(PoolElement):
return self.getTimerName()
def getMonitorName(self):
- return self.getConfiguration().monitor
+ return self.getMonitor()['name']
def getMonitor(self):
cfg = self.getConfiguration()
@@ -1258,7 +1378,7 @@ class MeasurementGroup(PoolElement):
def setTimer(self, timer_name):
try:
- channel = self.getChannel(timer_name)
+ self.getChannel(timer_name)
except KeyError:
raise Exception("%s does not contain a channel named '%s'"
% (str(self),timer_name))
@@ -1266,58 +1386,72 @@ class MeasurementGroup(PoolElement):
cfg['timer'] = timer_name
import json
self.write_attribute("configuration", json.dumps(cfg))
-
+
def getChannels(self):
return self.getConfiguration().channel_list
-
+
def getCounters(self):
cfg = self.getConfiguration()
- return [ ch for ch in self.getChannels() if ch['name'] != cfg.timer ]
-
+ return [ ch for ch in self.getChannels() if ch['full_name'] != cfg.timer ]
+
def getChannelNames(self):
return [ ch['name'] for ch in self.getChannels() ]
-
+
def getCounterNames(self):
- cfg = self.getConfiguration()
return [ ch['name'] for ch in self.getCounters() ]
-
+
+ def getChannelLabels(self):
+ return [ ch['label'] for ch in self.getChannels() ]
+
+ def getCounterLabels(self):
+ return [ ch['label'] for ch in self.getCounters() ]
+
def getChannel(self, name):
return self.getConfiguration().channels[name]
-
+
def getChannelInfo(self, name):
return self.getConfiguration().getChannelInfo(name)
-
+
def getChannelsInfo(self):
return self.getConfiguration().getChannelsInfoList()
-
+
def getCountersInfo(self):
return self.getConfiguration().getCountersInfoList()
-
- def getValues(self):
- return self.getConfiguration().read()
-
+
+ def getValues(self, parallel=True):
+ return self.getConfiguration().read(parallel=parallel)
+
def getIntegrationTime(self):
return self._getAttrValue('IntegrationTime')
-
+
def getIntegrationTimeObj(self):
return self._getAttrEG('IntegrationTime')
-
+
def setIntegrationTime(self, ctime):
self.getIntegrationTimeObj().write(ctime)
-
+
+ def putIntegrationTime(self, ctime):
+ if self._last_integ_time == ctime:
+ return
+ self._last_integ_time = ctime
+ self.getIntegrationTimeObj().write(ctime)
+
def _start(self, *args, **kwargs):
self.Start()
-
+
def go(self, *args, **kwargs):
+ start_time = time.time()
cfg = self.getConfiguration()
cfg.prepare()
duration = args[0]
if duration is None or duration == 0:
return self.getStateEG().readValue(), self.getValues()
- self.setIntegrationTime(duration)
+ self.putIntegrationTime(duration)
PoolElement.go(self, *args, **kwargs)
- return self.getStateEG().readValue(), self.getValues()
-
+ ret = self.getStateEG().readValue(), self.getValues()
+ self._total_go_time = time.time() - start_time
+ return ret
+
startCount = PoolElement.start
waitCount = PoolElement.waitFinish
count = go
@@ -1333,10 +1467,10 @@ class IORegister(PoolElement):
def getValueObj(self):
return self._getAttrEG('value')
-
+
def readValue(self, force=False):
return self._getAttrValue('value', force=force)
-
+
def startWriteValue(self, new_value, timeout=None):
try:
self.getValueObj().write(new_value)
@@ -1347,10 +1481,10 @@ class IORegister(PoolElement):
raise RuntimeError, '%s is already chaging' % self
else:
raise
-
+
def waitWriteValue(self, timeout=None):
pass
-
+
def writeValue(self, new_value, timeout=None):
self.startWriteValue(new_value, timeout=timeout)
self.waitWriteValue(timeout=timeout)
@@ -1361,24 +1495,23 @@ class IORegister(PoolElement):
class Instrument(BaseElement):
-
+
def __init__(self, **kw):
self.__dict__.update(kw)
- self._name_lower = self.full_name.lower()
-
+
def getFullName(self):
return self.full_name
-
+
def getParentInstrument(self):
return self.getPoolObj().getObj(self.parent_instrument)
def getParentInstrumentName(self):
return self.parent_instrument
-
+
def getChildrenInstruments(self):
raise NotImplementedError
return self._children
-
+
def getElements(self):
raise NotImplementedError
return self._elements
@@ -1392,14 +1525,14 @@ class Instrument(BaseElement):
class Pool(TangoDevice, MoveableSource):
""" Class encapsulating device Pool functionality."""
-
+
def __init__(self, name, **kw):
self.call__init__(TangoDevice, name, **kw)
self.call__init__(MoveableSource)
-
+
self._elements = BaseSardanaElementContainer()
self.getAttribute("Elements").addListener(self.on_elements_changed)
-
+
def getObject(self, element_info):
elem_type = element_info.getType()
data = element_info._data
@@ -1407,11 +1540,12 @@ class Pool(TangoDevice, MoveableSource):
klass = globals()[elem_type]
kwargs = dict(data)
kwargs['_pool_data'] = data
+ kwargs['_pool_obj'] = self
return klass(**kwargs)
obj = Factory().getDevice(element_info.full_name, _pool_obj=self,
_pool_data=data)
return obj
-
+
def on_elements_changed(self, evt_src, evt_type, evt_value):
if evt_type == TaurusEventType.Error:
msg = evt_value
@@ -1443,30 +1577,34 @@ class Pool(TangoDevice, MoveableSource):
elements.addElement(element)
for element_data in elems.get('del', ()):
element = self.getElementInfo(element_data['name'])
- elements.removeElement(element)
+ try:
+ elements.removeElement(element)
+ except:
+ self.warning("Failed to remove %s", element_data)
+
return elems
-
+
def getElementsInfo(self):
return self._elements
-
+
def getElements(self):
return self.getElementsInfo().getElements()
-
+
def getElementInfo(self, name):
return self.getElementsInfo().getElement(name)
-
+
def getElementNamesOfType(self, elem_type):
return self.getElementsInfo().getElementNamesOfType(elem_type)
-
+
def getElementsOfType(self, elem_type):
return self.getElementsInfo().getElementsOfType(elem_type)
-
+
def getElementsWithInterface(self, interface):
return self.getElementsInfo().getElementsWithInterface(interface)
-
+
def getElementWithInterface(self, elem_name, interface):
return self.getElementsInfo().getElementWithInterface(elem_name, interface)
-
+
def getObj(self, name, elem_type=None):
if elem_type is None:
return self.getElementInfo(name)
@@ -1474,12 +1612,16 @@ class Pool(TangoDevice, MoveableSource):
elem_types = elem_type,
else:
elem_types = elem_type
+ name = name.lower()
for e_type in elem_types:
elems = self.getElementsOfType(e_type)
+ for elem in elems.values():
+ if elem.name.lower() == name:
+ return elem
elem = elems.get(name)
if elem is not None:
return elem
-
+
def __repr__(self):
return self.getNormalName()
@@ -1488,37 +1630,42 @@ class Pool(TangoDevice, MoveableSource):
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# MoveableSource interface
- #
-
+ #
+
def getMoveable(self, names):
"""getMoveable(seq<string> names) -> Moveable
- Returns a moveable object that handles all the moveable items given in
+ Returns a moveable object that handles all the moveable items given in
names."""
# if simple motor just return it (if the pool has it)
if isinstance(names, (str, unicode)):
names = names,
-
+
if len(names) == 1:
name = names[0]
return self.getObj(name, elem_type=MOVEABLE_TYPES)
-
+
# find a motor group that contains elements
moveable = self.__findMotorGroupWithElems(names)
-
+
# if none exists create one
if moveable is None:
mgs = self.getElementsOfType('MotorGroup')
- i, cont = 1, True
+ i = 1
pid = os.getpid()
- while cont:
+ while True:
name = "_mg_ms_{0}_{1}".format(pid, i)
- if name not in mgs:
- cont = False
+ exists = False
+ for mg in mgs.values():
+ if mg.name == name:
+ exists = True
+ break
+ if not exists:
+ break
i += 1
moveable = self.createMotorGroup(name, names)
return moveable
-
+
def __findMotorGroupWithElems(self, names):
names_lower = map(str.lower, names)
len_names = len(names)
@@ -1532,11 +1679,11 @@ class Pool(TangoDevice, MoveableSource):
break
else:
return mg
-
+
#
# End of MoveableSource interface
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-
+
def _wait_for_element_in_container(self, container, elem_name, timeout=0.5,
contains=True):
start = time.time()
@@ -1559,24 +1706,24 @@ class Pool(TangoDevice, MoveableSource):
elem_name)
return
time.sleep(nap)
-
+
def createMotorGroup(self, mg_name, elements):
params = [mg_name,] + map(str, elements)
self.debug('trying to create motor group for elements: %s', params)
self.command_inout('CreateMotorGroup', params)
elements_info = self.getElementsInfo()
return self._wait_for_element_in_container(elements_info, mg_name)
-
+
def createMeasurementGroup(self, mg_name, elements):
params = [mg_name,] + map(str,elements)
self.debug('trying to create measurement group: %s', params)
self.command_inout('CreateMeasurementGroup', params)
elements_info = self.getElementsInfo()
return self._wait_for_element_in_container(elements_info, mg_name)
-
+
def deleteMeasurementGroup(self, name):
return self.deleteElement(name)
-
+
def createElement(self, name, ctrl, axis=None):
ctrl_type = ctrl.types[0]
if axis is None:
@@ -1588,14 +1735,14 @@ class Pool(TangoDevice, MoveableSource):
self.command_inout(cmd, pars)
elements_info = self.getElementsInfo()
return self._wait_for_element_in_container(elements_info, name)
-
+
def deleteElement(self, name):
self.debug('trying to delete element: %s', name)
self.command_inout('DeleteElement', name)
elements_info = self.getElementsInfo()
return self._wait_for_element_in_container(elements_info, name,
contains=False)
-
+
def createController(self, class_name, name, *props):
ctrl_class = self.getObj(class_name, elem_type='ControllerClass')
if ctrl_class is None:
@@ -1606,7 +1753,7 @@ class Pool(TangoDevice, MoveableSource):
self.command_inout(cmd, pars)
elements_info = self.getElementsInfo()
return self._wait_for_element_in_container(elements_info, name)
-
+
def deleteController(self, name):
return self.deleteElement(name)
@@ -1614,14 +1761,14 @@ class Pool(TangoDevice, MoveableSource):
def registerExtensions():
factory = Factory()
factory.registerDeviceClass("Pool", Pool)
-
+
hw_type_names = [
'Controller',
'ComChannel', 'Motor', 'PseudoMotor',
'CTExpChannel','ZeroDExpChannel','OneDExpChannel', 'TwoDExpChannel',
'PseudoCounter', 'IORegister', 'MotorGroup', 'MeasurementGroup']
-
+
hw_type_map = [ (name, globals()[name]) for name in hw_type_names ]
-
+
for klass_name, klass in hw_type_map:
factory.registerDeviceClass(klass_name, klass)
diff --git a/lib/taurus/core/tango/sardana/sardana.py b/lib/taurus/core/tango/sardana/sardana.py
index f9b0542..2e2c576 100644
--- a/lib/taurus/core/tango/sardana/sardana.py
+++ b/lib/taurus/core/tango/sardana/sardana.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -41,19 +41,19 @@ __docformat__ = 'restructuredtext'
import socket
-import time
import PyTango
+import taurus
from taurus.core.util import Enumeration, Singleton, Logger, CaselessDict, \
CodecFactory
PoolElementType = Enumeration("PoolElementType",
- ("0D", "1D", "2D", "Communication", "CounterTimer", "IORegister",
+ ("0D", "1D", "2D", "Communication", "CounterTimer", "IORegister",
"Motor","PseudoCounter", "PseudoMotor"))
-ChannelView = Enumeration("ChannelView",
- ("Channel", "Enabled", "Output", "PlotType", "PlotAxes", "Timer",
- "Monitor", "Trigger", "Conditioning", "Normalization","NXPath",
+ChannelView = Enumeration("ChannelView",
+ ("Channel", "Enabled", "Output", "PlotType", "PlotAxes", "Timer",
+ "Monitor", "Trigger", "Conditioning", "Normalization","NXPath",
"Shape", "DataType",
"Unknown"))
@@ -64,7 +64,7 @@ Normalization = Enumeration("Normalization", ("No", "Avg", "Integ"))
#: an enumeration describing all possible acquisition trigger types
AcqTriggerType = Enumeration("AcqTriggerType", ( \
"Software", # channel triggered by software - start and stop by software
- "Gate", # channel triggered by HW - start and stop by external
+ "Gate", # channel triggered by HW - start and stop by external
"Unknown") )
#: an enumeration describing all possible acquisition mode types
@@ -76,113 +76,113 @@ AcqMode = Enumeration("AcqMode", ( \
class BaseSardanaElement(object):
"""Generic sardana element"""
-
+
def __init__(self, *args, **kwargs):
self._manager = kwargs.pop('manager')
self.__dict__.update(kwargs)
self._data = kwargs
self._object = None
-
+
def __repr__(self):
return "{0}({1})".format(self.type, self.full_name)
-
+
def __str__(self):
return self.name
-
+
def __getattr__(self, name):
return getattr(self.getObj(), name)
-
+
def __cmp__(self, elem):
return cmp(self.name, elem.name)
-
+
def getData(self):
return self._data
-
+
def getName(self):
return self.name
-
+
def getId(self):
return self.full_name
-
+
def getType(self):
return self.getTypes()[0]
-
+
def getTypes(self):
elem_types = self.type
if isinstance(elem_types, (str, unicode)):
return [elem_types]
return elem_types
-
+
def serialize(self, *args, **kwargs):
kwargs.update(self._data)
return kwargs
-
+
def str(self, *args, **kwargs):
#TODO change and check which is the active protocol to serialize
#acordingly
return CodecFactory().encode(('json', self.serialize(*args, **kwargs)))
-
+
def getObj(self):
obj = self._object
if obj is None:
self._object = obj = self._manager.getObject(self)
return obj
-
+
class BaseSardanaElementContainer:
-
+
def __init__(self):
# dict<str, dict> where key is the type and value is:
- # dict<str, MacroServerElement> where key is the element alias and
- # value is the Element object
+ # dict<str, MacroServerElement> where key is the element full name
+ # and value is the Element object
self._type_elems_dict = CaselessDict()
-
+
# dict<str, container> where key is the interface and value is the set
# of elements which implement that interface
self._interfaces_dict = {}
-
+
def addElement(self, elem):
elem_type = elem.getType()
- elem_name = elem.getName()
-
+ elem_full_name = elem.full_name
+
#update type_elems
type_elems = self._type_elems_dict.get(elem_type)
if type_elems is None:
self._type_elems_dict[elem_type] = type_elems = CaselessDict()
- type_elems[elem_name] = elem
-
+ type_elems[elem_full_name] = elem
+
# update interfaces
for interface in elem.interfaces:
interface_elems = self._interfaces_dict.get(interface)
if interface_elems is None:
self._interfaces_dict[interface] = interface_elems = CaselessDict()
- interface_elems[elem_name] = elem
-
+ interface_elems[elem_full_name] = elem
+
def removeElement(self, e):
- type = e.getType()
-
+ elem_type = e.getType()
+
# update type_elems
- type_elems = self._type_elems_dict.get(type)
+ type_elems = self._type_elems_dict.get(elem_type)
if type_elems:
- del type_elems[e.name]
-
+ del type_elems[e.full_name]
+
# update interfaces
for interface in e.interfaces:
interface_elems = self._interfaces_dict.get(interface)
- del interface_elems[e.name]
-
+ del interface_elems[e.full_name]
+
def removeElementsOfType(self, t):
for elem in self.getElementsOfType(t):
self.removeElement(elem)
-
+
def getElementsOfType(self, t):
elems = self._type_elems_dict.get(t, {})
return elems
-
+
def getElementNamesOfType(self, t):
- return [ e.name for e in self.getElementsOfType(t).values() ]
-
+ return [e.name for e in self.getElementsOfType(t).values()]
+
def getElementsWithInterface(self, interface):
elems = self._interfaces_dict.get(interface, {})
return elems
@@ -194,32 +194,42 @@ class BaseSardanaElementContainer:
return ret
def getElementNamesWithInterface(self, interface):
- return [ e.name for e in self.getElementsWithInterface(interface).values() ]
-
+ return [e.name for e in self.getElementsWithInterface(interface).values()]
+
def hasElementName(self, elem_name):
return self.getElement(elem_name) != None
-
+
def getElement(self, elem_name):
+ elem_name = elem_name.lower()
for elems in self._type_elems_dict.values():
- elem = elems.get(elem_name)
+ elem = elems.get(elem_name) # full_name?
if elem is not None:
return elem
-
+ for elem in elems.values():
+ if elem.name.lower() == elem_name:
+ return elem
+
def getElementWithInterface(self, elem_name, interface):
- return self._interfaces_dict.get(interface, {}).get(elem_name)
-
+ elem_name = elem_name.lower()
+ elems = self._interfaces_dict.get(interface, {})
+ if elem_name in elems:
+ return elems[elem_name]
+ for elem in elems.values():
+ if elem.name.lower() == elem_name:
+ return elem
+
def getElements(self):
ret = set()
for elems in self._type_elems_dict.values():
ret.update(elems.values())
return ret
-
+
def getInterfaces(self):
return self._interfaces_dict
-
+
def getTypes(self):
return self._type_elems_dict
-
+
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# T E M P O R A R Y I M P L E M E N T A T I O N
#
@@ -231,19 +241,19 @@ class PropertyInfo():
self._type = type
self._format = format
self._default_value=default_value
-
+
def get_name(self):
return self._name
def get_type(self):
return self._type
-
+
def get_format(self):
return self._format
-
+
def get_default_value(self):
return self._default_value
-
+
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# T E M P O R A R Y I M P L E M E N T A T I O N
@@ -251,32 +261,32 @@ class PropertyInfo():
# THIS IS USED FOR TEST PURPOSES ONLY. DO NOT USE IT OUTSIDE SARDANA TESTS
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
class ControllerClassInfo(object):
-
+
def __init__(self, name, type, library):
self._name = name
self._type = type
self._library = library
-
+
def get_max_elements(self):
return 20
-
+
def get_name(self):
return self._name
-
+
def get_model(self):
# fake data ###############
- return "Model of "+ self._name
-
+ return "Model of "+ self._name
+
def get_icon(self):
# fake data ###############
import taurus.qt.qtgui.resource
-
+
return taurus.qt.qtgui.resource.getIcon(":/designer/extra_motor.png")
-
+
def get_organization(self):
# fake data ###############
- return "Organization of "+ self._name
-
+ return "Organization of "+ self._name
+
def get_description(self):
#fake data############
descr="This is description of "
@@ -284,19 +294,19 @@ class ControllerClassInfo(object):
descr=descr + " and " +self._name
####################
return descr
-
+
def get_family(self):
# fake data ###############
- return "Family of "+ self._name
-
+ return "Family of "+ self._name
+
def get_properties(self):
properties = []
# fake data ######################
properties.append(PropertyInfo("my parameter", "string", "0D", "deviceName"))
properties.append(PropertyInfo("asdsadasd", "integer", "0D", 5))
properties.append(PropertyInfo("boollll0", "boolean", "0D", False))
- properties.append(PropertyInfo("boollll0", "boolean", "0D", True))
- properties.append(PropertyInfo("boollll0", "boolean", "0D", False))
+ properties.append(PropertyInfo("boollll0", "boolean", "0D", True))
+ properties.append(PropertyInfo("boollll0", "boolean", "0D", False))
properties.append(PropertyInfo("number1", "float", "0D", 3.5))
properties.append(PropertyInfo("string2", "string", "0D", "hehe"))
properties.append(PropertyInfo("tableIntegerD1", "integer", "1D", [1,2,3]))
@@ -308,9 +318,9 @@ class ControllerClassInfo(object):
properties.append(PropertyInfo("tableinteger2", "integer", "2D",[ [1,2,3],[11,22,33],[-10,-20,-30] ]))
properties.append(PropertyInfo("tablefloatD2", "float", "2D",[ [0.5,0.6,0.8],[0.4,0.0,0.333333],[-0.1111,1,123123.6] ]))
properties.append(PropertyInfo("tablestringD2", "string", "2D",[ ["aaaa","bbb","ccc"],["aaaa2","bbb2","ccc2"],["aaaa3","bbb3","ccc3"] ]))
-
+
return properties
-
+
def get_controller_type(self):
return self._type
@@ -321,7 +331,7 @@ class ControllerClassInfo(object):
# THIS IS USED FOR TEST PURPOSES ONLY. DO NOT USE IT OUTSIDE SARDANA TESTS
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
class ControllerInfo(object):
-
+
def __init__(self, name, ctrl_class_info):
self._name = name
self._ctrl_class_info = ctrl_class_info
@@ -331,13 +341,13 @@ class ControllerInfo(object):
def get_controller_type(self):
return self._ctrl_class_info.get_controller_type()
-
+
def get_name(self):
return self._name
-
+
def get_max_elements(self):
return self._ctrl_class_info.get_max_elements()
-
+
def is_axis_free(self, axis):
#fake data
if axis == 3:
@@ -354,8 +364,6 @@ class ControllerInfo(object):
def get_icon(self):
return self._ctrl_class_info.get_icon()
-
- return properties
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# T E M P O R A R Y I M P L E M E N T A T I O N
@@ -363,7 +371,7 @@ class ControllerInfo(object):
# THIS IS USED FOR TEST PURPOSES ONLY. DO NOT USE IT OUTSIDE SARDANA TESTS
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
class Pool(object):
-
+
def __init__(self, sardana, name, poolpath, version, alias=None, device_name=None):
self._sardana = sardana
self._name = name
@@ -371,16 +379,16 @@ class Pool(object):
self._version = version
self._alias = alias
self._device_name = device_name
-
+
def starter_run(self, host, level=1):
return True
-
+
def get_name(self):
return self._name
-
+
def local_run(self):
return True
-
+
def get_element_types(self):
return sorted(PoolElementType.keys())
@@ -391,7 +399,7 @@ class Pool(object):
data.append(ControllerClassInfo("motorController"+str(i), PoolElementType.Motor, None))
for i in range(5):
data.append(ControllerClassInfo("counterTimerController"+str(i), PoolElementType.CounterTimer, None))
-
+
return data
def get_controller_infos(self):
@@ -402,10 +410,10 @@ class Pool(object):
for i in range(2):
data.append(ControllerInfo("My_ct_ctrl_"+str(i), ctrl_classes[i+5]))
return data
-
+
def create_controller(self,controller_class_info, name, properties ):
pass
-
+
def create_element(self, controller_name, name, axis):
pass
@@ -416,7 +424,7 @@ class Pool(object):
# THIS IS USED FOR TEST PURPOSES ONLY. DO NOT USE IT OUTSIDE SARDANA TESTS
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
class MacroServer(object):
-
+
def __init__(self, sardana, name, macropath, pool_names, version, alias=None, device_name=None):
self._sardana = sardana
self._name = name
@@ -426,7 +434,7 @@ class MacroServer(object):
self._alias = alias
self._device_name = device_name
self._doors = []
-
+
def create_door(self, alias, device_name):
try:
return self._create_door(alias, device_name)
@@ -434,7 +442,7 @@ class MacroServer(object):
db = self.get_database()
db.delete_device(device_name)
raise
-
+
def _create_door(self, alias, device_name):
db = self.get_database()
info = PyTango.DbDevInfo()
@@ -447,16 +455,16 @@ class MacroServer(object):
door = Door(alias=alias, device_name=device_name)
self._doors.append(door)
return door
-
+
def remove_door(self, device_name):
pass
-
+
def starter_run(self, host, level=1):
return True
-
+
def local_run(self):
return True
-
+
def get_database(self):
return self._sardana.get_database()
@@ -466,7 +474,7 @@ class MacroServer(object):
# THIS IS USED FOR TEST PURPOSES ONLY. DO NOT USE IT OUTSIDE SARDANA TESTS
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
class Door(object):
-
+
def __init__(self, alias=None, device_name=None):
self._name = alias
self._device_name = device_name
@@ -478,7 +486,7 @@ class Door(object):
# THIS IS USED FOR TEST PURPOSES ONLY. DO NOT USE IT OUTSIDE SARDANA TESTS
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
class Sardana(object):
-
+
def __init__(self, sardana_db , name, device_name=None):
self._sardana_db = sardana_db
self._name = name
@@ -486,7 +494,7 @@ class Sardana(object):
self._pools = []
self._macroservers = []
self._init()
-
+
def _init(self):
if not self._device_name:
return
@@ -516,23 +524,23 @@ class Sardana(object):
pool_alias = pool_dev_info.alias()
pool = Pool(self, pool_name, pool_props.get("poolpath"), pool_props.get("version"), pool_alias, pool_dev_name)
self._pools.append(pool)
-
+
def get_name(self):
return self._name
-
+
def set_device_name(self, device_name):
self._device_name = device_name
self._init()
-
+
def get_device_name(self):
return self._device_name
-
+
def get_pools(self):
return self._pools
-
+
def get_macro_servers(self):
return self._macro_servers
-
+
def create_pool(self, name, poolpath, version, alias=None, device_name=None):
try:
return self._create_pool(name, poolpath, version, alias=alias, device_name=device_name)
@@ -540,7 +548,7 @@ class Sardana(object):
db = self.get_database()
db.delete_device(device_name)
raise
-
+
def _create_pool(self, name, poolpath, version, alias=None, device_name=None):
db = self.get_database()
info = PyTango.DbDevInfo()
@@ -550,13 +558,13 @@ class Sardana(object):
db.add_device(info)
if alias:
db.put_device_alias(device_name, alias)
-
+
db.put_device_property(device_name,{"PoolPath" : poolpath, "Version": version} )
pool = Pool(self, name, poolpath, version, alias=alias, device_name=device_name)
self._pools.append(pool)
db.cache().refresh()
return pool
-
+
def create_macroserver(self, name, macropath, pool_names, version, alias=None, device_name=None):
try:
return self._create_macroserver(name, macropath, pool_names, version, alias=alias, device_name=device_name)
@@ -564,7 +572,7 @@ class Sardana(object):
db = self.get_database()
db.delete_device(device_name)
raise
-
+
def _create_macroserver(self, name, macropath, pool_names, version, alias=None, device_name=None):
db = self.get_database()
info = PyTango.DbDevInfo()
@@ -574,16 +582,16 @@ class Sardana(object):
db.add_device(info)
if alias:
db.put_device_alias(device_name, alias)
-
+
db.put_device_property(device_name,{"MacroPath" : macropath, "Version": version, "PoolNames":pool_names} )
ms = MacroServer(self, name, macropath, pool_names, version, alias=alias, device_name=device_name)
self._macroservers.append(ms)
db.cache().refresh()
return ms
-
+
def remove_pool(self):
pass
-
+
def remove_macroserver(self):
pass
@@ -597,12 +605,12 @@ class Sardana(object):
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
class DatabaseSardana(object):
"""A class containning all sardanas for a single database"""
-
+
def __init__(self, db):
assert(db is not None)
self._db = db
self.refresh()
-
+
def refresh(self):
self._sardanas = sardanas = {}
services = self._db.get_service_list("Sardana/.*")
@@ -612,7 +620,7 @@ class DatabaseSardana(object):
sardanas[service_instance] = Sardana(self, service_instance, dev)
except:
pass
-
+
def create_sardana(self, name, device_name):
if self._sardanas.has_key(name):
raise Exception("Sardana '%s' already exists" % name)
@@ -620,20 +628,20 @@ class DatabaseSardana(object):
sardana = Sardana(self, name)
self._sardanas[name] = sardana
return sardana
-
+
def remove_sardana(self, name):
try:
- sardana = self._sardanas.pop(name)
+ self._sardanas.pop(name)
except KeyError:
raise Exception("Sardana '%s' does NOT exist" % name)
self._db.unregister_service("Sardana", name)
def get_sardanas(self):
return self._sardanas
-
+
def get_sardana(self, name):
return self._sardanas[name]
-
+
def get_database(self):
return self._db
@@ -643,7 +651,7 @@ class DatabaseSardana(object):
# THIS IS USED FOR TEST PURPOSES ONLY. DO NOT USE IT OUTSIDE SARDANA TESTS
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
class SardanaManager(Singleton, Logger):
-
+
def __init__(self):
""" Initialization. Nothing to be done here for now."""
pass
@@ -654,7 +662,7 @@ class SardanaManager(Singleton, Logger):
name = self.__class__.__name__
self.call__init__(Logger, name)
self._db_sardanas = {}
-
+
def _get_db_sardana(self, db=None):
if db is None:
db = taurus.Database()
@@ -668,36 +676,36 @@ class SardanaManager(Singleton, Logger):
def remove_sardana(self, name, db=None):
self._get_db_sardana(db).remove_sardana(name)
-
+
def get_sardanas(self, db=None):
return self._get_db_sardana(db).get_sardanas()
-
+
def get_sardana(self, name, db=None):
return self._get_db_sardana(db).get_sardana(name)
-
+
def get_hosts(self):
return ["localhost"] + ["controls%02d" % i for i in range(5)]
-
+
def get_level_range(self):
return 1, 200
-
+
def has_localhost_starter(self):
return socket.gethostname() in self.get_hosts()
-
+
@classmethod
def get_default_pool_path(cls):
pathList = []
pathList.append("/homelocal/sicilia/lib/poolcontrollers")
pathList.append("/homelocal/sicilia/lib/python/site-packages/poolcontrollers")
return pathList
-
+
@classmethod
def get_default_ms_path(cls):
pathList = []
pathList.append("/homelocal/sicilia/lib/python/site-packages/macroserver/macros")
return pathList
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
diff --git a/lib/taurus/core/tango/search.py b/lib/taurus/core/tango/search.py
new file mode 100644
index 0000000..86c2ee5
--- /dev/null
+++ b/lib/taurus/core/tango/search.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""
+search.py: methods for getting matching device/attribute/alias names from Tango database
+
+These methods have been borrowed from fandango modules.
+"""
+
+import re
+import taurus
+
+###############################################################################
+# Utils
+
+def searchCl(regexp,target):
+ return re.search(regexp.lower(),target.lower())
+
+def matchCl(regexp,target):
+ return re.match(regexp.lower(),target.lower())
+
+def is_regexp(s):
+ return any(c in s for c in '.*[]()+?')
+
+def extend_regexp(s):
+ s = str(s).strip()
+ if '.*' not in s:
+ s = s.replace('*','.*')
+ if '.*' not in s:
+ if ' ' in s: s = s.replace(' ','.*')
+ if '/' not in s: s = '.*'+s+'.*'
+ else:
+ if not s.startswith('^'): s = '^'+s
+ if not s.endswith('$'): s = s+'$'
+ return s
+
+def isString(s):
+ typ = s.__class__.__name__.lower()
+ return not hasattr(s,'__iter__') and 'str' in typ and 'list' not in typ
+
+def isCallable(obj):
+ return hasattr(obj,'__call__')
+
+def isMap(obj):
+ return hasattr(obj,'has_key') or hasattr(obj,'items')
+
+def isDictionary(obj):
+ return isMap(obj)
+
+def isSequence(obj):
+ typ = obj.__class__.__name__.lower()
+ return (hasattr(obj,'__iter__') or 'list' in typ) and not isString(obj) and not isMap(obj)
+
+def split_model_list(modelNames):
+ '''convert str to list if needed (commas and whitespace are considered as separators)'''
+ if isString(modelNames): #isinstance(modelNames,(basestring,Qt.QString)):
+ modelNames = str(modelNames).replace(',',' ')
+ modelNames = modelNames.split()
+ if isSequence(modelNames): #isinstance(modelNames,(list.Qt.QStringList)):
+ modelNames = [str(s) for s in modelNames]
+ return modelNames
+
+def get_matching_devices(expressions,limit=0,exported=False):
+ """
+ Searches for devices matching expressions, if exported is True only running devices are returned
+ """
+ db = taurus.Database()
+ all_devs = [s.lower() for s in db.get_device_name('*','*')]
+ #This code is used to get data from multiples hosts
+ #if any(not fun.matchCl(rehost,expr) for expr in expressions): all_devs.extend(get_all_devices(exported))
+ #for expr in expressions:
+ #m = fun.matchCl(rehost,expr)
+ #if m:
+ #host = m.groups()[0]
+ #print 'get_matching_devices(%s): getting %s devices ...'%(expr,host)
+ #odb = PyTango.Database(*host.split(':'))
+ #all_devs.extend('%s/%s'%(host,d) for d in odb.get_device_name('*','*'))
+ result = [e for e in expressions if e.lower() in all_devs]
+ expressions = [extend_regexp(e) for e in expressions if e not in result]
+ result.extend(filter(lambda d: any(matchCl(extend_regexp(e),d) for e in expressions),all_devs))
+ return result
+
+def get_device_for_alias(alias):
+ db = taurus.Database()
+ try: return db.get_device_alias(alias)
+ except Exception,e:
+ if 'no device found' in str(e).lower(): return None
+ return None #raise e
+
+def get_alias_for_device(dev):
+ db = taurus.Database()
+ try:
+ result = db.get_alias(dev) #.get_database_device().DbGetDeviceAlias(dev)
+ return result
+ except Exception,e:
+ if 'no alias found' in str(e).lower(): return None
+ return None #raise e
+
+def get_alias_dict(exp='*'):
+ tango = taurus.Database()
+ return dict((k,tango.get_device_alias(k)) for k in tango.get_device_alias_list(exp))
\ No newline at end of file
diff --git a/lib/taurus/core/tango/tangoattribute.py b/lib/taurus/core/tango/tangoattribute.py
index 4f4a849..bcca691 100644
--- a/lib/taurus/core/tango/tangoattribute.py
+++ b/lib/taurus/core/tango/tangoattribute.py
@@ -31,13 +31,12 @@ __docformat__ = "restructuredtext"
# -*- coding: utf-8 -*-
import time
-import numpy
import threading
import PyTango
import taurus.core
from taurus.core import TaurusEventType, TaurusSerializationMode, \
- SubscriptionState
+ SubscriptionState, WriteAttrOperation
from taurus.core.util import EventListener
from enums import EVENT_TO_POLLING_EXCEPTIONS
@@ -209,7 +208,7 @@ class TangoAttribute(taurus.core.TaurusAttribute):
try:
dev = self.getParentObj()
name, value = self.getSimpleName(), self.encode(value)
- if self.isUsingEvents():
+ if self.isUsingEvents() or not self.isReadWrite():
with_read = False
if with_read:
try:
diff --git a/lib/taurus/core/tango/tangodatabase.py b/lib/taurus/core/tango/tangodatabase.py
index 6324d78..a3cd49e 100644
--- a/lib/taurus/core/tango/tangodatabase.py
+++ b/lib/taurus/core/tango/tangodatabase.py
@@ -279,19 +279,20 @@ def get_env_var(env_var_name):
class TangoDatabase(taurus.core.TaurusDatabase):
def __init__(self,host=None,port=None,parent=None):
-
- pars = host, port
+ pars = ()
if host is None or port is None:
try:
- pars = TangoDatabase.get_default_tango_host().split(':')
+ host, port = TangoDatabase.get_default_tango_host().rsplit(':', 1)
+ pars = host, port
except Exception, e:
- pars = ()
print "Error getting env TANGO_HOST:", str(e)
+ else:
+ pars = host, port
self.dbObj = PyTango.Database(*pars)
self._dbProxy = None
self._dbCache = None
- complete_name = "%s:%s" % (self.dbObj.get_db_host(), self.dbObj.get_db_port())
+ complete_name = "%s:%s" % (host, port)
self.call__init__(taurus.core.TaurusDatabase, complete_name, parent)
try:
diff --git a/lib/taurus/core/tango/tangodevice.py b/lib/taurus/core/tango/tangodevice.py
index 7e82e05..a41e439 100644
--- a/lib/taurus/core/tango/tangodevice.py
+++ b/lib/taurus/core/tango/tangodevice.py
@@ -37,6 +37,14 @@ from taurus.core import TaurusSWDevState, TaurusLockInfo, LockStatus
DFT_TANGO_DEVICE_DESCRIPTION = "A TANGO device"
+class _TangoInfo(object):
+
+ def __init__(self):
+ self.dev_class = self.dev_type = 'TangoDevice'
+ self.doc_url = 'http://www.esrf.fr/computing/cs/tango/tango_doc/ds_doc/'
+ self.server_host = 'Unknown'
+ self.server_id = 'Unknown'
+ self.server_version = 1
class TangoDevice(taurus.core.TaurusDevice):
def __init__(self, name, **kw):
@@ -72,7 +80,7 @@ class TangoDevice(taurus.core.TaurusDevice):
def lock(self, force=False):
li = self.getLockInfo()
if force:
- if self.getLockInfo().status == LockInfo.Locked:
+ if self.getLockInfo().status == TaurusLockInfo.Locked:
self.unlock(force=True)
return self.getHWObj().lock()
@@ -202,4 +210,22 @@ class TangoDevice(taurus.core.TaurusDevice):
v, err = da, None
attr = attrs[da.name]
attr.poll(single=False, value=v, error=err, time=t)
-
+
+ def _repr_html_(self):
+ try:
+ info = self.getHWObj().info()
+ except:
+ info = _TangoInfo()
+ txt = """\
+<table>
+ <tr><td>Short name</td><td>{simple_name}</td></tr>
+ <tr><td>Standard name</td><td>{normal_name}</td></tr>
+ <tr><td>Full name</td><td>{full_name}</td></tr>
+ <tr><td>Device class</td><td>{dev_class}</td></tr>
+ <tr><td>Server</td><td>{server_id}</td></tr>
+ <tr><td>Documentation</td><td><a target="_blank" href="{doc_url}">{doc_url}</a></td></tr>
+</table>
+""".format(simple_name=self.getSimpleName(), normal_name=self.getNormalName(),
+ full_name=self.getFullName(), dev_class=info.dev_class,
+ server_id=info.server_id, doc_url=info.doc_url)
+ return txt
diff --git a/lib/taurus/core/taurusattribute.py b/lib/taurus/core/taurusattribute.py
index f4c63cc..ebfde94 100644
--- a/lib/taurus/core/taurusattribute.py
+++ b/lib/taurus/core/taurusattribute.py
@@ -33,7 +33,7 @@ import weakref
import taurusmodel
import taurusconfiguration
-from enums import TaurusEventType
+#from enums import TaurusEventType
class TaurusAttribute(taurusmodel.TaurusModel):
@@ -159,7 +159,7 @@ class TaurusAttribute(taurusmodel.TaurusModel):
def getValueObj(self, cache=True):
try:
return self.read(cache=cache)
- except Exception, e:
+ except Exception:
return None
def getDisplayDescrObj(self,cache=True):
@@ -226,10 +226,9 @@ class TaurusAttribute(taurusmodel.TaurusModel):
ret = None
try:
if self.isScalar():
- format = self.getFormat()
- if self.isNumeric() and format is not None:
- format = self.getFormat()
- ret = self.getFormat() % value
+ fmt = self.getFormat()
+ if self.isNumeric() and fmt is not None:
+ ret = fmt % value
else:
ret = str(value)
elif self.isSpectrum():
diff --git a/lib/taurus/core/taurusconfiguration.py b/lib/taurus/core/taurusconfiguration.py
index dcc0c6c..a737cd9 100644
--- a/lib/taurus/core/taurusconfiguration.py
+++ b/lib/taurus/core/taurusconfiguration.py
@@ -30,11 +30,10 @@ __all__ = ["TaurusConfigurationProxy", "TaurusConfiguration"]
__docformat__ = "restructuredtext"
# -*- coding: utf-8 -*-
-import time
-import weakref
+
import numpy
-from enums import TaurusEventType, AttrAccess
+from enums import AttrAccess
import taurusmodel
class TaurusConfigurationProxy(object):
@@ -92,6 +91,9 @@ class TaurusConfiguration(taurusmodel.TaurusModel):
self._dev_hw_obj = self._getDev().getHWObj()
self._subscribeEvents()
+
+ def __str__(self):
+ return self.getFullName()
def _subscribeEvents(self):
pass
@@ -172,7 +174,7 @@ class TaurusConfiguration(taurusmodel.TaurusModel):
if cache is set to True (default) and the the configuration has
events active then it will return the local cached value. Otherwise
it will read from the tango layer."""
- raise RuntimeException("May not be called in abstract TaurusConfiguration")
+ raise RuntimeError("May not be called in abstract TaurusConfiguration")
def getDisplayValue(self,cache=True):
confvalue = self.getValueObj(cache=cache)
@@ -189,8 +191,8 @@ class TaurusConfiguration(taurusmodel.TaurusModel):
return [('name', self.getLabel(cache=cache))]
return attrObj.getDisplayDescrObj(cache=cache)
- def isWritable(self):
- return True
+# def isWritable(self):
+# return True
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# API for listeners
@@ -393,7 +395,7 @@ class TaurusConfiguration(taurusmodel.TaurusModel):
def setDescription(self,descr):
config = self.getValueObj()
if config:
- config.description = description
+ config.description = descr
self._applyConfig()
def setLabel(self,lbl):
diff --git a/lib/taurus/core/taurusdevice.py b/lib/taurus/core/taurusdevice.py
index 097ab9a..a15b112 100644
--- a/lib/taurus/core/taurusdevice.py
+++ b/lib/taurus/core/taurusdevice.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -29,10 +29,9 @@ __all__ = ["TaurusDevice"]
__docformat__ = "restructuredtext"
-import weakref
import sys
-from enums import TaurusSWDevState, TaurusEventType, LockStatus
+from enums import TaurusSWDevState, TaurusEventType
from taurusbasetypes import TaurusLockInfo
import taurusmodel
@@ -40,8 +39,8 @@ DFT_DEVICE_DESCRIPTION = "A device"
class TaurusDevice(taurusmodel.TaurusModel):
-
- SHUTDOWNS = (TaurusSWDevState.Shutdown, TaurusSWDevState.Crash,
+
+ SHUTDOWNS = (TaurusSWDevState.Shutdown, TaurusSWDevState.Crash,
TaurusSWDevState.EventSystemShutdown)
"""A Device object representing an abstraction of the PyTango.DeviceProxy
@@ -53,13 +52,13 @@ class TaurusDevice(taurusmodel.TaurusModel):
storeCallback = kw.pop('storeCallback', None)
self.__dict__.update(kw)
self.call__init__(taurusmodel.TaurusModel, name, parent)
-
+
self._deviceObj = self._createHWObject()
self._deviceStateObj = None
self._lock_info = TaurusLockInfo()
self._descr = None
self._deviceSwState = self.decode(TaurusSWDevState.Uninitialized)
-
+
if storeCallback:
storeCallback(self)
@@ -76,10 +75,16 @@ class TaurusDevice(taurusmodel.TaurusModel):
# Export the DeviceProxy interface into this object.
# This way we can call for example read_attribute on an object of this class
def __getattr__(self, name):
- if not self._deviceObj is None:
- return getattr(self._deviceObj,name)
- return None
-
+ if '_deviceObj' in self.__dict__ and self._deviceObj is not None:
+ return getattr(self._deviceObj, name)
+ cls_name = self.__class__.__name__
+ raise AttributeError("'%s' has no attribute '%s'" % (cls_name, name))
+
+ #def __setattr__(self, name, value):
+ # if '_deviceObj' in self.__dict__ and self._deviceObj is not None:
+ # return setattr(self._deviceObj, name, value)
+ # super(TaurusDevice, self).__setattr__(name, value)
+
# Export the 'act like dictionary' feature of PyTango.DeviceProxy
def __getitem__(self, key):
attr = self.getAttribute(key)
@@ -94,10 +99,10 @@ class TaurusDevice(taurusmodel.TaurusModel):
if hw is None:
return False
return hw.__contains__(key)
-
+
def getHWObj(self):
return self._deviceObj
-
+
def getStateObj(self):
if self._deviceStateObj is None:
self._deviceStateObj = self.factory().getAttribute("%s/state" % self.getFullName())
@@ -108,13 +113,13 @@ class TaurusDevice(taurusmodel.TaurusModel):
if not stateAttrValue is None:
return stateAttrValue.value
return None
-
+
def getSWState(self, cache=True):
return self.getValueObj(cache=cache).value
def getAttribute(self, attrname):
"""Returns the attribute object given its name"""
-
+
slashnb = attrname.count('/')
if slashnb == 0:
attrname = "%s/%s" % (self.getFullName(), attrname)
@@ -122,10 +127,10 @@ class TaurusDevice(taurusmodel.TaurusModel):
attrname = "%s%s" % (self.getFullName(), attrname)
import taurusattribute
return self.factory().getObject(taurusattribute.TaurusAttribute,attrname)
-
+
def isValidDev(self):
"""returns True if the device is in "working conditions
-
+
The default implementation always returns True. Reimplement it in
subclasses if there are cases in which the device cannot be queried
(e.g. in Tango, the TangoDevice object may exist even if there is not a real
@@ -144,23 +149,23 @@ class TaurusDevice(taurusmodel.TaurusModel):
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Mandatory implementation in sub class
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+
def _createHWObject(self):
raise NotImplementedError
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# TaurusModel implementation
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
def getTaurusElementType(self):
import taurus.core
return taurus.core.TaurusElementType.Device
-
+
@classmethod
def buildModelName(cls, parent_model, relative_name):
"""build an 'absolute' model name from the parent model and the 'relative'
- name.
+ name.
- If parent_model is a TaurusDatabase, the return is a composition of
the database model name and is device name
- If parent_model is a TaurusDevice, the relative name is ignored and
@@ -173,8 +178,8 @@ class TaurusDevice(taurusmodel.TaurusModel):
return relative_name
if isinstance(parent_model, cls):
return parent_name
- return '%s/%s' % (parent_name,relative_name)
-
+ return '%s/%s' % (parent_name,relative_name)
+
@classmethod
def getNameValidator(cls):
import taurusvalidator
@@ -188,7 +193,7 @@ class TaurusDevice(taurusmodel.TaurusModel):
v = sys.exc_info()[1]
self._deviceSwState = self.decode(v)
return self._deviceSwState
-
+
def getDisplayValue(self,cache=True):
return TaurusSWDevState.whatis(self.getValueObj(cache).value)
@@ -201,7 +206,7 @@ class TaurusDevice(taurusmodel.TaurusModel):
obj.append(('device state', self.getStateObj().getDisplayValue()) or self.getNoneValue())
obj.append(('SW state', self.getDisplayValue()))
return obj
-
+
def getDescription(self,cache=True):
if self._descr is None or not cache:
try:
@@ -209,19 +214,19 @@ class TaurusDevice(taurusmodel.TaurusModel):
except:
self._descr = self._getDefaultDescription()
return self._descr
-
+
def removeListener(self, listener):
ret = taurusmodel.TaurusModel.removeListener(self, listener)
if not ret or self.hasListeners():
return ret # False, None or True
return self.getStateObj().removeListener(self)
-
+
def addListener(self, listener):
weWereListening = self.hasListeners()
ret = taurusmodel.TaurusModel.addListener(self, listener)
if not ret:
return ret
-
+
# We are only listening to State if someone is listening to us
if weWereListening:
# We were listening already, so we must fake an event to the new
@@ -231,7 +236,7 @@ class TaurusDevice(taurusmodel.TaurusModel):
# We were not listening to events, but now we have to
self.getStateObj().addListener(self)
return ret
-
+
def getChildObj(self, child_name):
if child_name is None or len(child_name) == 0:
return None
@@ -254,7 +259,7 @@ class TaurusDevice(taurusmodel.TaurusModel):
return DFT_DEVICE_DESCRIPTION
def poll(self, attrs):
- '''Polling certain attributes of the device. This default
+ '''Polling certain attributes of the device. This default
implementation simply polls each attribute one by one'''
for attr in attrs.values():
attr.poll()
diff --git a/lib/taurus/core/taurusexception.py b/lib/taurus/core/taurusexception.py
index abc9262..ad3e9ae 100644
--- a/lib/taurus/core/taurusexception.py
+++ b/lib/taurus/core/taurusexception.py
@@ -30,10 +30,14 @@ __all__ = ["TaurusException", "DoubleRegistration"]
__docformat__ = "restructuredtext"
class TaurusException(Exception):
+
def __init__(self, description, code = None):
- Exception.__init__(self, description, code)
+ #Exception.__init__(self, description, code)
self.code = code
self.description = description
-
+
+ def __str__(self):
+ return str(self.description)
+
class DoubleRegistration(TaurusException):
- pass
\ No newline at end of file
+ pass
diff --git a/lib/taurus/core/taurushelper.py b/lib/taurus/core/taurushelper.py
index bed27e1..33da855 100644
--- a/lib/taurus/core/taurushelper.py
+++ b/lib/taurus/core/taurushelper.py
@@ -62,7 +62,7 @@ def __translate_version_str2int(version_str):
try:
v += int(parts[0])
l -= 1
- except ValueError,ve:
+ except ValueError:
return v
if not l: return v
@@ -464,26 +464,14 @@ resetLogFormat = util.Logger.resetLogFormat
enableLogOutput = util.Logger.enableLogOutput
disableLogOutput = util.Logger.disableLogOutput
-def log(level, msg, *args, **kw):
- return util.Logger.getRootLog().log(level, msg, *args, **kw)
-
-def trace(msg, *args, **kw):
- return log(Trace, msg, *args, **kw)
-
-def debug(msg, *args, **kw):
- return util.Logger.getRootLog().debug(msg, *args, **kw)
-
-def info(msg, *args, **kw):
- return util.Logger.getRootLog().info(msg, *args, **kw)
-
-def warning(msg, *args, **kw):
- return util.Logger.getRootLog().warning(msg, *args, **kw)
-
-def error(msg, *args, **kw):
- return util.Logger.getRootLog().error(msg, *args, **kw)
+log = util._log
+trace = util.trace
+debug = util.debug
+info = util.info
+warning = util.warning
+error = util.error
+critical = util.critical
-def critical(msg, *args, **kw):
- return util.Logger.getRootLog().critical(msg, *args, **kw)
def changeDefaultPollingPeriod(period):
- Manager().changeDefaultPollingPeriod(period)
\ No newline at end of file
+ Manager().changeDefaultPollingPeriod(period)
diff --git a/lib/taurus/core/taurusmanager.py b/lib/taurus/core/taurusmanager.py
index 850083d..5067f8d 100644
--- a/lib/taurus/core/taurusmanager.py
+++ b/lib/taurus/core/taurusmanager.py
@@ -306,7 +306,7 @@ class TaurusManager(util.Singleton, util.Logger):
m = __import__(full_module_name, globals(), locals(), ['*'])
except:
self.debug('Failed to inspect %s' % (package_name))
- self.traceback()
+ self.debug('Details:', exc_info=1)
continue
for s in m.__dict__.values():
plugin = None
diff --git a/lib/taurus/core/taurusoperation.py b/lib/taurus/core/taurusoperation.py
index 9a5cb04..f5d8624 100644
--- a/lib/taurus/core/taurusoperation.py
+++ b/lib/taurus/core/taurusoperation.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -32,49 +32,49 @@ __docformat__ = "restructuredtext"
import util
class TaurusOperation(util.Logger):
-
+
def __init__(self, name='TaurusOperation', parent=None, callbacks = None):
self.call__init__(util.Logger, name, parent)
if callbacks is None: callbacks = []
self._callbacks = callbacks
self._dangerMessage = None
self._isDangerous = False
-
+
def getDevice(self):
pass
-
+
def setCallbacks(self, callbacks):
self._callbacks = callbacks
-
+
def getCallbacks(self):
- return self._callbacks
-
+ return self._callbacks
+
def execute(self):
for f in self._callbacks:
f(operation = self)
-
+
def isDangerous(self):
return self._isDangerous
-
+
def setDangerMessage(self, dangerMessage=None):
'''if dangerMessage is None, the operation is considered safe'''
self._dangerMessage = dangerMessage
self._isDangerous = dangerMessage is not None
-
+
def getDangerMessage(self):
return self._dangerMessage
-
+
def resetDangerMessage(self):
self.setDangerMessage(None)
-
-
+
+
class WriteAttrOperation(TaurusOperation):
-
+
def __init__(self, attr, value, callbacks = None):
self.call__init__(TaurusOperation, 'WriteAttrOperation', attr, callbacks=callbacks)
self.attr = attr
self.value = value
-
+
def execute(self):
self.attr.write(self.value)
TaurusOperation.execute(self)
diff --git a/lib/taurus/core/util/argparse/taurusargparse.py b/lib/taurus/core/util/argparse/taurusargparse.py
index ce73a51..a1df355 100644
--- a/lib/taurus/core/util/argparse/taurusargparse.py
+++ b/lib/taurus/core/util/argparse/taurusargparse.py
@@ -3,42 +3,42 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""Helper command line parser for taurus based on :mod:`optparse`.
+"""Helper command line parser for taurus based on :mod:`optparse`.
Suppose you have an application file::
-
+
import sys
from PyQt4 import Qt
-
-
+
+
class GUI(Qt.QMainWindow):
pass
-
-
+
+
def main():
import taurus.core.util.argparse as argparse
-
+
parser, options, args = argparse.init_taurus_args()
-
+
app = Qt.QApplication(sys.argv)
w = GUI()
w.show()
@@ -46,7 +46,7 @@ Suppose you have an application file::
if __name__ == "__main__":
main()
-
+
The call to :func:`taurus.core.util.argparse.init_taurus_args` will initialize
taurus environment based on the command line options given by the user.
Currently, the known options are:
@@ -56,10 +56,11 @@ Currently, the known options are:
#. ``--tango-host`` sets the default tango host
#. ``--taurus-polling-period`` sets the default taurus global polling period (milliseconds)
#. ``--taurus-serialization-mode`` sets the default taurus serialization mode
-
+ #. ``--remote-console-port`` enables remote debugging using the given port
+
You can easily extend the taurus options with your application specific options.
-Supose you want to add an option like ``--model=<model name>``::
-
+Suppose you want to add an option like ``--model=<model name>``::
+
def main():
import taurus.core.util.argparse as argparse
parser = argparse.get_taurus_parser(parser=parser)
@@ -68,23 +69,24 @@ Supose you want to add an option like ``--model=<model name>``::
parser.add_option("--model")
parser, options, args = argparse.init_taurus_args(parser=parser)
-
+
app = Qt.QApplication(sys.argv)
w = GUI()
w.show()
sys.exit(app.exec_())
"""
-__all__ = ["get_taurus_parser", "init_taurus_args", "parse_taurus_args"]
+__all__ = ["get_taurus_parser", "init_taurus_args", "parse_taurus_args",
+ "split_taurus_args"]
__docformat__ = "restructuredtext"
def get_taurus_parser(parser=None):
- """ Returns a :class:`optparse.OptionParser` initialized with a
+ """ Returns a :class:`optparse.OptionParser` initialized with a
:class:`optparse.OptionGroup` containning some taurus options.
If a parser is given as parameter then it uses this parser instead of
creating a new one.
-
+
:param parser: an option parser or None (default) to create a new parser
:type parser: :class:`optparse.OptionParser`
:return: an option parser or the given parser if it is not None
@@ -92,18 +94,19 @@ def get_taurus_parser(parser=None):
import optparse
if parser is None:
parser = optparse.OptionParser()
-
+
g = parser.get_option_group('--taurus-log-level')
if g is None:
group = optparse.OptionGroup(parser, "Taurus Options",
"Basic options present in any taurus application")
-
+
help_tauruslog = "taurus log level. Allowed values are (case insensitive): critical, "\
"error, warning/warn, info, debug, trace"
help_tangohost = "Tango host name"
help_tauruspolling = "taurus global polling period in milliseconds"
help_taurusserial= "taurus serialization mode. Allowed values are (case insensitive): "\
"serial, concurrent (default)"
+ help_rcport = "enables remote debugging using the given port"
group.add_option("--taurus-log-level", dest="taurus_log_level", metavar="LEVEL",
help=help_tauruslog, type="str", default="info")
group.add_option("--taurus-polling-period", dest="taurus_polling_period", metavar="MILLISEC",
@@ -112,23 +115,25 @@ def get_taurus_parser(parser=None):
help=help_taurusserial, type="str", default="Concurrent")
group.add_option("--tango-host", dest="tango_host", metavar="TANGO_HOST",
help=help_tangohost, type="str", default=None)
+ group.add_option("--remote-console-port", dest="remote_console_port", metavar="PORT",
+ help=help_rcport, type="int", default=None)
parser.add_option_group(group)
return parser
-
+
def parse_taurus_args(parser=None, args=None, values=None):
"""Parses the command line. If parser is not given, then a new parser
is created. In any case, the parser is initialized using the
:func:`taurus.core.util.argparse.get_taurus_parser`.
args and values are the optional parameters that will be given when
executing :meth:`optparse.OptionParser.parse_args`.
-
+
:param parser: an option parser or None (default) to create a new parser
:type parser: :class:`optparse.OptionParser`
:param args: the list of arguments to process (default is None meaning: sys.argv[1:])
:type args: seq<str>
- :param values: a :class:`optparse.Values` object to store option arguments in
- (default is None meaning: a new instance of Values) - if you give an
- existing object, the option defaults will not be initialized on it
+ :param values: a :class:`optparse.Values` object to store option arguments in
+ (default is None meaning: a new instance of Values) - if you give an
+ existing object, the option defaults will not be initialized on it
:return: a tuple of three elements: parser, options, args
:rtype: :class:`optparse.OptionParser`, :class:`optparse.Values`, seq<str> """
parser = get_taurus_parser(parser=parser)
@@ -137,28 +142,28 @@ def parse_taurus_args(parser=None, args=None, values=None):
def init_taurus_args(parser=None, args=None, values=None):
"""Parses the command line using :func:`taurus.core.util.argparse.parse_taurus_args`.
-
+
After the command line is parsed, actions are taken on each recognized parameter.
For example, the taurus log level and the default tango host are set accordingly.
-
+
:param parser: an option parser or None (default) to create a new parser
:type parser: :class:`optparse.OptionParser`
:param args: the list of arguments to process (default is None meaning: sys.argv[1:])
:type args: seq<str>
- :param values: a :class:`optparse.Values` object to store option arguments in
- (default is None meaning: a new instance of Values) - if you give an
- existing object, the option defaults will not be initialized on it
+ :param values: a :class:`optparse.Values` object to store option arguments in
+ (default is None meaning: a new instance of Values) - if you give an
+ existing object, the option defaults will not be initialized on it
:return: a tuple of three elements: parser, options, args
:rtype: :class:`optparse.OptionParser`, :class:`optparse.Values`, seq<str> """
import taurus
parser, options, args = parse_taurus_args(parser=parser, args=args, values=values)
-
+
# initialize taurus log level
log_level_str = options.taurus_log_level.capitalize()
if hasattr(taurus, log_level_str):
log_level = getattr(taurus, log_level_str)
taurus.setLogLevel(log_level)
-
+
# initialize tango host
if options.tango_host is not None:
tango_factory = taurus.Factory("tango")
@@ -175,6 +180,41 @@ def init_taurus_args(parser=None, args=None, values=None):
if hasattr(taurus.core.TaurusSerializationMode, m):
m = getattr(taurus.core.TaurusSerializationMode, m)
taurus.Manager().setSerializationMode(m)
-
+
+ # initialize remote console port
+ if options.remote_console_port is not None:
+ try:
+ import rfoo.utils.rconsole
+ rfoo.utils.rconsole.spawn_server(port=options.remote_console_port)
+ taurus.info("rconsole started. You can connect to it by typing: rconsole -p %d", options.remote_console_port)
+ except Exception, e:
+ taurus.warning("Cannot spawn debugger. Reason: %s",str(e))
+
return parser, options, args
-
\ No newline at end of file
+
+def split_taurus_args(parser, args=None):
+ """Splits arguments into valid parser arguments and non valid parser
+ arguments.
+
+ :param parser: an option parser
+ :type parser: :class:`optparse.OptionParser`
+ :param args: the list of arguments to process
+ (default is None meaning: sys.argv)
+ :type args: seq<str>
+ :return: a tuple of two elements: parser args, non parser args
+ :rtype: seq<seq<str>, seq<str>>"""
+
+ if args is None:
+ import sys
+ args = sys.argv
+
+ taurus_args, non_taurus_args = [args[0]], [args[0]]
+ for arg in args[1:]:
+ arg_name = arg.split("=", 1)[0]
+ if parser.has_option(arg_name):
+ taurus_args.append(arg)
+ else:
+ non_taurus_args.append(arg)
+
+ return taurus_args, non_taurus_args
+
diff --git a/lib/taurus/core/util/codecs.py b/lib/taurus/core/util/codecs.py
index 1b6c01b..91e447e 100644
--- a/lib/taurus/core/util/codecs.py
+++ b/lib/taurus/core/util/codecs.py
@@ -72,6 +72,10 @@ import copy
import operator
import types
+#need by VideoImageCodec
+import struct
+import numpy
+
from singleton import Singleton
from log import Logger, DebugIt
from containers import CaselessDict
@@ -437,7 +441,9 @@ class FunctionCodec(Codec):
self._func_name = func_name
def encode(self, data, *args, **kwargs):
- return self._func_name, { 'type' : self._func_name, 'data' : data }
+ format = self._func_name
+ if len(data[0]): format += '_%s' % data[0]
+ return format, { 'type' : self._func_name, 'data' : data[1] }
def decode(self, data, *args, **kwargs):
if not data[0].startswith(self._func_name):
@@ -452,6 +458,164 @@ class PlotCodec(FunctionCodec):
FunctionCodec.__init__(self, 'plot')
+class VideoImageCodec(Codec):
+ """A codec able to encode/decode to/from LImA video_image format.
+
+ Example::
+
+ >>> from taurus.core.util import CodecFactory
+ >>> import PyTango
+
+ >>> #first get an image from a LImA device to decode
+ >>> data = PyTango.DeviceProxy(ccdName).read_attribute('video_last_image').value
+ >>> cf = CodecFactory()
+ >>> codec = cf.getCodec('VIDEO_IMAGE')
+ >>> format,decoded_data = codec.decode(data)
+ >>> # encode it again to check
+ >>> format, encoded_data = codec.encode(("",decoded_data))
+ >>> #compare images excluding the header:
+ >>> data[1][32:] == encoded_data[32:]
+ >>> #The headers can be different in the frameNumber
+ >>> struct.unpack('!IHHqiiHHHH',data[1][:32])
+ (1447314767, 1, 0, 6868, 1294, 964, 0, 32, 0, 0)
+ >>> struct.unpack('!IHHqiiHHHH',encoded_data[:32])
+ (1447314767, 1, 0, -1, 1294, 964, 0, 32, 0, 0)
+ """
+
+ VIDEO_HEADER_FORMAT = '!IHHqiiHHHH'
+
+ def encode(self, data, *args, **kwargs):
+ """encodes the given data to a LImA's video_image. The given data **must** be an numpy.array
+
+ :param data: (sequence[str, obj]) a sequence of two elements where the first item is the encoding format of the second item object
+
+ :return: (sequence[str, obj]) a sequence of two elements where the first item is the encoding format of the second item object"""
+
+ format = 'VIDEO_IMAGE'
+ if len(data[0]): format += '_%s' % data[0]
+ #imgMode depends on numpy.array dtype
+ imgMode = self.__getModeId(str(data[1].dtype))
+ #frameNumber, unknown then -1
+ height,width = data[1].shape
+ header = self.__packHeader(imgMode,-1,width,height)
+ img2D = data[1]
+ img1D = img2D.flatten()
+ buffer = struct.pack(self.__getFormatId(imgMode)*img1D.size,*img1D)
+ return format,header+buffer
+
+ def decode(self, data, *args, **kwargs):
+ """decodes the given data from a LImA's video_image.
+
+ :param data: (sequence[str, obj]) a sequence of two elements where the first item is the encoding format of the second item object
+
+ :return: (sequence[str, obj]) a sequence of two elements where the first item is the encoding format of the second item object"""
+
+ if not data[0] == 'VIDEO_IMAGE':
+ return data
+ header = self.__unpackHeader(data[1][:struct.calcsize(self.VIDEO_HEADER_FORMAT)])
+
+ imgBuffer = data[1][struct.calcsize(self.VIDEO_HEADER_FORMAT):]
+ fmt = self.__getFormatId(header['imageMode'])
+ img1D = numpy.array(struct.unpack(fmt*(len(imgBuffer)/struct.calcsize(fmt)),
+ imgBuffer),
+ dtype=self.__getDtypeId(header['imageMode']))
+ img2D = img1D.reshape(header['height'],header['width'])
+ return '',img2D
+
+ def __unpackHeader(self,header):
+ h = struct.unpack(self.VIDEO_HEADER_FORMAT,header)
+ headerDict={}
+ headerDict['magic'] = h[0]
+ headerDict['headerVersion'] = h[1]
+ headerDict['imageMode'] = h[2]
+ headerDict['frameNumber'] = h[3]
+ headerDict['width'] = h[4]
+ headerDict['height'] = h[5]
+ headerDict['endianness'] = h[6]
+ headerDict['headerSize'] = h[7]
+ headerDict['padding'] = h[7:]
+ return headerDict
+
+ def __packHeader(self,imgMode,frameNumber,width,height):
+ magic = 0x5644454f
+ version = 1
+ endian = ord(struct.pack('=H',1)[-1])
+ hsize = struct.calcsize(self.VIDEO_HEADER_FORMAT)
+ return struct.pack(self.VIDEO_HEADER_FORMAT,
+ magic,
+ version,
+ imgMode,
+ frameNumber,
+ width,
+ height,
+ endian,
+ hsize,
+ 0,0)#padding
+
+ def __getModeId(self,mode):
+ return {#when encode
+ 'uint8' : 0,#Core.Y8,
+ 'uint16' : 1,#Core.Y16,
+ 'uint32' : 2,#Core.Y32,
+ 'uint64' : 3,#Core.Y64,
+ #when decode
+ 'Y8' : 0,#Core.Y8,
+ 'Y16' : 1,#Core.Y16,
+ 'Y32' : 2,#Core.Y32,
+ 'Y64' : 3,#Core.Y64,
+ #TODO: other modes
+ #'RGB555' : Core.RGB555,
+ #'RGB565' : Core.RGB565,
+ #'RGB24' : Core.RGB24,
+ #'RGB32' : Core.RGB32,
+ #'BGR24' : Core.BGR24,
+ #'BGR32' : Core.BGR32,
+ #'BAYER RG8' : Core.BAYER_RG8,
+ #'BAYER RG16' : Core.BAYER_RG16,
+ #'I420' : Core.I420,
+ #'YUV411' : Core.YUV411,
+ #'YUV422' : Core.YUV422,
+ #'YUV444' : Core.YUV444
+ }[mode]
+
+ def __getFormatId(self,mode):
+ return {0 : 'B',
+ 1 : 'H',
+ 2 : 'I',
+ 3 : 'L',
+ #'RGB555' : Core.RGB555,
+ #'RGB565' : Core.RGB565,
+ #'RGB24' : Core.RGB24,
+ #'RGB32' : Core.RGB32,
+ #'BGR24' : Core.BGR24,
+ #'BGR32' : Core.BGR32,
+ #'BAYER RG8' : Core.BAYER_RG8,
+ #'BAYER RG16' : Core.BAYER_RG16,
+ #'I420' : Core.I420,
+ #'YUV411' : Core.YUV411,
+ #'YUV422' : Core.YUV422,
+ #'YUV444' : Core.YUV444
+ }[mode]
+
+ def __getDtypeId(self,mode):
+ return {0 : 'uint8',
+ 1 : 'uint16',
+ 2 : 'uint32',
+ 3 : 'uint64',
+ #'RGB555' : Core.RGB555,
+ #'RGB565' : Core.RGB565,
+ #'RGB24' : Core.RGB24,
+ #'RGB32' : Core.RGB32,
+ #'BGR24' : Core.BGR24,
+ #'BGR32' : Core.BGR32,
+ #'BAYER RG8' : Core.BAYER_RG8,
+ #'BAYER RG16' : Core.BAYER_RG16,
+ #'I420' : Core.I420,
+ #'YUV411' : Core.YUV411,
+ #'YUV422' : Core.YUV422,
+ #'YUV444' : Core.YUV444
+ }[mode]
+
class CodecPipeline(Codec, list):
"""The codec class used when encoding/decoding data with multiple encoders
@@ -555,6 +719,7 @@ class CodecFactory(Singleton, Logger):
'zip' : ZIPCodec,
'pickle' : PickleCodec,
'plot' : PlotCodec,
+ 'VIDEO_IMAGE' : VideoImageCodec,
'null' : NullCodec,
'none' : NullCodec,
'' : NullCodec })
@@ -632,4 +797,4 @@ class CodecFactory(Singleton, Logger):
def encode(self, format, data, *args, **kwargs):
return self.getCodec(format).encode(data, *args, **kwargs)
-
\ No newline at end of file
+
diff --git a/lib/taurus/core/util/containers.py b/lib/taurus/core/util/containers.py
index 6290188..8296fc7 100644
--- a/lib/taurus/core/util/containers.py
+++ b/lib/taurus/core/util/containers.py
@@ -809,6 +809,79 @@ class ThreadDict(dict):
itervalues = self_locked(dict.itervalues)
iteritems = self_locked(dict.iteritems)
+class SortedDict(dict):
+ """ This class implements a dictionary that returns keys in the same order they were inserted. """
+
+ def __init__(self,other=None):
+ dict.__init__(self)
+ self._keys = []
+ if other is not None:
+ self.update(other)
+ return
+
+ def sort(self,key):
+ """
+ This method modifies the sorting of the dictionary overriding the existing sort key.
+ :param key: it can be a sequence containing all the keys already existing in the dictionary
+ or a callable providing a sorting key algorithm.
+ """
+ import operator
+ if operator.isCallable(key):
+ self._keys = sorted(self._keys,key=key)
+ else:
+ for k in self._keys:
+ if k not in self._keys: raise KeyError(k)
+ self._keys = list(key)
+ return self._keys[:]
+
+ def __setitem__(self,k,v):
+ if k not in self._keys:
+ self._keys.append(k)
+ dict.__setitem__(self,k,v)
+
+ def update(self,other):
+ if hasattr(other,'items'):
+ other = other.items()
+ for k,v in other:
+ self.__setitem__(k,v)
+
+ @staticmethod
+ def fromkeys(S,v=None):
+ return SortedDict((s,v) for s in S)
+
+ def pop(self,k,d=None):
+ """Removes key and returns its (self[key] or d or None)"""
+ if k not in self: return d
+ self._keys.remove(k)
+ return dict.pop(self,k)
+
+ def popitem(self):
+ """Removes and returns last key,value pair"""
+ k = self._keys[-1]
+ v = self[k]
+ self.pop(k)
+ return k,v
+
+ def clear(self):
+ self._keys = []
+ return dict.clear(self)
+
+ def keys(self):
+ return self._keys[:]
+ def values(self):
+ return [self[k] for k in self._keys]
+ def items(self):
+ return [(k,self[k]) for k in self._keys]
+
+ def __iter__(self):
+ return (i for i in self._keys)
+ def iteritems(self):
+ return ((k,self[k]) for k in self._keys)
+ def iterkeys(self):
+ return (i for i in self._keys)
+ def itervalues(self):
+ return (self[k] for k in self._keys)
+
try:
from collections import defaultdict
except:
@@ -847,7 +920,6 @@ except:
return 'defaultdict(%s, %s)' % (self.default_factory,
dict.__repr__(self))
-
class defaultdict_fromkey(defaultdict):
""" Creates a dictionary with a default_factory function that creates new elements using key as argument.
Usage : new_dict = defaultdict_fromkey(method); where method like (lambda key: return new_obj(key))
diff --git a/lib/taurus/qt/qtgui/table/qtable.py b/lib/taurus/core/util/decorator/memoize.py
similarity index 52%
copy from lib/taurus/qt/qtgui/table/qtable.py
copy to lib/taurus/core/util/decorator/memoize.py
index 4f5d6a7..7db9c05 100644
--- a/lib/taurus/qt/qtgui/table/qtable.py
+++ b/lib/taurus/core/util/decorator/memoize.py
@@ -3,45 +3,55 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This module provides base table widget"""
-
-__all__ = ["QBaseTableWidget"]
-
-__docformat__ = 'restructuredtext'
-
-from taurus.qt import Qt
-from taurus.qt.qtgui.model import QBaseModelWidget
-
-
-class QBaseTableWidget(QBaseModelWidget):
-
- def tableView(self):
- return self.viewWidget()
-
- def createViewWidget(self):
- table = Qt.QTableView(self)
- table.setSortingEnabled(True)
- table.setAlternatingRowColors(True)
- table.setSelectionBehavior(Qt.QAbstractItemView.SelectRows)
- table.setSelectionMode(Qt.QAbstractItemView.ExtendedSelection)
- return table
\ No newline at end of file
+import functools
+
+
+class memoized(object):
+ '''Decorator. Caches a function's return value each time it is called.
+ If called later with the same arguments, the cached value is returned
+ (not reevaluated).
+ '''
+
+ def __init__(self, func):
+ self.func = func
+ self.cache = {}
+
+ def __call__(self, *args):
+ try:
+ return self.cache[args]
+ except KeyError:
+ value = self.func(*args)
+ self.cache[args] = value
+ return value
+ except TypeError:
+ # uncachable -- for instance, passing a list as an argument.
+ # Better to not cache than to blow up entirely.
+ return self.func(*args)
+
+ def __repr__(self):
+ '''Return the function's docstring.'''
+ return self.func.__doc__
+
+ def __get__(self, obj, objtype):
+ '''Support instance methods.'''
+ return functools.partial(self.__call__, obj)
diff --git a/lib/taurus/core/util/log.py b/lib/taurus/core/util/log.py
index 88e2953..2f63302 100644
--- a/lib/taurus/core/util/log.py
+++ b/lib/taurus/core/util/log.py
@@ -3,32 +3,33 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This module contains a set of useful logging elements based on python's
+"""This module contains a set of useful logging elements based on python's
:mod:`logging` system."""
__all__ = ["LogIt", "TraceIt", "DebugIt", "InfoIt", "WarnIt", "ErrorIt",
"CriticalIt", "MemoryLogHandler", "LogExceptHook", "Logger",
- "LogFilter"]
+ "LogFilter",
+ "_log", "trace", "debug", "info", "warning", "error", "critical"]
__docformat__ = "restructuredtext"
@@ -39,77 +40,101 @@ import weakref
import warnings
import traceback
import inspect
-import functools
import threading
from object import Object
+from wrap import wraps
from excepthook import BaseExceptHook
TRACE = 5
logging.addLevelName(TRACE, "TRACE")
+#
+# _srcfile is used when walking the stack to check when we've got the first
+# caller stack frame.
+#
+if hasattr(sys, 'frozen'): #support for py2exe
+ _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:])
+elif __file__[-4:].lower() in ['.pyc', '.pyo']:
+ _srcfile = __file__[:-4] + '.py'
+else:
+ _srcfile = __file__
+_srcfile = os.path.normcase(_srcfile)
+
+# next bit filched from 1.5.2's inspect.py
+def currentframe():
+ """Return the frame object for the caller's stack frame."""
+ try:
+ raise Exception
+ except:
+ return sys.exc_info()[2].tb_frame.f_back
+
+if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
+# done filching
+
+
class LogIt(object):
"""A function designed to be a decorator of any method of a Logger subclass.
The idea is to log the entrance and exit of any decorated method of a Logger
subclass.
Example::
-
+
from taurus.core.util import *
-
+
class Example(Logger):
-
+
@LogIt(Logger.Debug)
def go(self):
print "Hello world "
-
+
This will generate two log messages of Debug level, one before the function
go is called and another when go finishes. Example output::
-
+
MainThread DEBUG 2010-11-15 15:36:11,440 Example: -> go
Hello world of mine
MainThread DEBUG 2010-11-15 15:36:11,441 Example: <- go
-
+
This decorator can receive two optional arguments **showargs** and **showret**
which are set to False by default. Enabling them will had verbose infomation
about the parameters and return value. The following example::
from taurus.core.util import *
-
+
class Example(Logger):
-
+
@LogIt(Logger.Debug, showargs=True, showret=True)
def go(self, msg):
msg = "Hello world",msg
print msg
return msg
-
+
would generate an output like::
MainThread DEBUG 2010-11-15 15:42:02,353 Example: -> go('of mine',)
Hello world of mine
MainThread DEBUG 2010-11-15 15:42:02,353 Example: <- go = Hello world of mine
-
- .. note::
+
+ .. note::
it may happen that in these examples that the output of the method
appears before or after the log messages. This is because log
messages are, by default, written to the *stardard error* while the print
message inside the go method outputs to the *standard ouput*. On many
systems these two targets are not synchronized.
"""
-
+
def __init__(self, level=logging.DEBUG, showargs=False, showret=False, col_limit=0):
self._level = level
self._showargs = showargs
self._showret = showret
self._col_limit = col_limit
-
+
def __call__(self, f):
- @functools.wraps(f)
+ @wraps(f)
def wrapper(*args, **kwargs):
f_self = args[0]
if f_self.log_level > self._level:
return f(*args, **kwargs)
-
+
has_log = hasattr(f_self, "log")
fname = f.func_name
log_obj = f_self
@@ -146,14 +171,14 @@ class LogIt(object):
class TraceIt(LogIt):
"""Specialization of LogIt for trace level messages.
Example::
-
+
from taurus.core.util import TraceIt
class Example(Logger):
-
+
@TraceIt()
def go(self):
print "Hello world"
-
+
.. seealso:: :class:`LogIt`"""
def __init__(self, showargs=False, showret=False):
LogIt.__init__(self, level=TRACE, showargs=showargs, showret=showret)
@@ -162,14 +187,14 @@ class TraceIt(LogIt):
class DebugIt(LogIt):
"""Specialization of LogIt for debug level messages.
Example::
-
+
from taurus.core.util import DebugIt
class Example(Logger):
-
+
@DebugIt()
def go(self):
print "Hello world"
-
+
.. seealso:: :class:`LogIt`"""
def __init__(self, showargs=False, showret=False):
LogIt.__init__(self, level=logging.DEBUG, showargs=showargs, showret=showret)
@@ -178,14 +203,14 @@ class DebugIt(LogIt):
class InfoIt(LogIt):
"""Specialization of LogIt for info level messages.
Example::
-
+
from taurus.core.util import InfoIt
class Example(Logger):
-
+
@InfoIt()
def go(self):
print "Hello world"
-
+
.. seealso:: :class:`LogIt`"""
def __init__(self, showargs=False, showret=False):
LogIt.__init__(self, level=logging.INFO, showargs=showargs, showret=showret)
@@ -194,14 +219,14 @@ class InfoIt(LogIt):
class WarnIt(LogIt):
"""Specialization of LogIt for warn level messages.
Example::
-
+
from taurus.core.util import WarnIt
class Example(Logger):
-
+
@WarnIt()
def go(self):
print "Hello world"
-
+
.. seealso:: :class:`LogIt`"""
def __init__(self, showargs=False, showret=False):
LogIt.__init__(self, level=logging.WARN, showargs=showargs, showret=showret)
@@ -210,14 +235,14 @@ class WarnIt(LogIt):
class ErrorIt(LogIt):
"""Specialization of LogIt for error level messages.
Example::
-
+
from taurus.core.util import ErrorIt
class Example(Logger):
-
+
@ErrorIt()
def go(self):
print "Hello world"
-
+
.. seealso:: :class:`LogIt`"""
def __init__(self, showargs=False, showret=False):
LogIt.__init__(self, level=logging.ERROR, showargs=showargs, showret=showret)
@@ -226,14 +251,14 @@ class ErrorIt(LogIt):
class CriticalIt(LogIt):
"""Specialization of LogIt for critical level messages.
Example::
-
+
from taurus.core.util import CriticalIt
class Example(Logger):
-
+
@CriticalIt()
def go(self):
print "Hello world"
-
+
.. seealso:: :class:`LogIt`"""
def __init__(self, showargs=False, showret=False):
LogIt.__init__(self, level=logging.CRITICAL, showargs=showargs, showret=showret)
@@ -242,22 +267,22 @@ class CriticalIt(LogIt):
class MemoryLogHandler(list, logging.handlers.BufferingHandler):
"""An experimental log handler that stores temporary records in memory.
When flushed it passes the records to another handler"""
-
+
def __init__(self, capacity=1000):
list.__init__(self)
logging.handlers.BufferingHandler.__init__(self, capacity=capacity)
self._handler_list_changed = False
-
+
def shouldFlush(self, record):
"""Determines if the given record should trigger the flush
-
+
:param record: (logging.LogRecord) a log record
:return: (bool) wheter or not the handler should be flushed
"""
return (len(self.buffer) >= self.capacity) or \
(record.levelno >= Logger.getLogLevel()) or \
self._handler_list_changed
-
+
def flush(self):
"""Flushes this handler"""
for record in self.buffer:
@@ -269,13 +294,13 @@ class MemoryLogHandler(list, logging.handlers.BufferingHandler):
"""Closes this handler"""
self.flush()
del self[:]
- BufferingHandler.close(self)
+ logging.handlers.BufferingHandler.close(self)
class LogExceptHook(BaseExceptHook):
"""A callable class that acts as an excepthook that logs the exception in
the python logging system.
-
+
:param hook_to: callable excepthook that will be called at the end of
this hook handling [default: None]
:type hook_to: callable
@@ -283,13 +308,13 @@ class LogExceptHook(BaseExceptHook):
:type name: str
:param level: log level [default: logging.ERROR]
:type level: int"""
-
+
def __init__(self, hook_to=None, name=None, level=logging.ERROR):
BaseExceptHook.__init__(self, hook_to=hook_to)
name = name or self.__class__.__name__
self._level = level
self._log = Logger(name=name)
-
+
def report(self, *exc_info):
text = "".join(traceback.format_exception(*exc_info))
if text[-1] == '\n':
@@ -297,63 +322,90 @@ class LogExceptHook(BaseExceptHook):
self._log.log(self._level, "Unhandled exception:\n%s", text)
+class _Logger(logging.Logger):
+
+ def findCaller(self):
+ """
+ Find the stack frame of the caller so that we can note the source
+ file name, line number and function name.
+ """
+ f = currentframe()
+ #On some versions of IronPython, currentframe() returns None if
+ #IronPython isn't run with -X:Frames.
+ if f is not None:
+ f = f.f_back
+ rv = "(unknown file)", 0, "(unknown function)"
+ while hasattr(f, "f_code"):
+ co = f.f_code
+ filename = os.path.normcase(co.co_filename)
+ if filename in (_srcfile, logging._srcfile):
+ f = f.f_back
+ continue
+ rv = (co.co_filename, f.f_lineno, co.co_name)
+ break
+ return rv
+
+
class Logger(Object):
"""The taurus logger class. All taurus pertinent classes should inherit
directly or indirectly from this class if they need taurus logging
facilities."""
-
+
#: Internal usage
root_inited = False
-
+
#: Internal usage
root_init_lock = threading.Lock()
-
+
#: Critical message level (constant)
Critical = logging.CRITICAL
-
+
#: Error message level (constant)
Error = logging.ERROR
-
+
#: Warning message level (constant)
Warning = logging.WARNING
-
+
#: Info message level (constant)
Info = logging.INFO
-
+
#: Debug message level (constant)
Debug = logging.DEBUG
-
+
#: Trace message level (constant)
Trace = TRACE
-
+
#: Default log level (constant)
DftLogLevel = Info
-
+
+ #: Default log message format (constant)
+ DftLogMessageFormat = '%(threadName)-14s %(levelname)-8s %(asctime)s %(name)s: %(message)s'
+
#: Default log format (constant)
- DftLogFormat = logging.Formatter('%(threadName)-14s %(levelname)-8s %(asctime)s %(name)s: %(message)s')
-
+ DftLogFormat = logging.Formatter(DftLogMessageFormat)
+
#: Current global log level
log_level = DftLogLevel
-
+
#: Default log message format
log_format = DftLogFormat
-
+
#: the main stream handler
stream_handler = None
-
-
+
+
def __init__(self, name='', parent=None, format=None):
"""The Logger constructor
-
+
:param name: (str) the logger name (default is empty string)
:param parent: (Logger) the parent logger or None if no parent exists (default is None)
:param format: (str) the log message format or None to use the default log format (default is None)
"""
self.call__init__(Object)
-
+
if format: self.log_format = format
Logger.initRoot()
-
+
if name is None or len(name) == 0:
name = self.__class__.__name__
self.log_name = name
@@ -361,8 +413,8 @@ class Logger(Object):
self.log_full_name = '%s.%s' % (parent.log_full_name, name)
else:
self.log_full_name = name
-
- self.log_obj = logging.getLogger(self.log_full_name)
+
+ self.log_obj = self._getLogger(self.log_full_name)
self.log_handlers = []
self.log_parent = None
@@ -373,8 +425,7 @@ class Logger(Object):
def cleanUp(self):
"""The cleanUp. Default implementation does nothing
- Overwrite when necessary
- """
+ Overwrite when necessary"""
pass
@classmethod
@@ -383,16 +434,16 @@ class Logger(Object):
method directly in your code
"""
if cls.root_inited:
- return cls._getRootLog()
-
+ return cls._getLogger()
+
try:
cls.root_init_lock.acquire()
- root_logger = cls._getRootLog()
+ root_logger = cls._getLogger()
logging.addLevelName(cls.Trace, "TRACE")
cls.stream_handler = logging.StreamHandler(sys.__stderr__)
cls.stream_handler.setFormatter(cls.log_format)
root_logger.addHandler(cls.stream_handler)
-
+
console_log_level = os.environ.get("TAURUSLOGLEVEL", None)
if console_log_level is not None:
console_log_level = console_log_level.capitalize()
@@ -407,16 +458,16 @@ class Logger(Object):
@classmethod
def addRootLogHandler(cls, h):
"""Adds a new handler to the root logger
-
+
:param h: (logging.Handler) the new log handler
"""
h.setFormatter(cls.getLogFormat())
cls.initRoot().addHandler(h)
-
+
@classmethod
def removeRootLogHandler(cls, h):
"""Removes the given handler from the root logger
-
+
:param h: (logging.Handler) the handler to be removed
"""
cls.initRoot().removeHandler(h)
@@ -438,24 +489,24 @@ class Logger(Object):
@classmethod
def setLogLevel(cls,level):
"""sets the new log level (the root log level)
-
+
:param level: (int) the new log level
"""
cls.log_level = level
cls.initRoot().setLevel(level)
-
+
@classmethod
def getLogLevel(cls):
"""Retuns the current log level (the root log level)
-
+
:return: (int) a number representing the log level
"""
return cls.log_level
-
+
@classmethod
def setLogFormat(cls,format):
"""sets the new log message format
-
+
:param level: (str) the new log message format
"""
cls.log_format = logging.Formatter(format)
@@ -466,11 +517,11 @@ class Logger(Object):
@classmethod
def getLogFormat(cls):
"""Retuns the current log message format (the root log format)
-
+
:return: (str) the log message format
"""
return cls.log_format
-
+
@classmethod
def resetLogLevel(cls):
"""Resets the log level (the root log level)"""
@@ -480,11 +531,11 @@ class Logger(Object):
def resetLogFormat(cls):
"""Resets the log message format (the root log format)"""
cls.setLogFormat(cls.DftLogFormat)
-
+
@classmethod
def addLevelName(cls, level_no, level_name):
"""Registers a new log level
-
+
:param level_no: (int) the level number
:param level_name: (str) the corresponding name
"""
@@ -492,30 +543,40 @@ class Logger(Object):
level_name = level_name.capitalize()
if not hasattr(cls, level_name):
setattr(cls, level_name, level_no)
-
+
@classmethod
def getRootLog(cls):
"""Retuns the root logger
-
+
:return: (logging.Logger) the root logger
"""
- cls.initRoot()
- return cls._getRootLog()
+ return cls.initRoot()
+
+ @staticmethod
+ def _getLogger(name=None):
+ orig_logger_class = logging.getLoggerClass()
+ try:
+ logging.setLoggerClass(_Logger)
+ ret = logging.getLogger(name)
+ return ret
+ finally:
+ logging.setLoggerClass(orig_logger_class)
@classmethod
- def _getRootLog(cls):
- return logging.getLogger()
-
+ def getLogger(cls, name=None):
+ cls.initRoot()
+ return cls._getLogger(name=name)
+
def getLogObj(self):
"""Returns the log object for this object
-
+
:return: (logging.Logger) the log object
"""
return self.log_obj
-
+
def getParent(self):
"""Returns the log parent for this object or None if no parent exists
-
+
:return: (logging.Logger or None) the log parent for this object
"""
if self.log_parent is None:
@@ -524,7 +585,7 @@ class Logger(Object):
def getChildren(self):
"""Returns the log children for this object
-
+
:return: (sequence<logging.Logger) the list of log children
"""
children = []
@@ -533,10 +594,10 @@ class Logger(Object):
if child is not None:
children.append(child)
return children
-
+
def addChild(self, child):
"""Adds a new logging child
-
+
:param child: (logging.Logger) the new child
"""
if not self.log_children.get(id(child)):
@@ -544,7 +605,7 @@ class Logger(Object):
def addLogHandler(self, handler):
"""Registers a new handler in this object's logger
-
+
:param handler: (logging.Handler) the new handler to be added
"""
self.log_obj.addHandler(handler)
@@ -552,54 +613,54 @@ class Logger(Object):
def copyLogHandlers(self, other):
"""Copies the log handlers of other object to this object
-
+
:param other: (object) object which contains 'log_handlers'
"""
for handler in other.log_handlers:
self.addLogHandler(handler)
-
+
def trace(self, msg, *args, **kw):
"""Record a trace message in this object's logger. Accepted *args* and
*kwargs* are the same as :meth:`logging.Logger.log`.
-
+
:param msg: (str) the message to be recorded
:param args: list of arguments
:param kw: list of keyword arguments
"""
self.log_obj.log(self.Trace, msg, *args, **kw)
-
+
def traceback(self, level=Trace, extended=True):
"""Log the usual traceback information, followed by a listing of all the
local variables in each frame.
-
+
:param level: (int) the log level assigned to the traceback record
:param extended: (bool) if True, the log record message will have multiple lines
-
+
:return: (str) The traceback string representation
"""
out = traceback.format_exc()
if extended:
out += "\n"
out += self._format_trace()
-
+
self.log_obj.log(level, out)
return out
def stack(self, target=Trace):
"""Log the usual stack information, followed by a listing of all the
local variables in each frame.
-
+
:param target: (int) the log level assigned to the record
-
+
:return: (str) The stack string representation
"""
out = self._format_stack()
self.log_obj.log(target, out)
return out
-
+
def _format_trace(self):
return self._format_stack(inspect.trace)
-
+
def _format_stack(self, stack_func=inspect.stack):
line_count = 3
stack = stack_func(line_count)
@@ -629,7 +690,7 @@ class Logger(Object):
cut = False
v = str(v)
i = v.find('\n')
- if i == -1:
+ if i == -1:
i = 80
else:
i = min(i, 80)
@@ -640,22 +701,22 @@ class Logger(Object):
except:
out += '<could not find suitable string representation>'
return out
-
+
def log(self, level, msg, *args, **kw):
"""Record a log message in this object's logger. Accepted *args* and
*kwargs* are the same as :meth:`logging.Logger.log`.
-
+
:param level: (int) the record level
:param msg: (str) the message to be recorded
:param args: list of arguments
:param kw: list of keyword arguments
"""
self.log_obj.log(level, msg, *args, **kw)
-
+
def debug(self, msg, *args, **kw):
"""Record a debug message in this object's logger. Accepted *args* and
*kwargs* are the same as :meth:`logging.Logger.debug`.
-
+
:param msg: (str) the message to be recorded
:param args: list of arguments
:param kw: list of keyword arguments
@@ -665,7 +726,7 @@ class Logger(Object):
def info(self, msg, *args, **kw):
"""Record an info message in this object's logger. Accepted *args* and
*kwargs* are the same as :meth:`logging.Logger.info`.
-
+
:param msg: (str) the message to be recorded
:param args: list of arguments
:param kw: list of keyword arguments
@@ -675,7 +736,7 @@ class Logger(Object):
def warning(self, msg, *args, **kw):
"""Record a warning message in this object's logger. Accepted *args* and
*kwargs* are the same as :meth:`logging.Logger.warning`.
-
+
:param msg: (str) the message to be recorded
:param args: list of arguments
:param kw: list of keyword arguments
@@ -685,7 +746,7 @@ class Logger(Object):
def deprecated(self, msg, *args, **kw):
"""Record a deprecated warning message in this object's logger. Accepted *args* and
*kwargs* are the same as :meth:`logging.Logger.warning`.
-
+
:param msg: (str) the message to be recorded
:param args: list of arguments
:param kw: list of keyword arguments
@@ -697,7 +758,7 @@ class Logger(Object):
def error(self, msg, *args, **kw):
"""Record an error message in this object's logger. Accepted *args* and
*kwargs* are the same as :meth:`logging.Logger.error`.
-
+
:param msg: (str) the message to be recorded
:param args: list of arguments
:param kw: list of keyword arguments
@@ -707,23 +768,23 @@ class Logger(Object):
def critical(self, msg, *args, **kw):
"""Record a critical message in this object's logger. Accepted *args* and
*kwargs* are the same as :meth:`logging.Logger.critical`.
-
+
:param msg: (str) the message to be recorded
:param args: list of arguments
:param kw: list of keyword arguments
"""
self.log_obj.critical(msg, *args, **kw)
-
+
def exception(self, msg, *args):
- """Log a message with severity 'ERROR' on the root logger, with
- exception information.. Accepted *args* are the same as
+ """Log a message with severity 'ERROR' on the root logger, with
+ exception information.. Accepted *args* are the same as
:meth:`logging.Logger.exception`.
-
+
:param msg: (str) the message to be recorded
:param args: list of arguments
"""
self.log_obj.exception(msg, *args)
-
+
def flushOutput(self):
"""Flushes the log output"""
self.syncLog()
@@ -743,44 +804,44 @@ class Logger(Object):
sync()
synced.append(handler)
logger = logger.getParent()
-
+
def getLogName(self):
"""Gets the log name for this object
-
+
:return: (str) the log name
"""
return self.log_name
-
+
def getLogFullName(self):
"""Gets the full log name for this object
-
+
:return: (str) the full log name
"""
return self.log_full_name
def changeLogName(self,name):
"""Change the log name for this object.
-
+
:param name: (str) the new log name
- """
+ """
self.log_name = name
p = self.getParent()
if p is not None:
self.log_full_name = '%s.%s' % (p.log_full_name, name)
else:
self.log_full_name = name
-
+
self.log_obj = logging.getLogger(self.log_full_name)
for handler in self.log_handlers:
self.log_obj.addHandler(handler)
-
+
for child in self.getChildren():
child.changeLogName(child.log_name)
class LogFilter(logging.Filter):
"""Experimental log filter"""
-
+
def __init__(self, level):
self.filter_level = level
logging.Filter.__init__(self)
@@ -788,3 +849,29 @@ class LogFilter(logging.Filter):
def filter(self, record):
ok = (record.levelno == self.filter_level)
return ok
+
+def __getrootlogger():
+ return Logger.getLogger("TaurusRootLogger")
+
+# cannot export log because upper package taurus.core.util imports this 'log'
+# module and it would itself be overwritten by this log function
+def _log(level, msg, *args, **kw):
+ return __getrootlogger().log(level, msg, *args, **kw)
+
+def trace(msg, *args, **kw):
+ return _log(Logger.Trace, msg, *args, **kw)
+
+def debug(msg, *args, **kw):
+ return __getrootlogger().debug(msg, *args, **kw)
+
+def info(msg, *args, **kw):
+ return __getrootlogger().info(msg, *args, **kw)
+
+def warning(msg, *args, **kw):
+ return __getrootlogger().warning(msg, *args, **kw)
+
+def error(msg, *args, **kw):
+ return __getrootlogger().error(msg, *args, **kw)
+
+def critical(msg, *args, **kw):
+ return __getrootlogger().critical(msg, *args, **kw)
diff --git a/lib/taurus/core/util/remotelogmonitor.py b/lib/taurus/core/util/remotelogmonitor.py
new file mode 100644
index 0000000..ed9a052
--- /dev/null
+++ b/lib/taurus/core/util/remotelogmonitor.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""Useful module for remote logging"""
+
+from __future__ import print_function
+from __future__ import with_statement
+
+__all__ = ["LogRecordStreamHandler", "LogRecordSocketReceiver", "log"]
+
+import time
+import socket
+import pickle
+import logging
+import logging.handlers
+import struct
+import weakref
+
+try:
+ import socketserver
+except:
+ import SocketServer as socketserver
+
+
+class LogRecordStreamHandler(socketserver.StreamRequestHandler):
+
+ def handle(self):
+ try:
+ return self._handle()
+ except:
+ pass
+
+ def _handle(self):
+ self._stop = stop = 0
+ self.hostName = self.server.hostName
+ self.server.registerHandler(self)
+ while not stop:
+ chunk = self.connection.recv(4)
+ if len(chunk) < 4:
+ break
+ slen = struct.unpack('>L', chunk)[0]
+ chunk = self.connection.recv(slen)
+ while len(chunk) < slen:
+ chunk = chunk + self.connection.recv(slen - len(chunk))
+ obj = self.unPickle(chunk)
+ record = self.makeLogRecord(obj)
+ self.handleLogRecord(record)
+ stop = self._stop
+
+ def unPickle(self, data):
+ return pickle.loads(data)
+
+ def makeLogRecord(self, obj):
+ record = logging.makeLogRecord(obj)
+ if not hasattr(record, 'hostName'):
+ record.hostName = self.hostName
+ return record
+
+ def handleLogRecord(self, record):
+ logger = self.server.data.get("logger")
+ if logger is None:
+ logger = logging.getLogger(record.name)
+ if not logger.isEnabledFor(record.levelno):
+ return
+ logger.handle(record)
+
+ def stop(self):
+ self._stop = 1
+
+
+class LogRecordSocketReceiver(socketserver.ThreadingTCPServer):
+ """
+ Simple TCP socket-based logging receiver suitable for testing.
+ """
+
+ allow_reuse_address = 1
+ daemon_threads = True
+
+ def __init__(self, host='localhost',
+ port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
+ handler=LogRecordStreamHandler, **kwargs):
+ socketserver.ThreadingTCPServer.__init__(self, (host, port), handler)
+ self.hostName = socket.gethostbyaddr(host)[0]
+ self.port = port
+ self._stop = 0
+ self._stopped = 0
+ self.timeout = 1
+ self.data = kwargs
+ self.__handlers = []
+
+ def registerHandler(self, handler):
+ if handler is not None:
+ self.__handlers.append(weakref.ref(handler))
+
+ def unregisterHandler(self, handler):
+ try:
+ self.__handlers.remove(handler)
+ except ValueError:
+ pass
+
+ def serve_until_stopped(self):
+ import select
+ stop = 0
+ while not stop:
+ rd, wr, ex = select.select([self.socket.fileno()],
+ [], [],
+ self.timeout)
+ if rd:
+ self.handle_request()
+ stop = self._stop
+ self._stopped = 1
+
+ def stop(self):
+ self._stop = True
+ while not self._stopped:
+ time.sleep(0.1)
+ for handler in self.__handlers:
+ h = handler()
+ if h is not None:
+ h.stop()
+ h.finish()
+ self.close_request(h.connection)
+ self.socket.close()
+
+
+class LogNameFilter(logging.Filter):
+
+ def __init__(self, name=None):
+ self.name = name
+
+ def filter(self, record):
+ name = self.name
+ if name is None:
+ return True
+ return record.name == name
+
+
+def log(host, port, name=None, level=None):
+ local_logger_name = "RemoteLogger.%s.%d" % (host, port)
+ local_logger = logging.getLogger(local_logger_name)
+
+ if name is not None:
+ local_logger.addFilter(LogNameFilter(name=name))
+
+ if level is not None:
+ local_logger.setLevel(level)
+
+ tcpserver = LogRecordSocketReceiver(host=host, port=port,
+ logger=local_logger)
+ msg = "logging for '%s' on port %d" % (host, port)
+ if name is not None:
+ msg += " for " + name
+ print("Start", msg)
+
+ try:
+ tcpserver.serve_until_stopped()
+ except KeyboardInterrupt:
+ print("\nCancelled", msg)
+
+
+def main(argv=None):
+ import optparse
+ import socket
+
+ import taurus.core.util.log
+
+ taurus.setLogLevel(taurus.Trace)
+
+ dft_port = logging.handlers.DEFAULT_TCP_LOGGING_PORT
+
+ host = socket.gethostname()
+
+ help_port = "port where log server is running [default: %d]" % dft_port
+ help_name = "filter specific log object [default: None, meaning don't " \
+ "filter]"
+ help_level = "filter specific log level." \
+ "Allowed values are (case insensitive): critical, "\
+ "error, warning/warn, info, debug, trace [default: debug]."
+
+ parser = optparse.OptionParser()
+ parser.add_option("--log-port", dest="log_port", default=dft_port,
+ type="int", help=help_port)
+ parser.add_option("--log-name", dest="log_name", default=None,
+ type="string", help=help_name)
+ parser.add_option("--log-level", dest="log_level", default="debug",
+ type="string", help=help_level)
+
+ if argv is None:
+ import sys
+ argv = sys.argv
+
+ options, args = parser.parse_args(args=argv)
+
+ port, name = options.log_port, options.log_name
+ level_str = options.log_level.capitalize()
+
+ level = None
+ if hasattr(taurus, level_str):
+ level = getattr(taurus, level_str)
+
+ log(host, port, name=name, level=level)
+
+if __name__ == '__main__':
+ main()
diff --git a/lib/taurus/core/util/safeeval.py b/lib/taurus/core/util/safeeval.py
index 14157e2..75be46e 100644
--- a/lib/taurus/core/util/safeeval.py
+++ b/lib/taurus/core/util/safeeval.py
@@ -47,10 +47,10 @@ class SafeEvaluator:
Note: In order to use variables defined outside, the user must explicitly declare them safe.
"""
def __init__(self, safedict=None, defaultSafe=True):
- self._default_numpy = ('abs', 'array', 'arange','arccos', 'arcsin', 'arctan', 'arctan2',
+ self._default_numpy = ('abs', 'array', 'arange','arccos', 'arcsin', 'arctan', 'arctan2', 'average',
'ceil', 'cos', 'cosh', 'degrees', 'dot', 'e', 'exp', 'fabs', 'floor', 'fmod',
'frexp', 'hypot', 'ldexp', 'linspace', 'log', 'log10', 'logspace',
- 'modf', 'ones', 'pi', 'radians', 'shape', 'sin', 'sinh', 'sqrt', 'tan',
+ 'modf', 'ones', 'pi', 'radians', 'shape', 'sin', 'sinh', 'sqrt', 'sum', 'tan',
'tanh','zeros')
self._default_numpy_random = ('randn', 'rand', 'randint')
diff --git a/lib/taurus/core/util/wrap.py b/lib/taurus/core/util/wrap.py
new file mode 100644
index 0000000..1ef099b
--- /dev/null
+++ b/lib/taurus/core/util/wrap.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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__ = ["wraps", "wrapped", "is_wrapping", "is_wrapped"]
+
+import weakref
+from functools import wraps as _wraps
+
+__WRAPPED = "__wrapped__"
+__WRAPPER = "__wrapper__"
+
+def wraps(wrapped, *args, **kwargs):
+ """A wrap decorator which stores in the returned function a reference to
+ the wrapped function (in member '__wrapped__')"""
+ wrapper = _wraps(wrapped, *args, **kwargs)
+ setattr(wrapper, __WRAPPED, weakref.ref(wrapped))
+ setattr(wrapped, __WRAPPER, weakref.ref(wrapper))
+ return wrapper
+
+def is_wrapping(wrapper):
+ """Determines if the given callable is a wrapper for another callable"""
+ return hasattr(wrapper, __WRAPPED)
+
+def is_wrapped(wrapped):
+ """Determines if the given callable is being wrapped by another callable"""
+ return hasattr(wrapped, __WRAPPER)
+
+def wrapped(wrapper, recursive=True):
+ """Returns the wrapped function around the given wrapper. If the given
+ callable is not "wrapping" any function, the wrapper itself is returned"""
+ if is_wrapping(wrapper):
+ _wrapped = wrapper.__wrapped__()
+ else:
+ return wrapper
+
+ if recursive:
+ return wrapped(_wrapped)
+ return _wrapped
+
+
\ No newline at end of file
diff --git a/lib/taurus/qt/Qt.py b/lib/taurus/qt/Qt.py
index 0fd4224..d225295 100644
--- a/lib/taurus/qt/Qt.py
+++ b/lib/taurus/qt/Qt.py
@@ -27,9 +27,120 @@
from taurusqtoptions import QT_API, QT_API_PYQT, QT_API_PYSIDE
+def __to_qvariant_1(pyobj=None):
+ """Properly converts a python object into a proper QVariant according to
+ the PySide or PyQt4 API version in use
+
+ :param pyobj: object to be converted
+ :return: A proper QVariant"""
+ from PyQt4.QtCore import QVariant
+ if pyobj is None:
+ return QVariant() # PyQt 4.4 doesn't accept QVariant(None)
+ return QVariant(pyobj)
+
+def __from_qvariant_1(qobj=None, convfunc=None):
+ """Properly converts a QVariant/QVariant equivalent to a python object
+ according to the PySide or PyQt4 API version in use
+
+ :param qobj: object to be converted
+ :param convfunc:
+ conversion function. Not used if QVariant is not available.
+ If QVariant is available: [default: None, meaning use
+ qobj.toPyObject()]. Can be a function like str, int, bool, float or
+ a string containing the conversion method (ex.: 'toByteArray') will
+ call qobj.toByteArray()
+ :return: A proper python object"""
+ if convfunc is None:
+ return qobj.toPyObject()
+ elif callable(convfunc):
+ if convfunc in (unicode, str):
+ return convfunc(qobj.toString())
+ elif convfunc is bool:
+ return qobj.toBool()
+ elif convfunc is int:
+ return qobj.toInt()[0]
+ elif convfunc is float:
+ return qobj.toDouble()[0]
+ elif isinstance(convfunc, (str, unicode)):
+ return getattr(qobj, convfunc)()
+
+def __QVariant_2(pyobj=None):
+ return pyobj
+
+def __to_qvariant_2(pyobj=None):
+ """Properly converts a python object into a proper QVariant according to
+ the PySide or PyQt4 API version in use
+
+ :param pyobj: object to be converted
+ :return: A proper QVariant"""
+ return pyobj
+
+def __from_qvariant_2(qobj=None, convfunc=None):
+ """Properly converts a QVariant/QVariant equivalent to a python object
+ according to the PySide or PyQt4 API version in use
+
+ :param qobj: object to be converted
+ :param convfunc:
+ conversion function. Not used if QVariant is not available.
+ If QVariant is available: [default: None, meaning use
+ qobj.toPyObject()]. Can be a function like str, int, bool, float or
+ a string containing the conversion method (ex.: 'toByteArray') will
+ call qobj.toByteArray()
+ :return: A proper python object"""
+ return qobj
+
+#def __QString_2(pyobj=""):
+# return str(pyobj)
+
+__QString_2 = str
+
+#def __QStringList_2(pyobj=None):
+# if pyobj is None:
+# return list()
+# if isinstance(pyobj, (str, unicode)):
+# return [pyobj]
+# return list(pyobj)
+
+__QStringList_2 = list
+
# Now peform the imports.
if QT_API == QT_API_PYQT:
+ import PyQt4.Qt
+ import PyQt4.QtCore
from PyQt4.Qt import *
from PyQt4.Qt import Qt
+
+ import sip
+ try:
+ PYQT_QVARIANT_API_1 = sip.getapi('QVariant') == 1
+ except AttributeError:
+ # PyQt <v4.6
+ PYQT_QVARIANT_API_1 = True
+
+ if PYQT_QVARIANT_API_1:
+ to_qvariant = __to_qvariant_1
+ from_qvariant = __from_qvariant_1
+ else:
+ PyQt4.QtCore.QVariant = PyQt4.Qt.QVariant = QVariant = __QVariant_2
+ to_qvariant = __to_qvariant_2
+ from_qvariant = __from_qvariant_2
+
+ try:
+ PYQT_QSTRING_API_1 = sip.getapi('QString') == 1
+ except AttributeError:
+ # PyQt <v4.6
+ PYQT_QSTRING_API_1 = True
+
+ if not PYQT_QSTRING_API_1:
+ PyQt4.QtCore.QString = PyQt4.Qt.QString = QString = __QString_2
+ PyQt4.QtCore.QStringList = PyQt4.Qt.QStringList = QStringList = __QStringList_2
+
elif QT_API == QT_API_PYSIDE:
from PySide.Qt import *
+
+ QVariant = __QVariant_2
+ to_qvariant = __to_qvariant_2
+ from_qvariant = __from_qvariant_2
+ QString = __QString_2
+ QStringList = __QStringList_2
+
diff --git a/lib/taurus/qt/QtSvg.py b/lib/taurus/qt/QtDesigner.py
similarity index 94%
copy from lib/taurus/qt/QtSvg.py
copy to lib/taurus/qt/QtDesigner.py
index e7d2174..33a98b7 100644
--- a/lib/taurus/qt/QtSvg.py
+++ b/lib/taurus/qt/QtDesigner.py
@@ -29,6 +29,6 @@ from taurusqtoptions import QT_API, QT_API_PYQT, QT_API_PYSIDE
# Now peform the imports.
if QT_API == QT_API_PYQT:
- from PyQt4.QtSvg import *
+ from PyQt4.QtDesigner import *
elif QT_API == QT_API_PYSIDE:
- from PySide.QtSvg import *
\ No newline at end of file
+ from PySide.QtDesigner import *
diff --git a/lib/taurus/qt/QtSvg.py b/lib/taurus/qt/QtNetwork.py
similarity index 90%
copy from lib/taurus/qt/QtSvg.py
copy to lib/taurus/qt/QtNetwork.py
index e7d2174..29cc324 100644
--- a/lib/taurus/qt/QtSvg.py
+++ b/lib/taurus/qt/QtNetwork.py
@@ -3,32 +3,32 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This module exposes PyQt4.QtSvg module"""
+"""This module exposes PyQt4.QtGui module"""
from taurusqtoptions import QT_API, QT_API_PYQT, QT_API_PYSIDE
# Now peform the imports.
if QT_API == QT_API_PYQT:
- from PyQt4.QtSvg import *
+ from PyQt4.QtNetwork import *
elif QT_API == QT_API_PYSIDE:
- from PySide.QtSvg import *
\ No newline at end of file
+ from PySide.QtNetwork import *
diff --git a/lib/taurus/qt/QtSvg.py b/lib/taurus/qt/QtSvg.py
index e7d2174..c5e9274 100644
--- a/lib/taurus/qt/QtSvg.py
+++ b/lib/taurus/qt/QtSvg.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -31,4 +31,4 @@ from taurusqtoptions import QT_API, QT_API_PYQT, QT_API_PYSIDE
if QT_API == QT_API_PYQT:
from PyQt4.QtSvg import *
elif QT_API == QT_API_PYSIDE:
- from PySide.QtSvg import *
\ No newline at end of file
+ from PySide.QtSvg import *
diff --git a/lib/taurus/qt/QtSvg.py b/lib/taurus/qt/QtWebKit.py
similarity index 87%
copy from lib/taurus/qt/QtSvg.py
copy to lib/taurus/qt/QtWebKit.py
index e7d2174..1cbe81c 100644
--- a/lib/taurus/qt/QtSvg.py
+++ b/lib/taurus/qt/QtWebKit.py
@@ -3,32 +3,32 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This module exposes PyQt4.QtSvg module"""
+"""This module exposes PyQt4.QtGui module"""
from taurusqtoptions import QT_API, QT_API_PYQT, QT_API_PYSIDE
# Now peform the imports.
if QT_API == QT_API_PYQT:
- from PyQt4.QtSvg import *
+ from PyQt4.QtWebKit import * # analysis:ignore
elif QT_API == QT_API_PYSIDE:
- from PySide.QtSvg import *
\ No newline at end of file
+ from PySide.QtWebKit import * # analysis:ignore
diff --git a/lib/taurus/qt/QtSvg.py b/lib/taurus/qt/Qwt5.py
similarity index 94%
copy from lib/taurus/qt/QtSvg.py
copy to lib/taurus/qt/Qwt5.py
index e7d2174..60ec5ca 100644
--- a/lib/taurus/qt/QtSvg.py
+++ b/lib/taurus/qt/Qwt5.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -29,6 +29,6 @@ from taurusqtoptions import QT_API, QT_API_PYQT, QT_API_PYSIDE
# Now peform the imports.
if QT_API == QT_API_PYQT:
- from PyQt4.QtSvg import *
+ from PyQt4.Qwt5 import *
elif QT_API == QT_API_PYSIDE:
- from PySide.QtSvg import *
\ No newline at end of file
+ from PySide.Qwt5 import *
diff --git a/lib/taurus/qt/qtcore/communication/communication.py b/lib/taurus/qt/qtcore/communication/communication.py
index b92f60d..784cb95 100644
--- a/lib/taurus/qt/qtcore/communication/communication.py
+++ b/lib/taurus/qt/qtcore/communication/communication.py
@@ -28,7 +28,7 @@ comunications.py:
"""
from taurus.qt import QtCore
-import weakref, copy
+import weakref
_DEBUG = False
@@ -53,8 +53,6 @@ class DataModel(QtCore.QObject):
self.__dataUID = dataUID
self.__data = defaultData
self.__isDataSet = False
- self.__readers = 0
- self.__writers = 0
self.__readerSlots = []
self.__writerSignals = []
@@ -104,8 +102,8 @@ class DataModel(QtCore.QObject):
'''
self.connect(self, QtCore.SIGNAL("dataChanged"), slot)
if readOnConnect and self.__isDataSet: slot(self.__data)
- self.__readers += 1
- self.__readerSlots.append(weakref.ref(slot))
+ obj=getattr(slot,'__self__',slot)
+ self.__readerSlots.append((weakref.ref(obj), slot.__name__))
def connectWriter(self, writer, signalname):
'''
@@ -120,7 +118,6 @@ class DataModel(QtCore.QObject):
.. seealso:: :meth:`connectReader`, :meth:`setData`
'''
self.connect(writer, QtCore.SIGNAL(signalname),self.setData)
- self.__writers += 1
self.__writerSignals.append((weakref.ref(writer),signalname))
def disconnectWriter(self, writer, signalname):
@@ -132,7 +129,6 @@ class DataModel(QtCore.QObject):
.. seealso:: :meth:`SharedDataManager.disconnectWriter`
'''
ok = self.disconnect(writer, QtCore.SIGNAL(signalname), self.setData)
- if ok: self.__writers -= 1
self.__writerSignals.remove((weakref.ref(writer),signalname))
def disconnectReader(self, slot):
@@ -144,18 +140,32 @@ class DataModel(QtCore.QObject):
.. seealso:: :meth:`SharedDataManager.disconnectReader`, :meth:`getData`
'''
ok = self.disconnect(self, QtCore.SIGNAL("dataChanged"), slot)
- if ok: self.__readers -= 1
- self.__readerSlots.remove(weakref.ref(slot))
+ self.__readerSlots.remove((weakref.ref(slot.__self__),slot.__name__))
def isDataSet(self):
- '''Whether the data has been set at least once or if it is uninitialized'''
+ '''Whether the data has been set at least once or if it is uninitialized
+
+ :return: (bool) True if the data has been set. False it is uninitialized'''
return self.__isDataSet
def info(self):
- return "MODEL: %s\n\t Readers (%i): %s\n\t Writers (%i):%s\n"%(self.__repr__(), len(self.__readerSlots),
- repr(self.__readerSlots), len(self.__writerSignals),
- repr(self.__writerSignals))
+ readers=["%s::%s"%(repr(r()),s) for r,s in self.__readerSlots]
+ writers=["%s::%s"%(repr(r()),s) for r,s in self.__writerSignals]
+ return "UID: %s\n\t Readers (%i):%s\n\t Writers (%i):%s\n"%(self.__dataUID, len(readers),
+ readers, len(writers), writers)
+ def readerCount(self):
+ '''returns the number of currently registered readers of this model
+
+ :return: (int)
+ '''
+ return len(self.__readerSlots)
+
+ def writerCount(self):
+ '''returns the number of currently registered writers of this model
+ :return: (int)
+ '''
+ return len(self.__writerSignals)
class SharedDataManager(QtCore.QObject):
@@ -167,7 +177,7 @@ class SharedDataManager(QtCore.QObject):
'''
def __init__(self, parent):
QtCore.QObject.__init__(self, parent)
- self.__models = weakref.WeakValueDictionary()
+ self.__models = {}
def __getDataModel(self, dataUID):
@@ -247,7 +257,10 @@ class SharedDataManager(QtCore.QObject):
.. seealso:: :meth:`DataModel.disconnectWriter`
'''
- self.__getDataModel(dataUID).disconnectWriter(writer, signalname)
+ m = self.__getDataModel(dataUID)
+ m.disconnectWriter(writer, signalname)
+ if m.readerCount() < 1 and m.writerCount()<1:
+ self.__models.pop(dataUID)
def disconnectReader(self, dataUID, slot):
'''Unregister the given method as data receiver
@@ -257,7 +270,10 @@ class SharedDataManager(QtCore.QObject):
.. seealso:: :meth:`DataModel.disconnectReader`
'''
- self.__getDataModel(dataUID).disconnectReader(slot)
+ m = self.__getDataModel(dataUID)
+ m.disconnectReader(slot)
+ if m.readerCount() < 1 and m.writerCount()<1:
+ self.__models.pop(dataUID)
def activeDataUIDs(self):
'''
@@ -277,8 +293,8 @@ class SharedDataManager(QtCore.QObject):
def info(self):
s=""
- for uid,m in self.__models.iteritems():
- s+=m.info()
+ for uid,m in sorted(self.__models.iteritems()):
+ s+=m.info()+'\n'
return s
\ No newline at end of file
diff --git a/lib/taurus/qt/qtcore/configuration/configuration.py b/lib/taurus/qt/qtcore/configuration/configuration.py
index d33b1d1..56a640a 100644
--- a/lib/taurus/qt/qtcore/configuration/configuration.py
+++ b/lib/taurus/qt/qtcore/configuration/configuration.py
@@ -50,12 +50,14 @@ class configurableProperty:
else:
result = self.fget()
return result
+
def applyConfig(self, value, depth=-1):
'''calls the fset function for this property with the given value. The depth parameter is ignored'''
if isinstance(self.fget, basestring):# fget is not a method but a method name...
getattr(self._obj, self.fset)(value)
else:
self.fset(value)
+
def objectName(self):
'''returns the name of this property'''
return self.name
@@ -122,8 +124,11 @@ class BaseConfigurableClass:
implementing "perspectives" in your application.
'''
+
+ defaultConfigRecursionDepth = -1
+ _supportedConfigVersions = ("__UNVERSIONED__",)#the latest element of this list is considered the current version
+
def __init__(self):
- self._supportedConfigVersions = ["__UNVERSIONED__"] #the latest element of this list is considered the current version
self.resetConfigurableItems()
@staticmethod
@@ -185,7 +190,7 @@ class BaseConfigurableClass:
configdict["__orderedConfigNames__"] = self.__configurableItemNames
return configdict
- def applyConfig(self, configdict, depth=-1):
+ def applyConfig(self, configdict, depth=None):
"""applies the settings stored in a configdict to the current object.
In most usual situations, using :meth:`registerConfigProperty` and
@@ -198,12 +203,17 @@ class BaseConfigurableClass:
for this object, and not for any other object registered
via :meth:`registerConfigurableItem`. If depth > 0,
applyConfig will be called recursively as many times as
- the depth value. If depth < 0 (default), no limit is
- imposed to recursion (i.e., it will recurse for as deep as
- there are registered items)
+ the depth value. If depth < 0 (default, see note), no
+ limit is imposed to recursion (i.e., it will recurse for
+ as deep as there are registered items).
+
+ .. note:: the default recursion depth can be tweaked in derived classes
+ by changing the class property `defaultConfigRecursionDepth`
.. seealso:: :meth:`createConfig`
"""
+ if depth is None:
+ depth = self.defaultConfigRecursionDepth
if not self.checkConfigVersion(configdict):
raise ValueError('the given configuration is of unsupported version')
#delegate restoring the configuration of any registered configurable item
@@ -399,8 +409,8 @@ class BaseConfigurableClass:
import cPickle as pickle
if ofile is None:
from taurus.qt import Qt
- ofile = Qt.QFileDialog.getSaveFileName( self, 'Save Configuration', '%s.pck'%self.__class__.__name__, 'Configuration File (*.pck)')
- if ofile.isEmpty(): return
+ ofile = unicode(Qt.QFileDialog.getSaveFileName( self, 'Save Configuration', '%s.pck'%self.__class__.__name__, 'Configuration File (*.pck)'))
+ if not ofile: return
if not isinstance(ofile,file): ofile=open(ofile,'w')
configdict=self.createConfig(allowUnpickable=False)
self.info("Saving current settings in '%s'"%ofile.name)
@@ -417,8 +427,8 @@ class BaseConfigurableClass:
import cPickle as pickle
if ifile is None:
from taurus.qt import Qt
- ifile = Qt.QFileDialog.getOpenFileName( self, 'Load Configuration', '', 'Configuration File (*.pck)')
- if ifile.isEmpty(): return
+ ifile = unicode(Qt.QFileDialog.getOpenFileName( self, 'Load Configuration', '', 'Configuration File (*.pck)'))
+ if not ifile: return
if not isinstance(ifile,file): ifile=open(ifile,'r')
configdict=pickle.load(ifile)
self.applyConfig(configdict)
diff --git a/lib/taurus/qt/qtcore/model/taurusdatabasemodel.py b/lib/taurus/qt/qtcore/model/taurusdatabasemodel.py
index 16aab08..710c63c 100644
--- a/lib/taurus/qt/qtcore/model/taurusdatabasemodel.py
+++ b/lib/taurus/qt/qtcore/model/taurusdatabasemodel.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -83,10 +83,10 @@ class TaurusTreeDevicePartItem(TaurusTreeDbBaseItem):
def data(self, index):
column = index.column()
if column > 0: return
-
+
model = index.model()
role = model.role(column, self.depth())
-
+
if role == self.role():
return self._itemData
@@ -102,7 +102,7 @@ class TaurusTreeDeviceDomainItem(TaurusTreeDevicePartItem):
def role(self):
return ElemType.Domain
-
+
class TaurusTreeDeviceFamilyItem(TaurusTreeDevicePartItem):
"""A node designed to represent a the family part of a device name"""
@@ -123,10 +123,10 @@ class TaurusTreeDeviceMemberItem(TaurusTreeDevicePartItem):
class TaurusTreeSimpleDeviceItem(TaurusTreeDbBaseItem):
"""A node designed to represent a device (without any child nodes)"""
-
+
def hasChildren(self):
return False
-
+
def childCount(self):
return 0
@@ -162,7 +162,7 @@ class TaurusTreeSimpleDeviceItem(TaurusTreeDbBaseItem):
class TaurusTreeDeviceItem(TaurusTreeDbBaseItem):
"""A node designed to represent a device"""
-
+
SearchForAttributeHealth = DevHealth.Exported, DevHealth.ExportedAlive, \
DevHealth.NotExportedAlive
@@ -179,7 +179,7 @@ class TaurusTreeDeviceItem(TaurusTreeDbBaseItem):
if not data.health() in self.SearchForAttributeHealth:
return False
return True
-
+
def childCount(self):
nb = super(TaurusTreeDeviceItem, self).childCount()
if nb > 0:
@@ -189,7 +189,7 @@ class TaurusTreeDeviceItem(TaurusTreeDbBaseItem):
return 0
self.updateChilds()
return super(TaurusTreeDeviceItem, self).childCount()
-
+
def updateChilds(self):
if len(self._childItems) > 0:
return
@@ -230,7 +230,7 @@ class TaurusTreeDeviceItem(TaurusTreeDbBaseItem):
class TaurusTreeAttributeItem(TaurusTreeDbBaseItem):
"""A node designed to represent an attribute"""
-
+
def data(self, index):
column, model = index.column(), index.model()
role = model.role(column, self.depth())
@@ -241,7 +241,7 @@ class TaurusTreeAttributeItem(TaurusTreeDbBaseItem):
if data_info and hasattr(data_info, 'label'):
ret = "'" + data_info.label + "' (" + ret + ")"
return ret
-
+
def toolTip(self, index):
if index.column() > 0:
return TaurusTreeDbBaseItem.toolTip(self, index)
@@ -257,28 +257,28 @@ class TaurusTreeAttributeItem(TaurusTreeDbBaseItem):
limits = "[%s, %s]" % (di.min_value, di.max_value),
alarms = "[%s, %s]" % (di.alarms.min_alarm, di.alarms.max_alarm),
warnings = "[%s, %s]" % (di.alarms.min_warning, di.alarms.max_warning),)
-
+
for id, value in items.items():
ret += '<TR><TD WIDTH="80" ALIGN="RIGHT" VALIGN="MIDDLE"><B>%s:</B></TD><TD>%s</TD></TR>' % (id.capitalize(), value)
ret += '</TABLE>'
return ret
-
+
def mimeData(self, index):
return self.itemData().fullName()
-
+
def role(self):
return ElemType.Attribute
-
-
+
+
class TaurusTreeServerNameItem(TaurusTreeDbBaseItem):
"""A node designed to represent the server name part of a server"""
-
+
DisplayFunc = str
-
+
def data(self, index):
column, model = index.column(), index.model()
role = model.role(column, self.depth())
-
+
if role == ElemType.ServerName or role == ElemType.Name:
return self._itemData
@@ -288,11 +288,11 @@ class TaurusTreeServerNameItem(TaurusTreeDbBaseItem):
class TaurusTreeServerItem(TaurusTreeDbBaseItem):
"""A node designed to represent a server"""
-
+
def data(self, index):
column, model = index.column(), index.model()
role = model.role(column, self.depth())
-
+
if role == ElemType.Server or role == ElemType.Name:
return self._itemData.name()
elif role == ElemType.ServerName:
@@ -303,7 +303,7 @@ class TaurusTreeServerItem(TaurusTreeDbBaseItem):
return self._itemData.health()
elif role == ElemType.Host:
return self._itemData.host()
-
+
def mimeData(self, index):
return self.itemData().fullName()
@@ -313,11 +313,11 @@ class TaurusTreeServerItem(TaurusTreeDbBaseItem):
class TaurusTreeFullServerItem(TaurusTreeDbBaseItem):
"""A node designed to represent a server"""
-
+
def data(self, index):
column, model = index.column(), index.model()
role = model.role(column, self.depth())
-
+
if role == ElemType.Server or role == ElemType.Name:
return self._itemData.fullName()
elif role == ElemType.ServerName:
@@ -338,11 +338,11 @@ class TaurusTreeFullServerItem(TaurusTreeDbBaseItem):
class TaurusTreeDeviceClassItem(TaurusTreeDbBaseItem):
"""A node designed to represent a device class"""
-
+
def data(self, index):
column, model = index.column(), index.model()
role = model.role(column, self.depth())
-
+
if role == ElemType.Name or role == ElemType.DeviceClass:
return self._itemData.name()
@@ -357,31 +357,31 @@ class TaurusDbBaseModel(TaurusBaseModel):
"""The base class for all Taurus database Qt models.
By default, this model represents a plain device perspective of the underlying
database."""
-
+
ColumnNames = "Device", "Alias", "Server", "Class", "Alive", "Host"
ColumnRoles = (ElemType.Device, ElemType.Device), ElemType.DeviceAlias, ElemType.Server, ElemType.DeviceClass, ElemType.Exported, ElemType.Host
def createNewRootItem(self):
return TaurusTreeDbBaseItem(self, self.ColumnNames)
-
+
def refresh(self, refresh_source=False):
data = self.dataSource()
if refresh_source and data is not None:
data.refreshCache()
TaurusBaseModel.refresh(self, refresh_source=refresh_source)
-
+
def roleIcon(self, taurus_role):
return getElementTypeIcon(taurus_role)
-
+
def columnIcon(self, column):
return self.roleIcon(self.role(column))
-
+
def roleToolTip(self, taurus_role):
return getElementTypeToolTip(taurus_role)
def columnToolTip(self, column):
return self.roleToolTip(self.role(column))
-
+
def roleSize(self, taurus_role):
return getElementTypeSize(taurus_role)
@@ -389,7 +389,7 @@ class TaurusDbBaseModel(TaurusBaseModel):
taurus_role = self.role(column)
s = self.roleSize(taurus_role)
return s
-
+
def mimeTypes(self):
return ["text/plain", taurus.qt.qtcore.mimetypes.TAURUS_MODEL_LIST_MIME_TYPE, taurus.qt.qtcore.mimetypes.TAURUS_MODEL_MIME_TYPE]
@@ -415,11 +415,11 @@ class TaurusDbBaseModel(TaurusBaseModel):
def pyData(self, index, role):
if not index.isValid():
return None
-
+
item = index.internalPointer()
row, column, depth = index.row(), index.column(), item.depth()
taurus_role = self.role(column, depth)
-
+
ret = None
if role == Qt.Qt.DisplayRole:
if taurus_role != ElemType.Exported:
@@ -451,7 +451,7 @@ class TaurusDbBaseModel(TaurusBaseModel):
if isinstance(data, taurus.core.TaurusDatabase):
data = data.cache()
devices = data.devices()
-
+
rootItem = self._rootItem
for dev_name in data.getDeviceNames():
dev = devices[dev_name]
@@ -462,25 +462,25 @@ class TaurusDbBaseModel(TaurusBaseModel):
class TaurusDbSimpleDeviceModel(TaurusDbBaseModel):
"""A Qt model that structures device elements in 1 level tree with
device name as node leafs. This model contains only 1 column."""
-
+
ColumnNames = "Device",
ColumnRoles = (ElemType.Device, ElemType.Device),
-
+
class TaurusDbSimpleDeviceAliasModel(TaurusDbBaseModel):
"""A Qt model that structures device elements in 1 level tree with
device alias as node leafs. This model contains only 1 column."""
-
+
ColumnNames = "Alias",
ColumnRoles = (ElemType.DeviceAlias, ElemType.DeviceAlias),
-
+
def setupModelData(self, data):
if data is None:
return
if isinstance(data, taurus.core.TaurusDatabase):
data = data.cache()
devices = data.devices()
-
+
rootItem = self._rootItem
for dev_name in data.getDeviceNames():
dev = devices[dev_name]
@@ -492,7 +492,7 @@ class TaurusDbSimpleDeviceAliasModel(TaurusDbBaseModel):
class TaurusDbPlainDeviceModel(TaurusDbBaseModel):
"""A Qt model that structures device elements in 1 level tree. Device
nodes will have attribute child nodes if the device is running."""
-
+
ColumnNames = "Device", "Alias", "Server", "Class", "Alive", "Host"
ColumnRoles = (ElemType.Device, ElemType.Device, ElemType.Attribute), ElemType.DeviceAlias, ElemType.Server, ElemType.DeviceClass, ElemType.Exported, ElemType.Host
@@ -502,18 +502,18 @@ class TaurusDbPlainDeviceModel(TaurusDbBaseModel):
if isinstance(data, taurus.core.TaurusDatabase):
data = data.cache()
devices = data.devices()
-
+
rootItem = self._rootItem
for dev_name in data.getDeviceNames():
dev = devices[dev_name]
devItem = TaurusTreeDeviceItem(self, dev, rootItem)
rootItem.appendChild(devItem)
-
+
class TaurusDbDeviceModel(TaurusDbBaseModel):
"""A Qt model that structures device elements is a 3 level tree organized
as:
-
+
- <domain>
- <family>
- <member>"""
@@ -524,7 +524,7 @@ class TaurusDbDeviceModel(TaurusDbBaseModel):
return
if isinstance(data, taurus.core.TaurusDatabase):
data = data.deviceTree()
-
+
rootItem = self._rootItem
for domain in sorted(data.keys()):
families = data[domain]
@@ -543,26 +543,26 @@ class TaurusDbDeviceModel(TaurusDbBaseModel):
class TaurusDbPlainServerModel(TaurusDbBaseModel):
ColumnNames = "Server", "Alive", "Host"
ColumnRoles = (ElemType.Server, ElemType.ServerInstance), ElemType.Exported, ElemType.Host
-
+
def setupModelData(self, data):
if data is None:
return
if isinstance(data, taurus.core.TaurusDatabase):
data = data.cache()
-
+
servers = data.servers()
rootItem = self._rootItem
-
+
for server_name, server in servers.items():
serverInstanceItem = TaurusTreeFullServerItem(self, server, rootItem)
rootItem.appendChild(serverInstanceItem)
-
-
+
+
class TaurusDbServerModel(TaurusDbBaseModel):
"""A Qt model that structures server elements in a tree organized
as:
-
+
- <Server name>
- <Server instance>
- <Class>
@@ -571,33 +571,33 @@ class TaurusDbServerModel(TaurusDbBaseModel):
ColumnNames = "Server", "Alive", "Host"
ColumnRoles = (ElemType.Server, ElemType.ServerName, ElemType.ServerInstance, ElemType.DeviceClass, ElemType.Device, ElemType.Attribute), ElemType.Exported, ElemType.Host
-
+
def setupModelData(self, data):
if data is None:
return
if isinstance(data, taurus.core.TaurusDatabase):
data = data.cache()
-
+
servers, klasses, devices = data.servers(), data.klasses(), data.devices()
rootItem = self._rootItem
server_dict = {}
-
+
server_names = data.getServerNames()
for server_name in server_names:
server = servers[server_name]
name, instance = server.serverName(), server.serverInstance()
-
+
serverNameItem = server_dict.get(name)
if serverNameItem is None:
serverNameItem = TaurusTreeServerNameItem(self, name, rootItem)
rootItem.appendChild(serverNameItem)
server_dict[name] = serverNameItem
#rootItem.appendChild(serverNameItem)
-
+
serverInstanceItem = TaurusTreeServerItem(self, server, serverNameItem)
serverNameItem.appendChild(serverInstanceItem)
-
+
klass_names = server.getClassNames()
device_names = server.getDeviceNames()
for klass_name in klass_names:
@@ -613,20 +613,20 @@ class TaurusDbServerModel(TaurusDbBaseModel):
class TaurusDbDeviceClassModel(TaurusDbBaseModel):
"""A Qt model that structures class elements in a tree organized as:
-
+
* <Class>
* <Device>
* <Attribute>"""
ColumnNames = "Class", "Alive", "Host"
ColumnRoles = (ElemType.DeviceClass, ElemType.DeviceClass, ElemType.Device, ElemType.Attribute), ElemType.Exported, ElemType.Host
-
+
def setupModelData(self, data):
if data is None:
return
-
+
if isinstance(data, taurus.core.TaurusDatabase):
data = data.cache()
-
+
rootItem = self._rootItem
klasses, devices = data.klasses(), data.devices()
dev_nb = 0
@@ -651,76 +651,81 @@ class TaurusDbDeviceProxyModel(TaurusDbBaseProxyModel):
- TaurusDbDeviceModel
- TaurusDbSimpleDeviceModel
- TaurusDbPlainDeviceModel"""
-
+
def filterAcceptsRow(self, sourceRow, sourceParent):
sourceModel = self.sourceModel()
idx = sourceModel.index(sourceRow, 0, sourceParent)
treeItem = idx.internalPointer()
- expr = self.filterRegExp()
-
+ regexp = self.filterRegExp()
+
# if domain node, check if it will potentially have any children
if isinstance(treeItem, TaurusTreeDeviceDomainItem):
domain = treeItem.display()
devices = sourceModel.getDomainDevices(domain)
for device in devices:
- if self.deviceMatches(device, expr):
+ if self.deviceMatches(device, regexp):
return True
return False
-
+
# if family node, check if it will potentially have any children
if isinstance(treeItem, TaurusTreeDeviceFamilyItem):
domain = treeItem.parent().display()
family = treeItem.display()
devices = sourceModel.getFamilyDevices(domain, family)
for device in devices:
- if self.deviceMatches(device, expr):
+ if self.deviceMatches(device, regexp):
return True
return False
-
+
if isinstance(treeItem, TaurusTreeDeviceItem) or \
isinstance(treeItem, TaurusTreeSimpleDeviceItem) or \
isinstance(treeItem, TaurusTreeDeviceMemberItem):
device = treeItem.itemData()
- return self.deviceMatches(device, expr)
+ return self.deviceMatches(device, regexp)
return True
-
- def deviceMatches(self, device, expr):
+
+ def deviceMatches(self, device, regexp):
name = device.name()
- if Qt.QString(name).contains(expr):
+
+ # if Qt.QString(name).contains(regexp):
+ if regexp.indexIn(name) != -1:
return True
name = device.alias()
if name is None:
return False
- return Qt.QString(name).contains(expr)
+ #return Qt.QString(name).contains(regexp)
+ return regexp.indexIn(name) != -1
class TaurusDbServerProxyModel(TaurusDbBaseProxyModel):
"""A Qt filter & sort model for the TaurusDbServerModel"""
-
+
def filterAcceptsRow(self, sourceRow, sourceParent):
sourceModel = self.sourceModel()
idx = sourceModel.index(sourceRow, 0, sourceParent)
treeItem = idx.internalPointer()
- expr = self.filterRegExp()
-
+ regexp = self.filterRegExp()
+
# if server name node, check if it will potentially have any children
if isinstance(treeItem, TaurusTreeServerNameItem):
serverName = treeItem.display()
serverInstances = sourceModel.getServerNameInstances(serverName)
for serverInstance in serverInstances:
- if Qt.QString(serverInstance.name()).contains(expr):
+ #if Qt.QString(serverInstance.name()).contains(regexp):
+ if regexp.indexIn(serverInstance.name()) != -1:
return True
return False
-
+
if isinstance(treeItem, TaurusTreeServerItem):
- return treeItem.qdisplay().contains(expr)
+ #return treeItem.qdisplay().contains(regexp)
+ return regexp.indexIn(treeItem.qdisplay()) != -1
return True
class TaurusDbDeviceClassProxyModel(TaurusDbBaseProxyModel):
"""A Qt filter & sort model for the TaurusDbDeviceClassModel"""
-
+
def filterAcceptsRow(self, sourceRow, sourceParent):
sourceModel = self.sourceModel()
idx = sourceModel.index(sourceRow, 0, sourceParent)
@@ -728,7 +733,8 @@ class TaurusDbDeviceClassProxyModel(TaurusDbBaseProxyModel):
if not isinstance(treeItem, TaurusTreeDeviceClassItem):
return True
-
- expr = self.filterRegExp()
- return treeItem.qdisplay().contains(expr)
+ regexp = self.filterRegExp()
+
+ #return treeItem.qdisplay().contains(regexp)
+ return regexp.indexIn(treeItem.qdisplay()) != -1
diff --git a/lib/taurus/qt/qtcore/model/taurusmodel.py b/lib/taurus/qt/qtcore/model/taurusmodel.py
index a00e238..24bfffa 100644
--- a/lib/taurus/qt/qtcore/model/taurusmodel.py
+++ b/lib/taurus/qt/qtcore/model/taurusmodel.py
@@ -285,7 +285,7 @@ class TaurusBaseModel(Qt.QAbstractItemModel, Logger):
def _setData(self, index, qvalue, role=Qt.Qt.EditRole):
item = index.internalPointer()
- pyobj = qvalue.toPyObject()
+ pyobj = Qt.from_qvariant(qvalue)
if pyobj is NotImplemented:
self.warning("Failed attempt to convert a QValue. Maybe it is due to Qt<4.6")
item.setData(index, pyobj)
@@ -372,7 +372,7 @@ class TaurusBaseModel(Qt.QAbstractItemModel, Logger):
class TaurusBaseProxyModel(Qt.QSortFilterProxyModel):
- """A taurus database base Qt filter & sort model"""
+ """A taurus base Qt filter & sort model"""
def __init__(self, parent=None):
Qt.QSortFilterProxyModel.__init__(self, parent)
diff --git a/lib/taurus/qt/qtcore/tango/sardana/macroserver.py b/lib/taurus/qt/qtcore/tango/sardana/macroserver.py
index c65a9e0..0fd4056 100644
--- a/lib/taurus/qt/qtcore/tango/sardana/macroserver.py
+++ b/lib/taurus/qt/qtcore/tango/sardana/macroserver.py
@@ -27,12 +27,12 @@
__all__ = ["QDoor", "QMacroServer", "MacroServerMessageErrorHandler", "registerExtensions"]
-import taurus.core
+from taurus.core import TaurusEventType
from taurus.core.tango.sardana.macroserver import BaseMacroServer, BaseDoor
-
from taurus.qt import Qt
-CHANGE_EVTS = (taurus.core.TaurusEventType.Change, taurus.core.TaurusEventType.Periodic)
+CHANGE_EVTS = TaurusEventType.Change, TaurusEventType.Periodic
+
class QDoor(BaseDoor, Qt.QObject):
@@ -56,7 +56,7 @@ class QDoor(BaseDoor, Qt.QObject):
def macroStatusReceived(self, s, t, v):
res = BaseDoor.macroStatusReceived(self, s, t, v)
- if t == taurus.core.TaurusEventType.Error:
+ if t == TaurusEventType.Error:
macro = None
else:
macro = self.getRunningMacro()
@@ -97,11 +97,11 @@ class QMacroServer(BaseMacroServer, Qt.QObject):
macros, elements = 0, 0
for element in set.union(added, removed, changed):
- if "MacroCode" in element.interfaces:
+ if "MacroCode" in element.interfaces:
macros += 1
- elements += 1
- if elements and macros:
- break
+ elements += 1
+ if elements and macros:
+ break
if elements:
self.emit(Qt.SIGNAL("elementsChanged"))
if macros:
@@ -121,6 +121,7 @@ class QMacroServer(BaseMacroServer, Qt.QObject):
# handlers, maybe in TangoFactory & TaurusManager
from taurus.qt.qtgui.panel import TaurusMessageErrorHandler
+
class MacroServerMessageErrorHandler(TaurusMessageErrorHandler):
def setError(self, err_type=None, err_value=None, err_traceback=None):
@@ -139,7 +140,6 @@ class MacroServerMessageErrorHandler(TaurusMessageErrorHandler):
exc_info = "".join(err_traceback)
style = ""
try:
- import pygments
import pygments.formatters
import pygments.lexers
except:
diff --git a/lib/taurus/qt/qtcore/taurusqlistener.py b/lib/taurus/qt/qtcore/taurusqlistener.py
new file mode 100644
index 0000000..016dded
--- /dev/null
+++ b/lib/taurus/qt/qtcore/taurusqlistener.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+""""""
+
+from __future__ import print_function
+
+__all__ = ["QTaurusBaseListener", "QObjectTaurusListener"]
+
+__docformat__ = 'restructuredtext'
+
+from taurus.core import TaurusListener
+from taurus.qt import Qt
+
+
+class QTaurusBaseListener(TaurusListener):
+ """Base class for QObjects listening to taurus events. It is not
+ instanciable! Use a class that inherits from Qt class and from this class
+ (example: :class:`QObjectTaurusListener`)"""
+
+ def __init__(self, name=None, parent=None):
+ if name is None:
+ name = self.__class__.__name__
+ super(QTaurusBaseListener, self).__init__(name, parent=parent)
+ self._eventFilters = []
+ Qt.QObject.connect(self.getSignaller(), Qt.SIGNAL('taurusEvent'),
+ self.filterEvent)
+
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # Event handling chain
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+
+ def eventReceived(self, evt_src, evt_type, evt_value):
+ """The basic implementation of the event handling chain is as
+ follows:
+
+ - eventReceived just calls :meth:`fireEvent` which emits a "taurusEvent"
+ PyQt signal that is connected (by :meth:`preAttach`) to the
+ :meth:`filterEvent` method.
+ - After filtering, :meth:`handleEvent` is invoked with the resulting
+ filtered event
+
+ .. note::
+ in the earlier steps of the chain (i.e., in :meth:`eventReceived`/:meth:`fireEvent`),
+ the code is executed in a Python thread, while from eventFilter
+ ahead, the code is executed in a Qt thread.
+ When writing widgets, one should normally work on the Qt thread
+ (i.e. reimplementing :meth:`handleEvent`)
+
+ :param evt_src: (object) object that triggered the event
+ :param evt_type: (taurus.core.TaurusEventType) type of event
+ :param evt_value: (object) event value
+ """
+ self.fireEvent(evt_src, evt_type, evt_value)
+
+ def fireEvent(self, evt_src = None, evt_type = None, evt_value = None):
+ """Emits a "taurusEvent" signal.
+ It is unlikely that you may need to reimplement this method in subclasses.
+ Consider reimplementing :meth:`eventReceived` or :meth:`handleEvent`
+ instead depending on whether you need to execute code in the python
+ or Qt threads, respectively
+
+ :param evt_src: (object or None) object that triggered the event
+ :param evt_type: (taurus.core.TaurusEventType or None) type of event
+ :param evt_value: (object or None) event value
+ """
+ try:
+ emmiter = self.getSignaller()
+ emmiter.emit(Qt.SIGNAL('taurusEvent'), evt_src, evt_type, evt_value)
+ except:
+ pass
+
+ def filterEvent(self, evt_src=-1, evt_type=-1, evt_value=-1):
+ """The event is processed by each and all filters in strict order
+ unless one of them returns None (in which case the event is discarded)
+
+ :param evt_src: (object) object that triggered the event
+ :param evt_type: (taurus.core.TaurusEventType) type of event
+ :param evt_value: (object) event value
+ """
+ r = evt_src, evt_type, evt_value
+
+ if r == (-1,-1,-1):
+ # @todo In an ideal world the signature of this method should be
+ # (evt_src, evt_type, evt_value). However there's a bug in PyQt:
+ # If a signal is disconnected between the moment it is emitted and
+ # the moment the slot is called, then the slot is called without
+ # parameters (!?). We added this default values to detect if
+ # this is the case without printing an error message each time.
+ # If this gets fixed, we should remove this line.
+ return
+
+ for f in self._eventFilters:
+ r = f(*r)
+ if r is None: return
+ self.handleEvent(*r)
+
+ def handleEvent(self, evt_src, evt_type, evt_value):
+ """Event handling. Default implementation does nothing.
+ Reimplement as necessary
+
+ :param evt_src: (object or None) object that triggered the event
+ :param evt_type: (taurus.core.TaurusEventType or None) type of event
+ :param evt_value: (object or None) event value
+ """
+ pass
+
+ def setEventFilters(self, filters = None):
+ """sets the taurus event filters list.
+ The filters are run in order, using each output to feed the next filter.
+ A filter must be a function that accepts 3 arguments ``(evt_src, evt_type, evt_value)``
+ If the event is to be ignored, the filter must return None.
+ If the event is not to be ignored, filter must return a
+ ``(evt_src, evt_type, evt_value)`` tuple which may (or not) differ from the input.
+
+ For a library of common filters, see taurus/core/util/eventfilters.py
+
+ :param filters: (sequence) a sequence of filters
+
+ See also: insertEventFilter
+ """
+ if filters is None: filters = []
+ self._eventFilters = list(filters)
+
+ def getEventFilters(self):
+ """Returns the list of event filters for this widget
+
+ :return: (sequence<callable>) the event filters
+ """
+ return self._eventFilters
+
+ def insertEventFilter(self, filter, index=-1):
+ """insert a filter in a given position
+
+ :param filter: (callable(evt_src, evt_type, evt_value)) a filter
+ :param index: (int) index to place the filter (default = -1 meaning place at the end)
+
+ See also: setEventFilters
+ """
+ self._eventFilters.insert(index, filter)
+
+ def getSignaller(self):
+ '''Reimplement this method if your derived class does not inherit from
+ QObject. The return value should be a permanent object capable of
+ emitting Qt signals. See :class:`TaurusImageItem` as an example
+ '''
+ return self
+
+
+class QObjectTaurusListener(Qt.QObject, QTaurusBaseListener):
+
+ def __init__(self, name=None, parent=None):
+ self.call__init__wo_kw(Qt.QObject, parent)
+ self.call__init__(QTaurusBaseListener, name=name)
+
+
+class ListenerDemo(QObjectTaurusListener):
+
+ def eventReceived(self, evt_src, evt_type, evt_value):
+ self.info("New %s event from %s", taurus.core.TaurusEventType[evt_type], evt_src)
+ return super(ListenerDemo, self).eventReceived(evt_src, evt_type, evt_value)
+
+ def handleEvent(self, evt_src, evt_type, evt_value):
+ self.info("New %s event from %s", taurus.core.TaurusEventType[evt_type], evt_src)
+
+
+if __name__ == "__main__":
+ import time
+ import taurus
+ qlistener = ListenerDemo(name="DemoObject")
+
+ attr = taurus.Attribute("sys/tg_test/1/double_scalar")
+ attr.addListener(qlistener)
+
+ try:
+ while True:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ print("Finished!")
diff --git a/lib/taurus/qt/qtcore/util/emitter.py b/lib/taurus/qt/qtcore/util/emitter.py
index 9010fb6..e91e942 100644
--- a/lib/taurus/qt/qtcore/util/emitter.py
+++ b/lib/taurus/qt/qtcore/util/emitter.py
@@ -28,8 +28,8 @@ emitter.py: This module provides a task scheduler used by TaurusGrid and TaurusD
"""
from functools import partial
-from PyQt4 import Qt
import taurus
+from taurus.qt import Qt
from taurus.core.util import Logger,Singleton
import Queue,traceback
@@ -47,7 +47,7 @@ def modelSetter(obj,model):
This class is used for convenience as TaurusEmitterThread standard method
"""
#print 'In modelSetter(%s,%s)' % (str(obj),str(model))
- if hasattr(obj,'setModel') and model: obj.setModel(model)
+ if hasattr(obj,'setModel') and model is not None: obj.setModel(model)
return
class MethodModel(object):
@@ -147,7 +147,7 @@ class TaurusEmitterThread(Qt.QThread):
self.method = method
self.cursor = Qt.QCursor(Qt.Qt.WaitCursor) if cursor is True else cursor
self._cursor = False
- self.sleep = sleep
+ self.timewait = sleep
self.emitter = Qt.QObject()
self.emitter.moveToThread(Qt.QApplication.instance().thread())
@@ -165,13 +165,34 @@ class TaurusEmitterThread(Qt.QThread):
def getDone(self):
""" Returns % of done tasks in 0-1 range """
return self._done/(self._done+self.getQueue().qsize()) if self._done else 0.
+
+ def clear(self):
+ while not self.todo.empty():
+ self.todo.get()
+ while not self.getQueue().empty():
+ self.getQueue().get()
+ self._done+=1
+
+ def purge(obj):
+ nqueue = Queue.Queue()
+ while not self.todo.empty():
+ i = self.todo.get()
+ if obj not in i:
+ nqueue.put(i)
+ while not self.queue.empty():
+ i = self.queue.get()
+ if obj not in i:
+ nqueue.put(i)
+ while not nqueue.empty():
+ self.queue.put(nqueue.get())
+ self.next()
- def _doSomething(self,args):
- self.log.debug('At TaurusEmitterThread._doSomething(%s)'%str(args))
+ def _doSomething(self,params):
+ self.log.debug('At TaurusEmitterThread._doSomething(%s)'%str(params))
if not self.method:
- method,args = args[0],args[1:]
+ method,args = params[0],params[1:]
else:
- method = self.method
+ method,args = self.method,params
if method:
try:
method(*args)
@@ -197,7 +218,7 @@ class TaurusEmitterThread(Qt.QThread):
Qt.QApplication.instance().restoreOverrideCursor()
self._cursor = False
- except Queue.Empty,e:
+ except Queue.Empty:
self.log.warning(traceback.format_exc())
pass
except:
@@ -205,7 +226,7 @@ class TaurusEmitterThread(Qt.QThread):
return
def run(self):
- Qt.QApplication.instance().thread().wait(self.sleep)
+ Qt.QApplication.instance().thread().sleep(int(self.timewait/1000) if self.timewait>10 else int(self.timewait)) #wait(self.sleep)
self.log.info('#'*80)
self.log.info('At TaurusEmitterThread.run()')
self.next()
@@ -243,7 +264,7 @@ class SingletonWorker():#Qt.QObject):
"""
_thread = None
- def __init__(self,parent=None,name='',queue=None,method=None,cursor=None,sleep=5000,log=Logger.Warning):
+ def __init__(self,parent=None,name='',queue=None,method=None,cursor=None,sleep=5000,log=Logger.Warning,start=True):
self.name = name
self.log = Logger('SingletonWorker(%s)'%self.name)
self.log.setLogLevel(log)
@@ -255,11 +276,17 @@ class SingletonWorker():#Qt.QObject):
SingletonWorker._thread = TaurusEmitterThread(parent,name='SingletonWorker',cursor=cursor,sleep=sleep)
self.thread = SingletonWorker._thread
self.queue = queue or Queue.Queue()
-
- return
+ if start: self.start()
- def next(self):
- if self.queue.empty(): return
+ def put(self,item,block=True,timeout=None):
+ self.getQueue().put(item,block,timeout)
+
+ def size(self):
+ self.getQueue().qsize()
+
+ def next(self,item=None):
+ if item is not None: self.put(item)
+ elif self.queue.empty(): return
msg = 'At SingletonWorker.next(), %d items not passed yet to Emitter.' % self.queue.qsize()
self.log.info(msg)
#(queue.empty() and self.log.info or self.log.debug)(msg)
@@ -274,9 +301,8 @@ class SingletonWorker():#Qt.QObject):
i+=1
self.log.info('%d Items added to emitter queue' % i)
self.thread.emitter.emit(Qt.SIGNAL("newQueue"))
- except Queue.Empty,e:
+ except Queue.Empty:
self.log.warning(traceback.format_exc())
- pass
except:
self.log.warning(traceback.format_exc())
return
@@ -298,11 +324,28 @@ class SingletonWorker():#Qt.QObject):
Qt.QObject.disconnect(self.thread.emitter, Qt.SIGNAL("newQueue"), self.thread.next)
self._running = False
return
+
+ def clear(self):
+ """
+ This method will clear queue only if next() has not been called.
+ If you call self.thread.clear() it will clear objects for all workers!, be careful
+ """
+ while not self.queue.empty(): self.queue.get()
+ #self.thread.clear()
+
+ def purge(obj):
+ nqueue = Queue.Queue()
+ while not self.queue.empty():
+ i = self.queue.get()
+ if obj not in i:
+ nqueue.put(i)
+ while not nqueue.empty():
+ self.queue.put(nqueue.get())
def isRunning(self): return self._running
def isFinished(self): return self.thread.isFinished()
def finished(self): return self.thread.finished()
def started(self): return self._running
def terminated(self): return self.thread.terminated()
- def sleep(self,ms): return self.thread.sleep(ms)
+ def sleep(self,s): return self.thread.sleep(s)
diff --git a/lib/taurus/qt/qtcore/util/properties.py b/lib/taurus/qt/qtcore/util/properties.py
new file mode 100644
index 0000000..8a98f35
--- /dev/null
+++ b/lib/taurus/qt/qtcore/util/properties.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""
+properties.py: Methods for adding QProperties to QObjects
+
+A call like
+ set_property_methods(self,'Filters','QString',default='',
+ set_callback=lambda s=self:s.loadTree(s.getFilters(),clear=True),
+ reset_callback=lambda s=self:s.loadTree('',clear=True)
+ )
+
+Would replace all these lines:
+
+ def setFilters(self,filters):
+ self._filters = filters
+ self.loadTree(self._filters,clear=True)
+
+ def getFilters(self):
+ return self._filters
+
+ def resetFilters(self):
+ self._filters=""
+ self.loadTree(self._filters)
+
+ filters = QtCore.pyqtProperty("QString", getFilters, setFilters, resetFilters)
+
+Not tested yet with the classical declaration:
+
+ #model = QtCore.pyqtProperty("QString", TaurusBaseWidget.getModel,
+ #TaurusBaseWidget.setModel,
+ #TaurusBaseWidget.resetModel)
+
+"""
+
+from functools import partial
+from taurus.qt import Qt
+from taurus.core.tango.search import *
+
+def join(*seqs):
+ """ It returns a list containing the objects of all given sequences. """
+ if len(seqs)==1 and isSequence(seqs[0]):
+ seqs = seqs[0]
+ result = []
+ for seq in seqs:
+ if isSequence(seq): result.extend(seq)
+ else: result.append(seq)
+ # result += list(seq)
+ return result
+
+def djoin(a,b):
+ """ This method merges dictionaries and/or lists """
+ if not any(map(isDictionary,(a,b))): return join(a,b)
+ other,dct = sorted((a,b),key=isDictionary)
+ if not isDictionary(other):
+ other = dict.fromkeys(other if isSequence(other) else [other,])
+ for k,v in other.items():
+ dct[k] = v if not k in dct else djoin(dct[k],v)
+ return dct
+
+def get_property_attribute(name):
+ return '_'+str(name).lower()
+
+def get_property(obj,name,callback=None):
+ return (callback and callback()) or getattr(obj,get_property_attribute(name))
+
+def set_property(obj,name,value,callback=None):
+ #print 'set_property(%s,%s,%s,%s)'%(obj,name,value,callback)
+ setattr(obj,get_property_attribute(name),value)
+ try: callback and callback(value)
+ except: callback()
+
+def reset_property(obj,name,default=None,callback=None):
+ setattr(obj,get_property_attribute(name),default)
+ if callback: callback()
+
+COMMON_PROPERTIES = (
+ 'ModelInConfig', #If True, it will automatically register Model
+ 'modifiableByUser',
+ #'useParentModel', #Only for widgets, not for components
+ #'Model', #Controlled by ModelInConfig
+ )
+
+def set_property_methods(obj,name,type_="QString",default=None,getter=None,setter=None,reset=None,get_callback=None,set_callback=None,reset_callback=None,qt=False,config=False):
+ """
+ This method allows to add QProperties dynamically with calls like:
+ <pre>
+ set_property_methods(self,'Filters','QString',default='',
+ set_callback=lambda s=self:s.loadTree(s.getFilters(),clear=True),
+ reset_callback=lambda s=self:s.loadTree('',clear=True)
+ )
+ </pre>
+
+ @TODO: This method should be refactored using python descriptors/properties and types.MethodType
+ """
+ klass = obj.__class__
+ mname = '%s%s'%(name[0].upper(),name[1:])
+ lname = '%s%s'%(name[0].lower(),name[1:])
+ getter = getter or (lambda o=obj,n=name,c=get_callback: get_property(o,n,c)) #partial(get_property,name=name,callback=get_callback)
+ setter = setter or (lambda x,y=None,o=obj,d=default,n=name,c=set_callback: set_property(o,n,x if x is not obj else y,c)) #partial(set_property),name=name,callback=set_callback)
+ reset = reset or (lambda o=obj,n=name,d=default,c=reset_callback: reset_property(o,n,d,c)) #partial(reset_property,name=name,default=default,callback=reset_callback)
+ setattr(obj,'set%s'%mname,setter)
+ setattr(obj,'get%s'%mname,getter)
+ setattr(obj,'reset%s'%mname,reset)
+ if qt: setattr(klass,lname,Qt.pyqtProperty("QString", getter,setter,reset))
+ if config: obj.registerConfigProperty(getter,setter,name)
+ reset()
\ No newline at end of file
diff --git a/lib/taurus/qt/qtdesigner/tauruspluginplugin.py b/lib/taurus/qt/qtdesigner/tauruspluginplugin.py
index 54ecda6..374c2bb 100644
--- a/lib/taurus/qt/qtdesigner/tauruspluginplugin.py
+++ b/lib/taurus/qt/qtdesigner/tauruspluginplugin.py
@@ -27,10 +27,8 @@
tauruspluginplugin.py:
"""
-import os
+from taurus.qt import QtDesigner
-from taurus.qt import Qt
-from PyQt4 import QtDesigner
def build_qtdesigner_widget_plugin(klass):
import taurusplugin
@@ -49,7 +47,7 @@ def main():
import taurus
import taurus.core.util
import taurus.qt.qtgui.util
- #taurus.setLogLevel(taurus.Debug)
+ taurus.setLogLevel(taurus.Debug)
_log = taurus.core.util.Logger(__name__)
taurus.Manager().setOperationMode(taurus.core.OperationMode.OFFLINE)
diff --git a/lib/taurus/qt/qtgui/application/taurusapplication.py b/lib/taurus/qt/qtgui/application/taurusapplication.py
index c163dac..274954f 100644
--- a/lib/taurus/qt/qtgui/application/taurusapplication.py
+++ b/lib/taurus/qt/qtgui/application/taurusapplication.py
@@ -3,28 +3,30 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This module provides the base :class:`taurus.qt.qtgui.application.TaurusApplication`
-class."""
+"""This module provides the base
+:class:`taurus.qt.qtgui.application.TaurusApplication` class."""
+
+from __future__ import with_statement
__all__ = ["TaurusApplication"]
@@ -34,6 +36,7 @@ import os
import sys
import logging
import optparse
+import threading
from taurus.qt import Qt
@@ -42,11 +45,13 @@ import taurus.core.util.argparse
class STD(Logger):
-
+
+ FlushWaterMark = 1000
+
def __init__(self, name='', parent=None, format=None, std=None,
pure_text=True):
"""The STD Logger constructor
-
+
:param name: (str) the logger name (default is empty string)
:param parent: (Logger) the parent logger or None if no parent exists
(default is None)
@@ -54,27 +59,31 @@ class STD(Logger):
log format (default is None)
:param std: std to forward write
:param pure_text: if True, writes the 'message' parameter of the log
- message in a separate line preserving the indentation
+ message in a separate line preserving the
+ indentation
"""
Logger.__init__(self, name=name, parent=parent, format=format)
self.buffer = ''
self.log_obj.propagate = False
self.std = std
-
+
def addLogHandler(self, handler):
- """When called, set to use a private handler and DON'T send messages to
- parent loggers (basically will act as an independent logging system
- by itself)
+ """When called, set to use a private handler and DON'T send messages
+ to parent loggers (basically will act as an independent logging system
+ by itself)
:param handler: new handler"""
Logger.addLogHandler(self, handler)
self.log_obj.propagate = not len(self.log_handlers)
-
+
def write(self, msg):
try:
self.buffer += msg
# while there is no new line, just accumulate the buffer
- if msg and (msg[-1] == '\n' or msg.index('\n') >= 0):
+ msgl = len(msg)
+ if msgl > 0 and \
+ (msg[-1] == '\n' or msg.index('\n') >= 0 or \
+ msgl >= self.FlushWaterMark):
self.flush()
except ValueError:
pass
@@ -85,7 +94,7 @@ class STD(Logger):
except:
pass
pass
-
+
def flush(self):
try:
buff = self.buffer
@@ -93,7 +102,8 @@ class STD(Logger):
return
#take the '\n' because the output is a list of strings, each to be
#interpreted as a separate line in the client
- if buff[-1] == '\n': buff = buff[:-1]
+ if buff[-1] == '\n':
+ buff = buff[:-1]
if self.log_handlers:
self.log(Logger.Console, '\n' + buff)
self.buffer = ""
@@ -108,75 +118,82 @@ class STD(Logger):
class TaurusApplication(Qt.QApplication, Logger):
"""A QApplication that additionally parses the command line looking
- for taurus options. This is done using the :mod:`taurus.core.util.argparse`.
+ for taurus options. This is done using the
+ :mod:`taurus.core.util.argparse`.
To create a TaurusApplication object you should use the same parameters
- as in QApplication.
-
+ as in QApplication.
+
The optional keyword parameters:
- app_name: (str) application name
- app_version: (str) application version
- org_name: (str) organization name
- org_domain: (str) organization domain
-
- ...And at last the 'cmd_line_parser' which should be an instance of
+
+ ...And at last the 'cmd_line_parser' which should be an instance of
:class:`optparse.OptionParser`. Simple example::
import sys
import taurus.qt.qtgui.application
import taurus.qt.qtgui.display
-
+
app = taurus.qt.qtgui.application.TaurusApplication()
-
+
w = taurus.qt.qtgui.display.TaurusLabel()
w.model = 'sys/tg_test/1/double_scalar'
w.show()
sys.exit(app.exec_())
-
+
A more complex example showing how to add options and a usage help::
-
+
import sys
import taurus.core.util.argparse
import taurus.qt.qtgui.application
import taurus.qt.qtgui.display
-
+
parser = taurus.core.util.argparse.get_taurus_parser()
parser.usage = "%prog [options] <model>"
parser.add_option("--hello")
-
+
app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser)
args = app.get_command_line_args()
if len(args) < 1:
sys.stderr.write("Need to supply model attribute")
sys.exit(1)
-
+
w = taurus.qt.qtgui.display.TaurusLabel()
w.model = args[1]
w.show()
sys.exit(app.exec_())
-
- For more details on taurus command line parsing check
+
+ For more details on taurus command line parsing check
:mod:`taurus.core.util.argparse`.
"""
-
+
def __init__(self, *args, **kwargs):
"""The constructor. Parameters are the same as QApplication plus a
keyword parameter: 'cmd_line_parser' which should be an instance of
:class:`optparse.OptionParser`"""
+
+ # lock to safely get singleton elements (like IPython taurus
+ # console app)
+ self._lock = threading.Lock()
+
if len(args) == 0:
- args = ([],)
+ args = getattr(sys, 'argv', []),
+
parser=None
app_name, app_version, org_name, org_domain = None, None, None, None
- if kwargs.has_key('app_name'):
+ if 'app_name' in kwargs:
app_name = kwargs.pop('app_name')
- if kwargs.has_key('app_version'):
+ if 'app_version' in kwargs:
app_version = kwargs.pop('app_version')
- if kwargs.has_key('org_name'):
+ if 'org_name' in kwargs:
org_name = kwargs.pop('org_name')
- if kwargs.has_key('org_domain'):
+ if 'org_domain' in kwargs:
org_domain = kwargs.pop('org_domain')
- if kwargs.has_key('cmd_line_parser'):
+ if 'cmd_line_parser' in kwargs:
parser = kwargs.pop('cmd_line_parser')
try:
@@ -186,6 +203,9 @@ class TaurusApplication(Qt.QApplication, Logger):
Logger.__init__(self)
+ self._out = None
+ self._err = None
+
if app_name is not None:
self.setApplicationName(app_name)
if app_version is not None:
@@ -195,9 +215,6 @@ class TaurusApplication(Qt.QApplication, Logger):
if org_domain is not None:
self.setOrganizationDomain(org_domain)
- self._out = None
- self._err = None
-
# if the constructor was called without a parser or with a parser that
# doesn't contain version information and with an application
# name and/or version, then add the --version capability
@@ -211,19 +228,20 @@ class TaurusApplication(Qt.QApplication, Logger):
parser.version = v
parser._add_version_option()
- p, opt, args = taurus.core.util.argparse.init_taurus_args(parser=parser)
-
+ p, opt, args = \
+ taurus.core.util.argparse.init_taurus_args(parser=parser, args=args[0][1:])
+
self._cmd_line_parser = p
self._cmd_line_options = opt
self._cmd_line_args = args
self.__registerQtLogger()
self.__registerExtensions()
self.__redirect_std()
-
+
def __registerQtLogger(self):
import taurus.qt.qtcore.util
taurus.qt.qtcore.util.initTaurusQtLogger()
-
+
def __registerExtensions(self):
"""Registers taurus Qt extensions"""
try:
@@ -246,8 +264,10 @@ class TaurusApplication(Qt.QApplication, Logger):
sys.stdout = self._out
self._err = STD(name="ERR", std=sys.stderr)
sys.stderr = self._err
-
- def __buildLogFileName(self, prefix="/tmp", name=None):
+
+ def __buildLogFileName(self, prefix=None, name=None):
+ if prefix is None:
+ prefix = os.path.expanduser('~/tmp')
appName = str(self.applicationName())
if not appName:
appName = os.path.splitext(os.path.basename(sys.argv[0]))[0]
@@ -258,42 +278,42 @@ class TaurusApplication(Qt.QApplication, Logger):
name = appName + '.log'
fileName = os.path.join(dirName, name)
return fileName
-
+
def get_command_line_parser(self):
- """Returns the :class:`optparse.OptionParser` used to parse the command
- line parameters.
-
+ """Returns the :class:`optparse.OptionParser` used to parse the
+ command line parameters.
+
:return: the parser used in the command line
:rtype: :class:`optparse.OptionParser`"""
return self._cmd_line_parser
-
+
def get_command_line_options(self):
"""Returns the :class:`optparse.Option` that resulted from parsing the
command line parameters.
-
+
:return: the command line options
:rtype: :class:`optparse.Option`"""
return self._cmd_line_options
-
+
def get_command_line_args(self):
"""Returns the list of arguments that resulted from parsing the
command line parameters.
-
+
:return: the command line arguments
:rtype: list of strings"""
return self._cmd_line_args
-
+
def setTaurusStyle(self, styleName):
"""Sets taurus application style to the given style name
-
+
:param styleName: the new style name to be applied
:type styleName: str"""
import taurus.qt.qtgui.style
taurus.qt.qtgui.style.setTaurusStyle(styleName)
-
+
def basicConfig(self, log_file_name=None, maxBytes=1E7, backupCount=5,
with_gui_exc_handler=True):
-
+
# prepare exception handler to report exceptions as error log messages
# and to taurus message box (if with_gui is set)
hook_to = None
@@ -301,19 +321,18 @@ class TaurusApplication(Qt.QApplication, Logger):
import taurus.qt.qtgui.dialog
hook_to = taurus.qt.qtgui.dialog.TaurusExceptHookMessageBox()
sys.excepthook = LogExceptHook(hook_to=hook_to)
-
+
# create a file log handler
try:
if log_file_name is None:
log_file_name = self.__buildLogFileName()
f_h = logging.handlers.RotatingFileHandler(log_file_name,
- maxBytes=maxBytes,
- backupCount=backupCount)
+ maxBytes=maxBytes, backupCount=backupCount)
Logger.addRootLogHandler(f_h)
- if self._out is not None:
+ if self._out is not None:
self._out.std = sys.__stdout__
self._out.addLogHandler(f_h)
- if self._out is not None:
+ if self._out is not None:
self._err.std = sys.__stderr__
self._err.addLogHandler(f_h)
self.info("Logs will be saved in %s", log_file_name)
@@ -321,4 +340,4 @@ class TaurusApplication(Qt.QApplication, Logger):
self.warning("'%s' could not be created. Logs will not be stored",
log_file_name)
self.debug("Error description", exc_info=1)
-
+
diff --git a/lib/taurus/qt/qtgui/base/tauruscontroller.py b/lib/taurus/qt/qtgui/base/tauruscontroller.py
index e865f2d..a61ac78 100644
--- a/lib/taurus/qt/qtgui/base/tauruscontroller.py
+++ b/lib/taurus/qt/qtgui/base/tauruscontroller.py
@@ -250,7 +250,9 @@ class TaurusConfigurationControllerHelper(object):
if self._configParam is None:
model = self.widget().model
try:
- self._configParam = model[model.rfind('=')+1:].lower() #@todo: !!This is assuming tango names.A regexp or a call to a validator should be used
+ #@todo: This works for tango, eval and epics configuration names but is not general.
+ #@todo: This should be done calling to the ConfigurationNameValidator
+ self._configParam = model[model.rfind('?configuration=')+15:].lower()
except:
self._configParam = ''
return self._configParam
@@ -269,14 +271,29 @@ class TaurusConfigurationControllerHelper(object):
val = widget.getNoneValue()
except:
pass
- except:
+ except AttributeError:
+ if param:
+ val = str(param)
+ attr = self.attrObj()
+ if attr is not None:
+ val = val.replace('<label>', attr.label or '---')
+ val = val.replace('<attr_name>',attr.name or '---')
+ val = val.replace('<attr_fullname>',attr.getFullName() or '---')
+ val = val.replace('<dev_alias>',attr.dev_alias or '---')
+ val = val.replace('<dev_name>',attr.dev_name or '---')
+ dev = self.deviceObj()
+ if dev is not None:
+ val = val.replace('<dev_fullname>',dev.getFullName() or '---')
+ else:
+ val = widget.getNoneValue()
+ except:
widget.debug("Invalid configuration parameter '%s'" % param)
val = widget.getNoneValue()
if val is None:
val = widget.getNoneValue()
return val
-
-
+
+
StyleSheetTemplate = """border-style: outset; border-width: 2px; border-color: {0}; {1}"""
def _updatePaletteColors(widget, bgBrush, fgBrush, frameBrush):
diff --git a/lib/taurus/qt/qtgui/button/taurusbutton.py b/lib/taurus/qt/qtgui/button/taurusbutton.py
index f71085d..dcab028 100644
--- a/lib/taurus/qt/qtgui/button/taurusbutton.py
+++ b/lib/taurus/qt/qtgui/button/taurusbutton.py
@@ -38,7 +38,7 @@ from taurus.qt.qtgui.base import TaurusBaseWidget
from taurus.core.util import eventfilters
from taurus.core.util import Enumeration
from taurus.qt.qtgui.resource import getIcon
-from taurus.qt.qtgui.dialog import TaurusMessageBox
+from taurus.qt.qtgui.dialog import ProtectTaurusMessageBox
class _ButtonDialog(Qt.QDialog):
_widget = None
@@ -276,10 +276,11 @@ class TaurusCommandButton(Qt.QPushButton, TaurusBaseWidget):
return '---'
return self._command
- import taurus.qt.qtgui.dialog
-
- @taurus.qt.qtgui.dialog.ProtectTaurusMessageBox(title="Unexpected error when executing command")
+ @ProtectTaurusMessageBox(title="Unexpected error when executing command")
def onClicked(self):
+ return self._onClicked()
+
+ def _onClicked(self):
'''Slot called when the button is clicked. It executes the command with
parameters. It may issue a warning if the command is flagged as
dangerous. On successful execution, it returns the command result and it
@@ -381,10 +382,19 @@ class TaurusCommandButton(Qt.QPushButton, TaurusBaseWidget):
elements of the sequence are not of the right type
required for the parameter, an automatic conversion
will be attempted on execution time. As a special
- case, if parameters is a string, it will be splitted
- on whitespace to obtain a sequence of parameters.
+ case, if parameters is a string not starting and
+ ending in quote characters, it will be splitted on
+ whitespace to obtain a sequence of parameters. If
+ it is a string starting and ending with quotes, the
+ quotes will be removed and the quoted text will not
+ be splitted.
'''
- if isinstance(parameters,(basestring, Qt.QString)): parameters = str(parameters).split()
+ if isinstance(parameters,(basestring, Qt.QString)):
+ parameters = str(parameters).strip()
+ if parameters[0] in ('"',"'") and parameters[0] == parameters[-1]:
+ parameters = [parameters[1:-1]]
+ else:
+ parameters = parameters.split()
self._parameters = parameters
def getParameters(self):
@@ -507,6 +517,7 @@ class TaurusLockButton(Qt.QPushButton, TaurusBaseWidget):
self._on_toggle(down)
except:
import sys
+ from taurus.qt.qtgui.dialog import TaurusMessageBox
msgbox = TaurusMessageBox(*sys.exc_info())
msgbox.setWindowTitle("Error locking device")
if self.update_button().status == LockStatus.Locked:
@@ -544,12 +555,13 @@ class TaurusLockButton(Qt.QPushButton, TaurusBaseWidget):
#
#
+
def demo():
lock_button = TaurusLockButton()
lock_button.model = "sys/tg_test/1"
return lock_button
-def main():
+def lockButtonMain():
import sys
import taurus.qt.qtgui.application
Application = taurus.qt.qtgui.application.TaurusApplication
@@ -586,5 +598,23 @@ def main():
else:
return w
+def main():
+ lockButtonMain()
+
+def commandButtonMain():
+ import sys
+ from taurus.qt.qtgui.application import TaurusApplication
+
+ app = TaurusApplication()
+ form = TaurusCommandButton(parent=None, designMode=False, command = 'DevBoolean', parameters=[123], icon=':/taurus.png', text = 'launch: DevBoolean 123')
+ form.setModel('sys/tg_test/1')
+ form.setDangerMessage('Booo scary command!!\n Maybe you should think twice!')
+ def f(*a):print a
+ form.connect(form, Qt.SIGNAL('commandExecuted'),f)
+ form.show()
+ sys.exit(app.exec_())
+
+
if __name__ == '__main__':
- main()
+ lockButtonMain()
+ #commandButtonMain()
diff --git a/lib/taurus/qt/qtgui/console/__init__.py b/lib/taurus/qt/qtgui/console/__init__.py
index 0d3a289..0886060 100644
--- a/lib/taurus/qt/qtgui/console/__init__.py
+++ b/lib/taurus/qt/qtgui/console/__init__.py
@@ -28,7 +28,10 @@
__docformat__ = 'restructuredtext'
try:
- from .taurusconsole import *
-except:
- from taurus.qt.qtgui.display import create_taurus_fallback as __create
- TaurusConsole = __create("TaurusConsole")
+ from .taurusconsole import TaurusConsole
+except Exception,e:
+ from taurus.qt.qtgui.display import TaurusFallBackWidget
+
+ class TaurusConsole(TaurusFallBackWidget):
+ pass
+
diff --git a/lib/taurus/qt/qtgui/console/taurusconsole.py b/lib/taurus/qt/qtgui/console/taurusconsole.py
index 1b36a12..8ee7355 100644
--- a/lib/taurus/qt/qtgui/console/taurusconsole.py
+++ b/lib/taurus/qt/qtgui/console/taurusconsole.py
@@ -3,68 +3,78 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This package contains a collection of taurus console widgets"""
+"""A Qt MainWindow for the TaurusConsole
-__docformat__ = 'restructuredtext'
-
-from IPython.frontend.qt.console.qtconsoleapp import IPythonQtConsoleApp
+This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
+common actions.
+"""
-from taurus.qt import Qt, QtGui
-if hasattr(Qt, 'QString'):
- raise Exception, "Using Qt SIP API v1. IPython requires Qt SIP API v2"
+__all__ = ["TaurusConsole"]
+__docformat__ = 'restructuredtext'
-class TaurusConsoleApp(IPythonQtConsoleApp):
- pass
+from taurus.qt import Qt
+from taurusconsolefactory import TaurusConsoleFactory
-class TaurusConsole(QtGui.QWidget):
+class TaurusConsole(Qt.QWidget):
- def __init__(self, parent=None, designMode=False):
- QtGui.QWidget.__init__(self, parent)
- l = QtGui.QVBoxLayout()
- l.setContentsMargins(0,0,0,0)
- l.setSpacing(0)
+ def __init__(self, parent=None, kernels=None):
+ super(TaurusConsole, self).__init__(parent)
+ l = Qt.QVBoxLayout(self)
+ l.setContentsMargins(0, 0, 0, 0)
self.setLayout(l)
- self.app = app = TaurusConsoleApp()
- app.initialize(argv=["--pylab=inline"])
- l.addWidget(app.widget)
-
-
+ self._window = window = TaurusConsoleFactory().new_window(kernels=kernels)
+ l.addWidget(window)
+
+ def window(self):
+ return self._window
+
+ def __getattr__(self, name):
+ return getattr(self.window(), name)
-#-----------------------------------------------------------------------------
-# Main entry point
-#-----------------------------------------------------------------------------
-def main():
+def main(argv=None):
+ import taurus.core.util.argparse
import taurus.qt.qtgui.application
- #app = taurus.qt.qtgui.application.TaurusApplication()
+ targp = taurus.core.util.argparse
- taurus_app = TaurusConsoleApp()
- taurus_app.initialize()
- taurus_app.start()
+ if argv is None:
+ import sys
+ argv = sys.argv
+
+ parser = targp.get_taurus_parser()
+ taurus_args, ipython_args = targp.split_taurus_args(parser, args=argv)
+
+ app = taurus.qt.qtgui.application.TaurusApplication(taurus_args,
+ cmd_line_parser=parser)
+ TaurusConsoleFactory(ipython_args=ipython_args)
+ console = TaurusConsole()
+ console.window().create_tab_with_new_frontend(name='tango', label="Tango")
+ console.show()
+ app.exec_()
if __name__ == '__main__':
- main()
+ main()
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/shell/__init__.py b/lib/taurus/qt/qtgui/console/taurusconsoleapplication.py
similarity index 59%
rename from lib/taurus/qt/qtgui/shell/__init__.py
rename to lib/taurus/qt/qtgui/console/taurusconsoleapplication.py
index 32286d1..ff06805 100644
--- a/lib/taurus/qt/qtgui/shell/__init__.py
+++ b/lib/taurus/qt/qtgui/console/taurusconsoleapplication.py
@@ -3,34 +3,57 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This package contains a collection of taurus shell widgets"""
+""" A minimal application using the Qt console-style IPython frontend.
+
+This is not a complete console app, as subprocess will not be able to receive
+input, there is no real readline support, among other limitations.
+"""
+
+__all__ = ["TaurusConsoleApplication"]
__docformat__ = 'restructuredtext'
-try:
- from .taurusshell import *
-except:
- from taurus.qt.qtgui.display import create_fallback as __create
- TaurusShell = __create("TaurusShell")
+
+from taurus.qt import Qt
+
+from IPython.frontend.qt.console.qtconsoleapp import IPythonQtConsoleApp
+
+
+class TaurusConsoleApplication(IPythonQtConsoleApp):
+
+ name='taurusconsole'
+
+ def init_qt_elements(self):
+ self.app = Qt.QApplication.instance()
+ self.app.icon = Qt.QIcon()
+
+ def init_signal(self):
+ pass
+
+ def init_kernel_manager(self):
+ # avoid starting a default kernel
+ self.kernel_manager = None
+
-#from .spockshell import *
+
+
diff --git a/lib/taurus/qt/qtgui/console/taurusconsoleextensions.py b/lib/taurus/qt/qtgui/console/taurusconsoleextensions.py
new file mode 100644
index 0000000..b472a6f
--- /dev/null
+++ b/lib/taurus/qt/qtgui/console/taurusconsoleextensions.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""This package contains a collection of taurus console widgets"""
+
+__docformat__ = 'restructuredtext'
+
+import weakref
+
+from taurus import Device
+from taurus.core.util import Enumeration
+from taurus.qt import Qt
+
+from IPython.config.loader import Config
+
+
+EnvironmentMode = Enumeration("EnvironmentMode", ("Overwrite", "Merge"))
+
+
+class BaseConsoleExtension(object):
+
+ Name = 'ipython'
+ Label = 'IPython'
+
+ def __init__(self, console_factory, profile='default', config=None,
+ mode=EnvironmentMode.Merge):
+ self.console_factory = console_factory
+ self.profile = profile
+ self.profile_arg = '--profile=' + profile
+ self.config = config or Config()
+ self.mode = mode
+
+ def __enter__(self):
+ app = self.console_factory.get_ipython_application()
+ self.orig_config = app.config
+ self.orig_kernel_argv = argv = app.kernel_argv
+ argv, has_profile = list(argv), False
+ for i, arg in enumerate(argv):
+ if arg.startswith('--profile'):
+ has_profile = True
+ if self.mode == EnvironmentMode.Overwrite:
+ argv[i] = self.profile_arg
+ if not has_profile:
+ argv.append(self.profile_arg)
+ app.kernel_argv = argv
+
+ config = self.config
+ if self.mode == EnvironmentMode.Merge:
+ config = config.copy()
+ config.update(app.config)
+ app.config = config
+
+ def __exit__(self,exc_type, exc_value, traceback):
+ app = self.console_factory.get_ipython_application()
+ app.config = self.orig_config
+ app.kernel_argv = self.orig_kernel_argv
+
+ @classmethod
+ def is_enabled(cls):
+ return True
+
+
+class TangoConsoleExtension(BaseConsoleExtension):
+
+ Name = 'tango'
+ Label = 'Tango'
+
+ def __init__(self, console_factory, config=None):
+ if config is None:
+ config = Config()
+ import PyTango.ipython
+ PyTango.ipython.load_config(config)
+ super(TangoConsoleExtension, self).__init__(console_factory,
+ profile='tango',
+ config=config)
+
+ @classmethod
+ def is_enabled(cls):
+ try:
+ import PyTango
+ v = list(PyTango.__version_info__[:2])
+ return v >= [7,2]
+ except:
+ return False
+
+
+from IPython.core.profiledir import ProfileDir, ProfileDirError
+from IPython.utils.path import get_ipython_dir
+
+from sardana.spock.genutils import create_spock_profile, get_macroserver_for_door
+
+def create_sardana_profile(profile, door_name):
+
+ ipython_dir = get_ipython_dir()
+ try:
+ ProfileDir.find_profile_dir_by_name(ipython_dir, profile)
+ except ProfileDirError:
+ create_spock_profile(ipython_dir, "spock", profile, door_name)
+
+def get_profile_from_args(args):
+ for arg in args:
+ if arg.startswith("--profile="):
+ profile = arg[10:]
+ return True, profile
+ return False, "spockdoor"
+
+
+class SDMDoorReader(Qt.QObject):
+
+ def __init__(self, console, sdm=None, parent=None):
+ super(Qt.QObject, self).__init__(parent)
+ self._console = weakref.ref(console)
+ if sdm is None:
+ if not hasattr(Qt.qApp, 'SDM'):
+ raise Exception("Cannot connect to shared data manager")
+ sdm = Qt.qApp.SDM
+ sdm.connectReader("doorName", self.onDoorChanged)
+
+ @property
+ def console(self):
+ return self._console()
+
+ def onDoorChanged(self, door_name):
+ door = Device(door_name)
+ dalias, dname = door.getSimpleName(), door.getNormalName()
+ create_sardana_profile(dalias, dname)
+
+
+class SardanaConsoleExtension(BaseConsoleExtension):
+
+ Name = 'spock'
+ Label = 'Spock'
+
+ def fill_sardana_config(self, config):
+ import sardana.spock
+ profile = 'spockdoor'
+ if not config.Spock:
+ if hasattr(Qt.qApp, 'SDM'):
+ dm = Qt.qApp.SDM.getDataModel('doorName')
+ door_name = dm.getData()
+ door = Device(door_name)
+ dalias, dname = door.getSimpleName(), door.getNormalName()
+ create_sardana_profile(dalias, dname)
+ profile = dalias
+ config.Spock.door_name = dname
+ sardana.spock.load_config(config)
+ return profile
+
+ def __init__(self, console_factory, config=None):
+ if config is None:
+ config = Config()
+ profile = self.fill_sardana_config(config)
+ super(SardanaConsoleExtension, self).__init__(console_factory,
+ profile=profile,
+ config=config)
+
+ @classmethod
+ def is_enabled(cls):
+ try:
+ import sardana
+ v = list(sardana.Release.version_info[:2])
+ return v >= [1,2]
+ except:
+ return False
diff --git a/lib/taurus/qt/qtgui/console/taurusconsolefactory.py b/lib/taurus/qt/qtgui/console/taurusconsolefactory.py
new file mode 100644
index 0000000..4f8c635
--- /dev/null
+++ b/lib/taurus/qt/qtgui/console/taurusconsolefactory.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+""" A minimal application using the Qt console-style IPython frontend.
+
+This is not a complete console app, as subprocess will not be able to receive
+input, there is no real readline support, among other limitations.
+"""
+
+__all__ = ["TaurusConsoleFactory"]
+
+__docformat__ = 'restructuredtext'
+
+from taurus.core.util import Singleton
+from taurus.qt import Qt
+
+from taurusconsolewidget import TaurusConsoleWidget
+from taurusconsolewindow import TaurusConsoleWindow
+from taurusconsoleapplication import TaurusConsoleApplication
+
+import taurusconsoleextensions
+
+from IPython.frontend.qt.kernelmanager import QtKernelManager
+
+
+class TaurusConsoleFactory(Singleton):
+
+ ipython_application_class = TaurusConsoleApplication
+ widget_factory_class = TaurusConsoleWidget
+ kernel_manager_class = QtKernelManager
+
+ def init(self, *args, **kwargs):
+ self.ipython_application = None
+ self.ipython_args = kwargs.pop('ipython_args', [])
+
+ def get_ipython_application(self):
+ app = self.ipython_application
+ if app is None:
+ self.ipython_application = app = self.ipython_application_class()
+ app.kernel_manager_class = self.new_kernel_manager
+ app.initialize(argv=self.ipython_args)
+ app.widget_factory = self.new_frontend_widget
+ self.orig_config = app.config.copy()
+ return app
+
+ def get_extensions(self):
+ import inspect
+ ret = {}
+ for obj_name in dir(taurusconsoleextensions):
+ if obj_name.startswith("_"):
+ continue
+ obj = getattr(taurusconsoleextensions, obj_name)
+ if inspect.isclass(obj):
+ if issubclass(obj, taurusconsoleextensions.BaseConsoleExtension):
+ if obj.is_enabled():
+ ret[obj.Name] = obj
+ return ret
+
+ def get_extension(self, name):
+ import inspect
+ ret = {}
+ for obj_name in dir(taurusconsoleextensions):
+ if obj_name.startswith("_"):
+ continue
+ obj = getattr(taurusconsoleextensions, obj_name)
+ if inspect.isclass(obj):
+ if issubclass(obj, taurusconsoleextensions.BaseConsoleExtension):
+ if obj.is_enabled():
+ if obj.Name == name:
+ return obj
+ ret[obj.Name] = obj
+
+ def new_kernel_manager(self, **kwargs):
+ return self.kernel_manager_class(**kwargs)
+
+ def new_frontend_widget(self, *args, **kwargs):
+ return self.widget_factory_class(*args, **kwargs)
+
+ def new_frontend_slave(self, widget):
+ app = self.get_ipython_application()
+ new_widget = app.new_frontend_slave(widget)
+ return new_widget
+
+ def new_frontend_master(self, name="ipython"):
+ app = self.get_ipython_application()
+ extension = self.get_extension(name)
+ with extension(self):
+ return app.new_frontend_master()
+
+ def new_window(self, kernels=None):
+ qtapp = Qt.QApplication.instance()
+ window = TaurusConsoleWindow(qtapp,
+ new_frontend_factory=self.new_frontend_master,
+ slave_frontend_factory=self.new_frontend_slave)
+ extensions = self.get_extensions()
+ for extension in extensions.values():
+ window.register_kernel_extension(extension)
+ if kernels is not None:
+ for kernel in kernels:
+ if isinstance(kernel, tuple):
+ name, label = kernel
+ else:
+ name, label = kernel, kernel
+ window.create_tab_with_new_frontend(name=name, label=label)
+ return window
+
+def main(argv=None):
+ import taurus.core.util.argparse
+ import taurus.qt.qtgui.application
+
+ targp = taurus.core.util.argparse
+
+ if argv is None:
+ import sys
+ argv = sys.argv
+
+ parser = targp.get_taurus_parser()
+ taurus_args, ipython_args = targp.split_taurus_args(parser, args=argv)
+
+ app = taurus.qt.qtgui.application.TaurusApplication(taurus_args,
+ cmd_line_parser=parser)
+
+ console_factory = TaurusConsoleFactory(ipython_args=ipython_args)
+ window = console_factory.new_window(kernels=[('ipython', 'IPython')])
+ window.create_tab_with_new_frontend(name='tango', label="Tango")
+ window.show()
+ app.exec_()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/console/taurusconsole.py b/lib/taurus/qt/qtgui/console/taurusconsolewidget.py
similarity index 51%
copy from lib/taurus/qt/qtgui/console/taurusconsole.py
copy to lib/taurus/qt/qtgui/console/taurusconsolewidget.py
index 1b36a12..e07df9e 100644
--- a/lib/taurus/qt/qtgui/console/taurusconsole.py
+++ b/lib/taurus/qt/qtgui/console/taurusconsolewidget.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -25,46 +25,35 @@
"""This package contains a collection of taurus console widgets"""
-__docformat__ = 'restructuredtext'
-
-from IPython.frontend.qt.console.qtconsoleapp import IPythonQtConsoleApp
+__all__ = ["TaurusConsoleWidget"]
-from taurus.qt import Qt, QtGui
-if hasattr(Qt, 'QString'):
- raise Exception, "Using Qt SIP API v1. IPython requires Qt SIP API v2"
+__docformat__ = 'restructuredtext'
+from IPython.utils.traitlets import Unicode
+from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
-class TaurusConsoleApp(IPythonQtConsoleApp):
- pass
+default_gui_banner = """\
+Taurus console -- An enhanced IPython console for taurus.
+? -> Introduction and overview of IPython's features.
+%quickref -> Quick reference.
+help -> Python's own help system.
+object? -> Details about 'object', use 'object??' for extra details.
+%guiref -> A brief reference about the graphical user interface.
+"""
-class TaurusConsole(QtGui.QWidget):
-
- def __init__(self, parent=None, designMode=False):
- QtGui.QWidget.__init__(self, parent)
- l = QtGui.QVBoxLayout()
- l.setContentsMargins(0,0,0,0)
- l.setSpacing(0)
- self.setLayout(l)
- self.app = app = TaurusConsoleApp()
- app.initialize(argv=["--pylab=inline"])
- l.addWidget(app.widget)
-
-
-
-#-----------------------------------------------------------------------------
-# Main entry point
-#-----------------------------------------------------------------------------
-def main():
- import taurus.qt.qtgui.application
-
- #app = taurus.qt.qtgui.application.TaurusApplication()
-
- taurus_app = TaurusConsoleApp()
- taurus_app.initialize()
- taurus_app.start()
+class TaurusConsoleWidget(RichIPythonWidget):
+ banner = Unicode(config=True)
-if __name__ == '__main__':
- main()
+ #------ Trait default initializers ---------------------------------------
+ def _banner_default(self):
+ banner = default_gui_banner
+ if 'TerminalInteractiveShell' in self.config:
+ shell = self.config.TerminalInteractiveShell
+ if 'banner' in shell:
+ banner = shell.banner
+ elif 'banner1' in shell and 'banner2' in shell:
+ banner = "\n".join((shell.banner1, shell.banner2))
+ return banner
diff --git a/lib/taurus/qt/qtgui/console/taurusconsolewindow.py b/lib/taurus/qt/qtgui/console/taurusconsolewindow.py
new file mode 100644
index 0000000..ddcd36f
--- /dev/null
+++ b/lib/taurus/qt/qtgui/console/taurusconsolewindow.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""A Qt MainWindow for the TaurusConsole
+
+This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
+common actions.
+"""
+
+__all__ = ["TaurusConsoleWindow"]
+
+__docformat__ = 'restructuredtext'
+
+import functools
+
+from taurus.qt import Qt
+from IPython.frontend.qt.console.mainwindow import MainWindow
+
+
+class TaurusConsoleWindow(MainWindow):
+
+ def __init__(self, *args, **kwargs):
+ self._pending_kernel_actions = []
+ self.new_kernel_menu = None
+ MainWindow.__init__(self, *args, **kwargs)
+
+ def add_tab_with_frontend(self, frontend, name=None):
+ init_menu = self.tab_widget.count() == 0
+ super(TaurusConsoleWindow, self).add_tab_with_frontend(frontend, name=name)
+ if init_menu:
+ self.init_menu_bar()
+
+ def remove_menu_action(self, menu, action):
+ menu.removeAction(action)
+ self.removeAction(action)
+
+ def add_new_kernel_action(self, action):
+ self.add_menu_action(self.new_kernel_menu, action)
+
+ def init_file_menu(self):
+ super(TaurusConsoleWindow, self).init_file_menu()
+ file_menu = self.file_menu
+ self.remove_menu_action(file_menu, self.new_kernel_tab_act)
+
+ self.new_kernel_menu = new_kernel_menu = Qt.QMenu(self.new_kernel_tab_act.text())
+ file_menu.insertMenu(self.slave_kernel_tab_act, new_kernel_menu)
+
+ #self.new_kernel_tab_act.setText("IPython")
+ #self.add_menu_action(new_kernel_menu, self.new_kernel_tab_act)
+
+ for kernel_action in self._pending_kernel_actions:
+ self.add_new_kernel_action(kernel_action)
+
+ def add_new_tango_action(self):
+ tango_action = Qt.QAction("Tango", self,
+ triggered=self.create_tab_with_new_tango_frontend)
+ self.add_new_kernel_action(tango_action)
+
+ def create_tab_with_new_frontend(self, name='ipython', label=None):
+ widget = self.new_frontend_factory(name=name)
+ self.add_tab_with_frontend(widget, name=label)
+
+ def register_kernel_extension(self, extension):
+ f = functools.partial(self.create_tab_with_new_frontend,
+ name=extension.Name,
+ label=extension.Label)
+ action = Qt.QAction(extension.Label, self, triggered=f)
+
+ if self.new_kernel_menu is None:
+ self._pending_kernel_actions.append(action)
+ else:
+ self.add_new_kernel_action(action)
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/container/__init__.py b/lib/taurus/qt/qtgui/container/__init__.py
index af8c7eb..9613f40 100644
--- a/lib/taurus/qt/qtgui/container/__init__.py
+++ b/lib/taurus/qt/qtgui/container/__init__.py
@@ -30,8 +30,8 @@ __docformat__ = 'restructuredtext'
from .qcontainer import *
from .taurusbasecontainer import *
from .taurusframe import *
+from .tauruswidget import *
from .taurusgroupbox import *
from .taurusgroupwidget import *
-from .taurusmainwindow import *
from .taurusscrollarea import *
-from .tauruswidget import *
\ No newline at end of file
+from .taurusmainwindow import *
diff --git a/lib/taurus/qt/qtgui/container/taurusmainwindow.py b/lib/taurus/qt/qtgui/container/taurusmainwindow.py
index 1fd0287..dfec696 100644
--- a/lib/taurus/qt/qtgui/container/taurusmainwindow.py
+++ b/lib/taurus/qt/qtgui/container/taurusmainwindow.py
@@ -42,7 +42,6 @@ from taurus.qt.qtgui.util import ExternalAppAction
from taurus.qt.qtgui.resource import getIcon, getThemeIcon
from taurus.qt.qtgui.dialog import protectTaurusMessageBox
-import cPickle as pickle
class CommandArgsLineEdit(Qt.QLineEdit):
''' An specialized QLineEdit that can transform its text from/to command argument lists'''
@@ -125,33 +124,51 @@ class Rpdb2WaitDialog(Qt.QMessageBox):
class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
- '''A Taurus-aware QMainWindow with several customizations:
+ '''
+ A Taurus-aware QMainWindow with several customizations:
- It takes care of (re)storing its geometry and state (see :meth:`loadSettings`)
- - It provides a splashScreen (which can be disabled)
- - It provides a statusBar (@TODO)
- - It provides basic Taurus menus and actions:
- The following Menus are already Provided:
- - Help (self.helpMenu)
- - About
- - View (self.viewMenu)
- - Taurus (self.taurusMenu)
- The following actions are already provided:
- -Help-->About
- - It incorporates a TaurusLogo (@TODO)
+ - Supports perspectives (programmatic access and, optionally,
+ accessible by user), and allows defining a set of "factory settings"
+ - It provides a customizable splashScreen (optional)
+ - Supports spawning remote consoles and remote debugging
+ - Supports full-screen mode toggling
+ - Supports adding launchers to external applications
+ - It provides a statusBar with an optional heart-beat LED
+ - The following Menus are optionally provided and populated with basic actions:
+ - File (accessible by derived classes as `self.fileMenu`)
+ - View (accessible by derived classes as `self.viewMenu`)
+ - Taurus (accessible by derived classes as `self.taurusMenu`)
+ - Tools (accessible by derived classes as `self.toolsMenu`)
+ - Help (accessible by derived classes as `self.helpMenu`)
+
'''
__pyqtSignals__ = ("modelChanged(const QString &)",)
-
- def __init__(self, parent = None, designMode = False, splash=True):
+
+ #customization options:
+ _heartbeat = 1500 #blinking semi-period in ms. Set to None for not showing the Heart beat LED
+ _showFileMenu = True
+ _showViewMenu = True
+ _showTaurusMenu = True
+ _showToolsMenu = True
+ _showHelpMenu = True
+ _supportUserPerspectives = True #Allows the user to change/create/delete perspectives
+ _showLogger = True
+ _splashLogo = ":/TaurusSplash.png" #set to None for disabling splash screen
+ _splashMessage = "Initializing Main window..."
+
+ def __init__(self, parent = None, designMode = False, splash=None):
name = self.__class__.__name__
self.call__init__wo_kw(Qt.QMainWindow, parent)
self.call__init__(TaurusBaseContainer, name, designMode=designMode)
+ if splash is None:
+ splash = bool(self._splashLogo)
self.__splashScreen = None
if splash and not designMode:
- self.__splashScreen = Qt.QSplashScreen(Qt.QPixmap(":/logo.png"))
+ self.__splashScreen = Qt.QSplashScreen(Qt.QPixmap(self._splashLogo))
self.__splashScreen.show()
- self.__splashScreen.showMessage("Initializing Main window...")
+ self.__splashScreen.showMessage(self._splashMessage)
self.__tangoHost = ""
self.__settings = None
@@ -162,6 +179,14 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
self.helpManualBrowser = None
self.resetHelpManualURI()
+ #Heartbeat
+ if self._heartbeat is not None:
+ from taurus.qt.qtgui.display import QLed
+ self.heartbeatLed = QLed()
+ self.heartbeatLed.setToolTip('Heartbeat: if it does not blink, the application is hung')
+ self.statusBar().addPermanentWidget(self.heartbeatLed)
+ self.resetHeartbeat()
+
#The configuration Dialog (which is launched with the configurationAction)
self.configurationDialog = ConfigurationDialog(self)
@@ -170,59 +195,93 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
self.__createActions()
#logger dock widget
- import taurus.qt.qtgui.table
- loggingWidget = taurus.qt.qtgui.table.QLoggingWidget()
+ if self._showLogger:
+ self.addLoggerWidget()
+
+ #Create Menus
+ if self._showFileMenu:#File menu
+ self.createFileMenu()
+ if self._showViewMenu:#View menu
+ self.createViewMenu()
+ if self._showTaurusMenu:#Taurus Menu
+ self.createTaurusMenu()
+ if self._showToolsMenu:#Tools Menu
+ self.createToolsMenu()
+ if self._showHelpMenu:#Help Menu
+ self.createHelpMenu()
+
+ #View Toolbar
+ self.viewToolBar = self.addToolBar("View")
+ self.viewToolBar.setObjectName("viewToolBar")
+ self.viewToolBar.addAction(self.toggleFullScreenAction)
+
+ #Perspectives Toolbar
+ if self._supportUserPerspectives:
+ self.createPerspectivesToolBar()
+
+ #disable the configuration action if there is nothing to configure
+ self.configurationAction.setEnabled(self.configurationDialog._tabwidget.count())
+
+ def addLoggerWidget(self, hidden=True):
+ '''adds a QLoggingWidget as a dockwidget of the main window (and hides it by default)'''
+ from taurus.qt.qtgui.table import QLoggingWidget
+ loggingWidget = QLoggingWidget()
self.__loggerDW = Qt.QDockWidget("Taurus logs", self)
self.__loggerDW.setWidget(loggingWidget)
self.__loggerDW.setObjectName("loggerDW")
self.addDockWidget(Qt.Qt.BottomDockWidgetArea, self.__loggerDW)
- self.__loggerDW.hide()
-
- #Create Menus
- #File menu
+ if hidden:
+ self.__loggerDW.hide()
+
+ def createFileMenu(self):
+ '''adds a "File" Menu'''
self.fileMenu = self.menuBar().addMenu("File")
- self.fileMenu.addAction(self.importSettingsFileAction)
- self.fileMenu.addAction(self.exportSettingsFileAction)
- #self.fileMenu.addAction(self.resetSettingsAction)
- self.fileMenu.addSeparator()
+ if self._supportUserPerspectives:
+ self.fileMenu.addAction(self.importSettingsFileAction)
+ self.fileMenu.addAction(self.exportSettingsFileAction)
+ #self.fileMenu.addAction(self.resetSettingsAction)
+ self.fileMenu.addSeparator()
self.fileMenu.addAction(self.quitApplicationAction)
- #View menu
+ def createViewMenu(self):
+ '''adds a "View" Menu'''
self.viewMenu = self.menuBar().addMenu("View")
- self.viewMenu.addAction(self.__loggerDW.toggleViewAction())
+ if self._showLogger:
+ self.viewMenu.addAction(self.__loggerDW.toggleViewAction())
self.viewToolBarsMenu = self.viewMenu.addMenu("Tool Bars")
self.viewMenu.addSeparator()
self.viewMenu.addAction(self.toggleFullScreenAction)
- self.viewMenu.addSeparator()
- self.perspectivesMenu = Qt.QMenu("Load Perspectives", self)
- self.viewMenu.addMenu(self.perspectivesMenu)
- self.viewMenu.addAction(self.savePerspectiveAction)
- self.viewMenu.addAction(self.deletePerspectiveAction)
-
- #Taurus Menu
+ if self._supportUserPerspectives:
+ self.viewMenu.addSeparator()
+ self.perspectivesMenu = Qt.QMenu("Load Perspectives", self)
+ self.viewMenu.addMenu(self.perspectivesMenu)
+ self.viewMenu.addAction(self.savePerspectiveAction)
+ self.viewMenu.addAction(self.deletePerspectiveAction)
+
+ def createTaurusMenu(self):
+ '''adds a "Taurus" Menu'''
self.taurusMenu = self.menuBar().addMenu("Taurus")
self.taurusMenu.addAction(self.changeTangoHostAction)
- #Tools Menu
+ def createToolsMenu(self):
+ '''adds a "Tools" Menu'''
self.toolsMenu = self.menuBar().addMenu("Tools")
self.externalAppsMenu = self.toolsMenu.addMenu("External Applications")
self.toolsMenu.addAction(self.configurationAction)
- #Help Menu
+ def createHelpMenu(self):
+ '''adds a "Help" Menu'''
self.helpMenu = self.menuBar().addMenu("Help")
self.helpMenu.addAction("About ...", self.showHelpAbout)
- self.helpMenu.addAction(taurus.qt.qtgui.resource.getThemeIcon("help-browser"),"Manual", self.onShowManual)
-
- #View Toolbar
- self.viewToolBar = self.addToolBar("View")
- self.viewToolBar.setObjectName("viewToolBar")
- self.viewToolBar.addAction(self.toggleFullScreenAction)
+ self.helpMenu.addAction(getThemeIcon("help-browser"),"Manual", self.onShowManual)
- #Perspectives Toolbar
+ def createPerspectivesToolBar(self):
+ '''adds a Perspectives ToolBar'''
self.perspectivesToolBar = self.addToolBar("Perspectives")
self.perspectivesToolBar.setObjectName("perspectivesToolBar")
- self.viewToolBarsMenu.addAction(self.perspectivesToolBar.toggleViewAction())
pbutton = Qt.QToolButton()
+ if not hasattr(self, 'perspectivesMenu'): #it may have been created earlier (for the view menu)
+ self.perspectivesMenu = Qt.QMenu("Load Perspectives", self)
self.perspectivesMenu.setIcon(getThemeIcon("document-open"))
pbutton.setToolTip("Load Perspectives")
pbutton.setText("Load Perspectives")
@@ -230,9 +289,8 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
pbutton.setMenu(self.perspectivesMenu)
self.perspectivesToolBar.addWidget(pbutton)
self.perspectivesToolBar.addAction(self.savePerspectiveAction)
-
- #disable the configuration action if there is nothing to configure
- self.configurationAction.setEnabled(self.configurationDialog._tabwidget.count())
+ if self._showViewMenu:
+ self.viewToolBarsMenu.addAction(self.perspectivesToolBar.toggleViewAction())
def updatePerspectivesMenu(self):
'''re-checks the perspectives available to update self.perspectivesMenu
@@ -240,8 +298,10 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
.. note:: This method may need be called by derived classes at the end
of their initialization.
- :return: (QMenu) the updated perspectives menu
+ :return: (QMenu) the updated perspectives menu (or None if self._supportUserPerspectives is False)
'''
+ if not self._supportUserPerspectives:
+ return None
self.perspectivesMenu.clear()
for pname in self.getPerspectivesList():
self.perspectivesMenu.addAction(pname, self.__onPerspectiveSelected)
@@ -488,28 +548,33 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
if group is not None:
settings.beginGroup(group)
if not ignoreGeometry:
- self.restoreGeometry(settings.value("MainWindow/Geometry").toByteArray())
+ ba = Qt.from_qvariant(settings.value("MainWindow/Geometry"), 'toByteArray') or Qt.QByteArray() #With API2, from_qvariant is returning None instead
+ # of an empty QByTeArray
+ # and this caused an exception later on. Hence the "or"
+ self.restoreGeometry(ba)
#restore the Taurus config
try:
- self.applyQConfig(settings.value('TaurusConfig').toByteArray())
+ ba = Qt.from_qvariant(settings.value("TaurusConfig"), 'toByteArray') or Qt.QByteArray()
+ self.applyQConfig(ba)
except Exception,e:
msg = 'Problem loading configuration from "%s". Some settings may not be restored.\n Details: %s'%(unicode(settings.fileName()), repr(e))
self.error(msg)
Qt.QMessageBox.warning(self,'Error Loading settings', msg, Qt.QMessageBox.Ok)
- self.restoreState(settings.value("MainWindow/State").toByteArray())
+ ba = Qt.from_qvariant(settings.value("MainWindow/State"), 'toByteArray') or Qt.QByteArray()
+ self.restoreState(ba)
#hide all dockwidgets (so that they are shown only if they were present in the settings)
dockwidgets = [c for c in self.children() if isinstance(c, Qt.QDockWidget)]
for d in dockwidgets:
r = self.restoreDockWidget(d)
d.hide()
- self.restoreState(settings.value("MainWindow/State").toByteArray())
+ ba = Qt.from_qvariant(settings.value("MainWindow/State"), 'toByteArray') or Qt.QByteArray()
+ self.restoreState(ba)
if group is not None:
settings.endGroup()
self.updatePerspectivesMenu()
self.info('MainWindow settings restored')
-
def saveSettings(self, group=None):
'''saves the application settings (so that they can be restored with :meth:`loadSettings`)
@@ -532,7 +597,6 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
settings.endGroup()
self.info('MainWindow settings saved in "%s"'%settings.fileName())
-
def savePerspective(self, name=None):
'''Stores current state of the application as a perspective with the given name
@@ -553,7 +617,8 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
self.updatePerspectivesMenu()
def loadPerspective(self, name=None, settings=None):
- '''Loads the settings saved for the given perspective
+ '''Loads the settings saved for the given perspective.
+ It emits a 'perspectiveChanged' signal with name as its parameter
:param name: (str) name of the perspective
:param settings: (QSettings or None) a QSettings object. If None given,
@@ -562,11 +627,12 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
'''
if name is None:
perspectives = self.getPerspectivesList()
- if perspectives.isEmpty(): return
+ if len(perspectives) == 0: return
name,ok = Qt.QInputDialog.getItem(self, "Load Perspective", "Change perspective to:",
perspectives, 0, False)
if not ok: return
self.loadSettings(settings=settings, group="Perspectives/%s"%name, ignoreGeometry=True)
+ self.emit(Qt.SIGNAL('perspectiveChanged'), name)
def getPerspectivesList(self, settings=None):
'''Returns the list of saved perspectives
@@ -595,7 +661,7 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
if settings is None: settings = self.getQSettings()
if name is None:
perspectives = self.getPerspectivesList()
- if perspectives.isEmpty(): return
+ if len(perspectives) == 0: return
name,ok = Qt.QInputDialog.getItem(self, "Delete Perspective", "Choose perspective to be deleted:",
perspectives, 0, False)
if not ok: return
@@ -650,6 +716,11 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
# self.__settings = self.newQSettings()
# self.saveSettings()
+ def showEvent(self,event):
+ '''This event handler receives widget show events'''
+ if self.__splashScreen is not None and not event.spontaneous():
+ self.__splashScreen.finish(self)
+
def closeEvent(self,event):
'''This event handler receives widget close events'''
self.saveSettings() #save current window state before closing
@@ -687,15 +758,13 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
self.extAppsBar = self.addToolBar("External Applications")
self.extAppsBar.setObjectName("External Applications")
self.extAppsBar.setToolButtonStyle(Qt.Qt.ToolButtonTextBesideIcon)
- self.viewToolBarsMenu.addAction(self.extAppsBar.toggleViewAction())
+ if self._showViewMenu:
+ self.viewToolBarsMenu.addAction(self.extAppsBar.toggleViewAction())
self.extAppsBar.addAction(extapp)
- if toMenu:
+ if toMenu and self._showToolsMenu:
if self.toolsMenu is None:
- self.toolsMenu = Qt.QMenu("Tools", self)
- self.menuBar().insertMenu(self.helpMenu.menuAction(), self.toolsMenu) #insert it before the Help menu
- if self.externalAppsMenu is None:
- self.externalAppsMenu = self.toolsMenu.addMenu("External Applications")
+ self.createToolsMenu()
self.externalAppsMenu.addAction(extapp)
#register this action for config
self.registerConfigDelegate(extapp, "_extApp[%s]"%str(extapp.text()))
@@ -729,7 +798,7 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
self.__helpManualURI = uri
if self.helpManualBrowser is None:
try:
- from PyQt4.QtWebKit import QWebView
+ from taurus.qt.QtWebKit import QWebView
self.helpManualBrowser = QWebView()
except:
self.helpManualBrowser = Qt.QLabel('QWebkit is not available')
@@ -776,7 +845,7 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
username = getSystemUserName()
appname = unicode(Qt.QApplication.applicationName())
key = "__socket_%s-%s__"%(username,appname)
- from PyQt4 import QtNetwork
+ from taurus.qt import QtNetwork
socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(key)
alive = socket.waitForConnected(3000)
@@ -788,7 +857,12 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
self.connect(self.socketServer, Qt.SIGNAL("newConnection()"), self.onIncommingSocketConnection)
ok = self.socketServer.listen(key)
if not ok:
- if self.socketServer.serverError() == Qt.QAbstractSocket.AddressInUseError:
+ try:
+ AddressInUseError = Qt.QAbstractSocket.AddressInUseError #This fails in some PyQt4 versions...
+ except:
+ from PyQt4.QtNetwork import QAbstractSocket #...so we try this other way of accessing
+ AddressInUseError = QAbstractSocket.AddressInUseError
+ if self.socketServer.serverError() == AddressInUseError:
self.info('Resetting unresponsive socket with key "%s"',key)
if hasattr(self.socketServer, 'removeServer'): #removeServer() was added in Qt4.5. (In Qt4.4 a call to listen() removes a previous server)
self.socketServer.removeServer(key)
@@ -814,10 +888,25 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
socket.deleteLater()
self.raise_()
self.activateWindow()
+
+ def setHeartbeat(self, interval):
+ '''sets the interval of the heartbeat LED for the window.
+ The heartbeat is displayed by a Led in the status bar unless
+ it is disabled by setting the interval to 0
+ :param interval: (int) heart beat interval in millisecs. Set to 0 to disable
+ '''
+ self.heartbeatLed.setBlinkingInterval(interval)
+ self.heartbeatLed.setVisible(interval>0)
+ def getHeartbeat(self):
+ '''returns the heart beat interval'''
+ return self.heartbeatLed.getBlinkingInterval()
-
+ def resetHeartbeat(self):
+ '''resets the heartbeat interval'''
+ self.setHeartbeat(self.__class__._heartbeat)
+
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Public slots for apply/restore changes
@@ -855,18 +944,46 @@ class TaurusMainWindow(Qt.QMainWindow, TaurusBaseContainer):
helpManualURI = Qt.pyqtProperty("QString", getHelpManualURI,
setHelpManualURI,
resetHelpManualURI)
+
+ heartbeat = Qt.pyqtProperty("int", getHeartbeat,
+ setHeartbeat,
+ resetHeartbeat)
#---------
if __name__ == "__main__":
-
- import sys
+
import taurus.qt.qtgui.application
app = taurus.qt.qtgui.application.TaurusApplication()
app.setApplicationName('TaurusMainWindow-test')
app.setOrganizationName('ALBA')
app.basicConfig()
- form = TaurusMainWindow()
+
+ class MyMainWindow(TaurusMainWindow):
+ _heartbeat = 300 #blinking semi-period in ms. Set to None for not showing the Heart beat LED
+ _showFileMenu = True
+ _showViewMenu = True
+ _showTaurusMenu = False
+ _showToolsMenu = True
+ _showHelpMenu = True
+ _supportUserPerspectives = True #Allows the user to change/create/delete perspectives
+ _showLogger = True
+ _splashLogo = ":/TaurusSplash.png" #set to None for disabling splash screen
+ _splashMessage = "Initializing Main window..."
+ def __init__(self):
+ TaurusMainWindow.__init__(self, parent=None, designMode=False, splash=None)
+ #simulating a lengthy initialization
+ import time
+ for i in range(5):
+ time.sleep(0.5)
+ self.splashScreen().showMessage("starting: step %i/5"%(i+1))
+
+
+
+
+ #MainWindowKlass = TaurusMainWindow
+
+ form = MyMainWindow()
#ensure only a single instance of this application is running
single = form.checkSingleInstance()
@@ -879,10 +996,9 @@ if __name__ == "__main__":
#form.setCentralWidget(Qt.QMdiArea()) #just for testing
-# form.addExternalAppLauncher('pwd')
+ #form.addExternalAppLauncher('pwd')
form.show()
- form.splashScreen().finish(form)
sys.exit(app.exec_())
diff --git a/lib/taurus/qt/qtgui/dialog/__init__.py b/lib/taurus/qt/qtgui/dialog/__init__.py
index 3363961..7c43618 100644
--- a/lib/taurus/qt/qtgui/dialog/__init__.py
+++ b/lib/taurus/qt/qtgui/dialog/__init__.py
@@ -29,3 +29,4 @@ panels like forms or panels to be inserted in dialogs"""
__docformat__ = 'restructuredtext'
from .taurusmessagebox import *
+from .taurusinputdialog import *
diff --git a/lib/taurus/qt/qtgui/dialog/taurusconfigurationdialog.py b/lib/taurus/qt/qtgui/dialog/taurusconfigurationdialog.py
index 76abf7b..2dabc57 100644
--- a/lib/taurus/qt/qtgui/dialog/taurusconfigurationdialog.py
+++ b/lib/taurus/qt/qtgui/dialog/taurusconfigurationdialog.py
@@ -29,10 +29,10 @@ __all__ = ["TaurusConfigurationDialog"]
__docformat__ = 'restructuredtext'
-import sys
from taurus.qt import Qt
from taurus.qt.qtgui.panel.taurusconfigurationpanel import TaurusConfigurationPanel
+
class TaurusConfigurationDialog(Qt.QDialog):
def __init__(self, parent=None, designMode=False):
@@ -70,4 +70,5 @@ def main():
return a.exec_()
if __name__ == "__main__":
+ import sys
sys.exit(main())
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/dialog/taurusinputdialog.py b/lib/taurus/qt/qtgui/dialog/taurusinputdialog.py
new file mode 100644
index 0000000..34fd64e
--- /dev/null
+++ b/lib/taurus/qt/qtgui/dialog/taurusinputdialog.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""This module provides a set of dialog based widgets"""
+
+__all__ = ["TaurusInputDialog", "get_input"]
+
+__docformat__ = 'restructuredtext'
+
+from taurus.qt import Qt
+
+def get_input(input_data, parent=None, input_panel_klass=None):
+ """Static convenience function to get an input from the user using a
+ dialog. The dialog will be modal.
+
+ The input_data is a dictionary which contains information on how to build
+ the input dialog. It **must** contains the following keys:
+
+ - *prompt* <str>: message to be displayed
+
+ The following are optional keys (and their corresponding default values):
+
+ - *title* <str> (doesn't have default value)
+ - *key* <str> (doesn't have default value):
+ a label to be presented left to the input box represeting the label
+ - *unit* <str> (doesn't have default value):
+ a label to be presented right to the input box representing the units
+ - *data_type* <str or sequence> ('String'):
+ type of data to be requested. Standard
+ accepted data types are 'String', 'Integer', 'Float', 'Boolean',
+ 'Text'. A list of elements will be interpreted as a selection.
+ Default TaurusInputPanel class will interpret any custom data types as
+ 'String' and will display input widget accordingly. Custom
+ data types can be handled differently by supplying a different
+ input_panel_klass.
+ - *minimum* <int/float> (-sys.maxint):
+ minimum value (makes sence when data_type is 'Integer' or 'Float')
+ - *maximum* <int/float> (sys.maxint):
+ maximum value (makes sence when data_type is 'Integer' or 'Float')
+ - *step* <int/float> (1):
+ step size value (makes sence when data_type is 'Integer' or 'Float')
+ - *decimals* <int> (1):
+ number of decimal places to show (makes sence when data_type is
+ 'Float')
+ - *default_value* <obj> (doesn't have default value):
+ default value
+ - *allow_multiple* <bool> (False):
+ allow more than one value to be selected (makes sence when data_type
+ is a sequence of possibilities)
+
+ :param input_data:
+ a dictionary with information on how to build the input dialog
+ :type input_data: :py:obj:`dict`
+ :param parent: parent widget
+ :type parent: PyQt4.QtGui.QWidget
+ :param input_panel_klass:
+ python class to be used as input panel [default: :class:`~taurus.qt.qtgui.panel.TaurusInputPanel`]
+ :type input_panel_klass: :class:`~taurus.qt.qtgui.panel.TaurusInputPanel`
+
+ :return: a tuple containing value selected and boolean which is true if
+ user accepted the dialog (pressed Ok) or false otherwise
+ :rtype: tuple< obj, bool >
+
+ Examples::
+
+ d1 = dict(prompt="What's your name?", data_type="String")
+ d2 = dict(prompt="What's your age?", data_type="Integer",
+ default_value=4, maximum=100, key="Age", unit="years")
+ d3 = dict(prompt="What's your favourite number?", data_type="Float",
+ default_value=0.1, maximum=88.8, key="Number")
+ d4 = dict(prompt="What's your favourite car brand?",
+ data_type=["Mazda", "Skoda", "Citroen", "Mercedes", "Audi", "Ferrari"],
+ default_value="Mercedes")
+ d5 = dict(prompt="Select some car brands", allow_multiple=True,
+ data_type=["Mazda", "Skoda", "Citroen", "Mercedes", "Audi", "Ferrari"],
+ default_value=["Mercedes", "Citroen"])
+ d6 = dict(prompt="What's your favourite color?", key="Color",
+ data_type=["blue", "red", "green"], default_value="red")
+ d7 = dict(prompt="Do you like bears?",
+ data_type='Boolean', key="Yes/No", default_value=True)
+ d8 = dict(prompt="Please write your memo",
+ data_type='Text', key="Memo", default_value="By default a memo is\na long thing")
+
+ for d in [d1, d2, d3, d4, d5, d6, d7, d8]:
+ get_input(input_data=d, title=d['prompt'])
+ """
+ if input_panel_klass is None:
+ from taurus.qt.qtgui.panel import TaurusInputPanel
+ input_panel_klass = TaurusInputPanel
+ dialog = TaurusInputDialog(input_data=input_data, parent=parent,
+ input_panel_klass=input_panel_klass)
+ dialog.exec_()
+ return dialog.value(), dialog.result()
+
+
+class TaurusInputDialog(Qt.QDialog):
+ """The TaurusInputDialog class provides a simple convenience dialog to get
+ a single value from the user.
+ """
+
+ def __init__(self, input_data=None, parent=None,
+ input_panel_klass=None, designMode=False):
+ if input_panel_klass is None:
+ from taurus.qt.qtgui.panel import TaurusInputPanel
+ input_panel_klass = TaurusInputPanel
+ self.input_data = input_data
+ self.input_panel_klass = input_panel_klass
+ Qt.QDialog.__init__(self, parent)
+ if input_data and 'title' in input_data:
+ self.setWindowTitle(input_data['title'])
+ layout = Qt.QVBoxLayout()
+ self.setLayout(layout)
+ self._panel = panel = input_panel_klass(input_data, self)
+ layout.setContentsMargins(0, 0, 0, 0)
+ layout.addWidget(self._panel)
+ self.connect(panel.buttonBox(), Qt.SIGNAL("accepted()"), self.accept)
+ self.connect(panel.buttonBox(), Qt.SIGNAL("rejected()"), self.reject)
+ self._panel.setInputFocus()
+
+ def panel(self):
+ """Returns the :class:`taurus.qt.qtgui.panel.TaurusInputPanel`.
+
+ :return: the internal panel
+ :rtype: taurus.qt.qtgui.panel.TaurusInputPanel"""
+ return self._panel
+
+ def value(self):
+ """Returns the value selected by the user.
+
+ :return: the value selected by the user"""
+ return self.panel().value()
+
+
+def demo():
+ """Input Dialog"""
+
+ input_data = dict(prompt="What's your favourite car brand?",
+ data_type=["Mazda", "Skoda", "Citroen", "Mercedes", "Audi", "Ferrari"],
+ default_value="Mercedes")
+ w = TaurusInputDialog(input_data=input_data)
+ w.show()
+ return w
+
+
+def main():
+ import taurus.qt.qtgui.application
+
+ Application = taurus.qt.qtgui.application.TaurusApplication
+
+ app = Application.instance()
+ owns_app = app is None
+
+ if owns_app:
+ app = Qt.QApplication([])
+ app.setApplicationName("Taurus input dialog demo")
+ app.setApplicationVersion("1.0")
+
+ d1 = dict(prompt="What's your name?", data_type="String")
+ d2 = dict(prompt="What's your age?", data_type="Integer",
+ default_value=4, maximum=100, key="Age", unit="years")
+ d3 = dict(prompt="What's your favourite number?", data_type="Float",
+ default_value=0.1, maximum=88.8, key="Number")
+ d4 = dict(prompt="What's your favourite car brand?",
+ data_type=["Mazda", "Skoda", "Citroen", "Mercedes", "Audi", "Ferrari"],
+ default_value="Mercedes")
+ d5 = dict(prompt="Select some car brands", allow_multiple=True,
+ data_type=["Mazda", "Skoda", "Citroen", "Mercedes", "Audi", "Ferrari"],
+ default_value=["Mercedes", "Citroen"])
+ d6 = dict(prompt="What's your favourite color?", key="Color",
+ data_type=["blue", "red", "green"], default_value="red")
+ d7 = dict(prompt="Do you like bears?",
+ data_type='Boolean', key="Yes/No", default_value=True)
+ d8 = dict(prompt="Please write your memo",
+ data_type='Text', key="Memo", default_value="By default a memo is\na long thing")
+
+ for d in [d1, d2, d3, d4, d5, d6, d7, d8]:
+ print get_input(input_data=d, title=d['prompt'])
+
+if __name__ == "__main__":
+ main()
+
diff --git a/lib/taurus/qt/qtgui/dialog/taurusmessagebox.py b/lib/taurus/qt/qtgui/dialog/taurusmessagebox.py
index 3e63d7c..b2c20b1 100644
--- a/lib/taurus/qt/qtgui/dialog/taurusmessagebox.py
+++ b/lib/taurus/qt/qtgui/dialog/taurusmessagebox.py
@@ -31,38 +31,11 @@ __all__ = ["TaurusMessageBox", "protectTaurusMessageBox",
__docformat__ = 'restructuredtext'
import sys
-import functools
-import threading
from taurus.qt import Qt
from taurus.core.util.excepthook import BaseExceptHook
-import taurus.qt.qtgui.resource
+from taurus.core.util.wrap import wraps
-#_MESSAGE_BOX = None
-#_MESSAGE_BOX_LOCK = threading.RLock()
-
-
-#def TaurusMessageBox(err_type=None, err_value=None, err_traceback=None,
-# parent=None):
-# global _MESSAGE_BOX_LOCK
-# global _MESSAGE_BOX
-
-# with _MESSAGE_BOX_LOCK:
-# if _MESSAGE_BOX is None:
-# _MESSAGE_BOX = TaurusMessageDialog(err_type=err_type,
-# err_value=err_value,
-# err_traceback=err_traceback,
-# parent=parent)
-# else:
-# _MESSAGE_BOX.setError(err_type=err_type,
-# err_value=err_value,
-# err_traceback=err_traceback)
-# if _MESSAGE_BOX.panel().checkBoxState() == Qt.Qt.Checked:
-# return
-# return _MESSAGE_BOX
-
-
-#class TaurusMessageDialog(Qt.QDialog):
class TaurusMessageBox(Qt.QDialog):
"""A panel intended to display a taurus error.
Example::
@@ -210,7 +183,7 @@ def protectTaurusMessageBox(fn):
d.TurnOn()
"""
- @functools.wraps(fn)
+ @wraps(fn)
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
@@ -239,7 +212,7 @@ class ProtectTaurusMessageBox(object):
self._msg = msg
def __call__(self, fn):
- @functools.wraps(fn)
+ @wraps(fn)
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
@@ -315,7 +288,6 @@ def s3():
raise DemoException("A demo exception occurred")
def py_exc():
- import sys
try:
s1()
except:
@@ -323,7 +295,6 @@ def py_exc():
msgbox.exec_()
def tg_exc():
- import sys
import PyTango
try:
PyTango.Except.throw_exception('TangoException',
@@ -333,13 +304,12 @@ def tg_exc():
msgbox.exec_()
def tg_serv_exc():
- import sys
import PyTango
+ import taurus
dev = taurus.Device("sys/tg_test/1")
- exc_info = None
try:
dev.read_attribute("throw_exception")
- except PyTango.DevFailed, df:
+ except PyTango.DevFailed:
msgbox = TaurusMessageBox(*sys.exc_info())
msgbox.exec_()
except:
@@ -347,7 +317,6 @@ def tg_serv_exc():
msgbox.exec_()
def py_tg_serv_exc():
- import sys
import PyTango
try:
PyTango.Except.throw_exception('TangoException',
@@ -392,8 +361,6 @@ def demo():
return panel
def main():
-
- import sys
import taurus.qt.qtgui.application
Application = taurus.qt.qtgui.application.TaurusApplication
@@ -411,8 +378,8 @@ def main():
if owns_app:
sys.exit(app.exec_())
else:
- return panel
+ return w
if __name__ == "__main__":
main()
-
\ No newline at end of file
+
diff --git a/lib/taurus/qt/qtgui/display/demo/qpixmapwidgetdemo.py b/lib/taurus/qt/qtgui/display/demo/qpixmapwidgetdemo.py
index 16e7a9f..e362640 100644
--- a/lib/taurus/qt/qtgui/display/demo/qpixmapwidgetdemo.py
+++ b/lib/taurus/qt/qtgui/display/demo/qpixmapwidgetdemo.py
@@ -3,28 +3,28 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This module provides a demo for the :class:`taurus.qt.qtgui.display.TaurusLabel`
-widget """
+"""This module provides a demo for the
+:class:`taurus.qt.qtgui.display.TaurusLabel` widget """
__all__ = ["demo", "main"]
@@ -32,6 +32,7 @@ __docformat__ = 'restructuredtext'
from taurus.qt import Qt
+
def demo():
import sys
import taurus.qt.qtgui.application
@@ -41,18 +42,17 @@ def demo():
getPixmap = taurus.qt.qtgui.resource.getPixmap
Application = taurus.qt.qtgui.application.TaurusApplication
QPixmapWidget = taurus.qt.qtgui.display.QPixmapWidget
-
+
app = Application.instance()
owns_app = app is None
-
+
if owns_app:
- import taurus.core.util.argparse
app = Application()
M = 2
class QPixmapWidgetTestPanel(Qt.QWidget):
-
+
def __init__(self, parent=None):
Qt.QWidget.__init__(self, parent)
panel_l = Qt.QVBoxLayout()
@@ -83,7 +83,7 @@ def demo():
control_l.addRow("Transformation mode:", transformation_widget)
control_l.addRow("Horiz. alignment:", halign_widget)
control_l.addRow("Vert. alignment:", valign_widget)
-
+
panel_l.addWidget(display_panel, 1)
panel_l.addWidget(control_panel, 0)
@@ -95,13 +95,13 @@ def demo():
valign_widget.addItem("Top", Qt.QVariant(Qt.Qt.AlignTop))
valign_widget.addItem("Center", Qt.QVariant(Qt.Qt.AlignVCenter))
valign_widget.addItem("Bottom", Qt.QVariant(Qt.Qt.AlignBottom))
-
+
Qt.QObject.connect(pixmap_widget, Qt.SIGNAL("textChanged(const QString &)"), self.changePixmap)
Qt.QObject.connect(aspect_ratio_widget, Qt.SIGNAL("currentIndexChanged(int)"), self.changeAspectRatio)
Qt.QObject.connect(transformation_widget, Qt.SIGNAL("currentIndexChanged(int)"), self.changeTransformationMode)
Qt.QObject.connect(halign_widget, Qt.SIGNAL("currentIndexChanged(int)"), self.changeAlignment)
Qt.QObject.connect(valign_widget, Qt.SIGNAL("currentIndexChanged(int)"), self.changeAlignment)
-
+
self.w = w
self.w_pixmap = pixmap_widget
self.w_aspect_ratio = aspect_ratio_widget
@@ -114,10 +114,10 @@ def demo():
transformation_widget.setCurrentIndex(1)
halign_widget.setCurrentIndex(0)
valign_widget.setCurrentIndex(1)
-
+
def changePixmap(self, name):
self.w.pixmap = getPixmap(name)
-
+
def changeAspectRatio(self, i):
v = Qt.Qt.IgnoreAspectRatio
if i == 1:
@@ -125,18 +125,20 @@ def demo():
elif i == 2:
v = Qt.Qt.KeepAspectRatioByExpanding
self.w.setAspectRatioMode(v)
-
+
def changeTransformationMode(self, i):
v = Qt.Qt.FastTransformation
if i == 1:
v = Qt.Qt.SmoothTransformation
self.w.setTransformationMode(v)
-
+
def changeAlignment(self, i):
- halign = self.w_halign.itemData(self.w_halign.currentIndex()).toInt()[0]
- valign = self.w_valign.itemData(self.w_valign.currentIndex()).toInt()[0]
+ halign = self.w_halign.itemData(self.w_halign.currentIndex())
+ halign = Qt.from_qvariant(halign, int)
+ valign = self.w_valign.itemData(self.w_valign.currentIndex())
+ valign = Qt.from_qvariant(valign, int)
self.w.alignment = halign | valign
-
+
panel = Qt.QWidget()
layout=Qt.QGridLayout()
panel.setLayout(layout)
@@ -149,9 +151,9 @@ def demo():
sys.exit(app.exec_())
else:
return panel
-
+
def main():
return demo()
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/lib/taurus/qt/qtgui/display/demo/tauruslabeldemo.py b/lib/taurus/qt/qtgui/display/demo/tauruslabeldemo.py
index 38f309e..53dfe16 100644
--- a/lib/taurus/qt/qtgui/display/demo/tauruslabeldemo.py
+++ b/lib/taurus/qt/qtgui/display/demo/tauruslabeldemo.py
@@ -44,7 +44,6 @@ def demo():
owns_app = app is None
if owns_app:
- import taurus.core.util.argparse
app = Application()
M = 2
diff --git a/lib/taurus/qt/qtgui/display/demo/tauruslcddemo.py b/lib/taurus/qt/qtgui/display/demo/tauruslcddemo.py
index 2adf0c2..778b1ba 100644
--- a/lib/taurus/qt/qtgui/display/demo/tauruslcddemo.py
+++ b/lib/taurus/qt/qtgui/display/demo/tauruslcddemo.py
@@ -44,7 +44,6 @@ def demo():
owns_app = app is None
if owns_app:
- import taurus.core.util.argparse
app = Application()
M = 2
@@ -75,8 +74,6 @@ def demo():
model_index_widget = Qt.QLineEdit()
fg_widget = Qt.QComboBox()
bg_widget = Qt.QComboBox()
- prefix_widget = Qt.QLineEdit()
- suffix_widget = Qt.QLineEdit()
control_l.addRow("model:", model_widget)
control_l.addRow("model index:", model_index_widget)
control_l.addRow("foreground role:", fg_widget)
diff --git a/lib/taurus/qt/qtgui/display/demo/taurusleddemo.py b/lib/taurus/qt/qtgui/display/demo/taurusleddemo.py
index 8c4e5aa..60cac40 100644
--- a/lib/taurus/qt/qtgui/display/demo/taurusleddemo.py
+++ b/lib/taurus/qt/qtgui/display/demo/taurusleddemo.py
@@ -44,7 +44,6 @@ def demo():
owns_app = app is None
if owns_app:
- import taurus.core.util.argparse
app = Application()
M = 2
diff --git a/lib/taurus/qt/qtgui/display/qfallback.py b/lib/taurus/qt/qtgui/display/qfallback.py
index 8f65595..2899692 100644
--- a/lib/taurus/qt/qtgui/display/qfallback.py
+++ b/lib/taurus/qt/qtgui/display/qfallback.py
@@ -48,8 +48,10 @@ class QFallBackWidget(Qt.QWidget):
"""A FallBack widget to be used when a real widget cannot be loaded for any
reason (example: a dependency library is not installed)"""
- def __init__(self, replaces="UnknownWidget", parent=None, *args, **kwargs):
+ def __init__(self, replaces=None, parent=None, *args, **kwargs):
Qt.QWidget.__init__(self, parent)
+ if replaces is None:
+ replaces = self.__class__.__name__
self.replaces = replaces
self.exc_info = exc_info = kwargs.get("exc_info")
layout = Qt.QVBoxLayout(self)
@@ -77,7 +79,7 @@ class QFallBackWidget(Qt.QWidget):
class TaurusFallBackWidget(QFallBackWidget, TaurusBaseWidget):
- def __init__(self, replaces="UnknownWidget", parent=None, *args, **kwargs):
+ def __init__(self, replaces=None, parent=None, *args, **kwargs):
self.call__init__(QFallBackWidget, replaces=replaces,
parent=parent, *args, **kwargs)
designMode = kwargs.get("designMode", False)
diff --git a/lib/taurus/qt/qtgui/display/qled.py b/lib/taurus/qt/qtgui/display/qled.py
index e745bbc..f0c8c05 100644
--- a/lib/taurus/qt/qtgui/display/qled.py
+++ b/lib/taurus/qt/qtgui/display/qled.py
@@ -50,6 +50,7 @@ class QLed(qpixmapwidget.QPixmapWidget):
DefaultLedColor = "green"
DefaultLedStatus = True
DefaultLedInverted = False
+ DefaultBlinkingInterval = 0
def __init__(self, parent = None, designMode=False):
self._ledStatus = self.DefaultLedStatus
@@ -57,6 +58,7 @@ class QLed(qpixmapwidget.QPixmapWidget):
self._ledPatternName = self.DefaultLedPattern
self._ledInverted = self.DefaultLedInverted
self._ledName = self.toLedName()
+ self._timer = None
qpixmapwidget.QPixmapWidget.__init__(self, parent)
self._refresh()
@@ -159,6 +161,10 @@ class QLed(qpixmapwidget.QPixmapWidget):
def resetLedStatus(self):
"""Resets the led status"""
self.setLedStatus(self.DefaultLedStatus)
+
+ def toggleLedStatus(self):
+ """toggles the current status of the led"""
+ self.setLedStatus(not self.getLedStatus())
def getLedInverted(self):
"""Returns if the led is inverted.
@@ -196,6 +202,36 @@ class QLed(qpixmapwidget.QPixmapWidget):
def resetLedColor(self):
"""Resets the led color"""
self.setLedColor(self.DefaultLedColor)
+
+ def setBlinkingInterval(self, interval):
+ """sets the blinking interval (the time between status switching).
+ Set to a nonpositive number for disabling blinking
+
+ :param interval: (int) the blinking interval in millisecs. Set to 0 for disabling blinking
+ """
+ if interval > 0:
+ if self._timer is None:
+ self._timer = Qt.QTimer(self)
+ self.connect(self._timer, Qt.SIGNAL('timeout()'), self.toggleLedStatus)
+ self._timer.start(interval)
+ else:
+ if self._timer is not None:
+ self._timer.stop()
+ self._timer = None
+
+ def getBlinkingInterval(self):
+ """returns the blinking interval
+
+ :return: (int) blinking interval or 0 if blinking is not enabled.
+ """
+ if self._timer is None:
+ return 0
+ else:
+ return self._timer.interval()
+
+ def resetBlinkingInterval(self):
+ """resets the blinking interval"""
+ self.setBlinkingInterval(self.__class__.DefaultBlinkingInterval)
#: This property holds the led status: False means OFF, True means ON
#:
@@ -238,6 +274,18 @@ class QLed(qpixmapwidget.QPixmapWidget):
ledPattern = Qt.pyqtProperty("QString", getLedPatternName, setLedPatternName,
resetLedPatternName, doc="led pattern name")
+ #: This property holds the blinking interval in millisecs. 0 means no blinking
+ #:
+ #: **Access functions:**
+ #:
+ #: * :meth:`QLed.getBlinkingInterval`
+ #: * :meth:`QLed.setBlinkingInterval`
+ #: * :meth:`QLed.resetBlinkingInterval`
+ blinkingInterval = Qt.pyqtProperty("int", getBlinkingInterval, setBlinkingInterval,
+ resetBlinkingInterval, doc="blinking interval")
+
+
+
class QLedOld(Qt.QLabel):
ledDirPattern = ":leds/images%(size)d"
@@ -345,6 +393,10 @@ def main():
led.ledColor = color
led.ledStatus = False
layout.addWidget(led, i, 1)
+ led = QLed()
+ led.ledColor = color
+ led.blinkingInterval = 500
+ layout.addWidget(led, i, 2)
w.show()
if owns_app:
diff --git a/lib/taurus/qt/qtgui/display/qlogo.py b/lib/taurus/qt/qtgui/display/qlogo.py
index 15a3ebd..7896093 100644
--- a/lib/taurus/qt/qtgui/display/qlogo.py
+++ b/lib/taurus/qt/qtgui/display/qlogo.py
@@ -37,10 +37,7 @@ from taurus.qt.qtgui.resource import getPixmap
class QLogo(Qt.QLabel):
def __init__(self, parent=None, designMode=False):
- try:
- self.__name = name.__name__
- except:
- self.__name = self.__class__.__name__
+ self.__name = self.__class__.__name__
Qt.QLabel.__init__(self, parent)
sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Policy(0),Qt.QSizePolicy.Policy(0))
sizePolicy.setHorizontalStretch(0)
diff --git a/lib/taurus/qt/qtgui/display/qpixmapwidget.py b/lib/taurus/qt/qtgui/display/qpixmapwidget.py
index 213fe81..dd51a2d 100644
--- a/lib/taurus/qt/qtgui/display/qpixmapwidget.py
+++ b/lib/taurus/qt/qtgui/display/qpixmapwidget.py
@@ -234,5 +234,3 @@ def main():
if __name__ == "__main__":
main()
-
-
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/display/qsevensegment.py b/lib/taurus/qt/qtgui/display/qsevensegment.py
index 19e34ec..d1508e9 100644
--- a/lib/taurus/qt/qtgui/display/qsevensegment.py
+++ b/lib/taurus/qt/qtgui/display/qsevensegment.py
@@ -31,9 +31,6 @@ __all__ = ['Q7SegDigit']
__docformat__ = 'restructuredtext'
-import os
-import math
-
from taurus.qt import Qt
POLY = Qt.QPolygonF
@@ -242,7 +239,7 @@ class Q7SegDigit(Qt.QWidget):
painter.drawPath(seg)
def __str__(self):
- ret, idx = '', self.__valueStrToLedIndex(self._value)
+ _, idx = '', self.__valueStrToLedIndex(self._value)
leds = self.Leds[idx]
# line 0
@@ -551,7 +548,6 @@ def main2():
a.exec_()
def main3():
- import sys
a = Qt.QApplication([])
dw = Q7SegDisplay()
#dw.setValue(int(sys.argv[1]))
@@ -560,7 +556,6 @@ def main3():
def main():
global digitWidget
- import sys
a = Qt.QApplication([])
panel = Qt.QWidget()
l = Qt.QFormLayout(panel)
diff --git a/lib/taurus/qt/qtgui/display/tauruslabel.py b/lib/taurus/qt/qtgui/display/tauruslabel.py
index 29a550c..7602f85 100644
--- a/lib/taurus/qt/qtgui/display/tauruslabel.py
+++ b/lib/taurus/qt/qtgui/display/tauruslabel.py
@@ -29,15 +29,13 @@ __all__ = ["TaurusLabel"]
__docformat__ = 'restructuredtext'
-import weakref
import operator
# shame of me for importing PyTango!
import PyTango
+from taurus.core import TaurusElementType, TaurusEventType
from taurus.qt import Qt
-import taurus.core.util
-from taurus.qt.qtgui.util import QT_ATTRIBUTE_QUALITY_PALETTE, QT_DEVICE_STATE_PALETTE
from taurus.qt.qtgui.base import TaurusBaseWidget
from taurus.qt.qtgui.base import TaurusBaseController
from taurus.qt.qtgui.base import TaurusScalarAttributeControllerHelper
@@ -50,8 +48,8 @@ _QT_PLUGIN_INFO = {
'icon' : ":/designer/label.png",
}
-TaurusModelType = taurus.core.TaurusElementType
-EventType = taurus.core.TaurusEventType
+TaurusModelType = TaurusElementType
+EventType = TaurusEventType
class TaurusLabelController(TaurusBaseController):
@@ -66,7 +64,7 @@ class TaurusLabelController(TaurusBaseController):
def _setStyle(self):
TaurusBaseController._setStyle(self)
label = self.label()
- label.setAlignment(Qt.Qt.AlignRight | Qt.Qt.AlignVCenter)
+ label.setAlignment(label.DefaultAlignment)
# if update as palette
if self.usePalette():
label.setFrameShape(Qt.QFrame.Box)
@@ -231,7 +229,8 @@ class TaurusLabel(Qt.QLabel, TaurusBaseWidget):
DefaultShowText = True
DefaultModelIndex = None
DefaultAutoTrim = True
-
+ DefaultAlignment = Qt.Qt.AlignRight | Qt.Qt.AlignVCenter
+
def __init__(self, parent=None, designMode=False):
self._prefix = self.DefaultPrefix
self._suffix = self.DefaultSuffix
@@ -254,12 +253,12 @@ class TaurusLabel(Qt.QLabel, TaurusBaseWidget):
self.controllerUpdate()
def _calculate_controller_class(self):
- map = _CONTROLLER_MAP
+ ctrl_map = _CONTROLLER_MAP
if self._designMode:
- map = _DESIGNER_CONTROLLER_MAP
+ ctrl_map = _DESIGNER_CONTROLLER_MAP
model_type = self.getModelType()
- ctrl_klass = map.get(model_type, TaurusLabelController)
+ ctrl_klass = ctrl_map.get(model_type, TaurusLabelController)
return ctrl_klass
def controller(self):
diff --git a/lib/taurus/qt/qtgui/display/tauruslcd.py b/lib/taurus/qt/qtgui/display/tauruslcd.py
index 0a0b9dd..491fdee 100644
--- a/lib/taurus/qt/qtgui/display/tauruslcd.py
+++ b/lib/taurus/qt/qtgui/display/tauruslcd.py
@@ -29,16 +29,13 @@ __all__ = ["TaurusLCD"]
__docformat__ = 'restructuredtext'
-import weakref
import operator
# shame of me for importing PyTango!
import PyTango
+from taurus.core import TaurusElementType, TaurusEventType
from taurus.qt import Qt
-
-import taurus.core.util
-from taurus.qt.qtgui.util import QT_ATTRIBUTE_QUALITY_PALETTE, QT_DEVICE_STATE_PALETTE
from taurus.qt.qtgui.base import TaurusBaseWidget
from taurus.qt.qtgui.base import TaurusBaseController
from taurus.qt.qtgui.base import TaurusScalarAttributeControllerHelper
@@ -51,8 +48,8 @@ _QT_PLUGIN_INFO = {
'icon' : ":/designer/lcdnumber.png",
}
-TaurusModelType = taurus.core.TaurusElementType
-EventType = taurus.core.TaurusEventType
+TaurusModelType = TaurusElementType
+EventType = TaurusEventType
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Controller classes for LCD
@@ -197,12 +194,12 @@ TaurusModelType.Configuration : TaurusLCDControllerConfigurationDesignMode,
def Controller(lcd):
- map = __CONTROLLER_MAP
+ ctrl_map = _CONTROLLER_MAP
if lcd._designMode:
- map = __DESIGNER_CONTROLLER_MAP
+ ctrl_map = _DESIGNER_CONTROLLER_MAP
model_type = lcd.getModelType()
- ctrl_klass = map.get(model_type, TaurusLCDController)
+ ctrl_klass = ctrl_map.get(model_type, TaurusLCDController)
return ctrl_klass(lcd)
@@ -229,12 +226,12 @@ class TaurusLCD(Qt.QLCDNumber, TaurusBaseWidget):
self.controller().update()
def _calculate_controller_class(self):
- map = _CONTROLLER_MAP
+ ctrl_map = _CONTROLLER_MAP
if self._designMode:
- map = _DESIGNER_CONTROLLER_MAP
+ ctrl_map = _DESIGNER_CONTROLLER_MAP
model_type = self.getModelType()
- ctrl_klass = map.get(model_type, TaurusLCDController)
+ ctrl_klass = ctrl_map.get(model_type, TaurusLCDController)
return ctrl_klass
def controller(self):
diff --git a/lib/taurus/qt/qtgui/display/tauruslcdvalue.py b/lib/taurus/qt/qtgui/display/tauruslcdvalue.py
index 7aa4f97..e674b34 100644
--- a/lib/taurus/qt/qtgui/display/tauruslcdvalue.py
+++ b/lib/taurus/qt/qtgui/display/tauruslcdvalue.py
@@ -30,11 +30,10 @@ __all__ = ["TaurusLCDValue"]
__docformat__ = 'restructuredtext'
from taurus.qt import Qt
-
-import taurus.core
-from taurus.qt.qtgui.util import QT_ATTRIBUTE_QUALITY_PALETTE, QT_DEVICE_STATE_PALETTE
+from taurus.qt.qtgui.util import QT_ATTRIBUTE_QUALITY_PALETTE
from taurus.qt.qtgui.base import TaurusBaseWidget
+
class TaurusLCDValue(Qt.QLCDNumber, TaurusBaseWidget):
"""
A LCD widget displaying a tango attribute value
@@ -143,18 +142,6 @@ class TaurusLCDValue(Qt.QLCDNumber, TaurusBaseWidget):
TaurusBaseWidget.resetShowQuality)
def main():
- import sys
-
- app = Qt.QApplication(sys.argv)
-
- panel = Qt.QMainWindow()
- lcd = TaurusLCDValue(panel)
- lcd.setModel(sys.argv[1])
- panel.setVisible(True)
-
- sys.exit(app.exec_())
-
-def main():
"""hello"""
import sys
import taurus.qt.qtgui.application
diff --git a/lib/taurus/qt/qtgui/display/taurusled.py b/lib/taurus/qt/qtgui/display/taurusled.py
index 5f8db59..d82f89c 100644
--- a/lib/taurus/qt/qtgui/display/taurusled.py
+++ b/lib/taurus/qt/qtgui/display/taurusled.py
@@ -42,7 +42,6 @@ State = PyTango.DevState
DataFormat = PyTango.AttrDataFormat
Quality = PyTango.AttrQuality
-import taurus.core.util
from taurus.qt.qtgui.base import TaurusBaseWidget
from qled import QLed
@@ -119,7 +118,7 @@ class _TaurusLedController(object):
key = None
try:
key = self.value()
- except Exception, e:
+ except Exception:
pass
ledMap = self.LedMap
if widget.fgRole == 'quality':
@@ -222,7 +221,6 @@ class TaurusLed(QLed, TaurusBaseWidget):
def _calculate_controller_class(self):
model = self.getModelObj()
- fgRole = self.fgRole
klass = _TaurusLedController
if self._designMode:
diff --git a/lib/taurus/qt/qtgui/display/taurusstateled.py b/lib/taurus/qt/qtgui/display/taurusstateled.py
index df07c16..604c9a7 100644
--- a/lib/taurus/qt/qtgui/display/taurusstateled.py
+++ b/lib/taurus/qt/qtgui/display/taurusstateled.py
@@ -33,11 +33,10 @@ __docformat__ = 'restructuredtext'
# ugly
import PyTango
+from taurus.core import TaurusEventType
from taurus.qt import Qt
-import taurus.core.util
-from taurus.qt.qtgui.util import QT_ATTRIBUTE_QUALITY_PALETTE, QT_DEVICE_STATE_PALETTE
from taurus.qt.qtgui.base import TaurusBaseWidget
-from qled import LedStatus, LedColor, LedSize
+from qled import LedStatus, LedColor
from qled import QLedOld as QLed
@@ -102,7 +101,7 @@ class TaurusStateLed(QLed, TaurusBaseWidget):
def handleEvent(self, evt_src, evt_type, evt_value):
- if evt_type == taurus.core.TaurusEventType.Error:
+ if evt_type == TaurusEventType.Error:
self._setProblemsBackground(True)
self.updateStyle()
return
@@ -148,11 +147,6 @@ class TaurusStateLed(QLed, TaurusBaseWidget):
@Qt.pyqtSignature("setBoolIndex(int)")
def setBoolIndex(self,i):
self._boolIndex = i
- try:
- getattr(BoolIndex,str(self._boolIndex))
- self.changeSize(self._boolIndex)
- except:
- pass
def resetBoolIndex(self):
self.setBoolIndex(0)
diff --git a/lib/taurus/qt/qtgui/display/taurusvaluelabel.py b/lib/taurus/qt/qtgui/display/taurusvaluelabel.py
index 2cc364a..b37f68b 100644
--- a/lib/taurus/qt/qtgui/display/taurusvaluelabel.py
+++ b/lib/taurus/qt/qtgui/display/taurusvaluelabel.py
@@ -179,7 +179,7 @@ class TaurusValueLabel(Qt.QLabel, TaurusBaseWidget):
ss = QT_ATTRIBUTE_QUALITY_PALETTE.qtStyleSheet(quality)
elif self.getShowState():
try: state = self.getModelObj().getParent().getState()
- except Exception,e:
+ except Exception:
state = None
ss = QT_DEVICE_STATE_PALETTE.qtStyleSheet(state)
elif self.getShowValueStateAsBackground():
@@ -328,12 +328,15 @@ class TaurusValueLabel(Qt.QLabel, TaurusBaseWidget):
class TaurusStateLabel(TaurusValueLabel):
-
+ '''
+ .. deprecated::
+ Use :class:`taurus.qt.qtgui.display.TaurusLabel` instead.
+ '''
def __init__(self, parent = None, designMode = False):
self.call__init__(TaurusValueLabel, parent=parent, designMode=designMode, background = 'value_state')
self.setAlignment(Qt.Qt.AlignCenter)
- def getFormatedToolTip(self, cache=True):
+ def getFormatedToolTip(self, cache=True, **notused):
""" The tooltip should refer to the device and not the state attribute.
That is why this method is being rewritten
"""
@@ -359,9 +362,10 @@ class TaurusStateLabel(TaurusValueLabel):
@classmethod
def getQtDesignerPluginInfo(cls):
- ret = TaurusValueLabel.getQtDesignerPluginInfo()
- ret['icon'] = ":/designer/state.png"
- return ret
+ return None
+# ret = TaurusValueLabel.getQtDesignerPluginInfo()
+# ret['icon'] = ":/designer/state.png"
+# return ret
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# QT properties
diff --git a/lib/taurus/qt/qtgui/extra_guiqwt/builder.py b/lib/taurus/qt/qtgui/extra_guiqwt/builder.py
index 37deeab..3943455 100644
--- a/lib/taurus/qt/qtgui/extra_guiqwt/builder.py
+++ b/lib/taurus/qt/qtgui/extra_guiqwt/builder.py
@@ -32,9 +32,10 @@ __docformat__ = 'restructuredtext'
import guiqwt.builder
from curve import TaurusCurveItem, TaurusTrendItem
-from image import TaurusImageItem, TaurusRGBImageItem
+from image import TaurusImageItem, TaurusRGBImageItem, TaurusEncodedImageItem, TaurusXYImageItem
from guiqwt.curve import CurveParam
-from guiqwt.image import ImageParam
+from guiqwt.image import ImageParam, XYImageItem
+from guiqwt.styles import XYImageParam
from guiqwt.config import _
from guiqwt.baseplot import BasePlot
import numpy
@@ -99,7 +100,7 @@ class TaurusPlotItemBuilder(guiqwt.builder.PlotItemBuilder):
yformat = kwargs.get('yformat','%.1f')
zformat = kwargs.get('zformat','%.1f')
forceRGB = kwargs.get('force_rgb', False)
-
+
assert isinstance(xdata, (tuple, list)) and len(xdata) == 2
assert isinstance(ydata, (tuple, list)) and len(ydata) == 2
assert filename is None
@@ -113,9 +114,9 @@ class TaurusPlotItemBuilder(guiqwt.builder.PlotItemBuilder):
else:
attr = taurus.Attribute(taurusmodel)
valueobj = attr.read()
- attrdata = getattr(valueobj, 'value', numpy.zeros((1,1)))
+ attrdata = getattr(valueobj, 'value', numpy.zeros((1,1)))
xmin, xmax, ymin, ymax = self.compute_bounds(attrdata, pixel_size)
-
+
self.set_image_param(param, title, alpha_mask, alpha, interpolation,
background=background_color,
colormap=colormap,
@@ -125,20 +126,65 @@ class TaurusPlotItemBuilder(guiqwt.builder.PlotItemBuilder):
if forceRGB:
image = TaurusRGBImageItem(param)
else:
- image = TaurusImageItem(param)
+ from taurus import Attribute
+ try:
+ from PyTango import DevEncoded #@todo: replace this (Tango-centric).
+ except:
+ DevEncoded = 28 #@todo: hardcoded fallback to be replaced when the data types are handled in Taurus
+ if Attribute(taurusmodel).read().type == DevEncoded:
+ image = TaurusEncodedImageItem(param)
+ else:
+ image = TaurusImageItem(param)
image.setModel(taurusmodel)
if eliminate_outliers is not None:
image.set_lut_range(lut_range_threshold(image, 256, eliminate_outliers))
return image
+ def xyimage(self, taurusmodel=None, **kwargs):
+
+ if taurusmodel is None:
+ return guiqwt.builder.PlotItemBuilder.xyimage(self, **kwargs)
+
+ title = kwargs.get('title', taurusmodel)
+ data = kwargs.get('data',None)
+ alpha_mask = kwargs.get('alpha_mask',None)
+ alpha = kwargs.get('alpha',None)
+ background_color = kwargs.get('background_color',None)
+ colormap = kwargs.get('colormap',None)
+ interpolation = kwargs.get('interpolation','linear')
+ eliminate_outliers = kwargs.get('eliminate_outliers',None)
+ xformat = kwargs.get('xformat','%.1f')
+ yformat = kwargs.get('yformat','%.1f')
+ zformat = kwargs.get('zformat','%.1f')
+ if data is None:
+ x=y=None
+ else:
+ x=numpy.arange(data.shape[0])
+ y=numpy.arange(data.shape[1])
+
+ data=numpy.ones((251,251))
+ x,y=numpy.arange(251)*333,numpy.arange(251)*1000
+
+ param = XYImageParam(title=_("Image"), icon='image.png')
+ self.set_image_param(param, title, alpha_mask, alpha, interpolation,
+ background=background_color, colormap=colormap,
+ xformat=xformat, yformat=yformat,
+ zformat=zformat)
+
+ image = TaurusXYImageItem(param)
+ image.setModel(taurusmodel)
+ if eliminate_outliers is not None:
+ image.set_lut_range(lut_range_threshold(image, 256,
+ eliminate_outliers))
+ return image
+
def rgbimage(self, taurusmodel=None, **kwargs):
if taurusmodel is None:
image = guiqwt.builder.PlotItemBuilder.rgbimage(self, **kwargs)
else:
image = self.image(taurusmodel=taurusmodel, force_rgb=True, **kwargs)
return image
-
def ttrend(self, model, taurusparam =None, title=u"",
color=None, linestyle=None, linewidth=None,
@@ -161,8 +207,6 @@ class TaurusPlotItemBuilder(guiqwt.builder.PlotItemBuilder):
item.setModel(model)
item.update_params()
return item
-
-
#"make" is an instance of the builder (this mimics the structure of guiqwt.builder.make)
make = TaurusPlotItemBuilder()
diff --git a/lib/taurus/qt/qtgui/extra_guiqwt/curvesmodel.py b/lib/taurus/qt/qtgui/extra_guiqwt/curvesmodel.py
index 393e21c..cb54333 100644
--- a/lib/taurus/qt/qtgui/extra_guiqwt/curvesmodel.py
+++ b/lib/taurus/qt/qtgui/extra_guiqwt/curvesmodel.py
@@ -31,8 +31,7 @@ __all__=['TaurusCurveItemTableModel','CurveItemConf', 'CurveItemConfDlg']
import copy
-from taurus.qt import Qt
-from PyQt4 import Qwt5
+from taurus.qt import Qt,Qwt5
import taurus
from taurus.core import TaurusException
from taurus.qt.qtcore.mimetypes import TAURUS_MODEL_LIST_MIME_TYPE, TAURUS_ATTR_MIME_TYPE
@@ -216,7 +215,7 @@ class TaurusCurveItemTableModel(Qt.QAbstractTableModel):
row = index.row()
curve = self.curves[row]
column = index.column()
- value = str(value.toString())
+ value = Qt.from_qvariant(value, str)
if column == X:
curve.taurusparam.xModel = value
curve.x.processSrc(value)
@@ -284,8 +283,10 @@ class TaurusCurveItemTableModel(Qt.QAbstractTableModel):
def mimeData(self, indexes):
mimedata = Qt.QAbstractTableModel.mimeData(self, indexes)
if len(indexes)==1:
-# mimedata.setData(TAURUS_ATTR_MIME_TYPE, str(self.data(indexes[0]).toString()))
- mimedata.setText(self.data(indexes[0],role=SRC_ROLE).toString())
+# data = Qt.from_qvariant(self.data(indexes[0], str)
+# mimedata.setData(TAURUS_ATTR_MIME_TYPE, data)
+ data = Qt.from_qvariant(self.data(indexes[0], role=SRC_ROLE), str)
+ mimedata.setText(data)
return mimedata
#mimedata.setData()
@@ -437,4 +438,4 @@ class CurveItemConfDlg(Qt.QWidget):
#
#if __name__ == "__main__":
# import sys
-# test2()
\ No newline at end of file
+# test2()
diff --git a/lib/taurus/qt/qtgui/extra_guiqwt/image.py b/lib/taurus/qt/qtgui/extra_guiqwt/image.py
index 9cdfe9f..9fa9bef 100644
--- a/lib/taurus/qt/qtgui/extra_guiqwt/image.py
+++ b/lib/taurus/qt/qtgui/extra_guiqwt/image.py
@@ -26,7 +26,8 @@
"""
Extension of :mod:`guiqwt.image`
"""
-__all__=["TaurusImageItem","TaurusRGBImageItem","TaurusTrend2DItem","TaurusTrend2DScanItem"]
+__all__=["TaurusImageItem","TaurusRGBImageItem","TaurusTrend2DItem",
+ "TaurusTrend2DScanItem","TaurusEncodedImageItem"]
from taurus.qt import Qt
from taurus.qt.qtgui.base import TaurusBaseComponent
@@ -71,13 +72,42 @@ class TaurusBaseImageItem(TaurusBaseComponent):
p.update_colormap_axis(self)
p.replot()
-
class TaurusImageItem(ImageItem, TaurusBaseImageItem):
'''A ImageItem that gets its data from a taurus attribute'''
def __init__(self, param=None):
ImageItem.__init__(self, numpy.zeros((1,1)), param=param)
TaurusBaseImageItem.__init__(self, self.__class__.__name__)
+class TaurusEncodedImageItem(TaurusImageItem):
+ '''A ImageItem that gets its data from a DevEncoded attribute'''
+ def __init__(self, param=None):
+ TaurusImageItem.__init__(self,param=param)
+
+ def setModel(self, model):
+ #do the standard stuff
+ TaurusBaseComponent.setModel(self, model)
+ #... and fire a fake event for initialization
+ try:
+ format,value = self.codec.decode(self.getModelObj().read())
+ self.fireEvent(self, taurus.core.TaurusEventType.Change, value)
+ except:
+ pass
+
+ def set_data(self, data, lut_range=None, **kwargs):
+ '''reimplementation to decode data before passing it to
+ TaurusImageItem implementation'''
+ if type(data) == tuple:
+ from taurus.core.util import CodecFactory
+ codec = CodecFactory().getCodec(data[0])
+ format,decoded_data = codec.decode(data)
+ TaurusImageItem.set_data(self, decoded_data, lut_range=lut_range)
+
+class TaurusXYImageItem(XYImageItem, TaurusBaseImageItem):
+ '''A XYImageItem that gets its data from a taurus attribute'''
+ def __init__(self, param=None):
+ XYImageItem.__init__(self, numpy.arange(2), numpy.arange(2), numpy.zeros((2,2)), param=param)
+ TaurusBaseImageItem.__init__(self, self.__class__.__name__)
+
class TaurusRGBImageItem(RGBImageItem, TaurusBaseImageItem):
'''A RGBImageItem that gets its data from a taurus attribute'''
@@ -417,12 +447,14 @@ def test1():
#define a taurus image
model1 = 'sys/tg_test/1/short_image_ro'
- taurusimage = make.image(taurusmodel= model1)
- taurusrgbimage = make.rgbimage(taurusmodel= 'eval://array([[[ 222, 0, 0], [0, 222, 0]], [[0, 0, 222], [222, 222, 222]]])')
+ #taurusimage = make.image(taurusmodel= model1)
+ #taurusrgbimage = make.rgbimage(taurusmodel= 'eval://array([[[ 222, 0, 0], [0, 222, 0]], [[0, 0, 222], [222, 222, 222]]])')
+ taurusxyimage= make.xyimage(taurusmodel= model1)
+ taurusxyimage.set_xy(numpy.arange(251)*10,numpy.arange(251)*100 )
#define normal image (guiqwt standard)
data = numpy.random.rand(100,100)
- image = make.image(data=data)
+ #image = make.image(data=data)
#create a dialog with a plot and add the images
win = ImageDialog(edit=False, toolbar=True, wintitle="Taurus Cross sections test",
@@ -430,18 +462,19 @@ def test1():
from taurus.qt.qtgui.extra_guiqwt.tools import TaurusImageChooserTool
win.add_tool(TaurusImageChooserTool)
plot = win.get_plot()
- plot.add_item(taurusimage)
- plot.add_item(image)
- plot.add_item(taurusrgbimage)
+# plot.add_item(taurusimage)
+ plot.add_item(taurusxyimage)
+# plot.add_item(image)
+# plot.add_item(taurusrgbimage)
+
# win.get_itemlist_panel().show()
#IMPORTANT: connect the cross section plots to the taurusimage so that they are updated when the taurus data changes
- win.connect(taurusimage.getSignaller(), Qt.SIGNAL("dataChanged"), win.update_cross_sections)
+ #win.connect(taurusimage.getSignaller(), Qt.SIGNAL("dataChanged"), win.update_cross_sections)
win.exec_()
-
if __name__ == "__main__":
- #test1()
- taurusImageMain()
+ test1()
+ #taurusImageMain()
diff --git a/lib/taurus/qt/qtgui/extra_guiqwt/tools.py b/lib/taurus/qt/qtgui/extra_guiqwt/tools.py
index 79ef1f3..a6bb23b 100644
--- a/lib/taurus/qt/qtgui/extra_guiqwt/tools.py
+++ b/lib/taurus/qt/qtgui/extra_guiqwt/tools.py
@@ -31,7 +31,6 @@ __docformat__ = 'restructuredtext'
from taurus.qt import Qt
-from PyQt4 import Qwt5
from guiqwt.tools import CommandTool, ToggleTool, DefaultToolbarID, QActionGroup, add_actions
from guiqwt.signals import SIG_ITEMS_CHANGED
@@ -39,7 +38,7 @@ from taurus.qt.qtgui.resource import getIcon
from taurus.qt.qtgui.extra_guiqwt.builder import make
from taurus.qt.qtgui.extra_guiqwt.curve import TaurusCurveItem,TaurusTrendItem
from taurus.qt.qtgui.extra_guiqwt.image import TaurusTrend2DItem
-from taurus.qt.qtgui.extra_guiqwt.curvesmodel import CurveItemConfDlg, CurveItemConf
+from taurus.qt.qtgui.extra_guiqwt.curvesmodel import CurveItemConfDlg
from taurus.qt.qtgui.panel import TaurusModelChooser
from taurus.core import TaurusElementType
from taurus.qt.qtgui.plot import DateTimeScaleEngine
@@ -119,38 +118,35 @@ class TimeAxisTool(CommandTool):
"""Create and return menu for the tool's action"""
menu = Qt.QMenu()
group = QActionGroup(manager.get_main())
- y_x = manager.create_action("y(x)", toggled=self.set_scale_y_x)
- y_t = manager.create_action("y(t)", toggled=self.set_scale_y_t)
- t_x = manager.create_action("t(x)", toggled=self.set_scale_t_x)
- t_t = manager.create_action("t(t)", toggled=self.set_scale_t_t)
- self.scale_menu = {(False, False): y_x, (False, True): y_t,
- (True, False): t_x, (True, True): t_t}
- for obj in (group, menu):
- add_actions(obj, (y_x, y_t, t_x, t_t))
+ y_x = manager.create_action("y(x)", triggered=self.set_scale_y_x)
+ y_t = manager.create_action("y(t)", triggered=self.set_scale_y_t)
+ t_x = manager.create_action("t(x)", triggered=self.set_scale_t_x)
+ t_t = manager.create_action("t(t)", triggered=self.set_scale_t_t)
+ self.scale_menu = {(False, False): y_x, (True, False): y_t,
+ (False, True): t_x, (True, True): t_t}
+ for action in (y_x, y_t, t_x, t_t):#I need to do this because the manager.create_action does not accept the "checkable" keyword.
+ action.setCheckable(True) #see http://code.google.com/p/guiqwt/issues/detail?id=41
+ for obj in (group, menu):
+ add_actions(obj, (y_x, y_t, t_x, t_t))
+
return menu
- def _getAxesUseTime(self, item):
+ def _getAxesUseTime(self, plot):
"""
- Returns a tuple (xIsTime, yIsTime) where xIsTime is True if the item's x
- axis uses a TimeScale. yIsTime is True if the item's y axis uses a Time
+ Returns a tuple (xIsTime, yIsTime) where xIsTime is True if the plot's active x
+ axis uses a TimeScale. yIsTime is True if plot's active y axis uses a Time
Scale. Otherwise they are False.
"""
- plot = item.plot()
if plot is None:
return (False,False)
- xEngine = plot.axisScaleEngine(item.xAxis())
- yEngine = plot.axisScaleEngine(item.yAxis())
+ xaxis,yaxis = plot.get_active_axes()
+ xEngine = plot.axisScaleEngine(xaxis)
+ yEngine = plot.axisScaleEngine(yaxis)
return isinstance(xEngine, DateTimeScaleEngine), isinstance(yEngine, DateTimeScaleEngine)
def update_status(self, plot):
- item = plot.get_active_item()
- active_scale = (False, False)
- if item is not None:
- active_scale = self._getAxesUseTime(item)
+ active_scale = self._getAxesUseTime(plot)
for scale_type, scale_action in self.scale_menu.items():
- if item is None:
- scale_action.setEnabled(True)
- else:
scale_action.setEnabled(True)
if active_scale == scale_type:
scale_action.setChecked(True)
@@ -167,7 +163,6 @@ class TimeAxisTool(CommandTool):
DateTimeScaleEngine.disableInAxis(plot, axis)
plot.replot()
-
def set_scale_y_x(self, checked):
if not checked:
return
@@ -244,4 +239,4 @@ def test_timeAxis():
if __name__ == "__main__":
- test_timeAxis()
\ No newline at end of file
+ test_timeAxis()
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/__init__.py b/lib/taurus/qt/qtgui/extra_macroexecutor/__init__.py
index 5fdfc81..4d30941 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/__init__.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/__init__.py
@@ -31,4 +31,4 @@ from sequenceeditor import TaurusSequencerWidget, TaurusSequencer
from common import TaurusMacroConfigurationDialog
from macrodescriptionviewer import TaurusMacroDescriptionViewer
from dooroutput import DoorOutput, DoorDebug, DoorResult
-from macrobutton import MacroButton
+from macrobutton import MacroButton, MacroButtonAbortDoor
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/common.py b/lib/taurus/qt/qtgui/extra_macroexecutor/common.py
index cb446ae..51fbcc2 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/common.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/common.py
@@ -27,7 +27,7 @@ import PyTango
import taurus
from taurus.qt.qtgui.base import TaurusBaseWidget
-from taurus.core import TaurusEventType, TaurusDevice
+from taurus.core import TaurusEventType
from taurus.qt import Qt
from taurus.qt.qtgui.input import TaurusAttrListComboBox
from taurus.qt.qtgui.container import TaurusMainWindow
@@ -108,7 +108,7 @@ class MacroComboBox(Qt.QComboBox, TaurusBaseWidget):
ms = self.getModelObj()
if ms == None: return
macros = ms.getElementsWithInterface('MacroCode')
- macroNames = macros.keys()
+ macroNames = [macro.name for macro in macros.values()]
macroNames.sort()
macroNames.insert(0, '') #adding blank item
self.addItems(macroNames)
@@ -337,4 +337,4 @@ if __name__ == "__main__":
ms_name = args[0]
test_macrocombobox(ms_name)
sys.exit(app.exec_())
-
\ No newline at end of file
+
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/lib/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py
index d0bcdca..d41d916 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py
@@ -92,6 +92,7 @@ class MacroButton(TaurusWidget):
self.ui.progress.setVisible(visible)
def setModel(self, model):
+ TaurusWidget.setModel(self, model)
if self.door is not None:
self.disconnect(self.door, Qt.SIGNAL('macroStatusUpdated'), self.statusUpdated)
self.disconnect(self.door, Qt.SIGNAL('resultUpdated'), self.resultUpdated)
@@ -176,6 +177,9 @@ class MacroButton(TaurusWidget):
self.macro_args[index] = str(value)
+ def updateMacroArgumentFromSignal(self, index, obj, signal):
+ self.connect(obj, signal, functools.partial(self.updateMacroArgument,index))
+
def button_clicked(self):
if self.ui.button.isChecked():
self.runMacro()
@@ -187,9 +191,15 @@ class MacroButton(TaurusWidget):
if self.door is None:
return
- macro_cmd = self.macro_name + ' ' + ' '.join(self.macro_args)
+ # Thanks to gjover for the hint... :-D
+ #macro_cmd = self.macro_name + ' ' + ' '.join(self.macro_args)
+ macro_cmd_xml = '<macro name="%s">\n' % self.macro_name
+ for arg in self.macro_args:
+ macro_cmd_xml += '<param value="%s"/>\n' % arg
+ macro_cmd_xml += '</macro>'
try:
- self.door.runMacro(macro_cmd)
+ #self.door.runMacro(macro_cmd)
+ self.door.runMacro(macro_cmd_xml)
except Exception,e:
self.ui.button.setChecked(False)
raise e
@@ -213,6 +223,39 @@ class MacroButton(TaurusWidget):
self.ui.button.setChecked(True)
self.door.ResumeMacro()
+
+
+class MacroButtonAbortDoor(TaurusWidget):
+ def __init__(self, parent=None, designMode=False):
+ TaurusWidget.__init__(self, parent, designMode)
+ self.setLayout(Qt.QGridLayout())
+ self.layout().setMargin(0)
+ self.layout().setSpacing(0)
+
+ self.btn_abort = Qt.QPushButton('Abort')
+ sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ self.btn_abort.setSizePolicy(sizePolicy)
+
+ self.door = None
+ self.btn_abort.setToolTip('Abort Macro')
+
+ self.layout().addWidget(self.btn_abort, 0, 0)
+ self.connect(self.btn_abort, Qt.SIGNAL('clicked()'), self.abort)
+
+ def setModel(self, model):
+ TaurusWidget.setModel(self, model)
+ try: self.door = taurus.Device(model)
+ except: self.door = None
+
+ @ProtectTaurusMessageBox(msg='An error occurred trying to abort the macro.')
+ def abort(self):
+ if self.door is not None:
+ self.door.stopMacro()
+
+
+
if __name__ == '__main__':
import sys
app = Qt.QApplication(sys.argv)
@@ -270,16 +313,21 @@ if __name__ == '__main__':
show_progress.setChecked(True)
w.layout().addWidget(show_progress, 5, 0)
+ mb_abort = MacroButtonAbortDoor()
+ mb_abort.setModel('door/gc/1')
+ w.layout().addWidget(mb_abort, 5, 1)
+
# Change macro name
Qt.QObject.connect(macro_name, Qt.SIGNAL('textChanged(QString)'), mb.setMacroName)
Qt.QObject.connect(macro_name, Qt.SIGNAL('textChanged(QString)'), mb.setButtonText)
# Change Nth macro argument
- Qt.QObject.connect(arg0, Qt.SIGNAL('textChanged(QString)'), functools.partial(mb.updateMacroArgument,0))
- Qt.QObject.connect(arg1, Qt.SIGNAL('textChanged(QString)'), functools.partial(mb.updateMacroArgument,1))
- Qt.QObject.connect(arg2, Qt.SIGNAL('textChanged(QString)'), functools.partial(mb.updateMacroArgument,2))
- Qt.QObject.connect(arg3, Qt.SIGNAL('textChanged(QString)'), functools.partial(mb.updateMacroArgument,3))
- Qt.QObject.connect(arg4, Qt.SIGNAL('textChanged(QString)'), functools.partial(mb.updateMacroArgument,4))
+ mb.updateMacroArgumentFromSignal(0, arg0, Qt.SIGNAL('textChanged(QString)'))
+ mb.updateMacroArgumentFromSignal(1, arg1, Qt.SIGNAL('textChanged(QString)'))
+ mb.updateMacroArgumentFromSignal(2, arg2, Qt.SIGNAL('textChanged(QString)'))
+ mb.updateMacroArgumentFromSignal(3, arg3, Qt.SIGNAL('textChanged(QString)'))
+ mb.updateMacroArgumentFromSignal(4, arg4, Qt.SIGNAL('textChanged(QString)'))
+
def update_result(result):
result_label.setText(str(result))
@@ -303,12 +351,9 @@ if __name__ == '__main__':
macro_name.setText('ascan')
arg0.setText('gcdmot1')
arg1.setText('1')
- arg2.setText('5')
- arg3.setText('3')
+ arg2.setText('10')
+ arg3.setText('5')
arg4.setText('0.1')
- #macro_name.setText('twice')
- #arg0.setText('2')
-
w.show()
sys.exit(app.exec_())
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/lib/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
index ee112e0..8acf366 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
@@ -234,11 +234,15 @@ class SpockCommandWidget(Qt.QLineEdit, TaurusBaseContainer):
self.model().setData(self.currentIndex, Qt.QVariant(propValue))
except Exception as e:
self.model().setData(self.currentIndex, Qt.QVariant('None'))
- message = "<b>"+ix.sibling(ix.row(),0).data().toString()+ "</b> "+e[0]
+ txt = Qt.from_qvariant(ix.sibling(ix.row(),0).data(), str)
+ message = "<b>" + txt + "</b> " + e[0]
problems.append(message)
except IndexError:
- problems.append("<b>"+ix.sibling(ix.row(),0).data().toString()+ "</b> is missing!")
- if ix.data().toString() != Qt.QString('None'):
+ txt = Qt.from_qvariant(ix.sibling(ix.row(),0).data(), str)
+ problems.append("<b>" + txt + "</b> is missing!")
+
+ data = Qt.from_qvariant(ix.data(), str)
+ if data != 'None':
self.model().setData(self.currentIndex, Qt.QVariant('None'))
counter+=1
ix = self.getIndex()
@@ -324,10 +328,11 @@ class SpockCommandWidget(Qt.QLineEdit, TaurusBaseContainer):
if not self.disableEditMode and self.disableSpockCommandUpdate:
self.validateAllExpresion()
else:
- txt = self.text().split(' ', Qt.QString.SkipEmptyParts)
- if txt.count() == 0: return
+ txt_parts = str(self.text()).split()
+ if len(txt_parts) == 0:
+ return
try:
- if self.validateMacro(txt[0]):
+ if self.validateMacro(txt_parts[0]):
self.validateAllExpresion()
except:
self.setToolTip("Read Mode")
@@ -477,9 +482,9 @@ class SpockCommandWidget(Qt.QLineEdit, TaurusBaseContainer):
type = "Macro"
if type == "Float":
- value = current.toDouble()[0]+0.1
+ value = Qt.from_qvariant(current, float) + 0.1
elif type == "Integer":
- value = current.toInt()[0]+1
+ value = Qt.from_qvariant(current, int) + 1
elif type == "Boolean":
value = True
else:
@@ -504,9 +509,9 @@ class SpockCommandWidget(Qt.QLineEdit, TaurusBaseContainer):
type = "Macro"
if type == "Float":
- value = current.toDouble()[0]-0.1
+ value = Qt.from_qvariant(current, float) - 0.1
elif type == "Integer":
- value = current.toInt()[0]-1
+ value = Qt.from_qvariant(current, int) - 1
elif type == "Boolean":
value = True
else:
@@ -525,7 +530,7 @@ class SpockCommandWidget(Qt.QLineEdit, TaurusBaseContainer):
#I had to make the macroname lowered as macros in comboBox (with macros), has names with all letter low.
#Because of that sometimes it was not loading macros in MacroEditor
#TO FIX
- self.emit(Qt.SIGNAL("spockComboBox"), macroName.toLower())
+ self.emit(Qt.SIGNAL("spockComboBox"), str(macroName).lower())
def measureSelection(self, position):
s = self.text()+" "
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py b/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py
index bb3ec38..e9d396d 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py
@@ -219,7 +219,7 @@ class ExtraColumnsDelegate(Qt.QItemDelegate):
def setEditorData(self, editor, index):
if index.column() == 2:
- text = index.model().data(index, Qt.Qt.DisplayRole).toString()
+ text = Qt.from_qvariant(index.model().data(index, Qt.Qt.DisplayRole), str)
editor.setCurrentText(text)
else:
Qt.QItemDelegate.setEditorData(self, editor, index)
@@ -241,7 +241,7 @@ class ExtraColumnsDelegate(Qt.QItemDelegate):
def sizeHint(self, option, index):
if index.column() == 0:
fm = option.fontMetrics
- text = index.model().data(index,Qt.Qt.DisplayRole).toString()
+ text = Qt.from_qvariant(index.model().data(index,Qt.Qt.DisplayRole), str)
document = Qt.QTextDocument()
document.setDefaultFont(option.font)
document.setHtml(text)
@@ -320,10 +320,10 @@ class ExtraColumnsModel(Qt.QAbstractTableModel):
if index.isValid() and (0 <= index.row() < self.rowCount()):
row = index.row()
column = index.column()
- value = unicode(value.toString())
- if column == 0: self.__columns[row]['label'] = str(value)
- elif column == 1: self.__columns[row]['model'] = str(value)
- elif column == 2: self.__columns[row]['instrument'] = str(value)
+ value = Qt.from_qvariant(value, str)
+ if column == 0: self.__columns[row]['label'] = value
+ elif column == 1: self.__columns[row]['model'] = value
+ elif column == 2: self.__columns[row]['instrument'] = value
self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),index, index)
return True
return False
@@ -370,4 +370,4 @@ if __name__=="__main__":
editor.setMacroNode(macroNode)
editor.show()
- sys.exit(app.exec_())
\ No newline at end of file
+ sys.exit(app.exec_())
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/delegate.py b/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/delegate.py
index bcde6b6..cf889c8 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/delegate.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/delegate.py
@@ -64,7 +64,7 @@ class ParamEditorDelegate(Qt.QStyledItemDelegate):
def setEditorData(self, editor, index):
if index.column() == 1:
- text = index.model().data(index, Qt.Qt.DisplayRole).toString()
+ text = Qt.from_qvariant(index.model().data(index, Qt.Qt.DisplayRole), str)
if text == "None" or text == "":
Qt.QStyledItemDelegate.setEditorData(self, editor, index)
else:
@@ -93,7 +93,7 @@ class ParamEditorDelegate(Qt.QStyledItemDelegate):
def sizeHint(self, option, index):
if index.column() == 0:
fm = option.fontMetrics
- text = index.model().data(index,Qt.Qt.DisplayRole).toString()
+ text = Qt.from_qvariant(index.model().data(index,Qt.Qt.DisplayRole), str)
document = Qt.QTextDocument()
document.setDefaultFont(option.font)
document.setHtml(text)
@@ -109,4 +109,4 @@ class ParamEditorDelegate(Qt.QStyledItemDelegate):
# editor.destroy()
else:
size = Qt.QStyledItemDelegate.sizeHint(self, option, index)
- return size
\ No newline at end of file
+ return size
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py b/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py
index 660a994..3fb375c 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py
@@ -3,28 +3,28 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
"""
-model.py:
+model.py:
"""
from taurus.qt import Qt
from taurus.core.util import etree
@@ -33,46 +33,46 @@ from taurus.qt.qtgui.extra_macroexecutor import globals
class ParamEditorModel(Qt.QAbstractItemModel):
-
+
def __init__(self, parent=None):
Qt.QAbstractItemModel.__init__(self, parent)
self.columns = 2
self.setRoot()
- self.headers = ["Parameter","Value"]
-
+ self.headers = ["Parameter","Value"]
+
def root(self):
- return self._root
-
+ return self._root
+
def setRoot(self, node=None):
if node == None: node = macro.MacroNode()
self._root = node
self.reset()
-
+
def flags(self, index):
- if index.column() == 0:
+ if index.column() == 0:
return Qt.Qt.ItemIsEnabled | Qt.Qt.ItemIsSelectable
-
+
node = self.nodeFromIndex(index)
-
- if (index.column() == 1 and
- isinstance(node, macro.SingleParamNode) and
+
+ if (index.column() == 1 and
+ isinstance(node, macro.SingleParamNode) and
not node.type() in globals.EDITOR_NONEDITABLE_PARAMS):
return Qt.Qt.ItemIsEnabled | Qt.Qt.ItemIsEditable
return Qt.Qt.ItemIsEnabled
-
+
def _insertRow(self, parentIndex, node=None, row=-1):
parentNode = self.nodeFromIndex(parentIndex)
-
+
if row == -1: row = len(parentNode)
-
+
if node == None: node = parentNode.newRepeat()
-
+
self.beginInsertRows(parentIndex, row, row)
row = parentNode.insertChild(node, row)
self.endInsertRows()
-
+
return self.index(row, 0, parentIndex)
-
+
def _removeRow(self, index):
"""This method is used remove macro (pased via index)"""
node = self.nodeFromIndex(index)
@@ -82,7 +82,7 @@ class ParamEditorModel(Qt.QAbstractItemModel):
self.beginRemoveRows(parentIndex, row, row)
parentNode.removeChild(node)
self.endRemoveRows()
-
+
def _upRow(self, index):
node = self.nodeFromIndex(index)
parentIndex = index.parent()
@@ -92,7 +92,7 @@ class ParamEditorModel(Qt.QAbstractItemModel):
newIndex = self._insertRow(parentIndex, node, row - 1)
parentNode.arrangeIndexes()
return newIndex
-
+
def _downRow(self, index):
node = self.nodeFromIndex(index)
parentIndex = index.parent()
@@ -103,13 +103,13 @@ class ParamEditorModel(Qt.QAbstractItemModel):
parentNode.arrangeIndexes()
return newIndex
-
+
def addRepeat(self, index, callReset=True):
paramRepeatNode = self.nodeFromIndex(index)
paramRepeatNode.addRepeat()
if callReset:
self.reset()
-
+
def delRepeat(self, index, callReset=True):
branchIndex = self.parent(index)
branch = self.nodeFromIndex(branchIndex)
@@ -117,7 +117,7 @@ class ParamEditorModel(Qt.QAbstractItemModel):
branch.removeChild(child)
if callReset:
self.reset()
-
+
def upRepeat(self, index, callReset=True):
branchIndex = self.parent(index)
branch = self.nodeFromIndex(branchIndex)
@@ -125,7 +125,7 @@ class ParamEditorModel(Qt.QAbstractItemModel):
branch.upChild(child)
if callReset:
self.reset()
-
+
def downRepeat(self, index, callReset=True):
branchIndex = self.parent(index)
branch = self.nodeFromIndex(branchIndex)
@@ -133,44 +133,44 @@ class ParamEditorModel(Qt.QAbstractItemModel):
branch.downChild(child)
if callReset:
self.reset()
-
+
def rowCount(self, index):
node = self.nodeFromIndex(index)
if node is None or isinstance(node, macro.SingleParamNode):
return 0
return len(node)
-
+
def columnCount(self, parent):
return self.columns
-
+
def data(self, index, role):
if not index.isValid() or not (0 <= index.row() < self.rowCount(index.parent())):
return Qt.QVariant()
-
+
if role == Qt.Qt.DisplayRole:
node = self.nodeFromIndex(index)
if index.column() == 0:
- return Qt.QVariant(node.name())
+ return Qt.QVariant(node.name())
elif index.column() == 1:
return Qt.QVariant(node.value())
-
+
return Qt.QVariant()
-
+
def setData (self, index, value, role=Qt.Qt.EditRole):
node = self.nodeFromIndex(index)
# if index.isValid() and 0 <= index.row() < len(node.parent()):
if index.column() == 1:
- node.setValue(str(value.toString()))
+ node.setValue(Qt.from_qvariant(value, str))
self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index, index)
return True
return False
-
+
def headerData(self, section, orientation, role):
if orientation == Qt.Qt.Horizontal and role == Qt.Qt.DisplayRole:
return Qt.QVariant(self.headers[section])
return Qt.QVariant()
-
+
def index(self, row, column, parent):
if not parent.isValid():
parentNode = self.root();
@@ -182,41 +182,41 @@ class ParamEditorModel(Qt.QAbstractItemModel):
else:
return self.createIndex(row, column, childNode);
-
+
def parent(self, child):
node = self.nodeFromIndex(child)
if node is None:
return Qt.QModelIndex()
parent = node.parent()
if parent is None or isinstance(parent, macro.SequenceNode):
- return Qt.QModelIndex()
+ return Qt.QModelIndex()
grandparent = parent.parent()
if grandparent is None:
return Qt.QModelIndex()
row = grandparent.rowOfChild(parent)
return self.createIndex(row, 0, parent)
-
+
def nodeFromIndex(self, index):
if index.isValid():
return index.internalPointer()
else:
return self.root()
-
+
def toSpockCommand(self):
"""
Converts root obj (MacroNode) to string representing spock command and returns it.
-
+
:return: (etree.Element)
"""
-
+
return self.root().toSpockCommand()
-
+
def toXmlString(self):
"""
Converts root obj (MacroNode) to xml string and returns it.
-
+
:return: (etree.Element)
"""
-
+
xmlElement = self.root().toXml()
return etree.tostring(xmlElement)
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/scanplotter.py b/lib/taurus/qt/qtgui/extra_macroexecutor/scanplotter.py
index d3bff26..d644f97 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/scanplotter.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/scanplotter.py
@@ -35,13 +35,11 @@ scanplotter.py:
import taurus
import taurus.core
-from taurus.qt import QtGui, QtCore
+from taurus.qt import QtGui, Qwt5
from taurus.qt.Qt import *
-from PyQt4 import Qwt5
from taurus.qt.qtgui.plot import TaurusTrend
-from taurus.core import TaurusManager
from taurus.core.util import dictFromSequence, CaselessDict, eventfilters
class ScanPlotter(TaurusTrend):
@@ -159,4 +157,4 @@ if __name__ == "__main__":
form._doorName= sys.argv[2]
form.populatePlotables(sys.argv[3:])
form.show()
- sys.exit(app.exec_())
\ No newline at end of file
+ sys.exit(app.exec_())
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py b/lib/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py
index b5b8d7c..16d3378 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py
@@ -101,7 +101,7 @@ class MacroParametersProxyDelegate(Qt.QItemDelegate):
def setEditorData(self, editor, index):
if index.column() == 1:
- text = index.model().data(index, Qt.Qt.DisplayRole).toString()
+ text = Qt.from_qvariant(index.model().data(index, Qt.Qt.DisplayRole), str)
if text == "None" or text == "":
pass
else:
@@ -132,7 +132,7 @@ class MacroParametersProxyDelegate(Qt.QItemDelegate):
def sizeHint(self, option, index):
if index.column() == 0:
fm = option.fontMetrics
- text = index.model().data(index,Qt.Qt.DisplayRole).toString()
+ text = Qt.from_qvariant(index.model().data(index,Qt.Qt.DisplayRole), str)
document = Qt.QTextDocument()
document.setDefaultFont(option.font)
document.setHtml(text)
@@ -150,4 +150,4 @@ class MacroParametersProxyDelegate(Qt.QItemDelegate):
size = Qt.QItemDelegate.sizeHint(self, option, index)
return size
-
\ No newline at end of file
+
diff --git a/lib/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py b/lib/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py
index e5d8620..326acd6 100644
--- a/lib/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py
+++ b/lib/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py
@@ -173,7 +173,7 @@ class MacroSequenceTreeModel(Qt.QAbstractItemModel):
node = self.nodeFromIndex(index)
if index.column() == 1:
if isinstance(node, macro.SingleParamNode):
- node.setValue(str(value.toString()))
+ node.setValue(Qt.from_qvariant(value, str))
self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index, index)
while True:
index = index.parent()
@@ -182,12 +182,11 @@ class MacroSequenceTreeModel(Qt.QAbstractItemModel):
self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index, index.sibling(index.row(), self.columnCount(index)))
break
elif index.column() == 2:
- progress,ok = value.toDouble()
- if not ok: progress = 0
+ progress = Qt.from_qvariant(value, float)
node.setProgress(progress)
self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index, index)
elif index.column() == 3:
- node.setPause(value.toBool())
+ node.setPause(Qt.from_qvariant(value, bool))
self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index, index)
return True
@@ -404,4 +403,4 @@ class MacroParametersProxyModel(Qt.QSortFilterProxyModel):
return True
-
\ No newline at end of file
+
diff --git a/lib/taurus/qt/qtgui/extra_pool/poolchannel.py b/lib/taurus/qt/qtgui/extra_pool/poolchannel.py
index d8b32a5..b745865 100644
--- a/lib/taurus/qt/qtgui/extra_pool/poolchannel.py
+++ b/lib/taurus/qt/qtgui/extra_pool/poolchannel.py
@@ -65,7 +65,9 @@ class PoolChannelTV(TaurusValue):
return _ParentDevButton
def setModel(self,model):
- TaurusValue.setModel(self, "%s/value"%model) #@todo: change this (it assumes tango naming!)
+ if model is not None:
+ model = "%s/value"%model #@todo: change this (it assumes tango naming!)
+ TaurusValue.setModel(self, model)
def showEvent(self, event):
TaurusValue.showEvent(self, event)
@@ -110,17 +112,18 @@ class PoolChannel(TaurusWidget):
self._devButton.setModel(m)
-if __name__ == '__main__':
- import sys
- app = Qt.QApplication(sys.argv)
-
- form = PoolChannel()
-
- model = 'tango://controls02:10000/expchan/bl97_simucotictrl_1/1'
- if len(sys.argv)>1:
- model = sys.argv[1]
- form.setModel(model)
-
-
- form.show()
- sys.exit(app.exec_())
+#if __name__ == '__main__':
+# import sys
+# app = Qt.QApplication(sys.argv)
+#
+# form = PoolChannel()
+#
+# #model = 'tango://controls02:10000/expchan/bl97_simucotictrl_1/1'
+# model = 'ct_cp1_1'
+# if len(sys.argv)>1:
+# model = sys.argv[1]
+# form.setModel(model)
+#
+#
+# form.show()
+# sys.exit(app.exec_())
diff --git a/lib/taurus/qt/qtgui/extra_pool/poolmotor.py b/lib/taurus/qt/qtgui/extra_pool/poolmotor.py
index 6a069ae..318bca5 100644
--- a/lib/taurus/qt/qtgui/extra_pool/poolmotor.py
+++ b/lib/taurus/qt/qtgui/extra_pool/poolmotor.py
@@ -97,7 +97,7 @@ class PoolMotorClient():
try:
min_value = self.motor_dev.getAttribute('Position').getConfig().getValueObj().min_value
neg_limit = float(min_value)
- except Exception,e:
+ except Exception:
pass
self.moveMotor(neg_limit)
@@ -110,7 +110,7 @@ class PoolMotorClient():
try:
max_value = self.motor_dev.getAttribute('Position').getConfig().getValueObj().max_value
pos_limit = float(max_value)
- except Exception,e:
+ except Exception:
pass
self.moveMotor(pos_limit)
@@ -134,7 +134,7 @@ class LabelWidgetDragsDeviceAndAttribute(DefaultLabelWidget):
drag = Qt.QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(event.pos() - self.rect().topLeft())
- dropAction = drag.start(Qt.Qt.CopyAction)
+ drag.start(Qt.Qt.CopyAction)
class PoolMotorConfigurationForm(TaurusAttrForm):
@@ -747,7 +747,7 @@ class TaurusAttributeListener(Qt.QObject):
if evt_type not in [taurus.core.TaurusEventType.Change, taurus.core.TaurusEventType.Periodic]:
return
value = evt_value.value
- self.emit(Qt.SIGNAL('eventReceived(PyQt_PyObject)'), value)
+ self.emit(Qt.SIGNAL('eventReceived'), value)
##################################################
@@ -773,7 +773,7 @@ class PoolMotorTVLabelWidget(TaurusWidget):
self.layout().addWidget(self.lbl_alias)
self.btn_poweron = Qt.QPushButton()
- self.btn_poweron.setText('Power ON')
+ self.btn_poweron.setText('Set ON')
self.layout().addWidget(self.btn_poweron)
# Align everything on top
@@ -800,7 +800,7 @@ class PoolMotorTVLabelWidget(TaurusWidget):
def setPowerOn(self):
motor_dev = self.taurusValueBuddy().motor_dev
if motor_dev is not None:
- poweron = (self.btn_poweron.text() == 'Power ON')
+ poweron = (self.btn_poweron.text() == 'Set ON')
motor_dev.getAttribute('PowerOn').write(poweron)
def setModel(self, model):
@@ -815,7 +815,6 @@ class PoolMotorTVLabelWidget(TaurusWidget):
self.disconnect(self.btn_poweron, Qt.SIGNAL('clicked()'), self.setPowerOn)
self.connect(self.btn_poweron, Qt.SIGNAL('clicked()'), self.setPowerOn)
-
def calculateExtendedTooltip(self, cache=False):
default_label_widget_tooltip = DefaultLabelWidget.getFormatedToolTip(self.lbl_alias, cache)
status_info = ''
@@ -861,7 +860,7 @@ class PoolMotorTVLabelWidget(TaurusWidget):
drag = Qt.QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(event.pos() - self.rect().topLeft())
- dropAction = drag.start(Qt.Qt.CopyAction)
+ drag.start(Qt.Qt.CopyAction)
##################################################
# READ WIDGET #
@@ -1190,10 +1189,18 @@ class PoolMotorTV(TaurusValue):
def setModel(self, model):
TaurusValue.setModel(self, model)
-
try:
- # Try to clean everything
-
+ # disconnect signals
+ if self.limits_listener is not None:
+ self.disconnect(self.limits_listener, Qt.SIGNAL('eventReceived'), self.updateLimits)
+ if self.poweron_listener is not None:
+ self.disconnect(self.poweron_listener, Qt.SIGNAL('eventReceived'), self.updatePowerOn)
+ if self.status_listener is not None:
+ self.disconnect(self.status_listener, Qt.SIGNAL('eventReceived'), self.updateStatus)
+ if self.position_listener is not None:
+ self.disconnect(self.position_listener, Qt.SIGNAL('eventReceived'), self.updatePosition)
+
+ #remove listeners
if self.motor_dev is not None:
if self.hasHwLimits():
self.motor_dev.getAttribute('Limit_Switches').removeListener(self.limits_listener)
@@ -1201,41 +1208,43 @@ class PoolMotorTV(TaurusValue):
self.motor_dev.getAttribute('PowerOn').removeListener(self.poweron_listener)
self.motor_dev.getAttribute('Status').removeListener(self.status_listener)
self.motor_dev.getAttribute('Position').removeListener(self.position_listener)
-
+
if model == '' or model is None:
self.motor_dev = None
return
-
+
self.motor_dev = taurus.Device(model)
-
+
# CONFIGURE A LISTENER IN ORDER TO UPDATE LIMIT SWITCHES STATES
+ self.limits_listener = TaurusAttributeListener()
if self.hasHwLimits():
- self.limits_listener = TaurusAttributeListener()
- self.connect(self.limits_listener, Qt.SIGNAL('eventReceived(PyQt_PyObject)'), self.updateLimits)
+ self.connect(self.limits_listener, Qt.SIGNAL('eventReceived'), self.updateLimits)
self.motor_dev.getAttribute('Limit_Switches').addListener(self.limits_listener)
# CONFIGURE AN EVENT RECEIVER IN ORDER TO PROVIDE POWERON <- True/False EXPERT OPERATION
+ self.poweron_listener = TaurusAttributeListener()
if self.hasPowerOn():
- self.poweron_listener = TaurusAttributeListener()
- self.connect(self.poweron_listener, Qt.SIGNAL('eventReceived(PyQt_PyObject)'), self.updatePowerOn)
+ self.connect(self.poweron_listener, Qt.SIGNAL('eventReceived'), self.updatePowerOn)
self.motor_dev.getAttribute('PowerOn').addListener(self.poweron_listener)
# CONFIGURE AN EVENT RECEIVER IN ORDER TO UPDATED STATUS TOOLTIP
self.status_listener = TaurusAttributeListener()
- self.connect(self.status_listener, Qt.SIGNAL('eventReceived(PyQt_PyObject)'), self.updateStatus)
+ self.connect(self.status_listener, Qt.SIGNAL('eventReceived'), self.updateStatus)
self.motor_dev.getAttribute('Status').addListener(self.status_listener)
# CONFIGURE AN EVENT RECEIVER IN ORDER TO ACTIVATE LIMIT BUTTONS ON SOFTWARE LIMITS
self.position_listener = TaurusAttributeListener()
- self.connect(self.position_listener, Qt.SIGNAL('eventReceived(PyQt_PyObject)'), self.updatePosition)
+ self.connect(self.position_listener, Qt.SIGNAL('eventReceived'), self.updatePosition)
self.motor_dev.getAttribute('Position').addListener(self.position_listener)
+
+ self.motor_dev.getAttribute('Position').enablePolling(force=True)
self.setExpertView(self._expertView)
-
except Exception,e:
self.warning("Exception caught while setting model: %s",repr(e))
- raise e
-
+ self.motor_dev = None
+ return
+
def hasPowerOn(self):
try: return hasattr(self.motor_dev, 'PowerOn')
except: return False
@@ -1276,14 +1285,11 @@ class PoolMotorTV(TaurusValue):
if pos_lim:
pos_btnstylesheet = 'QPushButton{%s}'%taurus.core.util.DEVICE_STATE_PALETTE.qtStyleSheet(PyTango.DevState.ALARM)
enabled = False
-
self.readWidget().btn_lim_pos.setStyleSheet(pos_btnstylesheet)
self.writeWidget().btn_step_up.setEnabled(enabled)
self.writeWidget().btn_step_up.setStyleSheet(pos_btnstylesheet)
-
self.writeWidget().btn_to_pos.setEnabled(enabled)
-
self.writeWidget().btn_to_pos_press.setEnabled(enabled)
neg_lim = limits[NEG]
@@ -1292,22 +1298,19 @@ class PoolMotorTV(TaurusValue):
if neg_lim:
neg_btnstylesheet = 'QPushButton{%s}'%taurus.core.util.DEVICE_STATE_PALETTE.qtStyleSheet(PyTango.DevState.ALARM)
enabled = False
-
self.readWidget().btn_lim_neg.setStyleSheet(neg_btnstylesheet)
self.writeWidget().btn_step_down.setEnabled(enabled)
self.writeWidget().btn_step_down.setStyleSheet(neg_btnstylesheet)
-
self.writeWidget().btn_to_neg.setEnabled(enabled)
-
self.writeWidget().btn_to_neg_press.setEnabled(enabled)
def updatePowerOn(self, poweron):
- btn_text = 'Power ON'
+ btn_text = 'Set ON'
if poweron:
- btn_text = 'Power OFF'
-
+ btn_text = 'Set OFF'
self.labelWidget().btn_poweron.setText(btn_text)
+
def updateStatus(self, status):
# SHOULD THERE BE A BETTER METHOD FOR THIS UPDATE?
@@ -1315,7 +1318,7 @@ class PoolMotorTV(TaurusValue):
# TaurusLabel.updateStyle DIDN'T WORK, SO I HAD TO GO DEEPER TO THE CONTROLLER...
#self.labelWidget().lbl_alias.updateStyle()
self.labelWidget().lbl_alias.controllerUpdate()
-
+
def updatePosition(self, position):
# we do not need the position for nothing...
# we just want to check if any software limit is 'active'
@@ -1338,15 +1341,15 @@ class PoolMotorTV(TaurusValue):
taurus_attr_form.setWindowTitle('%s Tango Attributes'%taurus.Factory().getDevice(model).getSimpleName())
taurus_attr_form.show()
- def showEvent(self, event):
- TaurusValue.showEvent(self, event)
- if self.motor_dev is not None:
- self.motor_dev.getAttribute('Position').enablePolling(force=True)
-
- def hideEvent(self, event):
- TaurusValue.hideEvent(self, event)
- if self.motor_dev is not None:
- self.motor_dev.getAttribute('Position').disablePolling()
+ ### def showEvent(self, event):
+ ### TaurusValue.showEvent(self, event)
+ ### if self.motor_dev is not None:
+ ### self.motor_dev.getAttribute('Position').enablePolling(force=True)
+ ###
+ ### def hideEvent(self, event):
+ ### TaurusValue.hideEvent(self, event)
+ ### if self.motor_dev is not None:
+ ### self.motor_dev.getAttribute('Position').disablePolling()
###################################################
# A SIMPLER WIDGET THAT MAY BE USED OUTSIDE FORMS #
diff --git a/lib/taurus/qt/qtgui/extra_sardana/cmdline.py b/lib/taurus/qt/qtgui/extra_sardana/cmdline.py
index f55a7b2..df83764 100644
--- a/lib/taurus/qt/qtgui/extra_sardana/cmdline.py
+++ b/lib/taurus/qt/qtgui/extra_sardana/cmdline.py
@@ -98,7 +98,7 @@ def main():
app_name="Taurus command line demo", app_version="1.0",
org_domain="Taurus", org_name="Tango community")
- w = CommandLineWidget()
+ w = TaurusCommandLineWidget()
w.show()
if owns_app:
diff --git a/lib/taurus/qt/qtgui/extra_sardana/expdescription.py b/lib/taurus/qt/qtgui/extra_sardana/expdescription.py
index 5caf7d1..c369964 100644
--- a/lib/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/lib/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -69,7 +69,6 @@ class ExpDescriptionEditor(Qt.QWidget, TaurusBaseWidget):
from ui.ui_ExpDescriptionEditor import Ui_ExpDescriptionEditor
self.ui = Ui_ExpDescriptionEditor()
self.ui.setupUi(self)
- BB = Qt.QDialogButtonBox
self.ui.buttonBox.setStandardButtons(Qt.QDialogButtonBox.Reset | Qt.QDialogButtonBox.Apply)
newperspectivesDict = copy.deepcopy(self.ui.sardanaElementTree.KnownPerspectives)
#newperspectivesDict[self.ui.sardanaElementTree.DftPerspective]['model'] = [SardanaAcquirableProxyModel, SardanaElementPlainModel]
@@ -152,7 +151,7 @@ class ExpDescriptionEditor(Qt.QWidget, TaurusBaseWidget):
#set a list of available channels
avail_channels = {}
for ch_info in door.macro_server.getExpChannelElements().values():
- avail_channels[ch_info.name] = ch_info.getData() #@todo: Once it changes in the Pool, this should use full_name!
+ avail_channels[ch_info.full_name] = ch_info.getData()
self.ui.channelEditor.getQModel().setAvailableChannels(avail_channels)
def _setDirty(self,dirty):
@@ -258,13 +257,31 @@ class ExpDescriptionEditor(Qt.QWidget, TaurusBaseWidget):
self._setDirty(True)
def createMntGrp(self):
- '''creates a new Measurement Group'''
+ '''creates a new Measurement Group'''
+
+ if self._localConfig is None:
+ return
+
mntGrpName, ok = Qt.QInputDialog.getText(self, "New Measurement Group",
"Enter a name for the new measurement Group")
if not ok: return
- if self._localConfig is None:
- return
mntGrpName = str(mntGrpName)
+
+ #check that the given name is not an existing pool element
+ ms = self.getModelObj().macro_server
+ poolElementNames = [v.name for v in ms.getElementsWithInterface("PoolElement").values()]
+ while mntGrpName in poolElementNames:
+ Qt.QMessageBox.warning(self, "Cannot create Measurement group",
+ "The name '%s' already is used for another pool element. Please Choose a different one."%mntGrpName,
+ Qt.QMessageBox.Ok)
+ mntGrpName, ok = Qt.QInputDialog.getText(self, "New Measurement Group",
+ "Enter a name for the new measurement Group",
+ Qt.QLineEdit.Normal,
+ mntGrpName)
+ if not ok: return
+ mntGrpName = str(mntGrpName)
+
+ #check that the measurement group is not already in the localConfig
if mntGrpName in self._localConfig['MntGrpConfigs']:
Qt.QMessageBox.warning(self, "%s already exists"%mntGrpName,
'A measurement group named "%s" already exists. A new one will not be created'%mntGrpName)
@@ -316,23 +333,34 @@ class ExpDescriptionEditor(Qt.QWidget, TaurusBaseWidget):
self._localConfig['ScanFile'] = [v.strip() for v in str(text).split(',')]
self._setDirty(True)
- def onPreScanSnapshotChanged(self, items):
- self._localConfig['PreScanSnapshot'] = [(e.src,e.display) for e in items]
+ def onPreScanSnapshotChanged(self, items):
+ door = self.getModelObj()
+ ms = door.macro_server
+ preScanList = []
+ for e in items:
+ nfo = ms.getElementInfo(e.src)
+ if nfo is None:
+ preScanList.append((e.src,e.display))
+ else:
+ preScanList.append((nfo.full_name,nfo.name))
+ self._localConfig['PreScanSnapshot'] = preScanList
self._setDirty(True)
-
-
-
-
-
-
-
-def demo(model="mydoor"):
- """Table panels"""
+
+
+def demo(model=None):
+ """Experiment configuration"""
#w = main_ChannelEditor()
- w = ExpDescriptionEditor(door=model)
+ w = ExpDescriptionEditor()
+ if model is None:
+ from taurus.qt.qtgui.extra_macroexecutor import TaurusMacroConfigurationDialog
+ dialog = TaurusMacroConfigurationDialog(w)
+ accept = dialog.exec_()
+ if accept:
+ model = str(dialog.doorComboBox.currentText())
+ if model is not None:
+ w.setModel(model)
return w
-
def main():
import sys
import taurus.qt.qtgui.application
diff --git a/lib/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/lib/taurus/qt/qtgui/extra_sardana/measurementgroup.py
index 982a276..d480bc3 100644
--- a/lib/taurus/qt/qtgui/extra_sardana/measurementgroup.py
+++ b/lib/taurus/qt/qtgui/extra_sardana/measurementgroup.py
@@ -3,30 +3,30 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This module provides a base widget that can be used to display a taurus
+"""This module provides a base widget that can be used to display a taurus
model in a table widget"""
-__all__ = ["MntGrpChannelEditor", "MntGrpChannelPanel"]
+__all__ = ["MntGrpChannelEditor"]
__docformat__ = 'restructuredtext'
@@ -35,7 +35,6 @@ from taurus.qt import Qt
import taurus
from taurus.qt.qtcore.model import TaurusBaseTreeItem, TaurusBaseModel
-from taurus.qt.qtgui.base import TaurusBaseWidget
from taurus.qt.qtgui.model import EditorToolBar
from taurus.qt.qtgui.resource import getIcon, getThemeIcon
from taurus.qt.qtgui.table import TaurusBaseTableWidget
@@ -46,8 +45,8 @@ from taurus.core import TaurusElementType
#===============================================================================
# some dummydict for developing the "Experimental Configuration widget"
-# This block is to be removed and the dictionaries will be defined and
-# initialized in Sardana's Door code
+# This block is to be removed and the dictionaries will be defined and
+# initialized in Sardana's Door code
# dict <str, obj> with (at least) keys:
@@ -58,7 +57,7 @@ from taurus.core import TaurusElementType
# - value: dict<str, dict> with (at least) keys:
# - 'units': dict<str, dict> with (at least) keys:
# - 'id' : the unit ID inside the controller
-# - 'timer' : the timer channel name / timer channel id
+# - 'timer' : the timer channel name / timer channel id
# - 'monitor' : the monitor channel name / monitor channel id
# - 'trigger_type' : a value from AcqTriggerType enum
# - 'channels' where value is a dict<str, obj> with (at least) keys:
@@ -80,83 +79,109 @@ from taurus.core import TaurusElementType
#===============================================================================
-DEFAULT_STRING_LENGTH = 80 #just an arbitrary value to use as default string length...
+DEFAULT_STRING_LENGTH = 80 #just an arbitrary value to use as default string length...
-def createChannelDict(name, index=None, **kwargs):
+def createChannelDict(channel, index=None, **kwargs):
from taurus.core.tango import FROM_TANGO_TO_STR_TYPE
import PyTango
import numpy
- ret = {
- ##@fixme: UGLY HACK! I am doing this for compatibility with the way Sardana creates the
- ## pool.ch_info dictionary for external tango channels.
- ## This should be fixed by using the full name (or maybe the source?) as the keys
- #'name': name,
- 'name' : name.split('/')[-1], #@fixme: to make things uglier, I lazily assume Tango attribute naming
- 'label': name.split('/')[-1], #channel label #@fixme (same as above)
- 'full_name': name,
+
+ if isinstance(channel, (str, unicode)):
+ #@fixme: to make things uglier, I lazily assume Tango attribute namin
+ dev_name, attr_name = channel.rsplit('/', 1)
+ name = attr_name
+ try:
+ dev = PyTango.DeviceProxy(dev_name)
+ db = dev.get_device_db()
+ try:
+ alias = db.get_alias(dev.name())
+ except:
+ # no alias...
+ alias = dev.name()
+ label = alias + "/" + attr_name
+ except:
+ label = channel
+ full_name = channel
+ source = channel
+ else:
+ name = channel['name']
+ label = name
+ full_name = channel['full_name']
+ source = channel['source']
+
+ ret = {
+ 'name' : name,
+ 'label': label,
+ 'full_name': full_name,
'enabled': True, # bool. Whether this channel is enabled (if not enabled, it won't be used for output or plot)
- 'output': True, # bool. Whether to show output in the stdout
+ 'output': True, # bool. Whether to show output in the stdout
'data_type':'float64',
'data_units': 'No unit',
# 'timer': '', #should contain a channel name
# 'monitor': '', #should contain a channel name
# 'trigger': '', #should contain a channel name
'conditioning': '', #this is a python expresion to be evaluated for conditioning the data. The data for this channel can be referred as 'x' and data from other channels can be referred by channel name
- 'normalization': Normalization.No, # one of the Normalization enumeration members
- 'nexus_path': '' #string indicating the location of the data of this channel within the nexus tree
+ 'normalization': Normalization.No, # one of the Normalization enumeration members
+ 'nexus_path': '', #string indicating the location of the data of this channel within the nexus tree
}
-
+
#If the channel is a Tango one, try to guess data_type, shape and data_units
attrproxy = attrconf = value = None
dtype = shape = None
- try:
- attrproxy = PyTango.AttributeProxy(name)
+ try:
+ attrproxy = PyTango.AttributeProxy(source)
attrconf = attrproxy.get_config()
- value = attrproxy.read().value
+ # avoid trying to read for scalars. We know that their shape must be ()
+ if attrconf.data_format != PyTango.AttrDataFormat.SCALAR:
+ value = attrproxy.read().value
except Exception,e:
print str(e)
+
if value is not None:
- shape = numpy.shape(value)
+ shape = list(numpy.shape(value))
dtype = getattr(value, 'dtype', numpy.dtype(type(value))).name
ret['data_units'] = attrconf.unit
elif attrconf is not None:
- shape = [n for n in (attrconf.max_dim_x,attrconf.max_dim_y) if n>0]
+ if attrconf.data_format == PyTango.AttrDataFormat.SCALAR:
+ shape = []
+ else:
+ shape = [n for n in (attrconf.max_dim_x,attrconf.max_dim_y) if n>0]
dtype = FROM_TANGO_TO_STR_TYPE[attrconf.data_type]
ret['data_units'] = attrconf.unit
-
+
if dtype is not None:
# if dtype.startswith('str'):
# dtype='char'
-# shape = list(shape)+[DEFAULT_STRING_LENGTH]
+# shape = list(shape)+[DEFAULT_STRING_LENGTH]
# elif dtype == 'bool':
# dtype='int8'
- ret['data_type'] = dtype
+ ret['data_type'] = dtype
if shape is not None:
- ret['shape'] = shape
+ ret['shape'] = shape
#now overwrite using the arguments
ret.update(kwargs)
-
+
#Calculate the index
if index is not None:
ret['index'] = index #an integer used for ordering the channel in this measurement group
-
+
#Choose a default plot_type for the channel
if 'plot_type' not in ret:
default_plot_type = {0:PlotType.Spectrum, 2:PlotType.Image, None:PlotType.No}
try: rank = len(ret['shape'])
- except KeyError:
+ except KeyError:
rank = None #if shape is not known, use the default plot_type
ret['plot_type'] = default_plot_type.get(rank, PlotType.No)
-
+
#And a default value for plot_axes
- if 'plot_axes' not in ret:
+ if 'plot_axes' not in ret:
default_axes = {PlotType.No:[], PlotType.Spectrum:['<mov>'], PlotType.Image:['<idx>','<idx>']}
- ret['plot_axes'] = default_axes[ret['plot_type']] # a string defining a colon-separated list of axis names. An axis can be a channel name or "<idx>". This shares the syntax of the NeXus @axes attribute
-
+ ret['plot_axes'] = default_axes[ret['plot_type']] # a string defining a colon-separated list of axis names. An axis can be a channel name or "<idx>". This shares the syntax of the NeXus @axes attribute
+
return ret
-
+
def getElementTypeIcon(t):
if t == ChannelView.Channel:
@@ -177,10 +202,10 @@ def getElementTypeIcon(t):
return getIcon(":/actions/system-shutdown.svg")
elif t == ChannelView.NXPath:
return getThemeIcon("document-save-as")
-
+
return getIcon(":/tango.png")
-
+
def getElementTypeSize(t):
if t == ChannelView.Channel:
return Qt.QSize(200,24)
@@ -228,24 +253,24 @@ class BaseMntGrpChannelItem(TaurusBaseTreeItem):
""" """
def data(self, index):
"""Returns the data of this node for the given index
-
+
:return: (object) the data for the given index
"""
return self._itemData
-
+
def role(self):
"""Returns the prefered role for the item.
This implementation returns ChannelView.Unknown
-
+
This method should be able to return any kind of python object as long
as the model that is used is compatible.
-
+
:return: (MacroView) the role in form of element type"""
return ChannelView.Unknown
class MntGrpChannelItem(BaseMntGrpChannelItem):
-
+
itemdata_keys_map = {ChannelView.Channel:'label',
ChannelView.Enabled:'enabled',
ChannelView.Output:'output',
@@ -260,10 +285,10 @@ class MntGrpChannelItem(BaseMntGrpChannelItem):
ChannelView.Normalization:'normalization',
ChannelView.NXPath:'nexus_path'
}
-
+
def data(self, index):
"""Returns the data of this node for the given index
-
+
:return: (object) the data for the given index
"""
taurus_role = index.model().role(index.column())
@@ -273,7 +298,7 @@ class MntGrpChannelItem(BaseMntGrpChannelItem):
if taurus_role == ChannelView.PlotType:
ret = PlotType[ret]
elif taurus_role == ChannelView.Normalization:
- ret = Normalization[ret]
+ ret = Normalization[ret]
elif taurus_role == ChannelView.PlotAxes:
ret = ":".join(ret)
elif taurus_role == ChannelView.Shape:
@@ -282,19 +307,20 @@ class MntGrpChannelItem(BaseMntGrpChannelItem):
def setData(self, index, qvalue):
taurus_role = index.model().role(index.column())
- if taurus_role in (ChannelView.Channel, ChannelView.Conditioning,
+ str_value = Qt.from_qvariant(qvalue, str)
+ if taurus_role in (ChannelView.Channel, ChannelView.Conditioning,
ChannelView.NXPath, ChannelView.DataType) :
- data = str(qvalue.toString())
+ data = str_value
elif taurus_role in (ChannelView.Enabled, ChannelView.Output) :
- data = qvalue.toBool()
+ data = Qt.from_qvariant(qvalue, bool)
elif taurus_role == ChannelView.PlotType:
- data = PlotType[str(qvalue.toString())]
+ data = PlotType[str_value]
elif taurus_role == ChannelView.Normalization:
- data = Normalization[str(qvalue.toString())]
+ data = Normalization[str_value]
elif taurus_role == ChannelView.PlotAxes:
- data = [a for a in str(qvalue.toString()).split(':')]
+ data = [a for a in str_value.split(':')]
elif taurus_role == ChannelView.Shape:
- s = str(qvalue.toString())
+ s = str_value
try:
data = eval(s,{},{})
if not isinstance(data,(tuple,list)):
@@ -304,7 +330,7 @@ class MntGrpChannelItem(BaseMntGrpChannelItem):
Logger(self.__class__.__name__).error('Invalid shape %s',s )
data = ()
else:
- raise UnimplementedError('Unknown role')
+ raise NotImplementedError('Unknown role')
ch_name, ch_data = self.itemData()
key = self.itemdata_keys_map[taurus_role]
ch_data[key] = data
@@ -319,22 +345,22 @@ class MntGrpChannelItem(BaseMntGrpChannelItem):
taurus_role = index.model().role(index.column())
if taurus_role == ChannelView.Channel:
return getIcon(":/actions/system-shutdown.svg")
-
+
class MntGrpUnitItem(TaurusBaseTreeItem):
pass
class BaseMntGrpChannelModel(TaurusBaseModel):
- ColumnNames = ("Channel", "enabled", "output", "Shape", "Data Type", "Plot Type", "Plot Axes", "Timer",
+ ColumnNames = ("Channel", "enabled", "output", "Shape", "Data Type", "Plot Type", "Plot Axes", "Timer",
"Monitor", "Trigger", "Conditioning", "Normalization","NeXus Path")
- ColumnRoles = ((ChannelView.Channel, ChannelView.Channel), ChannelView.Enabled,
+ ColumnRoles = ((ChannelView.Channel, ChannelView.Channel), ChannelView.Enabled,
ChannelView.Output, ChannelView.Shape, ChannelView.DataType, ChannelView.PlotType,
ChannelView.PlotAxes, ChannelView.Timer, ChannelView.Monitor,
ChannelView.Trigger, ChannelView.Conditioning,
ChannelView.Normalization, ChannelView.NXPath)
DftFont = Qt.QFont()
-
+
_availableChannels = {}
data_keys_map = {ChannelView.Timer:'timer',
ChannelView.Monitor:'monitor',
@@ -344,17 +370,17 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
def __init__(self, parent=None, data=None):
TaurusBaseModel.__init__(self, parent=parent, data=data)
self._mgconfig = None
- self._dirty = False
-
+ self._dirty = False
+
def setAvailableChannels(self,cdict):
self._availableChannels = cdict
-
+
def getAvailableChannels(self):
return self._availableChannels
-
+
def createNewRootItem(self):
return BaseMntGrpChannelItem(self, self.ColumnNames)
-
+
def roleIcon(self, taurus_role):
return getElementTypeIcon(taurus_role)
@@ -366,10 +392,10 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
def getPyData(self, ctrlname=None, unitid=None, chname=None, key=None):
'''
- If controller name, unitid and channel name are given, it returns the dictionary with the channel info.
- If only controller name and unit id are given, it returns the dictionary with the unit info.
+ If controller name, unitid and channel name are given, it returns the dictionary with the channel info.
+ If only controller name and unit id are given, it returns the dictionary with the unit info.
If only controller name is given, it returns the dictionary with the controller info.
-
+
Note that it will raise a KeyError exception if any of the keys are not
found or if chname is given without providing the unit id
'''
@@ -381,7 +407,7 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
return self._mgconfig['controllers'][ctrlname]['units'][unitid]
else:
return self._mgconfig['controllers'][ctrlname]['units'][unitid]['channels'][chname]
-
+
def setupModelData(self, mgconfig):
if mgconfig is None:
return
@@ -392,7 +418,7 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
self.updateMntGrpChannelIndex(root=root)
#store the whole config object for accessing the info that is not at the channel level
self._mgconfig = mgconfig
-
+
def setDataSource(self, data_src):
self._dirty = False
TaurusBaseModel.setDataSource(self, data_src)
@@ -411,13 +437,29 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
def flags(self, index):
flags = TaurusBaseModel.flags(self, index)
taurus_role = self.role(index.column())
- if taurus_role != ChannelView.Channel:
- flags |= Qt.Qt.ItemIsEditable #all except the channel column are editable
+ if taurus_role == ChannelView.Channel: #channel column is not editable
+ return flags
+ elif taurus_role == ChannelView.Trigger:
+ ch_name, ch_data = index.internalPointer().itemData()
+ if not ch_data['_controller_name'].startswith("__"):
+ ch_info = self.getAvailableChannels()[ch_name]
+ #only timer/monitor columns of counter timers are editable
+ if ch_info['type'] in ('CTExpChannel', 'OneDExpChannel', 'TwoDExpChannel'):
+ flags |= Qt.Qt.ItemIsEditable
+ elif taurus_role in (ChannelView.Timer, ChannelView.Monitor):
+ ch_name, ch_data = index.internalPointer().itemData()
+ if not ch_data['_controller_name'].startswith("__"):
+ #ch_info = self.getAvailableChannels()[ch_name]
+ #if 'CTExpChannel' == ch_info['type']: #only timer/monitor columns of counter timers are editable
+ # flags |= Qt.Qt.ItemIsEditable
+ flags |= Qt.Qt.ItemIsEditable
+ else:
+ flags |= Qt.Qt.ItemIsEditable
return flags
-
+
def data(self, index, role=Qt.Qt.DisplayRole):
"""Reimplemented from :meth:`TaurusBaseModel.data`
-
+
:return: (object) the data for the given index
"""
#Try with the normal TaurusBaseModel item-oriented approach
@@ -427,29 +469,56 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
pass
#For those things which are inter-item, we handle them here
taurus_role = self.role(index.column())
- if taurus_role in (ChannelView.Timer, ChannelView.Monitor, ChannelView.Trigger):
+ if taurus_role == ChannelView.Trigger:
ch_name, ch_data = index.internalPointer().itemData()
unitdict = self.getPyData(ctrlname=ch_data['_controller_name'], unitid=ch_data['_unit_id'])
key = self.data_keys_map[taurus_role]
- ret = unitdict.get(key, None)
- if taurus_role == ChannelView.Trigger:
- ret = AcqTriggerType[ret]
- return Qt.QVariant(ret)
+ return Qt.QVariant(AcqTriggerType[unitdict.get(key, None)])
+ elif taurus_role in (ChannelView.Timer, ChannelView.Monitor):
+ ch_name, ch_data = index.internalPointer().itemData()
+ ctrlname = ch_data['_controller_name']
+ if ctrlname.startswith("__"):
+ return Qt.QVariant()
+ ch_info = self.getAvailableChannels()[ch_name]
+ if ch_info['type'] in ('CTExpChannel', 'OneDExpChannel', 'TwoDExpChannel'):
+ unitdict = self.getPyData(ctrlname=ctrlname, unitid=ch_data['_unit_id'])
+ key = self.data_keys_map[taurus_role]
+ master_full_name = unitdict.get(key, None)
+ else:
+ key = taurus_role == ChannelView.Timer and 'timer' or 'monitor'
+ master_full_name = self._mgconfig.get(key, None)
+ if master_full_name is None:
+ return Qt.QVariant()
+ else:
+ master_info = self.getAvailableChannels()[master_full_name]
+ return Qt.QVariant(master_info['name'])
+
return Qt.QVariant()
-
+
def setData(self, index, qvalue, role=Qt.Qt.EditRole):
#For those things which are at the unit level, we handle them here
taurus_role = self.role(index.column())
if taurus_role in (ChannelView.Timer, ChannelView.Monitor, ChannelView.Trigger):
ch_name, ch_data = index.internalPointer().itemData()
+ ch_info = self.getAvailableChannels()[ch_name]
unit_data = self.getPyData(ctrlname=ch_data['_controller_name'], unitid=ch_data['_unit_id'])
key = self.data_keys_map[taurus_role]
- data = str(qvalue.toString())
- if taurus_role == ChannelView.Trigger:
- data = AcqTriggerType[data]
+ data = Qt.from_qvariant(qvalue, str)
+
self._dirty = True
self.beginResetModel()
- unit_data[key] = data
+ is_settable = ch_info['type'] in ('CTExpChannel', 'OneDExpChannel', 'TwoDExpChannel')
+ if taurus_role == ChannelView.Trigger:
+ data = AcqTriggerType[data]
+ if is_settable:
+ unit_data[key] = data
+ else:
+ if is_settable:
+ if unit_data[key] == self._mgconfig[key]:
+ self._mgconfig[key] = data
+ unit_data[key] = data
+ else:
+ self._mgconfig[key] = data
self.endResetModel()
return True
#for the rest, we use the regular TaurusBaseModel item-oriented approach
@@ -460,9 +529,11 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
self.emit(Qt.SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
index, index)
return True
-
- def addChannel(self, chname=None, ctrlname=None, unitname=None, external=False): #@todo: Very inefficient implementation. We should use {begin|end}InsertRows
-
+
+ def addChannel(self, chname=None, chinfo=None, ctrlname=None, unitname=None, external=False): #@todo: Very inefficient implementation. We should use {begin|end}InsertRows
+
+ if chname is None:
+ chname = chinfo['full_name']
chname = str(chname)
if ctrlname is None:
desc = self.getAvailableChannels()[chname]
@@ -470,15 +541,15 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
if unitname is None:
desc = self.getAvailableChannels()[chname]
unitname = desc.get('unit','0') #@fixme: at the moment of writing, the unit info cannot be obtained from desc
-
- #update the internal data
+
+ #update the internal data
self.beginResetModel() #we are altering the internal data here, so we need to protect it
ctrlsdict = self.dataSource()['controllers']
if not ctrlsdict.has_key(ctrlname): ctrlsdict[ctrlname] = {'units':{}}
unitsdict = ctrlsdict[ctrlname]['units']
if not unitsdict.has_key(unitname):
unitsdict[unitname] = unit = {'channels':{}}
- if not external:
+ if not external and chinfo['type'] in ('CTExpChannel', 'OneDExpChannel', 'TwoDExpChannel'):
unit['timer'] = chname
unit['monitor'] = chname
unit['trigger_type'] = AcqTriggerType.Software
@@ -486,17 +557,20 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
if channelsdict.has_key(chname):
self.error('Channel "%s" is already in the measurement group. It will not be added again'%chname)
return
-
+
self._dirty = True
- channelsdict[chname] = createChannelDict(chname)
+ if external:
+ channelsdict[chname] = createChannelDict(chname)
+ else:
+ channelsdict[chname] = createChannelDict(chinfo)
self.endResetModel() #we are altering the internal data here, so we need to protect it
- self.refresh() #note that another reset will be done here...
-
+ self.refresh() #note that another reset will be done here...
+
#import pprint
- #pprint.pprint(self.dataSource())
+ #pprint.pprint(self.dataSource())
- def removeChannels(self, chnames): #@todo: Very inefficient implementation. We should use {begin|end}InsertRows
- #update the internal data
+ def removeChannels(self, chnames): #@todo: Very inefficient implementation. We should use {begin|end}InsertRows
+ #update the internal data
self._dirty = True
self.beginResetModel() #we are altering the internal data here, so we need to protect it
for chname in chnames:
@@ -511,29 +585,37 @@ class BaseMntGrpChannelModel(TaurusBaseModel):
unitname = '0'
try:
self.dataSource()['controllers'][ctrlname]['units'][unitname]['channels'].pop(chname)
+ try:
+ if not self.dataSource()['controllers'][ctrlname]['units'][unitname]['channels']:
+ self.dataSource()['controllers'][ctrlname]['units'].pop(unitname)
+ if not self.dataSource()['controllers'][ctrlname]['units']:
+ self.dataSource()['controllers'].pop(ctrlname)
+ except:
+ self.error('error cleaning the data source dictionary')
except:
self.error('cannot find "%s" for removing'%chname)
- self.endResetModel() #we are altering the internal data here, so we need to protect it
+
+ self.endResetModel() #we are altering the internal data here, so we need to protect it
self.refresh() #note that another reset will be done here...
-
- def swapChannels(self, root, row1, row2): #@todo: Very inefficient implementation. We should use {begin|end}MoveRows
+
+ def swapChannels(self, root, row1, row2): #@todo: Very inefficient implementation. We should use {begin|end}MoveRows
self._dirty = True
n1,d1 = root.child(row1).itemData()
n2,d2 = root.child(row2).itemData()
d1['index'], d2['index'] = d2['index'], d1['index']
self.debug("swapping %s with %s"%(n1,n2))
self.refresh()
-
+
def isDataChanged(self):
return self._dirty
-
+
def setDataChanged(self, datachanged):
self._dirty = datachanged
-
+
class MntGrpChannelModel(BaseMntGrpChannelModel):
'''A BaseMntGrpChannelModel that communicates with a MntGrp device for setting and reading the configuration
- '''
-
+ '''
+
def setDataSource(self, mg):
if self._data_src is not None:
Qt.QObject.disconnect(self._data_src, Qt.SIGNAL('configurationChanged'), self.configurationChanged)
@@ -546,25 +628,25 @@ class MntGrpChannelModel(BaseMntGrpChannelModel):
def setupModelData(self, mg):
if mg is None:
- return
+ return
BaseMntGrpChannelModel.setupModelData(self, self.getSourceData())
def writeSourceData(self):
mg = self.dataSource()
- if mg is not None and self._mgconfig is not None:
+ if mg is not None and self._mgconfig is not None:
mg.setConfiguration(self._mgconfig)
-
+
def getSourceData(self):
"""Gets data from the dataSource"""
mg = self.dataSource()
if mg is not None:
return mg.getConfiguration()
-
+
def getLocalData(self):
"""Gets the local data (may be different from the one in the data source
since it may have been modified by the user)"""
return self._mgconfig
-
+
class AxesSelector(Qt.QWidget):
def __init__(self, parent, n=0, choices=None):
@@ -584,30 +666,30 @@ class AxesSelector(Qt.QWidget):
self._CBs.append(cb)
if choices is not None:
self.setChoices(choices)
-
+
def setChoices(self, choices):
for cb in self._CBs:
cb.addItems(choices)
-
+
def text(self):
return ":".join(self.getCurrentChoices())
-
+
def getCurrentChoices(self):
if self._LE is None:
return [str(cb.currentText()) for cb in self._CBs]
else:
return [str(self._LE.text())]
-
+
def setCurrentChoices(self, choice):
if self._LE is None:
texts = str(choice).split(':')
- for t,cb in zip(texts[:len(self._CBs)],self._CBs):
+ for t,cb in zip(texts[:len(self._CBs)],self._CBs):
cb.setCurrentIndex(max(0,cb.findText(t)))
else:
self._LE.setText(str(choice))
-class ChannelDelegate(Qt.QStyledItemDelegate):
+class ChannelDelegate(Qt.QStyledItemDelegate):
def createEditor(self, parent, option, index):
model = index.model()
taurus_role = model.role(index.column())
@@ -628,56 +710,124 @@ class ChannelDelegate(Qt.QStyledItemDelegate):
ret = Qt.QStyledItemDelegate.createEditor(self, parent, option, index)
ret.setAutoFillBackground(True)
return ret
-
+
def setEditorData(self, editor, index):
model = index.model()
+ dataSource = model.dataSource()
taurus_role = model.role(index.column())
- if taurus_role == ChannelView.Channel:
- editor.addItems(sorted(model.availableChannels.keys()))
- editor.setCurrentIndex(editor.findText(pydata))
- elif taurus_role == ChannelView.PlotType:
+ if taurus_role == ChannelView.PlotType:
editor.addItems(PlotType.keys())
- current = model.data(index).toString()
+ current = Qt.from_qvariant(model.data(index), str)
editor.setCurrentIndex(editor.findText(current))
elif taurus_role == ChannelView.Normalization:
editor.addItems(Normalization.keys())
- current = model.data(index).toString()
+ current = Qt.from_qvariant(model.data(index), str)
editor.setCurrentIndex(editor.findText(current))
elif taurus_role in (ChannelView.Timer, ChannelView.Monitor):
+ key = taurus_role == ChannelView.Timer and 'timer' or 'monitor'
ch_name, ch_data = index.internalPointer().itemData()
ctrl_filterlist = [ch_data['_controller_name']]
- selectables = [n for n,d in getChannelConfigs(model.dataSource(), ctrls=ctrl_filterlist)]
- editor.addItems(selectables)
- current = model.data(index).toString()
- editor.setCurrentIndex(editor.findText(current))
+ ctrl_dict = getChannelConfigs(dataSource, ctrls=ctrl_filterlist)
+ all_channels = model.getAvailableChannels()
+ # if it is a timer capable type of element
+ if all_channels[ch_name]['type'] in ('CTExpChannel', 'OneDExpChannel', 'TwoDExpChannel'):
+ for full_name, channel_data in ctrl_dict:
+ editor.addItem(channel_data['name'], Qt.QVariant(full_name))
+ current = Qt.from_qvariant(model.data(index), str)
+ editor.setCurrentIndex(editor.findText(current))
+ else:
+ for ctrl_data in dataSource['controllers'].values():
+ for unit_data in ctrl_data['units'].values():
+ if key in unit_data:
+ channel = all_channels[unit_data[key]]
+ editor.addItem(channel['name'], Qt.QVariant(channel['full_name']))
+ current = dataSource.get(key) # current global timer/monitor
+ editor.setCurrentIndex(editor.findData(Qt.QVariant(current)))
elif taurus_role == ChannelView.Trigger:
editor.addItems(AcqTriggerType.keys())
- current = model.data(index).toString()
+ current = Qt.from_qvariant(model.data(index), str)
editor.setCurrentIndex(editor.findText(current))
elif taurus_role == ChannelView.PlotAxes:
- selectables = ['<idx>','<mov>']+[n for n,d in getChannelConfigs(model.dataSource())]
+ selectables = ['<idx>','<mov>']+[n for n,d in getChannelConfigs(dataSource)]
editor.setChoices(selectables)
- current = model.data(index).toString()
+ current = Qt.from_qvariant(model.data(index), str)
editor.setCurrentChoices(current)
else:
Qt.QStyledItemDelegate.setEditorData(self, editor, index)
-
+
def setModelData(self, editor, model, index):
taurus_role = model.role(index.column())
+ dataSource = model.dataSource()
if taurus_role in (ChannelView.Channel, ChannelView.PlotType, ChannelView.Normalization):
data = Qt.QVariant(editor.currentText())
model.setData(index, data)
- elif taurus_role in (ChannelView.Timer, ChannelView.Monitor, ChannelView.Trigger):
+ elif taurus_role == ChannelView.Trigger:
+ old_value = Qt.from_qvariant(model.data(index), str)
+ new_value = str(editor.currentText())
+ if new_value == old_value:
+ return
ch_name, ch_data = index.internalPointer().itemData()
- affected = [n for n,d in getChannelConfigs(model.dataSource(), ctrls=[ch_data['_controller_name']], units=[ch_data['_unit_id']]) ]
+ channels = getChannelConfigs(dataSource, ctrls=[ch_data['_controller_name']], units=[ch_data['_unit_id']])
+ affected = [d['name'] for n,d in channels]
if len(affected) >1:
op = Qt.QMessageBox.question(editor, "Caution: multiple channels affected",
- "This change will also affect the following channels:\n- %s \nContinue?"%"\n- ".join(affected),
+ "This change will also affect the following channels:\n- %s \nContinue?"%"\n- ".join(affected),
Qt.QMessageBox.Yes|Qt.QMessageBox.Cancel)
- if op != Qt.QMessageBox.Yes:
+ if op != Qt.QMessageBox.Yes:
return
- data = Qt.QVariant(editor.currentText())
+ data = Qt.QVariant(new_value)
model.setData(index, data)
+ elif taurus_role in (ChannelView.Timer, ChannelView.Monitor):
+ key = taurus_role == ChannelView.Timer and 'timer' or 'monitor'
+ old_value = Qt.from_qvariant(model.data(index), str)
+ new_value = str(editor.currentText())
+ if new_value == old_value:
+ return
+ ch_name, ch_data = index.internalPointer().itemData()
+ all_channels = model.getAvailableChannels()
+ # if it is a timer capable type of element
+ ch_info = all_channels[ch_name]
+ selected_master = editor.itemData(editor.currentIndex())
+ if ch_info['type'] in ('CTExpChannel', 'OneDExpChannel', 'TwoDExpChannel'):
+ affected = []
+ unit_data = model.getPyData(ctrlname=ch_data['_controller_name'], unitid=ch_data['_unit_id'])
+ channels = getChannelConfigs(dataSource, ctrls=[ch_data['_controller_name']], units=[ch_data['_unit_id']])
+ for n, d in channels:
+ affected.append(d['name'])
+ # if old timer/monitor was also the global, then non
+ # timerable/monitorable channels must be changed
+ if unit_data[key] == dataSource.get(key):
+ for n, d in getChannelConfigs(dataSource):
+ if d['_controller_name'].startswith("__"):
+ continue
+ ch_info = all_channels[n]
+ if ch_info['type'] in ('CTExpChannel', 'OneDExpChannel', 'TwoDExpChannel'):
+ continue
+ affected.append(d['name'])
+
+ if len(affected) >1:
+ op = Qt.QMessageBox.question(editor, "Caution: multiple channels affected",
+ "This change will also affect the following channels:\n- %s \nContinue?"%"\n- ".join(affected),
+ Qt.QMessageBox.Yes|Qt.QMessageBox.Cancel)
+ if op != Qt.QMessageBox.Yes:
+ return
+ else:
+ affected = []
+ channels = getChannelConfigs(dataSource)
+ for n, d in channels:
+ if d['_controller_name'].startswith("__"):
+ continue
+ ch_info = all_channels[n]
+ if ch_info['type'] in ('CTExpChannel', 'OneDExpChannel', 'TwoDExpChannel'):
+ continue
+ affected.append(d['name'])
+ if len(affected) >1:
+ op = Qt.QMessageBox.question(editor, "Caution: multiple channels affected",
+ "This change will also affect the following channels:\n- %s \nContinue?"%"\n- ".join(affected),
+ Qt.QMessageBox.Yes|Qt.QMessageBox.Cancel)
+ if op != Qt.QMessageBox.Yes:
+ return
+ model.setData(index, selected_master)
elif taurus_role == ChannelView.PlotAxes:
data = Qt.QVariant(editor.text())
model.setData(index, data)
@@ -688,7 +838,7 @@ class ChannelDelegate(Qt.QStyledItemDelegate):
class MntGrpChannelEditor(TaurusBaseTableWidget):
"""
"""
-
+
KnownPerspectives = {
"Channel" : {
"label" : "Channels",
@@ -701,21 +851,21 @@ class MntGrpChannelEditor(TaurusBaseTableWidget):
DftPerspective = "Channel"
_simpleViewColumns = (ChannelView.Channel, ChannelView.Output, ChannelView.Shape, ChannelView.PlotType, ChannelView.PlotAxes)
_simpleView = False
-
+
def __init__(self,parent=None, designMode=False, with_filter_widget=True, perspective=None):
- TaurusBaseTableWidget.__init__(self, parent=parent, designMode=designMode,
+ TaurusBaseTableWidget.__init__(self, parent=parent, designMode=designMode,
with_filter_widget=with_filter_widget,
perspective=perspective, proxy=None)
- self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu)
+ self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu)
self._simpleViewAction = Qt.QAction("Simple View", self)
self._simpleViewAction.setCheckable(True)
self.connect(self._simpleViewAction, Qt.SIGNAL("toggled(bool)"), self.setSimpleView)
self.addAction(self._simpleViewAction)
self.registerConfigProperty(self.isSimpleView, self.setSimpleView, "simpleView")
-
+
def isSimpleView(self):
return self._simpleView
-
+
def setSimpleView(self, simpleview):
if simpleview == self.isSimpleView():
return
@@ -727,10 +877,10 @@ class MntGrpChannelEditor(TaurusBaseTableWidget):
self.tableView().setColumnHidden(i, hide)
self._simpleView = simpleview
self._simpleViewAction.setChecked(simpleview)
-
+
def resetSimpleView(self):
self.setSimpleView(False)
-
+
def createViewWidget(self):
tableView = TaurusBaseTableWidget.createViewWidget(self)
self._delegate = ChannelDelegate(self)
@@ -751,11 +901,12 @@ class MntGrpChannelEditor(TaurusBaseTableWidget):
def getModelClass(self):
return taurus.core.TaurusDevice
-
+
def addChannel(self, channel=None):
qmodel = self.getQModel()
+ dataSource = qmodel.dataSource()
if channel is None:
- shown = [n for n,d in getChannelConfigs(qmodel.dataSource())]
+ shown = [n for n,d in getChannelConfigs(dataSource)]
avail_channels = qmodel.getAvailableChannels()
clist = [ ch_info['name'] for ch_name, ch_info in avail_channels.items()
if ch_name not in shown ]
@@ -772,14 +923,16 @@ class MntGrpChannelEditor(TaurusBaseTableWidget):
for m in models:
qmodel.addChannel(chname=m, ctrlname='__tango__', unitname='0', external=True)
else:
- qmodel.addChannel(chname=chname)
-
+ for ch_info in avail_channels.values():
+ if ch_info['name'] == chname:
+ qmodel.addChannel(chinfo=ch_info)
+
def removeChannels(self, channels=None):
if channels is None:
channels = self.selectedItems()
chnames = [ch.itemData()[0] for ch in channels]
self.getQModel().removeChannels(chnames)
-
+
def moveUpChannel(self, channel=None):
if channel is None:
channels = self.selectedItems()
@@ -788,14 +941,14 @@ class MntGrpChannelEditor(TaurusBaseTableWidget):
channel = channels[0]
parent = channel.parent()
row = channel.row()
- if row < 1:
+ if row < 1:
return
-
+
model = self.getQModel()
model.swapChannels(parent, row, row-1)
idx = model.index(row-1,0)
self.viewWidget().setCurrentIndex(idx)
-
+
def moveDownChannel(self, channel=None):
if channel is None:
channels = self.selectedItems()
@@ -804,7 +957,7 @@ class MntGrpChannelEditor(TaurusBaseTableWidget):
channel = channels[0]
parent = channel.parent()
row = channel.row()
- if row >= parent.childCount() - 1:
+ if row >= parent.childCount() - 1:
return
model = self.getQModel()
self.getQModel().swapChannels(parent, row, row+1)
@@ -813,7 +966,7 @@ class MntGrpChannelEditor(TaurusBaseTableWidget):
def getLocalConfig(self):
return self.getQModel().getLocalData()
-
+
@classmethod
def getQtDesignerPluginInfo(cls):
ret = TaurusBaseTableWidget.getQtDesignerPluginInfo()
@@ -821,113 +974,7 @@ class MntGrpChannelEditor(TaurusBaseTableWidget):
ret['group'] = 'Taurus Sardana'
ret['icon'] = ":/designer/table.png"
return ret
-
+
simpleView = Qt.pyqtProperty("bool", isSimpleView, setSimpleView, resetSimpleView)
-class MntGrpChannelPanel(Qt.QWidget):
-
- def __init__(self, parent=None, designMode=False):
- Qt.QWidget.__init__(self, parent)
- l = Qt.QVBoxLayout()
- l.setContentsMargins(0,0,0,0)
- self.setLayout(l)
- self._editor = MntGrpChannelEditor(parent=self, designMode=designMode)
- self.connect(self._editor.getQModel(),
- Qt.SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
- self.onDataChanged)
- self.connect(self._editor.getQModel(),
- Qt.SIGNAL("modelReset()"),
- self.onDataReset)
- self._editor.show()
- l.addWidget(self._editor, 1)
- BB = Qt.QDialogButtonBox
- bts = BB.Ok | BB.Cancel | BB.Reset | BB.Apply
- bb = self._buttonBox = Qt.QDialogButtonBox(bts, Qt.Qt.Horizontal, self)
- self.connect(bb, Qt.SIGNAL("clicked(QAbstractButton *)"),
- self.onDialogButtonClicked)
- l.addWidget(self._buttonBox, 0, Qt.Qt.AlignRight)
-
- def getEditor(self):
- return self._editor
-
- def setModel(self, m):
- self.getEditor().setModel(m)
-
- def getEditorQModel(self):
- return self.getEditor().getQModel()
-
- def onDialogButtonClicked(self, button):
- role = self._buttonBox.buttonRole(button)
- qmodel = self.getEditorQModel()
- if role == Qt.QDialogButtonBox.ApplyRole:
- qmodel.writeSourceData()
- elif role == Qt.QDialogButtonBox.ResetRole:
- qmodel.refresh()
-
- def onDataChanged(self, i1, i2):
- self._updateButtonBox()
-
- def onDataReset(self):
- self._updateButtonBox()
-
- def _updateButtonBox(self):
- qmodel = self.getEditorQModel()
- changed = qmodel.isDataChanged()
- bb = self._buttonBox
- for button in bb.buttons():
- role = bb.buttonRole(button)
- if role == Qt.QDialogButtonBox.ApplyRole:
- button.setEnabled(changed)
- elif role == Qt.QDialogButtonBox.ResetRole:
- button.setEnabled(changed)
-
-
-def main_MntGrpChannelEditor(perspective="Channel"):
- w = MntGrpChannelEditor( perspective=perspective)
- w.setWindowTitle("A Taurus Measurement Group editor example")
- w.getQModel().setDataSource(DUMMY_MNGRPCFG_1)
- w.resize(1200,500)
- return w
-
-def main_MntGrpChannelPanel(mg, perspective="Channel"):
- w = MntGrpChannelPanel()
- w.setWindowIcon(getIcon(":/actions/system-shutdown.svg"))
- w.setWindowTitle("A Taurus Sardana measurement group Example")
- w.setModel(mg)
- w.show()
- return w
-
-
-def demo(model="mg2"):
- """Table panels"""
-# w = main_MntGrpChannelPanel(model)
- w = main_MntGrpChannelEditor()
- return w
-
-
-def main():
- import sys
- import taurus.qt.qtgui.application
- Application = taurus.qt.qtgui.application.TaurusApplication
-
- app = Application.instance()
- owns_app = app is None
-
- if owns_app:
- app = Application(app_name="Meas. group channel demo", app_version="1.0",
- org_domain="Sardana", org_name="Tango community")
-
- args = app.get_command_line_args()
- if len(args)==1:
- w = demo(model=args[0])
- else:
- w = demo()
- w.show()
-
- if owns_app:
- sys.exit(app.exec_())
- else:
- return w
-
-if __name__ == "__main__":
- main()
+
diff --git a/lib/taurus/qt/qtgui/graphic/jdraw/jdraw.py b/lib/taurus/qt/qtgui/graphic/jdraw/jdraw.py
index 2f03844..84d8707 100644
--- a/lib/taurus/qt/qtgui/graphic/jdraw/jdraw.py
+++ b/lib/taurus/qt/qtgui/graphic/jdraw/jdraw.py
@@ -36,7 +36,7 @@ from taurus.qt import Qt
import taurus.core
import taurus.core.util
-from taurus.qt.qtgui.graphic import TaurusBaseGraphicsFactory, TaurusGraphicsScene, TaurusGraphicsItem
+from taurus.qt.qtgui.graphic import TaurusBaseGraphicsFactory, TaurusGraphicsScene, TaurusGraphicsItem, parseTangoUri,TaurusTextAttributeItem,TaurusTextStateItem
LINESTYLE_JDW2QT = { 0: Qt.Qt.SolidLine,
@@ -186,7 +186,7 @@ class TaurusJDrawGraphicsFactory(taurus.core.util.Singleton, TaurusBaseGraphicsF
fnt = params.get('font')
if fnt:
family,style,size = fnt
- f = Qt.QFont(family, size, Qt.QFont.Light, False)
+ f = Qt.QFont(family, int(.85*size), Qt.QFont.Light, False)
f.setStyleHint(TEXTHINT_JDW2QT.get(family, Qt.QFont.AnyStyle))
f.setStyleStrategy(Qt.QFont.PreferMatch)
if style == 1:
@@ -209,8 +209,9 @@ class TaurusJDrawGraphicsFactory(taurus.core.util.Singleton, TaurusBaseGraphicsF
if children:
for child in children:
if child:
+ #self.info('jdraw.py: "%s".addItem("%s")'%(str(params.get('name')),str(child)))
item.addToGroup(child)
-
+ if item._fillStyle: self.set_item_filling(item,expand=True)
return item
def getSwingObjectObj(self,params):
@@ -245,16 +246,28 @@ class TaurusJDrawGraphicsFactory(taurus.core.util.Singleton, TaurusBaseGraphicsF
def set_common_params(self,item,params):
if not item:
return
- name = params.get('name')
+ item._params = params
+ name = params.get('name')
+ if self.alias:
+ for k,v in self.alias.items():
+ name = str(name).replace(k,v)
+
+ #Forcing not-Taurus items to have a name and be able to trigger events
+ setattr(item,'_name',name)
+ if name and not self._delayed:
+ if isinstance(item, TaurusGraphicsItem):
+ #self.debug('TaurusJDrawGraphicsFactory.set_common_params(): %s.setModel(%s)'%(item,name))
+ item.setModel(name)
+ else:
+ self.debug('TaurusJDrawGraphicsFactory.set_common_params(%s): %s is not a TaurusGraphicsItem'%(name,type(item).__name__))
visibilitymapper = params.get('visibilitymapper')
if not visibilitymapper is None:
mapping_type = visibilitymapper['mapping_type']
mode = visibilitymapper['mode']
default = visibilitymapper['default']
- map = visibilitymapper['map']
item._default = default
- item._map = map
+ item._map = visibilitymapper['map']
visible = params.get('visible')
if not visible is None:
@@ -264,66 +277,68 @@ class TaurusJDrawGraphicsFactory(taurus.core.util.Singleton, TaurusBaseGraphicsF
if extensions:
item._extensions = extensions
- try:
- getattr(item,'setPen')
+ if isinstance(item,Qt.QGraphicsTextItem):
+ try:
fg = params.get("foreground", (0,0,0))
- pen = Qt.QPen(Qt.QColor(fg[0],fg[1],fg[2]))
- pen.setWidth(params.get("lineWidth", 1))
- pen.setStyle(LINESTYLE_JDW2QT[params.get("lineStyle", 0)])
- item.setPen(pen)
- except AttributeError,ae:
- pass
- except Exception,e:
+ color = Qt.QColor(fg[0],fg[1],fg[2])
+ item.setDefaultTextColor(color)
+ except:
self.warning('jdraw.set_common_params(%s(%s)).(foreground,width,style) failed!: \n\t%s'%(type(item).__name__,name,traceback.format_exc()))
- fillStyle = FILLSTYLE_JDW2QT[params.get('fillStyle', 0)]
+ else:
+ try:
+ getattr(item,'setPen')
+ fg = params.get("foreground", (0,0,0))
+ pen = Qt.QPen(Qt.QColor(fg[0],fg[1],fg[2]))
+ pen.setWidth(params.get("lineWidth", 1))
+ pen.setStyle(LINESTYLE_JDW2QT[params.get("lineStyle", 0)])
+ item.setPen(pen)
+ except AttributeError,ae:
+ pass
+ except Exception,e:
+ self.warning('jdraw.set_common_params(%s(%s)).(foreground,width,style) failed!: \n\t%s'%(type(item).__name__,name,traceback.format_exc()))
- try:
- getattr(item,'setBrush')
- brush = Qt.QBrush()
- if fillStyle == Qt.Qt.RadialGradientPattern:
- x1, y1, x2, y2 = params.get('summit')
- w, h = (x2-x1)/2.0, (y2-y1)/2.0
- gradient = Qt.QLinearGradient(params.get('gradX1',0)+w,
- params.get('gradY1',0)+h,
- params.get('gradX2',0)+w,
- params.get('gradY2',0)+h)
- c = params.get('gradC1',(0,0,0))
- gradient.setColorAt(0,Qt.QColor(c[0],c[1],c[2]))
- c = params.get('gradC2',(255,255,255))
- gradient.setColorAt(1,Qt.QColor(c[0],c[1],c[2]))
- brush = Qt.QBrush(gradient)
- else:
- brush.setStyle(fillStyle)
-
- bg = params.get('background',(255,255,255))
- brush.setColor(Qt.QColor(bg[0],bg[1],bg[2]))
- item.setBrush(brush)
- except AttributeError,ae: pass
- except Exception,e:
- self.warning('jdraw.set_common_params(%s(%s)).(background,gradient,style) failed!: \n\t%s'%(type(item).__name__,name,traceback.format_exc()))
-
- name = params.get('name')
- if self.alias:
- for k,v in self.alias.items():
- name = str(name).replace(k,v)
+ fillStyle = FILLSTYLE_JDW2QT[params.get('fillStyle', 0)]
+ item._fillStyle = fillStyle
+ try:
+ if hasattr(item,'brush'):
+ brush = Qt.QBrush()
+ if fillStyle == Qt.Qt.RadialGradientPattern:
+ x1, y1, x2, y2 = params.get('summit')
+ w, h = (x2-x1)/2.0, (y2-y1)/2.0
+ gradient = Qt.QLinearGradient(params.get('gradX1',0)+w,
+ params.get('gradY1',0)+h,
+ params.get('gradX2',0)+w,
+ params.get('gradY2',0)+h)
+ c = params.get('gradC1',(0,0,0))
+ gradient.setColorAt(0,Qt.QColor(c[0],c[1],c[2]))
+ c = params.get('gradC2',(255,255,255))
+ gradient.setColorAt(1,Qt.QColor(c[0],c[1],c[2]))
+ brush = Qt.QBrush(gradient)
+ else:
+ brush.setStyle(fillStyle)
- #Forcing not-Taurus items to have a name and be able to trigger events
- setattr(item,'_name',name)
- if name and not self._delayed:
- if isinstance(item, TaurusGraphicsItem):
- self.debug('TaurusJDrawGraphicsFactory.set_common_params(): %s.setModel(%s)'%(item,name))
- item.setModel(name)
- else:
- self.debug('TaurusJDrawGraphicsFactory.set_common_params(%s): %s is not a TaurusGraphicsItem'%(name,type(item).__name__))
+ bg = params.get('background',(255,255,255))
+ brush.setColor(Qt.QColor(bg[0],bg[1],bg[2]))
+ item.setBrush(brush)
+ #except AttributeError,ae: pass
+ except Exception,e:
+ self.warning('jdraw.set_common_params(%s(%s)).(background,gradient,style) failed!: \n\t%s'%(type(item).__name__,name,traceback.format_exc()))
+ #self.debug('Out of TaurusJDrawGraphicsFactory.%s.set_common_params(%s)'%(type(item).__name__,name))
- self.debug('Out of TaurusJDrawGraphicsFactory.%s.set_common_params(%s)'%(type(item).__name__,name))
+ def set_item_filling(self,item,pattern=Qt.Qt.Dense4Pattern,expand=False):
+ count = 0
+ item._fillStyle = item._fillStyle or pattern
+ if hasattr(item,'brush'):
+ br = item.brush()
+ br.setStyle(item._fillStyle)
+ item.setBrush(br)
+ if expand:
+ for c in item.childItems():
+ if not c._fillStyle:
+ self.set_item_filling(c,pattern=pattern,expand=True)
+ return
if __name__ == "__main__":
- import sys
import jdraw_view
- app = Qt.QApplication([])
- gui = jdraw_view.TaurusJDrawSynopticsView()
- gui.setModel(sys.argv[1])
- gui.show()
- sys.exit(app.exec_())
\ No newline at end of file
+ jdraw_view.jdraw_view_main()
diff --git a/lib/taurus/qt/qtgui/graphic/jdraw/jdraw_parser.py b/lib/taurus/qt/qtgui/graphic/jdraw/jdraw_parser.py
index be499c3..95ff5ac 100644
--- a/lib/taurus/qt/qtgui/graphic/jdraw/jdraw_parser.py
+++ b/lib/taurus/qt/qtgui/graphic/jdraw/jdraw_parser.py
@@ -146,18 +146,21 @@ def p_single_element(p):
p[3] = {}
# determine the model name
- name = p[3].get('name')
+ org = name = p[3].get('name')
#change name: if item has no name it should take the name of its nearest-named-parent
+ #as no-name it is also considered any element with a name like a reserved keyword or plain value
keywords = ['JDGroup']+[n.replace('JD','') for n in reserved]
- if not name or name in keywords:
+ if not name or name in keywords or re.match('[0-9]+$',name):
+ #if re.match('[0-9]+$',name): p[3]['mapvalue'] = name
p[3]['name'] = name = ""
#print '\t name is empty, checking stack: %s' % [str(s) for s in reversed(p.parser.modelStack)]
for model in reversed(p.parser.modelStack):
- if model and model not in keywords:
+ if model and model not in keywords and not re.match('[0-9]+$',model):
+ #print 'Setting Model %s to %s' % (model,p[1])
p[3]['name'] = name = model
break
-
+ #print 'parser: %s => %s [%s]' % (org,name,p.parser.modelStack)
extension = p[3].get("extensions")
if p.parser.modelStack2:
if extension is None:
@@ -168,6 +171,7 @@ def p_single_element(p):
# create the corresponding element
factory = p.parser.factory
+ #p.parser.log.debug('ret = factory.getObj(%s,%s)'% (str(p[1]),str(p[3])))
ret = factory.getObj(p[1],p[3])
if ret is None:
@@ -208,8 +212,8 @@ def p_parameter(p):
def p_single_parameter(p):
'''parameter : SYMBOL TWOP param_value'''
+ #modelStacks added in this method control how extensions/models/names are propagated within JDGroup items
if p[1] == 'name':
- # if p[3]:
p.parser.modelStack.append(p[3])
if p[1] == 'extensions':
p.parser.modelStack2.append(p[3])
diff --git a/lib/taurus/qt/qtgui/graphic/jdraw/jdraw_view.py b/lib/taurus/qt/qtgui/graphic/jdraw/jdraw_view.py
index 7b15ad3..f735d85 100644
--- a/lib/taurus/qt/qtgui/graphic/jdraw/jdraw_view.py
+++ b/lib/taurus/qt/qtgui/graphic/jdraw/jdraw_view.py
@@ -32,10 +32,11 @@ __docformat__ = 'restructuredtext'
import os
import traceback
import subprocess
+import taurus
from taurus.qt import Qt
from taurus.core import DeviceNameValidator,AttributeNameValidator
+from taurus.qt.qtgui.graphic.taurusgraphic import parseTangoUri
from taurus.qt.qtcore.mimetypes import TAURUS_ATTR_MIME_TYPE, TAURUS_DEV_MIME_TYPE, TAURUS_MODEL_MIME_TYPE
-
from taurus.qt.qtgui.base import TaurusBaseWidget
import jdraw_parser
@@ -59,7 +60,7 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
allows to configure custom context menus for graphic items using a list
of tuples. Empty tuples will insert separators in the menu.
'''
- __pyqtSignals__ = ("itemsChanged","graphicItemSelected(QString)","graphicSceneClicked(QPoint)")
+ __pyqtSignals__ = ("itemsChanged","modelsChanged","graphicItemSelected(QString)","graphicSceneClicked(QPoint)")
def __init__(self, parent = None, designMode = False, updateMode=None, alias = None, resizable = True):
name = self.__class__.__name__
@@ -100,15 +101,11 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
self.emitColors()
def openJDraw(self):
- ifile = Qt.QFileDialog.getOpenFileName( self, 'Load JDraw File', '', 'JDraw File (*.jdw)')
- if ifile.isEmpty(): return
- if not isinstance(ifile,file):
- #clear TaurusPLot - emit sygnal
- #self.emit(Qt.SIGNAL("clearTaurusPlot"))
- fileName = ifile.split("/")
- #self.emit(Qt.SIGNAL("setTopItemName"),fileName[-1])
- self._fileName = fileName[-1]
- self.setModel(ifile)
+ ifile = unicode(Qt.QFileDialog.getOpenFileName( self, 'Load JDraw File', '', 'JDraw File (*.jdw)'))
+ if not ifile: return
+ fileName = ifile.split("/")
+ self._fileName = fileName[-1]
+ self.setModel(ifile)
return fileName[-1]
def setAlias(self,alias):
@@ -122,6 +119,10 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
def get_item_list(self):
return [item._name for item in self.scene().items() if hasattr(item,'_name') and item._name]
+ def get_device_list(self):
+ items = [(item,parseTangoUri(item)) for item in self.get_item_list()]
+ return list(set(v['devicename'] for k,v in items if v))
+
def get_item_colors(self,emit = False):
item_colors = {}
try:
@@ -142,12 +143,18 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
@Qt.pyqtSignature("graphicItemSelected(QString)")
def graphicItemSelected(self,item_name):
+ self.info(' => graphicItemSelected(QString)(%s)'%item_name)
self.emit(Qt.SIGNAL("graphicItemSelected(QString)"),item_name)
@Qt.pyqtSignature("graphicSceneClicked(QPoint)")
def graphicSceneClicked(self,point):
self.debug('In TaurusJDrawSynopticsView.graphicSceneClicked(%s,%s)'%(point.x(),point.y()))
self.emit(Qt.SIGNAL("graphicSceneClicked(QPoint)"),point)
+
+ def modelsChanged(self):
+ items = self.get_item_list()
+ self.debug('modelsChanged(%s)'%len(items))
+ self.emit(Qt.SIGNAL("modelsChanged"),items)
def emitColors(self):
'''emit signal which is used to refresh the tree and colors of icons depend of the current status in jdrawSynoptic'''
@@ -155,8 +162,12 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
def get_sizes(self):
srect = self.scene().sceneRect()
- sizes = [self.size(),self.sizeHint(),srect.size(),self.parent().size()]
- return tuple([x for s in sizes for x in [s.width(),s.height()]])
+ sizes = [x for s in (self.size(),self.sizeHint(),srect.size()) for x in (s.width(),s.height())]
+ try:
+ s = self.parent().size()
+ sizes.extend([s.width(),s.height()])
+ except: sizes.extend([0,0])
+ return tuple(sizes)
def fitting(self,ADJUST_FRAME = False):
"""
@@ -164,7 +175,8 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
Rect size never changes (fixed by the graphics objects)
Size and SizeHint move one around the other
- the method works well until an object is clicked, then the whole reference changes and doesn't work again.
+ the method works well until an object is clicked,
+ then the whole reference changes and doesn't work again.
"""
srect = self.scene().sceneRect()
@@ -180,9 +192,12 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
if ADJUST_FRAME: #This additional resizing adjust the "white" frame around the synoptic
self.debug('\tResizing: size(%s,%s),hint(%s,%s),srect(%s,%s),parent(%s,%s)'%self.get_sizes())
- self.resize(self.sizeHint())
- self.debug('\tFitting:: size(%s,%s),hint(%s,%s),srect(%s,%s),parent(%s,%s)'%self.get_sizes())
- self.fitInView(x0,y0,w,h,Qt.Qt.KeepAspectRatio) #Doesn't work
+ self.resize(self.sizeHint()+Qt.QSize(5,5))
+
+ #THIS LINE MUST BE ALWAYS EXECUTED, It prevents the UP/DOWN resize BUG!!!
+ #apparently Qt needs this 2 fitInView calls to be aware of it, maybe first offset was not good
+ self.debug('\tFitting:: size(%s,%s),hint(%s,%s),srect(%s,%s),parent(%s,%s)'%self.get_sizes())
+ self.fitInView(x0,y0,w,h,Qt.Qt.KeepAspectRatio)
self.debug('Done: size(%s,%s),hint(%s,%s),srect(%s,%s),parent(%s,%s)\n\n'%self.get_sizes())
@@ -241,34 +256,57 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
self.mousePos = event.scenePos().x(),event.scenePos().y()
except:
self.mousePos = event.x(), event.y()
- self.warning('Unable to get scene pos, using %s'%str(self.mousePos))
+ self.debug('MouseEvent received is not a GraphicsScene event, using raw position %s'%str(self.mousePos))
TaurusBaseWidget.mousePressEvent(self,event)
def getModelMimeData(self):
""" Used for drag events """
- mimeData = TaurusBaseWidget.getModelMimeData(self)
- model = getattr(self.scene().itemAt(*self.mousePos),'_name','')
- if model:
- if DeviceNameValidator().getParams(model):
- self.debug('getMimeData(): DeviceModel at %s: %s',self.mousePos,model)
- mimeData.setData(TAURUS_DEV_MIME_TYPE,model)
- elif AttributeNameValidator().getParams(model):
- self.debug('getMimeData(): AttributeModel at %s: %s',self.mousePos,model)
- mimeData.setData(TAURUS_ATTR_MIME_TYPE,model)
- else:
- self.debug('getMimeData(): UnknownModel at %s: %s',self.mousePos,model)
+ model,mimeData = '',None
+ try:
+ #model = getattr(self.scene().itemAt(*self.mousePos),'_name','')
+ model = getattr(self.scene()._selectedItems[0],'_name','')
+ self.debug('getModelMimeData(%s)'%model)
+ mimeData = Qt.QMimeData()
+ if model:
+ if DeviceNameValidator().getParams(model):
+ self.debug('getMimeData(): DeviceModel at %s: %s',self.mousePos,model)
+ mimeData.setData(TAURUS_DEV_MIME_TYPE,model)
+ elif AttributeNameValidator().getParams(model):
+ self.debug('getMimeData(): AttributeModel at %s: %s',self.mousePos,model)
+ mimeData.setData(TAURUS_ATTR_MIME_TYPE,model)
+ else:
+ self.debug('getMimeData(): UnknownModel at %s: %s',self.mousePos,model)
+ mimeData.setData(TAURUS_MODEL_MIME_TYPE, model)
+ except:
+ self.warning('jdrawView.getModelMimeData(%s): unable to get MimeData'%model)
+ self.warning(traceback.format_exc())
return mimeData
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# QT properties
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+
+ @staticmethod
+ def setDefaultPanelClass(other):
+ TaurusJDrawSynopticsView._defaultClass = other
+ @staticmethod
+ def defaultPanelClass():
+ if not hasattr(TaurusJDrawSynopticsView,'_defaultClass'):
+ #from taurus.qt.qtgui.panel import TaurusDevicePanel
+ TaurusJDrawSynopticsView._defaultClass = 'taurusdevicepanel'
+ #print('defaultPanelClass == %s'%TaurusJDrawSynopticsView._defaultClass)
+ obj = TaurusJDrawSynopticsView._defaultClass
+ return obj
@Qt.pyqtSignature("setModel(QString)")
- def setModel(self, model, alias = None, delayed = False):
+ def setModel(self, model, alias = None, delayed = False, trace = False):
self.modelName = str(model)
self._currF = str(model)
if alias is not None: self.setAlias(alias)
+ ll = taurus.getLogLevel()
+ if trace: taurus.setLogLevel(taurus.Debug)
+ self.info('setModel("%s")'%model)
if self._currF:
#filename = str(self._currFile.absoluteFilePath())
filename = self._currF
@@ -288,12 +326,23 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
self.setScene(scene)
Qt.QObject.connect(self.scene(), Qt.SIGNAL("graphicItemSelected(QString)"), self, Qt.SLOT("graphicItemSelected(QString)"))
Qt.QObject.connect(self.scene(), Qt.SIGNAL("graphicSceneClicked(QPoint)"), self, Qt.SLOT("graphicSceneClicked(QPoint)"))
- #self.emit_signal()
+ Qt.QObject.connect(Qt.QApplication.instance(), Qt.SIGNAL("lastWindowClosed()"), self.scene().panel_launcher.kill )
+ self.modelsChanged()
+ self.setWindowTitle(self.modelName)
+ #The emitted signal contains the filename and a dictionary with the name of items and its color
+ self.emitColors()#get_item_colors(emit=True)
+ self.fitting(True)
else:
self.setScene(None)
- #self.emit_signal()
- #The emitted signal contains the filename and a dictionary with the name of items and its color
- self.emitColors()#get_item_colors(emit=True)
+ self.info('out of setModel()')
+ taurus.setLogLevel(ll)
+
+ #def destroy(destroyWindow=True,destroySubWindows=True):
+ def closeEvent(self,event):
+ try: self.scene().panel_launcher.kill()
+ except: print(traceback.format_exc())
+ Qt.QGraphicsView.closeEvent(self,event)
+ #Qt.QGraphicsView.destroy(self,destroyWindow,destroySubWindows)
def setModels(self):
""" This method triggers item.setModel(item._name) in all internal items. """
@@ -305,20 +354,20 @@ class TaurusJDrawSynopticsView(Qt.QGraphicsView, TaurusBaseWidget):
def getModel(self):
return self._currF
- @classmethod
- def getQtDesignerPluginInfo(cls):
- ret = TaurusBaseWidget.getQtDesignerPluginInfo()
- ret['group'] = 'Taurus Display'
- ret['module'] = 'taurus.qt.qtgui.graphic'
- ret['icon'] = ":/designer/graphicsview.png"
- return ret
+ #@classmethod
+ #def getQtDesignerPluginInfo(cls):
+ #ret = TaurusBaseWidget.getQtDesignerPluginInfo()
+ #ret['group'] = 'Taurus Display'
+ #ret['module'] = 'taurus.qt.qtgui.graphic'
+ #ret['icon'] = ":/designer/graphicsview.png"
+ #return ret
- model = Qt.pyqtProperty("QString", getModel, setModel)
+ #model = Qt.pyqtProperty("QString", getModel, setModel)
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-if __name__ == "__main__":
- import sys
+def jdraw_view_main():
+ import sys,time
import taurus.qt.qtgui.graphic
taurus.setLogLevel(taurus.Info)
app = Qt.QApplication(sys.argv)
@@ -330,21 +379,17 @@ if __name__ == "__main__":
#for m in sys.argv[1:]:
#tv=TaurusJDrawSynopticsView(container, designMode=False)
#tv.setModel(m)
-
+ print '%s init()'%(time.ctime())
form = taurus.qt.qtgui.graphic.TaurusJDrawSynopticsView(designMode=False)
+ form.show()
+ print '%s setModel(%s)'%(time.ctime(),sys.argv[1])
form.setModel(sys.argv[1])
-
- def kk(*args):print("\tgraphicItemSelected(%s)"%str(args))
- form.connect(form,Qt.SIGNAL("graphicItemSelected(QString)"), kk)
-
- SCROLL_BARS_WORK_PROPERLY = True
- if SCROLL_BARS_WORK_PROPERLY:
- panel = Qt.QFrame()
- layout = Qt.QGridLayout()
- layout.addWidget(form)
- panel.setLayout(layout)
- panel.show()
- else:
- form.show()
-
+ form.setWindowTitle(sys.argv[1].rsplit('.',1)[0])
+ #def kk(*args):print("\tgraphicItemSelected(%s)"%str(args))
+ #form.connect(form,Qt.SIGNAL("graphicItemSelected(QString)"), kk)
+ print '%s fitting()'%time.ctime()
+ form.fitting()
sys.exit(app.exec_())
+
+if __name__ == "__main__":
+ jdraw_view_main()
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/graphic/taurusgraphic.py b/lib/taurus/qt/qtgui/graphic/taurusgraphic.py
index 4e93bf4..2724cea 100644
--- a/lib/taurus/qt/qtgui/graphic/taurusgraphic.py
+++ b/lib/taurus/qt/qtgui/graphic/taurusgraphic.py
@@ -45,12 +45,20 @@ import Queue
from taurus.qt import Qt
import taurus.core
-from taurus.core import DeviceNameValidator
+from taurus.core import DeviceNameValidator,AttributeNameValidator
import taurus.core.util
from taurus.qt.qtgui.base import TaurusBaseComponent
from taurus.qt.qtgui.util import QT_ATTRIBUTE_QUALITY_PALETTE, QT_DEVICE_STATE_PALETTE
+def parseTangoUri(name):
+ from taurus.core import tango,AttributeNameValidator,DeviceNameValidator
+ validator = {tango.TangoDevice:DeviceNameValidator,tango.TangoAttribute:AttributeNameValidator}
+ try:
+ params = validator[tango.TangoFactory().findObjectClass(name)]().getParams(name)
+ return (params if 'devicename' in params else None)
+ except:
+ return None
class TaurusGraphicsUpdateThread(Qt.QThread):
@@ -168,6 +176,8 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
of tuples. Empty tuples will insert separators in the menu.
'''
__pyqtSignals__ = ("refreshTree2","graphicItemSelected(QString)","graphicSceneClicked(QPoint)")
+ ANY_ATTRIBUTE_SELECTS_DEVICE = True
+ TRACE_ALL = False
def __init__(self, parent = None, strt = True):
name = self.__class__.__name__
@@ -177,31 +187,38 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
self.updateThread = None
self._itemnames = taurus.core.util.CaselessDefaultDict(lambda k:set())
self._selection = []
+ self._selectedItems = []
self.threads = []
self.pids = []
+ self.panel_launcher = taurus.qt.qtgui.util.ExternalAppAction(parent.defaultPanelClass().split() if parent else ['taurusdevicepanel'])
try:
self.logger = taurus.core.util.Logger(name)
#self.logger.setLogLevel(self.logger.Info)
- self.debug = lambda l: self.logger.debug(l)
- self.info = lambda l: self.logger.info(l)
- self.warning = lambda l: self.logger.warning(l)
- self.error = lambda l: self.logger.error(l)
+ if not self.TRACE_ALL:
+ self.debug = lambda l: self.logger.debug(l)
+ self.info = lambda l: self.logger.info(l)
+ self.warning = lambda l: self.logger.warning(l)
+ else: self.debug = self.info = self.warning = self.error = lambda l: self.logger.warning(l)
except:
print 'Unable to initialize TaurusGraphicsSceneLogger: %s'%traceback.format_exc()
self.setSelectionMark()
if strt:self.start()
+
+ def __del__(self):
+ self.panel_launcher.kill()
+ Qt.QGraphicsScene.__del__(self)
def addItem(self,item):
- self.debug('addItem(%s)'%item)
+ #self.debug('addItem(%s)'%item)
def expand(i):
name = str(getattr(i,'_name','')).lower()
if name:
self._itemnames[name].add(i)
- self.debug('addItem(%s): %s'%(name,i))
+ #self.debug('addItem(%s): %s'%(name,i))
if isinstance(i,Qt.QGraphicsItemGroup):
- for j in i.children():
+ for j in i.childItems():
expand(j)
expand(item)
Qt.QGraphicsScene.addItem(self,item)
@@ -213,34 +230,67 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
if flags is None: Qt.QGraphicsScene.addWidget(self,item)
else: Qt.QGraphicsScene.addWidget(self,item,flags)
- def getItemByName(self,item_name):
+ def getItemByName(self,item_name,strict=None):
"""
- Returns a list with all items matching a given name
+ Returns a list with all items matching a given name.
+ :param: strict, controls wheter full_name (strict=True) or only device name (False) must match
"""
+ strict = (not self.ANY_ATTRIBUTE_SELECTS_DEVICE) if strict is None else strict
+ alnum = '(?:[a-zA-Z0-9-_\*]|(?:\.\*))(?:[a-zA-Z0-9-_\*]|(?:\.\*))*'
target = str(item_name).strip().split()[0].lower().replace('/state','') #If it has spaces only the first word is used
- if DeviceNameValidator().getParams(target): target+='(/state)?'
- #if not target.endswith('$'): target+='$' #Device names should match also its attributes?
+ #Device names should match also its attributes or only state?
+ if not strict and AttributeNameValidator().getParams(target):
+ target = target.rsplit('/',1)[0]
+ if DeviceNameValidator().getParams(target):
+ if strict: target+='(/state)?'
+ else: target+='(/'+alnum+')?'
+ if not target.endswith('$'): target+='$'
result = []
for k in self._itemnames.keys():
if re.match(target.lower(),k.lower()):
- #self.debug('getItemByName(%s): _itemnames[%s]: %s'%(target,k,self._itemnames[k]))
+ #self.info('getItemByName(%s): _itemnames[%s]: %s'%(target,k,self._itemnames[k]))
result.extend(self._itemnames[k])
return result
+
+ def getItemByPosition(self,x,y):
+ """ This method will try first with named objects; if failed then with itemAt """
+ pos = Qt.QPointF(x,y)
+ itemsAtPos = []
+ for z,o in sorted((i.zValue(),i) for v in self._itemnames.values() for i in v if i.contains(pos)):
+ if not hasattr(o,'getExtensions'):
+ self.debug('getItemByPosition(%d,%d): adding Qt primitive %s'%(x,y,o))
+ itemsAtPos.append(o)
+ elif not o.getExtensions().get('noSelect'):
+ self.debug('getItemByPosition(%d,%d): adding GraphicsItem %s'%(x,y,o))
+ itemsAtPos.append(o)
+ else: self.debug('getItemByPosition(%d,%d): object ignored, %s'%(x,y,o))
+ if itemsAtPos:
+ obj = itemsAtPos[-1]
+ return self.getTaurusParentItem(obj) or obj
+ else:
+ #return self.itemAt(x,y)
+ self.warning('getItemByPosition(%d,%d): no items found!'%(x,y))
+ return None
+
+ def getItemClicked(self,mouseEvent):
+ pos = mouseEvent.scenePos()
+ x,y = pos.x(),pos.y()
+ self.emit(Qt.SIGNAL("graphicSceneClicked(QPoint)"),Qt.QPoint(x,y))
+ obj = self.getItemByPosition(x,y)
+ self.info('mouse clicked on %s(%s) at (%s,%s)'%(type(obj).__name__,getattr(obj,'_name',''),x,y))
+ return obj
def mousePressEvent(self,mouseEvent):
- #self.debug('In TaurusGraphicsScene.mousePressEvent(%s))'%str(mouseEvent.button()))
+ self.info('In TaurusGraphicsScene.mousePressEvent(%s,%s))'%(str(type(mouseEvent)),str(mouseEvent.button())))
try:
- x = mouseEvent.scenePos().x()
- y = mouseEvent.scenePos().y()
- self.emit(Qt.SIGNAL("graphicSceneClicked(QPoint)"),Qt.QPoint(x,y))
- obj = self.itemAt(x,y)
+ obj = self.getItemClicked(mouseEvent)
obj_name = getattr(obj,'_name', '')
- self.debug('mouse clicked on %s (%s,%s)' , type(obj).__name__,x,y)
-
+ if not obj_name and isinstance(obj,Qt.QGraphicsTextItem): obj_name = obj.toPlainText()
if (mouseEvent.button() == Qt.Qt.LeftButton):
- self.selectGraphicItem(obj_name) # A null obj_name should deselect all, we don't send obj because we want all similar to be matched
- self.emit(Qt.SIGNAL("graphicItemSelected(QString)"),obj_name) # A null obj_name should deselect all
-
+ ## A null obj_name should deselect all, we don't send obj because we want all similar to be matched
+ if self.selectGraphicItem(obj_name):
+ self.debug(' => graphicItemSelected(QString)(%s)'%obj_name)
+ self.emit(Qt.SIGNAL("graphicItemSelected(QString)"),obj_name) # A null obj_name should deselect all
def addMenuAction(menu,k,action,last_was_separator=False):
try:
if k:
@@ -255,14 +305,15 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
except Exception,e:
self.warning('Unable to add Menu Action: %s:%s'%(k,e))
return last_was_separator
-
if (mouseEvent.button() == Qt.Qt.RightButton):
''' This function is called when right clicking on TaurusDevTree area. A pop up menu will be shown with the available options. '''
- self.debug('RightButton Mouse Event on %s (%s,%s)',obj_name,x,y)
+ self.debug('RightButton Mouse Event on %s'%(obj_name))
if isinstance(obj,TaurusGraphicsItem) and (obj_name or obj.contextMenu() or obj.getExtensions()):
menu = Qt.QMenu(None)#self.parent)
last_was_separator = False
- if obj_name: menu.addAction(obj_name)
+ if obj_name:
+ #menu.addAction(obj_name)
+ addMenuAction(menu,obj_name,lambda x=obj_name: self.panel_launcher.actionTriggered([x]))
if obj.contextMenu():
if obj_name:
menu.addSeparator()
@@ -275,14 +326,23 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
addMenuAction(menu,'Execute',lambda d,x=obj: self.getShellCommand(x))
if obj.getExtensions().get('className'):
self.debug('launching className extension object')
- addMenuAction(menu,obj.getExtensions().get('className'),lambda d,x=obj: self.getClassName(x))
+ addMenuAction(menu,obj.getExtensions().get('className'),lambda d,x=obj: self.getClassName(x))
if not menu.isEmpty():
menu.exec_(Qt.QPoint(mouseEvent.screenPos().x(),mouseEvent.screenPos().y()))
del menu
except Exception,e:
- self.error( traceback.format_exc())
+ self.error( traceback.format_exc())
+ def mouseDoubleClickEvent(self,event):
+ try:
+ obj = self.getItemClicked(event)
+ obj_name = getattr(obj,'_name', '')
+ if obj_name: self.panel_launcher.actionTriggered([obj_name])
+ except:
+ self.error( traceback.format_exc())
+
def launchProcess(self,process):
+ """ This method is DEPRECATED, use taurus.qt.qtgui.util.ExternalAppAction instead! """
if not hasattr(self,'ChildrenProcesses'): self.ChildrenProcesses = {}
if process in self.ChildrenProcesses:
self.warning( 'Process %s is already running!'%process)
@@ -291,6 +351,7 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
return
def killProcess(self,regexp):
+ """ This method is DEPRECATED, use taurus.qt.qtgui.util.ExternalAppAction instead! """
if '*' in regexp and not '.*' in regexp:
regexp = regexp.replace('*','.*')
for name,process in self.ChildrenProcesses.iteritems():
@@ -305,25 +366,40 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
A blue circle is drawn around the matching item name.
If the item_name is empty, or it is a reserved keyword, or it has the "noSelect" extension, then the blue circle is removed from the synoptic.
"""
- #self.info('In TaurusGraphicsScene.selectGraphicItem(%s))',item_name)
+ self.debug('In TaurusGraphicsScene.selectGraphicItem(%s))'%item_name)
retval = False
- self.clearSelection()
+ selected = [str(getattr(item,'_name',item)) for item in self._selectedItems if item]
+ if selected:
+ iname = str(getattr(item_name,'_name',item_name))
+ #self.info('In TauGraphicsScene.selectGraphicItem(%s): already selected: %s'%(iname,selected))
+ if not iname.strip():
+ self.clearSelection()
+ return False
+ elif any(iname not in i for i in selected):
+ self.clearSelection()
+ else:
+ self.info('In TauGraphicsScene.selectGraphicItem(%s): already selected!'%item_name)
+ return True
if any(isinstance(item_name,t) for t in (TaurusGraphicsItem,Qt.QGraphicsItem)):
if not getattr(item_name,'_name', ''):
- self.warning('In TauGraphicsScene.selectGraphicItem(%s): item name not found.'%item_name)
+ self.debug('In TauGraphicsScene.selectGraphicItem(%s): item name not found.'%item_name)
return False
items = [item_name]
else:
from taurus.qt.qtgui.graphic import jdraw_parser
if not item_name or (str(item_name).startswith('JD') and str(item_name) in jdraw_parser.reserved):
- self.warning('In TauGraphicsScene.selectGraphicItem(%s): item name not found or name is a reserved keyword.'%item_name)
+ self.debug('In TauGraphicsScene.selectGraphicItem(%s): item name not found or name is a reserved keyword.'%item_name)
return False
- items = self.getItemByName(item_name) or []
+ items = self.getItemByName(item_name) or []
+ items = [i for i in items if self.getTaurusParentItem(i) not in (items+self._selectedItems)]
self.debug('In TaurusGraphicsScene.selectGraphicItem(%s)): matched %d items'%(item_name,len(items)))
for item in items:
try:
- if isinstance(item,TaurusGraphicsItem) and item.getExtensions().get('noSelect'):
+ if ( (isinstance(item,TaurusGraphicsItem) and item.getExtensions().get('noSelect'))
+ or (item in self._selection)
+ #or (item in tangoGroup)
+ ):
continue
x,y = item.x(),item.y()
rect = item.boundingRect()
@@ -334,12 +410,13 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
x,y = rx,ry #If the object is in the corner it will be also 0
w,h= rect.width(),rect.height()
if x<0 or y<0:
- self.warning('Cannot draw SelectionMark for %s(%s)(%s,%s) in a negative position (%f,%f)' % (type(item).__name__,item._name,w,h,x,y))
+ self.debug('Cannot draw SelectionMark for %s(%s)(%s,%s) in a negative position (%f,%f)' % (type(item).__name__,item._name,w,h,x,y))
else:
if type(item) in (TaurusTextAttributeItem,TaurusTextStateItem) and isinstance(self.getSelectionMark(),Qt.QGraphicsPixmapItem):
x,y,w,h = x-20,y,20,20
self.drawSelectionMark(x,y,w,h)
self.debug('> Moved the SelectionMark to item %s(%s)(%s,%s) at %f,%f' % (type(item).__name__,item._name,w,h,x,y))
+ if item not in self._selectedItems: self._selectedItems.append(item)
retval = True
except Exception,e:
self.warning('selectGraphicsItem(%s) failed! %s' % (getattr(item,'_name',item),str(e)))
@@ -348,11 +425,12 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
return retval
def clearSelection(self):
- self.debug('In clearSelection([%d])'%len(self._selection))
+ self.debug('In clearSelection([%d])'%len(self._selectedItems))
for i in self._selection:
i.hide()
self.removeItem(i)
self._selection = []
+ self._selectedItems = []
self.updateSceneViews()
def setSelectionMark(self,picture=None,w=10,h=10):
@@ -516,14 +594,38 @@ class TaurusGraphicsScene(Qt.QGraphicsScene):
obj.setVisible(True)
obj.exec_()
return
+
+ @staticmethod
+ def getTaurusParentItem(item,top=True):
+ """ Searches within a group hierarchy and returns a parent Taurus component or None if no parent TaurusBaseComponent
+ is found."""
+ if item is None: return None
+ first,p,next= None,item.parentItem(),None
+ while p:
+ if isinstance(p, TaurusGraphicsItem):
+ if first is None:
+ first = p
+ if not top: break
+ elif str(p.getModel())!=str(first.getModel()):
+ break
+ else: first = p
+ p = p.parentItem()
+ return first
+
+ def getAllChildren(self,item,klass=None):
+ """ Returns all children elements, filtering by klass if wanted """
+ result = []
+ try:
+ children = item.childItems()
+ result.extend(c for c in children if not klass or isinstance(c,klass))
+ result.extend(c.childItems() for c in children if not klass or isinstance(c,klass))
+ except: pass
+ return result
def getTaurusDevicePanel(self,obj,standAlone=False):
try:
- try:
- from taurusDevicePanel import taurusDevicePanel
- except:
- from taurusPanel.taurusDevicePanel import taurusDevicePanel
- nameclass = taurusDevicePanel()
+ from taurus.qt.qtgui.panel import TaurusDevicePanel
+ nameclass = TaurusDevicePanel()
name = "TaurusDevicePanel"
nameclass.setModel(obj._name)
nameclass.setSpectraAtkMode(True)
@@ -569,19 +671,27 @@ class TaurusGraphicsItem(TaurusBaseComponent):
"""Base class for all Taurus Graphics Items"""
def __init__(self, name = None, parent = None):
- self._name = name or self.__class__.__name__ #srubio at cells.es: modified to store ._name since initialization (even if a model is not set)
- self.call__init__(TaurusBaseComponent, name, parent)
- self._name = None #@todo A set/getGraphicName getter/setter should be implemented
+ self.call__init__(TaurusBaseComponent, name, parent) #<- log created here
+ #self.debug('TaurusGraphicsItem(%s,%s)' % (name,parent))
+ self.setName(name)
self._currFgBrush = None
self._currBgBrush = None
self._currText = None
- self._currHtmlText = None
+ self._currHtmlText = None
self._map = None
self._default = None
self._visible = None
- self.getExtensions()
+ #self.getExtensions() <= It must be called AFTER set_common_params() in getGraphicsItem()
self._contextMenu = []
+ def setName(self,name):
+ #print 'In %s.setName(%s)' % (self.__class__.__name__,name)
+ name = str(name or self.__class__.__name__)
+ self._name = name#srubio at cells.es: modified to store ._name since initialization (even if a model is not set)
+
+ def getName(self):
+ return self._name
+
def setContextMenu(self,menu):
'''Context Menu must be a list of tuples (ActionName,ActionMethod), empty tuples insert separators between options.'''
self._contextMenu = menu
@@ -603,9 +713,12 @@ class TaurusGraphicsItem(TaurusBaseComponent):
self.noPrompt = self._extensions.get('noPrompt',False)
self.standAlone = self._extensions.get('standAlone',False)
self.noTooltip = self._extensions.get('noTooltip',False)
- self.ignoreRepaint = self._extensions.get('ignoreRepaint',False)
- self.setToolTip('' if self.noTooltip else str(self._name))
- self.debug('getExtensions(): %s'%self._extensions)
+ self.ignoreRepaint = self._extensions.get('ignoreRepaint',False)
+ self.setName(self._extensions.get('name',self._name))
+ tooltip = '' if (self.noTooltip or self._name==self.__class__.__name__ or self._name is None) else str(self._name)
+ #self.debug('setting %s.tooltip = %s'%(self._name,tooltip))
+ self.setToolTip(tooltip)
+ #self.debug('%s.getExtensions(): %s'%(self._name,self._extensions))
return self._extensions
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
@@ -614,7 +727,7 @@ class TaurusGraphicsItem(TaurusBaseComponent):
def setModel(self,model):
#self.info('In %s.setModel(%s)'%(type(self).__name__,model))
- self._name = str(model)
+ self.setName(model)
if taurus.core.TaurusManager().findObjectClass(self._name) == taurus.core.tango.TangoDevice:
model = self._name+'/state'
TaurusBaseComponent.setModel(self, model)
@@ -637,12 +750,13 @@ class TaurusGraphicsItem(TaurusBaseComponent):
in the style."""
if self.scene():
self.scene().updateSceneItem(self)
-
- def getDisplayValue(self):
- attrvalue = self.getModelValueObj()
- if not attrvalue:
- return self.getNoneValue()
- return str(attrvalue.value)
+
+ #getDisplayValue is already (and better) implemented in TaurusBaseComponent
+ #def getDisplayValue(self):
+ #attrvalue = self.getModelValueObj()
+ #if not attrvalue:
+ #return self.getNoneValue()
+ #return str(attrvalue.value)
def isReadOnly(self):
return True
@@ -675,7 +789,7 @@ class TaurusGraphicsAttributeItem(TaurusGraphicsItem):
def updateStyle(self):
v = self.getModelValueObj()
self._currText = self.getDisplayValue()
-
+ #self.debug('In TaurusGraphicsAttributeItem(%s).updateStyle(%s)'%(self.getName(),self._currText))
if self.getShowQuality():
try:
quality = None
@@ -723,18 +837,28 @@ class TaurusGraphicsStateItem(TaurusGraphicsItem):
if self.getModelObj().getType() == PyTango.ArgType.DevState:
bg_brush, fg_brush = QT_DEVICE_STATE_PALETTE.qbrush(v.value)
elif self.getModelObj().getType() == PyTango.ArgType.DevBoolean:
- bg_brush, fg_brush = QT_DEVICE_STATE_PALETTE.qbrush((PyTango.DevState.FAULT,PyTango.DevState.ON)[v.value])
+ bg_brush, fg_brush = QT_DEVICE_STATE_PALETTE.qbrush((PyTango.DevState.FAULT,PyTango.DevState.ON)[v.value])
elif self.getShowQuality():
bg_brush, fg_brush = QT_ATTRIBUTE_QUALITY_PALETTE.qbrush(v.quality)
if None not in (bg_brush,fg_brush):
self._currBgBrush = bg_brush
self._currFgBrush = fg_brush
+ #If there's no filling, applying background brush to foreground
+ if Qt.Qt.NoBrush!=getattr(self,'_fillStyle',Qt.Qt.NoBrush):
+ #self.debug('In TaurusGraphicsStateItem(%s).updateStyle(%s): switching background to foreground'%(self._name,self._currText))
+ self._currFgBrush = bg_brush
if self._currText: self._currHtmlText = '<p style="color:%s">%s</p>' % (self._currBgBrush.color().name(),self._currText)
except:
self.warning('In TaurusGraphicsStateItem(%s).updateStyle(%s): colors failed!'%(self._name,self._currText))
self.warning(traceback.format_exc())
- states = {'ON':0,'OFF':1,'CLOSE':2,'OPEN':3,'INSERT':4,'EXTRACT':5,'MOVING':6,'STANDBY':7,'FAULT':8,'INIT':9,'RUNNING':10,'ALARM':11,'DISABLE':12,'UNKNOWN':13}
+ states = {
+ 'ON':0,'OFF':1,'CLOSE':2,'OPEN':3,
+ 'INSERT':4,'EXTRACT':5,'MOVING':6,
+ 'STANDBY':7,'FAULT':8,'INIT':9,
+ 'RUNNING':10,'ALARM':11,'DISABLE':12,
+ 'UNKNOWN':13
+ }
#Parsing _map to manage visibility (a list of values for which the item is visible or not)
if v and not self._map is None and self._currText in states:
#self.info('In TaurusGraphicsStateItem.updateStyle(): mapping %s'%self._currText)
@@ -783,7 +907,6 @@ class TaurusGroupStateItem(Qt.QGraphicsItemGroup, TaurusGraphicsStateItem):
def paint(self,painter,option,widget):
Qt.QGraphicsItemGroup.paint(self,painter,option,widget)
-
class TaurusPolygonStateItem(Qt.QGraphicsPolygonItem,TaurusGraphicsStateItem):
@@ -839,6 +962,7 @@ class TaurusTextAttributeItem(Qt.QGraphicsTextItem, TaurusGraphicsAttributeItem,
self.call__init__(TaurusGraphicsAttributeItem, name, parent)
def paint(self,painter,option,widget):
+ self.debug('TaurusTextAttributeItem(%s,%s,%s)'%(self.getName(),self._currText,self._currHtmlText))
if self._currHtmlText:
self.setHtml(self._currHtmlText)
else:
@@ -938,6 +1062,10 @@ class TaurusBaseGraphicsFactory:
params[self.getNameParam()] = name
cls = None
if '/' in name:
+ #replacing Taco identifiers in %s'%name
+ if name.lower().startswith('tango:') and (name.count('/')==2 or not 'tango:/' in name.lower()):
+ nname = name.split(':',1)[-1]
+ params[self.getNameParam()] = name = nname
if name.lower().endswith('/state'): name = name.rsplit('/',1)[0]
cls = taurus.core.TaurusManager().findObjectClass(name)
else:
@@ -946,6 +1074,11 @@ class TaurusBaseGraphicsFactory:
item = klass()
## It's here were Attributes are subscribed
self.set_common_params(item,params)
+ if hasattr(item,'getExtensions'):
+ item.getExtensions() #<= must be called here to take extensions from params
+ if 'text' in klass.__name__.lower():
+ #print '\tadjusting %s font size'%klass.__name__
+ item.scale(.8,.8)
return item
def getNameParam(self):
@@ -959,14 +1092,3 @@ class TaurusBaseGraphicsFactory:
Overwrite has necessary."""
pass
-#if __name__ == "__main__":
-# import sys
-# app = Qt.QApplication(sys.argv)
-# try:
-# from taurusDevicePanel import *
-# except:
-# from taurusPanel.taurusDevicePanel import *
-# cos = taurusDevicePanel()
-# print "ala ma kota"
-# cos.show()
-# sys.exit(app.exec_())
diff --git a/lib/taurus/qt/qtgui/input/qwheel.py b/lib/taurus/qt/qtgui/input/qwheel.py
index 3666ef3..0882cf7 100644
--- a/lib/taurus/qt/qtgui/input/qwheel.py
+++ b/lib/taurus/qt/qtgui/input/qwheel.py
@@ -83,7 +83,6 @@ class _DownArrowButton(_ArrowButton):
def __init__(self, id, parent = None):
_ArrowButton.__init__(self, id, parent)
self._inc = -self._inc
- i=self.icon()
def getPixmap(self):
pm = Qt.QPixmapCache.find(_DownArrowButton.ArrowPixmapKey)
@@ -727,7 +726,7 @@ class QWheelEdit(Qt.QFrame):
Slot called when the user finishes editing
"""
ed = self.getEditWidget()
- v,ok = ed.text().toDouble()
+ v = float(ed.text())
self._setValue(v)
self._updateValue()
diff --git a/lib/taurus/qt/qtgui/input/tauruscheckbox.py b/lib/taurus/qt/qtgui/input/tauruscheckbox.py
index a64077a..edb3ad4 100644
--- a/lib/taurus/qt/qtgui/input/tauruscheckbox.py
+++ b/lib/taurus/qt/qtgui/input/tauruscheckbox.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -30,27 +30,23 @@ __all__ = ["TaurusValueCheckBox"]
__docformat__ = 'restructuredtext'
from taurus.qt import Qt
-
-import sys
-import PyTango
-
from taurus.qt.qtgui.base import TaurusBaseWritableWidget
class TaurusValueCheckBox(Qt.QCheckBox, TaurusBaseWritableWidget):
"""A QCheckBox connected to a boolean writable attribute model"""
-
+
__pyqtSignals__ = ("modelChanged(const QString &)",)
-
+
def __init__(self, qt_parent = None, designMode = False):
- name = "TaurusValueCheckBox"
+ name = "TaurusValueCheckBox"
self.call__init__wo_kw(Qt.QCheckBox, qt_parent)
self.call__init__(TaurusBaseWritableWidget, name, designMode=designMode)
self.setObjectName(name)
self.updateStyle()
self.connect(self, Qt.SIGNAL('stateChanged(int)'),self.valueChanged)
-
+
def keyPressEvent(self, event):
if event.key() in (Qt.Qt.Key_Return, Qt.Qt.Key_Enter):
self.writeValue()
@@ -61,11 +57,11 @@ class TaurusValueCheckBox(Qt.QCheckBox, TaurusBaseWritableWidget):
def minimumSizeHint(self):
return Qt.QSize(20, 20)
-
+
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# TaurusBaseWritableWidget overwriting
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-
+
def updateStyle(self):
TaurusBaseWritableWidget.updateStyle(self)
# Show text only if it is not specifically hidden
@@ -78,16 +74,18 @@ class TaurusValueCheckBox(Qt.QCheckBox, TaurusBaseWritableWidget):
self.setText('')
#Update pending operations style
if self.hasPendingOperations():
- if self.text().trimmed().isEmpty(): self.setText("!")
+ txt = str(self.text()).strip()
+ if len(txt) == 0:
+ self.setText("!")
self.setStyleSheet('TaurusValueCheckBox {color: blue;}')
else:
if str(self.text()) == "!": self.setText(" ")
self.setStyleSheet('TaurusValueCheckBox {}')
self.update()
-
+
def setValue(self, v):
self.setChecked(bool(v))
-
+
def getValue(self):
return self.isChecked()
@@ -97,9 +95,9 @@ class TaurusValueCheckBox(Qt.QCheckBox, TaurusBaseWritableWidget):
ret['module'] = 'taurus.qt.qtgui.input'
ret['icon'] = ":/designer/checkbox.png"
return ret
-
+
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- # QT properties
+ # QT properties
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
model = Qt.pyqtProperty("QString", TaurusBaseWritableWidget.getModel,
@@ -109,7 +107,7 @@ class TaurusValueCheckBox(Qt.QCheckBox, TaurusBaseWritableWidget):
showText = Qt.pyqtProperty("bool", TaurusBaseWritableWidget.getShowText,
TaurusBaseWritableWidget.setShowText,
TaurusBaseWritableWidget.resetShowText)
-
+
useParentModel = Qt.pyqtProperty("bool", TaurusBaseWritableWidget.getUseParentModel,
TaurusBaseWritableWidget.setUseParentModel,
TaurusBaseWritableWidget.resetUseParentModel)
@@ -117,7 +115,7 @@ class TaurusValueCheckBox(Qt.QCheckBox, TaurusBaseWritableWidget):
autoApply = Qt.pyqtProperty("bool", TaurusBaseWritableWidget.getAutoApply,
TaurusBaseWritableWidget.setAutoApply,
TaurusBaseWritableWidget.resetAutoApply)
-
+
forcedApply = Qt.pyqtProperty("bool", TaurusBaseWritableWidget.getForcedApply,
TaurusBaseWritableWidget.setForcedApply,
TaurusBaseWritableWidget.resetForcedApply)
diff --git a/lib/taurus/qt/qtgui/input/tauruscombobox.py b/lib/taurus/qt/qtgui/input/tauruscombobox.py
index b881524..d393876 100644
--- a/lib/taurus/qt/qtgui/input/tauruscombobox.py
+++ b/lib/taurus/qt/qtgui/input/tauruscombobox.py
@@ -99,17 +99,16 @@ class TaurusValueComboBox(Qt.QComboBox, TaurusBaseWritableWidget):
return None
if PyTango.is_int_type(model.data_type):
- new_value, ok = new_value.toInt()
+ func = int
elif PyTango.is_float_type(model.data_type):
- new_value, ok = new_value.toDouble()
- elif model.data_type in [ PyTango.DevString ]:
- new_value, ok = str(new_value.toString()), True
- elif model.data_type in [ PyTango.DevBoolean ]:
- new_value, ok = new_value.toBool(), True
+ func = float
+ elif model.data_type in (PyTango.DevString,):
+ func = str
+ elif model.data_type in (PyTango.DevBoolean,):
+ func = bool
else:
return None
- if not ok:
- return None
+ new_value = Qt.from_qvariant(new_value, func)
return new_value
def setValue(self, value):
@@ -202,6 +201,15 @@ class TaurusValueComboBox(Qt.QComboBox, TaurusBaseWritableWidget):
model.getValueObj()
)
+ def setQModel(self, *args, **kwargs):
+ Qt.QComboBox.setModel(self, *args, **kwargs)
+
+ def setModel(self, m):
+ if isinstance(m, Qt.QAbstractItemModel):
+ self.warning("Deprecation warning: use setQModel() if you want to set a Qt Item Model. The setModel() method is reserved for Taurus models")
+ return Qt.QComboBox.setModel(self, m)
+ return TaurusBaseWritableWidget.setModel(self, m)
+
@classmethod
def getQtDesignerPluginInfo(cls):
ret = TaurusBaseWritableWidget.getQtDesignerPluginInfo()
diff --git a/lib/taurus/qt/qtgui/input/tauruslineedit.py b/lib/taurus/qt/qtgui/input/tauruslineedit.py
index 8cf8d24..4509a0e 100644
--- a/lib/taurus/qt/qtgui/input/tauruslineedit.py
+++ b/lib/taurus/qt/qtgui/input/tauruslineedit.py
@@ -175,6 +175,10 @@ class TaurusValueLineEdit(Qt.QLineEdit, TaurusBaseWritableWidget):
self._stepBy(numSteps)
def keyPressEvent(self, evt):
+ if evt.key() in (Qt.Qt.Key_Return, Qt.Qt.Key_Enter):
+ Qt.QLineEdit.keyPressEvent(self, evt)
+ evt.accept()
+ return
if Qt.QLineEdit.isReadOnly(self):
return Qt.QLineEdit.keyPressEvent(self, evt)
model = self.getModelObj()
diff --git a/lib/taurus/qt/qtgui/model/qbasemodel.py b/lib/taurus/qt/qtgui/model/qbasemodel.py
index 522013c..066194d 100644
--- a/lib/taurus/qt/qtgui/model/qbasemodel.py
+++ b/lib/taurus/qt/qtgui/model/qbasemodel.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -34,7 +34,6 @@ __docformat__ = 'restructuredtext'
from taurus.qt import Qt
-from taurus.qt.qtcore.model import *
from taurus.qt.qtgui.util import ActionFactory
from taurus.qt.qtgui.resource import getIcon, getThemeIcon
from taurus.qt.qtgui.base import TaurusBaseWidget
@@ -56,7 +55,7 @@ class BaseToolBar(Qt.QToolBar):
class FilterToolBar(BaseToolBar):
"""Internal widget providing quick filter to be placed in a _QToolArea"""
-
+
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus filter toolbar", view=view,
parent=parent, designMode=designMode)
@@ -67,6 +66,9 @@ class FilterToolBar(BaseToolBar):
Qt.QObject.connect(filterLineEdit,
Qt.SIGNAL("textChanged(const QString &)"),
self.onFilterChanged)
+ Qt.QObject.connect(filterLineEdit,
+ Qt.SIGNAL("textEdited(const QString &)"),
+ self.onFilterEdited)
self.addWidget(filterLineEdit)
af = ActionFactory()
@@ -76,26 +78,33 @@ class FilterToolBar(BaseToolBar):
triggered=self.onClearFilter)
self.addAction(self._clearFilterAction)
- def filterLineEdit(self):
+ def getFilterLineEdit(self):
return self._filterLineEdit
-
+
def onClearFilter(self):
- self.filterLineEdit().setText("")
+ self.getFilterLineEdit().setText("")
self.emit(Qt.SIGNAL("clearFilterTriggered"))
-
+
def onFilterChanged(self, text=None):
- text = text or self.filterLineEdit().text()
+ text = text or self.getFilterLineEdit().text()
self.emit(Qt.SIGNAL("filterChanged"), text)
+ def onFilterEdited(self, text=None):
+ text = text or self.getFilterLineEdit().text()
+ self.emit(Qt.SIGNAL("filterEdited"), text)
+
+ def setFilterText(self, text):
+ self.getFilterLineEdit().setText(text)
+
class EditorToolBar(BaseToolBar):
"""Internal widget to be placed in a _QToolArea providing buttons for
moving, adding and removing items from a view based widget"""
-
+
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus editor toolbar", view=view,
parent=parent, designMode=designMode)
-
+
af = ActionFactory()
self._addAction = af.createAction(self, "New item",
icon=getThemeIcon("list-add"),
@@ -128,7 +137,7 @@ class EditorToolBar(BaseToolBar):
self.addAction(self._moveDownAction)
self.addAction(self._moveBottomAction)
#self.setStyleSheet("QWidget {background : red; }")
-
+
def onAdd(self):
self.emit(Qt.SIGNAL("addTriggered"))
@@ -137,7 +146,7 @@ class EditorToolBar(BaseToolBar):
def onMoveTop(self):
self.emit(Qt.SIGNAL("moveTopTriggered"))
-
+
def onMoveUp(self):
self.emit(Qt.SIGNAL("moveUpTriggered"))
@@ -149,7 +158,7 @@ class EditorToolBar(BaseToolBar):
class SelectionToolBar(BaseToolBar):
-
+
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus selection toolbar", view=view,
parent=parent, designMode=designMode)
@@ -166,7 +175,7 @@ class SelectionToolBar(BaseToolBar):
self.addAction(self._selectAllAction)
self.addAction(self._clearSelectionAction)
-
+
def onSelectAll(self):
self.emit(Qt.SIGNAL("selectAllTriggered"))
@@ -175,7 +184,7 @@ class SelectionToolBar(BaseToolBar):
class RefreshToolBar(BaseToolBar):
-
+
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus refresh toolbar", view=view,
parent=parent, designMode=designMode)
@@ -186,13 +195,13 @@ class RefreshToolBar(BaseToolBar):
tip="Refresh view",
triggered=self.onRefresh)
self.addAction(self._refreshAction)
-
+
def onRefresh(self):
self.emit(Qt.SIGNAL("refreshTriggered"))
class PerspectiveToolBar(BaseToolBar):
-
+
def __init__(self, perspective, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus refresh toolbar", view=view,
parent=parent, designMode=designMode)
@@ -202,7 +211,7 @@ class PerspectiveToolBar(BaseToolBar):
b.setToolTip("Perspective selection")
b.setPopupMode(Qt.QToolButton.InstantPopup)
b.setToolButtonStyle(Qt.Qt.ToolButtonTextBesideIcon)
-
+
menu = Qt.QMenu("Perspective", b)
b.setMenu(menu)
af = ActionFactory()
@@ -216,16 +225,16 @@ class PerspectiveToolBar(BaseToolBar):
menu.addAction(action)
if persp == perspective:
b.setDefaultAction(action)
-
+
self._perspectiveAction = self.addWidget(b)
def switchPerspectiveButton(self):
"""Returns the QToolButton that handles the switch perspective.
-
+
:return: (PyQt4.QtGui.QToolButton) the switch perspective tool button
"""
return self._perspective_button
-
+
def onSwitchPerspective(self):
action = self.sender()
self._perspective = action.perspective
@@ -241,56 +250,115 @@ class QBaseModelWidget(Qt.QMainWindow):
example), envolved by optional toolbar and statusbar.
The Qt model associated with the internal Qt view widget should be a
:class:`taurus.qt.qtcore.model.TaurusBaseModel`"""
-
- def __init__(self, parent=None, designMode=False, with_filter_widget=True):
+
+ KnownPerspectives = { }
+ DftPerspective = None
+
+ def __init__(self, parent=None, designMode=False, with_filter_widget=True,
+ with_selection_widget=True, with_refresh_widget=True,
+ perspective=None, proxy=None):
Qt.QMainWindow.__init__(self, parent)
self.setWindowFlags(Qt.Qt.Widget)
self._baseQModel = None
self._toolBars = []
- self._with_filter_widget = with_filter_widget
-
+
+ if with_filter_widget:
+ if isinstance(with_filter_widget, (bool, int)):
+ self._with_filter_widget = FilterToolBar
+ else:
+ self._with_filter_widget = with_filter_widget
+ else:
+ self._with_filter_widget = None
+
+ if with_selection_widget:
+ if isinstance(with_selection_widget, (bool, int)):
+ self._with_selection_widget = SelectionToolBar
+ else:
+ self._with_selection_widget = with_selection_widget
+ else:
+ self._with_selection_widget = None
+
+ if with_refresh_widget:
+ if isinstance(with_refresh_widget, (bool, int)):
+ self._with_refresh_widget = RefreshToolBar
+ else:
+ self._with_refresh_widget = with_refresh_widget
+ else:
+ self._with_refresh_widget = None
+
+ self._proxyModel = proxy
+
toolBars = self.createToolArea()
self._viewWidget = self.createViewWidget()
statusbar = self.createStatusBar()
-
+
for toolBar in toolBars:
toolBar.addSeparator()
self.addToolBar(toolBar)
self.setContentsMargins(0, 0, 0, 0)
self.setCentralWidget(self._viewWidget)
self.setStatusBar(statusbar)
-
- def createViewWidget(self):
+
+ if perspective is None:
+ perspective = self.DftPerspective
+
+ if len(self.KnownPerspectives) > 1:
+ p_bar = self._perspectiveBar = PerspectiveToolBar(perspective, view=self, parent=self)
+ self.connect(p_bar, Qt.SIGNAL("perspectiveChanged"), self.onSwitchPerspective)
+ self.addToolBar(p_bar)
+ else:
+ self._perspectiveBar = None
+ self._setPerspective(perspective)
+
+ def createViewWidget(self, klass=None):
raise NotImplementedError
-
+
def createStatusBar(self):
sb = Qt.QStatusBar()
sb.setSizeGripEnabled(False)
return sb
-
+
def createToolArea(self):
tb = [] # tb = self._toolArea = QToolArea(self)
if self._with_filter_widget:
- f_bar = self._filterBar = FilterToolBar(view=self, parent=self)
+ f_bar = self._filterBar = self._with_filter_widget(view=self, parent=self)
Qt.QObject.connect(f_bar, Qt.SIGNAL("filterChanged"),
self.onFilterChanged)
tb.append(f_bar)
else:
self._filterBar = None
-
- s_bar = self._selectionBar = SelectionToolBar(view=self, parent=self)
- Qt.QObject.connect(s_bar, Qt.SIGNAL("selectAllTriggered"),
- self.onSelectAll)
- Qt.QObject.connect(s_bar, Qt.SIGNAL("clearSelectionTriggered"),
- self.onClearSelection)
- tb.append(s_bar)
- r_bar = self._selectionBar = RefreshToolBar(view=self, parent=self)
- Qt.QObject.connect(r_bar, Qt.SIGNAL("refreshTriggered"),
- self.onRefreshModel)
- tb.append(r_bar)
+ if self._with_selection_widget:
+ s_bar = self._selectionBar = self._with_selection_widget(view=self, parent=self)
+ Qt.QObject.connect(s_bar, Qt.SIGNAL("selectAllTriggered"),
+ self.onSelectAll)
+ Qt.QObject.connect(s_bar, Qt.SIGNAL("clearSelectionTriggered"),
+ self.onClearSelection)
+ tb.append(s_bar)
+ else:
+ self._selectionBar = None
+ if self._with_refresh_widget:
+ r_bar = self._refreshBar = self._with_refresh_widget(view=self, parent=self)
+ Qt.QObject.connect(r_bar, Qt.SIGNAL("refreshTriggered"),
+ self.onRefreshModel)
+ tb.append(r_bar)
+ else:
+ self._refreshBar = None
+
return tb
+
+ def getPerspectiveBar(self):
+ return self._perspectiveBar
+
+ def getFilterBar(self):
+ return self._filterBar
+
+ def getSelectionBar(self):
+ return self._selectionBar
+
+ def getRefreshBar(self):
+ return self._refreshBar
def onRefreshModel(self):
self.getQModel().refresh(True)
@@ -308,7 +376,7 @@ class QBaseModelWidget(Qt.QMainWindow):
as arguments'''
item = self._mapToSource(index).internalPointer()
self.emit(Qt.SIGNAL('itemClicked'),item, index.column())
-
+
def _onDoubleClicked (self, index):
'''Emits an "itemDoubleClicked" signal with the clicked item and column
as arguments'''
@@ -317,7 +385,7 @@ class QBaseModelWidget(Qt.QMainWindow):
def viewWidget(self):
return self._viewWidget
-
+
def getQModel(self):
return self.viewWidget().model()
@@ -337,11 +405,11 @@ class QBaseModelWidget(Qt.QMainWindow):
return index
def setQModel(self, qmodel):
-
+
self._baseQModel = qmodel
while isinstance(self._baseQModel, Qt.QAbstractProxyModel):
self._baseQModel = self._baseQModel.sourceModel()
-
+
view = self.viewWidget()
old_selection_model = view.selectionModel()
CC = 'currentChanged(const QModelIndex &,const QModelIndex &)'
@@ -360,40 +428,40 @@ class QBaseModelWidget(Qt.QMainWindow):
self.viewSelectionChanged)
view.setCurrentIndex(view.rootIndex())
self._updateToolBar()
-
+
def viewSelectionChanged(self, selected, deselected):
self.emit(Qt.SIGNAL("itemSelectionChanged"))
-
+
def viewCurrentIndexChanged(self, current, previous):
# if there is a proxy model we have to translate the selection
base_current = self._mapToSource(current)
base_previous = self._mapToSource(previous)
-
+
self._updateToolBar(current)
-
+
if base_current.isValid():
currentTaurusTreeItem = base_current.internalPointer()
else:
currentTaurusTreeItem = None
-
+
if base_previous.isValid():
previousTaurusTreeItem = base_previous.internalPointer()
else:
previousTaurusTreeItem = None
self.emit(Qt.SIGNAL("currentItemChanged"), currentTaurusTreeItem,
previousTaurusTreeItem)
-
+
def _updateToolBar(self, current=None, previous=None):
pass
-
+
def selectedItems(self):
"""Returns a list of all selected non-hidden items
-
+
:return: (list<TaurusTreeItem>)
"""
view = self.viewWidget()
return [self._mapToSource(index).internalPointer() for index in view.selectedIndexes()]
-
+
def onFilterChanged(self, filter):
if not self.usesProxyQModel():
return
@@ -404,60 +472,25 @@ class QBaseModelWidget(Qt.QMainWindow):
#proxy_model.setFilterFixedString(filter)
#proxy_model.setFilterWildcard(filter)
#self.update()
-
+
def refresh(self):
self.getQModel().refresh()
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- # QMainWindow overwriting
+ # Perspective handling
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- def addToolBar(self, toolbar):
- Qt.QMainWindow.addToolBar(self, toolbar)
- self._toolBars.append(toolbar)
-
- def insertToolBar(self, before, toolbar):
- if isinstance(before, Qt.QToolBar):
- index = self._toolBars.index(before)
- else:
- index = before
- before = self._toolBars[before]
- Qt.QMainWindow.insertToolBar(self, before, toolbar)
- self._toolBars.insert(index, toolbar)
-
-
-class TaurusBaseModelWidget(TaurusBaseWidget):
- """A class:`taurus.qt.qtgui.base.TaurusBaseWidget` that connects to a
- taurus model. It must be used together with class:`taurus.qt.qtgui.base.QBaseModelWidget`"""
-
- KnownPerspectives = { }
- DftPerspective = None
-
- def __init__(self, designMode=False, perspective=None, proxy=None):
- name = self.__class__.__name__
- self._proxyModel = proxy
- self.call__init__(TaurusBaseWidget, name, designMode=designMode)
- if perspective is None:
- perspective = self.DftPerspective
-
- if len(self.KnownPerspectives) > 1:
- p_bar = self._perspectiveBar = PerspectiveToolBar(perspective, view=self, parent=self)
- self.connect(p_bar, Qt.SIGNAL("perspectiveChanged"), self.onSwitchPerspective)
- self.addToolBar(p_bar)
- else:
- self._perspectiveBar = None
- self._setPerspective(perspective)
def perspective(self):
return self._perspectiveBar.perspective()
-
+
def onSwitchPerspective(self, perspective):
self._setPerspective(perspective)
-
+
def _setPerspective(self, perspective):
qmodel_classes = self.KnownPerspectives[perspective]["model"]
qmodel_class, qmodel_proxy_classes = qmodel_classes[-1], qmodel_classes[:-1]
qmodel_proxy_classes.reverse()
- qmodel = qmodel_class(self, self.getModelObj())
+ qmodel = qmodel_class(self)
qmodel_source = qmodel
if self._proxyModel is None:
for qmodel_proxy_class in qmodel_proxy_classes:
@@ -468,7 +501,33 @@ class TaurusBaseModelWidget(TaurusBaseWidget):
self._proxyModel.setSourceModel(qmodel_source)
qmodel_source = self._proxyModel
self.setQModel(qmodel_source)
-
+
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # QMainWindow overwriting
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+
+ def addToolBar(self, toolbar):
+ Qt.QMainWindow.addToolBar(self, toolbar)
+ self._toolBars.append(toolbar)
+
+ def insertToolBar(self, before, toolbar):
+ if isinstance(before, Qt.QToolBar):
+ index = self._toolBars.index(before)
+ else:
+ index = before
+ before = self._toolBars[before]
+ Qt.QMainWindow.insertToolBar(self, before, toolbar)
+ self._toolBars.insert(index, toolbar)
+
+
+class TaurusBaseModelWidget(TaurusBaseWidget):
+ """A class:`taurus.qt.qtgui.base.TaurusBaseWidget` that connects to a
+ taurus model. It must be used together with
+ class:`~taurus.qt.qtgui.base.QBaseModelWidget`"""
+
+ def __init__(self, designMode=False):#, perspective=None, proxy=None):
+ name = self.__class__.__name__
+ self.call__init__(TaurusBaseWidget, name, designMode=designMode)
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# TaurusBaseWidget overwriting
@@ -479,18 +538,19 @@ class TaurusBaseModelWidget(TaurusBaseWidget):
view, modelObj = self.viewWidget(), self.getModelObj()
model = view.model()
- if model is None: return
+ if model is None:
+ return
model.setDataSource(modelObj)
-
+
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# QT property definition
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-
- #: This property holds the unique URI string representing the model name
- #: with which this widget will get its data from. The convention used for
+
+ #: This property holds the unique URI string representing the model name
+ #: with which this widget will get its data from. The convention used for
#: the string can be found :ref:`here <model-concept>`.
- #:
- #: In case the property :attr:`useParentModel` is set to True, the model
+ #:
+ #: In case the property :attr:`useParentModel` is set to True, the model
#: text must start with a '/' followed by the attribute name.
#:
#: **Access functions:**
@@ -501,4 +561,4 @@ class TaurusBaseModelWidget(TaurusBaseWidget):
#:
#: .. seealso:: :ref:`model-concept`
model = Qt.pyqtProperty("QString", TaurusBaseWidget.getModel, setModel,
- TaurusBaseWidget.resetModel)
\ No newline at end of file
+ TaurusBaseWidget.resetModel)
diff --git a/lib/taurus/qt/qtgui/panel/__init__.py b/lib/taurus/qt/qtgui/panel/__init__.py
index 7edbdc3..338fa5d 100644
--- a/lib/taurus/qt/qtgui/panel/__init__.py
+++ b/lib/taurus/qt/qtgui/panel/__init__.py
@@ -30,13 +30,15 @@ __docformat__ = 'restructuredtext'
from .qrawdatachooser import *
from .qdataexportdialog import *
+from .taurusmessagepanel import *
+from .taurusinputpanel import *
from .taurusattributechooser import TaurusAttributeChooser as TaurusAttributeChooserOLD
from .taurusmodelchooser import *
TaurusAttributeChooser = TaurusModelChooser #for backwards compatibility
-from .taurusdevicepanel import *
from .taurusvalue import *
from .taurusform import *
-from .taurusmessagepanel import *
from .taurusmodellist import *
from .taurusconfigeditor import *
+from .qdoublelist import *
+from .taurusdevicepanel import *
diff --git a/lib/taurus/qt/qtgui/panel/qdoublelist.py b/lib/taurus/qt/qtgui/panel/qdoublelist.py
new file mode 100644
index 0000000..efd4252
--- /dev/null
+++ b/lib/taurus/qt/qtgui/panel/qdoublelist.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+###########################################################################
+
+
+"""
+qdoublelist.py: Provides a generic dialog containing two list which can move
+items from one to the other
+"""
+
+__all__ = ["QDoubleListDlg"]
+
+__docformat__ = 'restructuredtext'
+
+from taurus.qt import Qt
+from ui.ui_DoubleListDlg import Ui_DoubleListDlg
+
+class QDoubleListDlg(Qt.QDialog):
+ '''Generic dialog providing two lists. Items can be moved from one to the other
+ '''
+ #@todo: drag&drop is disabled because Qt<4.6 does not have QList.setDefaultDragAndDropMode()
+ def __init__(self, parent=None, designMode=False, winTitle='', mainLabel='',
+ label1='', label2='', list1=None, list2=None):
+
+ if list1 is None:
+ list1=[]
+ if list2 is None:
+ list2=[]
+
+ super(QDoubleListDlg,self).__init__(parent)
+
+ self.ui = Ui_DoubleListDlg()
+ self.ui.setupUi(self)
+
+ if winTitle:
+ self.setWindowTitle(winTitle)
+ self.ui.mainLabel.setText(mainLabel)
+ self.ui.group1.setTitle(label1)
+ self.ui.group2.setTitle(label2)
+
+ self.setList1(list1)
+ self.setList2(list2)
+
+ self.connect(self.ui.to1BT, Qt.SIGNAL('clicked(bool)'), self.onTo1)
+ self.connect(self.ui.to2BT, Qt.SIGNAL('clicked(bool)'), self.onTo2)
+
+ def _moveItem(self, fromlist, tolist):
+ selected = fromlist.selectedItems()
+ for item in selected:
+ fromlist.takeItem(fromlist.row(item))
+ tolist.addItem(item)
+
+ def setList1(self, list1):
+ '''sets the items to be present in the first list
+
+ :param list2: (seq<str>) a sequence of strings
+ '''
+ self.ui.list1.clear()
+ self.ui.list1.addItems(list1)
+
+ def setList2(self, list2):
+ '''sets the items to be present in the second list
+
+ :param list2: (seq<str>) a sequence of strings
+ '''
+ self.ui.list2.clear()
+ self.ui.list2.addItems(list2)
+
+ def onTo1(self, *args):
+ '''slot to be called when the "To1" button is pressed'''
+ self._moveItem(self.ui.list2, self.ui.list1)
+
+ def onTo2(self, *args):
+ '''slot to be called when the "To2" button is pressed'''
+ self._moveItem(self.ui.list1, self.ui.list2)
+
+ def getAll1(self):
+ '''returns a copy the items in the first list
+
+ :return: (list<str>)
+ '''
+ return [unicode(self.ui.list1.item(row).text()) for row in xrange(self.ui.list1.count())]
+
+ def getAll2(self):
+ '''returns a copy the items in the second list
+
+ :return: (list<str>)
+ '''
+ return [unicode(self.ui.list2.item(row).text()) for row in xrange(self.ui.list2.count())]
+
+ # note, for the moment we do not make it available in designer because it does not
+ # behave well as a widget (only as a dialog) (e.g., it closes if ESC is pressed
+# @classmethod
+# def getQtDesignerPluginInfo(cls):
+# return {
+# 'module' : 'taurus.qt.qtgui.panel',
+# 'group' : 'Taurus Input',
+# #'icon' : ":/designer/ledred.png",
+# 'container' : False,
+# }
+#------------------------------------------------------------------------------
+
+def main():
+ app = Qt.QApplication(sys.argv)
+
+
+ dlg = QDoubleListDlg(winTitle='foo', mainLabel='bla, bla',label1='1', label2='2',
+ list1=['11','22'], list2=['123','33'] )
+ result = dlg.exec_()
+
+ print "Result", result
+ print "list1", dlg.getAll1()
+ print "list2", dlg.getAll2()
+
+
+
+if __name__ == "__main__":
+ import sys
+ main()
+
+
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/table/qtable.py b/lib/taurus/qt/qtgui/panel/report/albareport.py
similarity index 51%
copy from lib/taurus/qt/qtgui/table/qtable.py
copy to lib/taurus/qt/qtgui/panel/report/albareport.py
index 4f5d6a7..c6c3d70 100644
--- a/lib/taurus/qt/qtgui/table/qtable.py
+++ b/lib/taurus/qt/qtgui/panel/report/albareport.py
@@ -3,45 +3,65 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This module provides base table widget"""
+"""This module provides a panel to display taurus messages"""
-__all__ = ["QBaseTableWidget"]
+__all__ = ["TicketReportHandler"]
__docformat__ = 'restructuredtext'
from taurus.qt import Qt
-from taurus.qt.qtgui.model import QBaseModelWidget
+from basicreport import SendMailDialog, SMTPReportHandler
+
+
+class SendTicketDialog(SendMailDialog):
+
+ def __init__(self, parent=None):
+ SendMailDialog.__init__(self, parent=parent)
+ self.ui.editTo.setParent(None)
+ self.ui.editTo = Qt.QComboBox(self)
+ self.ui.editTo.setEditable(True)
+ self.ui.editTo.addItems(["controls", "mis", "electronics",
+ "systems"])
+ self.ui.editTo.setCurrentIndex(0)
+ self.ui.mainLayout.addWidget(self.ui.editTo, 1, 1, 1, 1)
+
+ def getTo(self):
+ return str(self.ui.editTo.currentText() + "@rt.cells.es")
+
+
+class TicketReportHandler(SMTPReportHandler):
+ """Report a message by sending an ALBA ticket"""
+
+ Label = "Send ticket"
+
+ def getDialogClass(self):
+ return SendTicketDialog
-class QBaseTableWidget(QBaseModelWidget):
+def main():
+ app = Qt.QApplication([])
+ w = SendTicketDialog()
+ w.exec_()
- def tableView(self):
- return self.viewWidget()
-
- def createViewWidget(self):
- table = Qt.QTableView(self)
- table.setSortingEnabled(True)
- table.setAlternatingRowColors(True)
- table.setSelectionBehavior(Qt.QAbstractItemView.SelectRows)
- table.setSelectionMode(Qt.QAbstractItemView.ExtendedSelection)
- return table
\ No newline at end of file
+if __name__ == "__main__":
+ main()
diff --git a/lib/taurus/qt/qtgui/panel/report/basicreport.py b/lib/taurus/qt/qtgui/panel/report/basicreport.py
index 337be4d..b342644 100644
--- a/lib/taurus/qt/qtgui/panel/report/basicreport.py
+++ b/lib/taurus/qt/qtgui/panel/report/basicreport.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -25,98 +25,130 @@
"""This module provides a panel to display taurus messages"""
-__all__ = ["ClipboardReportHandler", "SmtpReportHandler"]
+__all__ = ["ClipboardReportHandler", "SMTPReportHandler"]
__docformat__ = 'restructuredtext'
from taurus.core.util.report import TaurusMessageReportHandler
from taurus.qt import Qt
+from ui import ui_SendMailForm
+
class ClipboardReportHandler(TaurusMessageReportHandler):
"""Report a message by copying it to the clipboard"""
-
+
Label = "Copy to Clipboard"
-
+
def report(self, message):
app = Qt.QApplication.instance()
clipboard = app.clipboard()
clipboard.setText(message)
-
- Qt.QMessageBox.information(self.parent, "Done!", "Message Copied to clipboard")
-
-class SmtpReportHandler(TaurusMessageReportHandler):
+ Qt.QMessageBox.information(None, "Done!",
+ "Message Copied to clipboard")
+
+
+class SendMailDialog(Qt.QDialog):
+
+ def __init__(self, parent=None):
+ Qt.QDialog.__init__(self, parent)
+ self.ui = ui = ui_SendMailForm.Ui_SendMailForm()
+ ui.setupUi(self)
+ self.connect(ui.buttonBox, Qt.SIGNAL("accepted()"), self.accept)
+ self.connect(ui.buttonBox, Qt.SIGNAL("rejected()"), self.reject)
+ self.ui.editMessage.setFont(Qt.QFont("Monospace"))
+
+ def setFrom(self, efrom):
+ self.ui.lineEditFrom.setText(efrom)
+
+ def setTo(self, eto):
+ self.ui.editTo.setText(eto)
+
+ def setSubject(self, subject):
+ self.ui.editSubject.setText(subject)
+
+ def setMessage(self, message):
+ self.ui.editMessage.setPlainText(message)
+
+ def getFrom(self):
+ return str(self.ui.editFrom.text())
+
+ def getTo(self):
+ return str(self.ui.editTo.text())
+
+ def getSubject(self):
+ return str(self.ui.editSubject.text())
+
+ def getMessage(self):
+ return str(self.ui.editMessage.toPlainText())
+
+ def getMailInfo(self):
+ return self.getFrom(), self.getTo(), self.getSubject(), \
+ self.getMessage()
+
+
+class SMTPReportHandler(TaurusMessageReportHandler):
"""Report a message by sending an email"""
-
+
Label = "Send email"
-
+
def report(self, message):
- import smtplib
- import email.mime.text
-
+
app = Qt.QApplication.instance()
- efrom, ok = Qt.QInputDialog.getText(None, "Email From", "From:")
- efrom = str(efrom)
- if not ok:
- return
-
- eto, ok = Qt.QInputDialog.getText(None, "Email To", "To:")
- eto = str(eto).split(",")
- if not ok:
- return
-
- subject, ok = Qt.QInputDialog.getText(None, "Subject",
- "Subject:",Qt.QLineEdit.Normal,
- "Error in " + app.applicationName())
- subject = str(subject)
- if not ok:
+
+ subject = "Error in " + app.applicationName()
+ dialog = self.createDialog(subject=subject, message=message)
+
+ if not dialog.exec_():
return
-
+
+ mail_info = dialog.getMailInfo()
+
+ try:
+ self.sendMail(*mail_info)
+ Qt.QMessageBox.information(None, "Done!",
+ "Email has been sent!")
+ except:
+ import sys
+ import traceback
+ einfo = sys.exc_info()[:2]
+ msg = "".join(traceback.format_exception_only(*einfo))
+ Qt.QMessageBox.warning(None, "Failed to send email",
+ "Failed to send email. Reason:\n\n" + msg)
+
+ def sendMail(self, efrom, eto, subject, message):
+ import smtplib
+ import email.mime.text
msg = email.mime.text.MIMEText(message)
- app = Qt.QApplication.instance()
msg['From'] = efrom
- msg['To'] = eto[0]
+ msg['To'] = eto
msg['Subject'] = subject
-
+
s = smtplib.SMTP('localhost')
s.sendmail(efrom, eto, msg.as_string())
s.quit()
-if False:
- class TicketReportHandler(TaurusMessageReportHandler):
- """Report a message by sending an email"""
-
- Label = "Send ticket"
-
- def report(self, message):
- import smtplib
- import email.mime.text
-
- app = Qt.QApplication.instance()
- efrom, ok = Qt.QInputDialog.getText(None, "Email From", "From:")
- efrom = str(efrom)
- if not ok:
- return
-
- options = [ i+"@rt.cells.es" for i in ["controls", "mis", "electronics", "systems"] ]
- eto, ok = Qt.QInputDialog.getItem(None, "Email To", "To:", options)
- eto = str(eto)
- if not ok:
- return
-
- subject, ok = Qt.QInputDialog.getText(None, "Subject",
- "Subject:",Qt.QLineEdit.Normal,
- "Error in " + app.applicationName())
- subject = str(subject)
- if not ok:
- return
-
- msg = email.mime.text.MIMEText(message)
- app = Qt.QApplication.instance()
- msg['From'] = efrom
- msg['To'] = eto[0]
- msg['Subject'] = subject
-
- s = smtplib.SMTP('localhost')
- s.sendmail(efrom, eto, msg.as_string())
- s.quit()
\ No newline at end of file
+ def getDialogClass(self):
+ return SendMailDialog
+
+ def createDialog(self, efrom=None, eto=None, subject=None, message=None):
+ dialog = self.getDialogClass()()
+ dialog.setWindowTitle("Compose message")
+ if efrom is not None:
+ dialog.setFrom(efrom)
+ if eto is not None:
+ dialog.setFrom(eto)
+ if subject is not None:
+ dialog.setSubject(subject)
+ if message is not None:
+ dialog.setMessage(message)
+ return dialog
+
+
+def main():
+ app = Qt.QApplication([])
+ w = SendMailDialog()
+ w.exec_()
+
+if __name__ == "__main__":
+ main()
diff --git a/lib/taurus/qt/qtgui/panel/report/ui/SendMailForm.ui b/lib/taurus/qt/qtgui/panel/report/ui/SendMailForm.ui
new file mode 100644
index 0000000..db14902
--- /dev/null
+++ b/lib/taurus/qt/qtgui/panel/report/ui/SendMailForm.ui
@@ -0,0 +1,69 @@
+<ui version="4.0" >
+ <class>SendMailForm</class>
+ <widget class="QWidget" name="SendMailForm" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="mainLayout" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="labelFrom" >
+ <property name="text" >
+ <string>From:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLineEdit" name="editFrom" />
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="labelTo" >
+ <property name="text" >
+ <string>To:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLineEdit" name="editTo" />
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="labelSubject" >
+ <property name="text" >
+ <string>Subject:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLineEdit" name="editSubject" />
+ </item>
+ <item row="3" column="0" colspan="2" >
+ <widget class="QPlainTextEdit" name="editMessage" />
+ </item>
+ <item row="4" column="0" colspan="2" >
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/lib/taurus/qt/qtgui/panel/report/ui/__init__.py b/lib/taurus/qt/qtgui/panel/report/ui/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/lib/taurus/qt/qtgui/panel/report/ui/ui_SendMailForm.py b/lib/taurus/qt/qtgui/panel/report/ui/ui_SendMailForm.py
new file mode 100644
index 0000000..42ddce5
--- /dev/null
+++ b/lib/taurus/qt/qtgui/panel/report/ui/ui_SendMailForm.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'SendMailForm.ui'
+#
+# Created: Wed May 23 10:10:22 2012
+# by: PyQt4 UI code generator 4.4.3
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt4 import QtCore, QtGui
+
+class Ui_SendMailForm(object):
+ def setupUi(self, SendMailForm):
+ SendMailForm.setObjectName("SendMailForm")
+ SendMailForm.resize(400, 300)
+ self.mainLayout = QtGui.QGridLayout(SendMailForm)
+ self.mainLayout.setObjectName("mainLayout")
+ self.labelFrom = QtGui.QLabel(SendMailForm)
+ self.labelFrom.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.labelFrom.setObjectName("labelFrom")
+ self.mainLayout.addWidget(self.labelFrom, 0, 0, 1, 1)
+ self.editFrom = QtGui.QLineEdit(SendMailForm)
+ self.editFrom.setObjectName("editFrom")
+ self.mainLayout.addWidget(self.editFrom, 0, 1, 1, 1)
+ self.labelTo = QtGui.QLabel(SendMailForm)
+ self.labelTo.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.labelTo.setObjectName("labelTo")
+ self.mainLayout.addWidget(self.labelTo, 1, 0, 1, 1)
+ self.editTo = QtGui.QLineEdit(SendMailForm)
+ self.editTo.setObjectName("editTo")
+ self.mainLayout.addWidget(self.editTo, 1, 1, 1, 1)
+ self.labelSubject = QtGui.QLabel(SendMailForm)
+ self.labelSubject.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.labelSubject.setObjectName("labelSubject")
+ self.mainLayout.addWidget(self.labelSubject, 2, 0, 1, 1)
+ self.editSubject = QtGui.QLineEdit(SendMailForm)
+ self.editSubject.setObjectName("editSubject")
+ self.mainLayout.addWidget(self.editSubject, 2, 1, 1, 1)
+ self.editMessage = QtGui.QPlainTextEdit(SendMailForm)
+ self.editMessage.setObjectName("editMessage")
+ self.mainLayout.addWidget(self.editMessage, 3, 0, 1, 2)
+ self.buttonBox = QtGui.QDialogButtonBox(SendMailForm)
+ self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.mainLayout.addWidget(self.buttonBox, 4, 0, 1, 2)
+
+ self.retranslateUi(SendMailForm)
+ QtCore.QMetaObject.connectSlotsByName(SendMailForm)
+
+ def retranslateUi(self, SendMailForm):
+ SendMailForm.setWindowTitle(QtGui.QApplication.translate("SendMailForm", "Form", None, QtGui.QApplication.UnicodeUTF8))
+ self.labelFrom.setText(QtGui.QApplication.translate("SendMailForm", "From:", None, QtGui.QApplication.UnicodeUTF8))
+ self.labelTo.setText(QtGui.QApplication.translate("SendMailForm", "To:", None, QtGui.QApplication.UnicodeUTF8))
+ self.labelSubject.setText(QtGui.QApplication.translate("SendMailForm", "Subject:", None, QtGui.QApplication.UnicodeUTF8))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtGui.QApplication(sys.argv)
+ SendMailForm = QtGui.QWidget()
+ ui = Ui_SendMailForm()
+ ui.setupUi(SendMailForm)
+ SendMailForm.show()
+ sys.exit(app.exec_())
+
diff --git a/lib/taurus/qt/qtgui/panel/taurusconfigbrowser.py b/lib/taurus/qt/qtgui/panel/taurusconfigbrowser.py
index 154d3c5..d0bd968 100644
--- a/lib/taurus/qt/qtgui/panel/taurusconfigbrowser.py
+++ b/lib/taurus/qt/qtgui/panel/taurusconfigbrowser.py
@@ -91,7 +91,7 @@ class QConfigViewer(Qt.QWidget):
def getTaurusConfigFromSettings(self, key='TaurusConfig'):
result = None
- qstate = self.settings.value(key).toByteArray()
+ qstate = Qt.from_qvariant(self.settings.value(key), 'toByteArray')
if not qstate.isNull():
try: result = pickle.loads(qstate.data())
except Exception,e:
diff --git a/lib/taurus/qt/qtgui/panel/taurusconfigeditor.py b/lib/taurus/qt/qtgui/panel/taurusconfigeditor.py
index 56210c0..a35265b 100644
--- a/lib/taurus/qt/qtgui/panel/taurusconfigeditor.py
+++ b/lib/taurus/qt/qtgui/panel/taurusconfigeditor.py
@@ -54,12 +54,14 @@ class QConfigEditorModel(Qt.QStandardItemModel):
def setData(self,index, value, role = Qt.Qt.DisplayRole):
'''see :meth:`Qt.QAbstractTableModel.setData`'''
-
- if str(index.data().toString()) == str(value.toString()):
+
+ idx_data_str = Qt.from_qvariant(index.data(), str)
+ value_str = Qt.from_qvariant(value, str)
+ if idx_data_str == value_str:
return False
#self.itemFromIndex(index).setData(value, role)
try:
- self.valueChanged(str(value.toString()), index)
+ self.valueChanged(value_str, index)
except:
self.emit(Qt.SIGNAL("showError"),'Wrong value!','The value you entered is wrong. The old value will be restored.')
return Qt.QStandardItemModel.setData(self,index,index.data(),role)
@@ -91,7 +93,7 @@ class QConfigEditorModel(Qt.QStandardItemModel):
'''
tmpindex = self._toDeleteIndex
item = self.itemFromIndex(tmpindex)
- path = str(item.data(Qt.Qt.UserRole).toString())
+ path = Qt.from_qvariant(item.data(Qt.Qt.UserRole), str)
self._delete = False
self._configurationDictionaries = self.removeBranch(self._configurationDictionaries, path)
@@ -152,7 +154,7 @@ class QConfigEditorModel(Qt.QStandardItemModel):
:param index: (QModelIndex) index of the model
'''
changedItem = self.itemFromIndex(index)
- path = changedItem.data(Qt.Qt.UserRole).toString()
+ path = Qt.from_qvariant(changedItem.data(Qt.Qt.UserRole), str)
self._configurationDictionaries = self.changeTreeValue(self._configurationDictionaries, path, value)
try: group = eval(str(path).split(';',1)[0])
except: group = str(path).split(';',1)[0]
@@ -248,7 +250,8 @@ class QConfigEditorModel(Qt.QStandardItemModel):
child.setEditable(False)
item.appendRow(child)
- path = Qt.QVariant(str(item.data(Qt.Qt.UserRole).toString()) + ";__itemConfigurations__;" + k)
+ txt = Qt.from_qvariant(item.data(Qt.Qt.UserRole), str)
+ path = Qt.QVariant(txt + ";__itemConfigurations__;" + k)
child.setData(path, Qt.Qt.UserRole)
self.fillTaurusConfig(child, value) #recursive call to fill all nodes
else:
@@ -260,7 +263,8 @@ class QConfigEditorModel(Qt.QStandardItemModel):
item.appendRow([child,typeV,valueV])
- path = Qt.QVariant(str(item.data(Qt.Qt.UserRole).toString()) + ";__itemConfigurations__;" + k)
+ txt = Qt.from_qvariant(item.data(Qt.Qt.UserRole), str)
+ path = Qt.QVariant(txt + ";__itemConfigurations__;" + k)
child.setEditable(False)
typeV.setEditable(False)
@@ -284,8 +288,8 @@ class QConfigEditorModel(Qt.QStandardItemModel):
if BaseConfigurableClass.isTaurusConfig(value):
child.setEditable(False)
item.appendRow(child)
-
- path = Qt.QVariant(str(item.data(Qt.Qt.UserRole).toString()) +";" + k)
+ txt = Qt.from_qvariant(item.data(Qt.Qt.UserRole), str)
+ path = Qt.QVariant(txt +";" + k)
child.setData(path, Qt.Qt.UserRole)
self.fillTaurusConfig(child, value) #recursive call to fill all nodes
else:
@@ -294,7 +298,8 @@ class QConfigEditorModel(Qt.QStandardItemModel):
typeV.setForeground(Qt.QBrush(Qt.QColor('gray')))
child.setForeground(Qt.QBrush(Qt.QColor('gray')))
item.appendRow([child,typeV,valueV])
- path = Qt.QVariant(str(item.data(Qt.Qt.UserRole).toString()) + ";" + k)
+ txt = Qt.from_qvariant(item.data(Qt.Qt.UserRole), str)
+ path = Qt.QVariant(txt + ";" + k)
child.setData(path, Qt.Qt.UserRole)
child.setEditable(False)
@@ -312,7 +317,7 @@ class QConfigEditorModel(Qt.QStandardItemModel):
:returns (dict)
'''
result = None
- qstate = self._settings.value(key).toByteArray()
+ qstate = Qt.from_qvariant(self._settings.value(key), 'toByteArray')
if not qstate.isNull():
try: result = pickle.loads(qstate.data())
except Exception,e:
@@ -400,7 +405,7 @@ class QConfigEditor(TaurusWidget):
'''Reimplemented from :meth:`QWidget.contextMenuEvent`'''
self.tree._toDeleteIndex = self.treeview.selectedIndexes()[0]
- text = str(self.tree._toDeleteIndex.data().toString())
+ text = Qt.from_qvariant(self.tree._toDeleteIndex.data(), str)
if self.tree._toDeleteIndex.column() in [1,2] or text in ['LAST', '[custom]'] or text in self.tree.perspectives:
return
menu = Qt.QMenu()
diff --git a/lib/taurus/qt/qtgui/panel/taurusdevicepanel.py b/lib/taurus/qt/qtgui/panel/taurusdevicepanel.py
index 2892f49..8cc3c48 100644
--- a/lib/taurus/qt/qtgui/panel/taurusdevicepanel.py
+++ b/lib/taurus/qt/qtgui/panel/taurusdevicepanel.py
@@ -24,23 +24,383 @@
#############################################################################
"""
-tauruspanel.py:
+TaurusDevicePanel.py:
"""
-__all__ = ["TaurusDevPanel"]
+__all__ = ["TaurusDevicePanel","TaurusDevPanel"]
__docformat__ = 'restructuredtext'
+import re,traceback
from taurus.qt import Qt
import taurus.core
-from taurus.qt.qtgui.container import TaurusMainWindow
+#from taurus.qt.qtgui.container import TaurusMainWindow
+
+import taurus.qt.qtgui.resource
+
+from taurus.qt.qtgui.container import TaurusWidget, TaurusMainWindow
+from taurus.qt.qtgui.display import TaurusValueLabel as LABEL_CLASS #@todo: TaurusValueLabel is deprecated. Use TaurusLabel instead
+from taurus.qt.qtgui.display import TaurusStateLed as LED_CLASS #@todo: TaurusStateLed is deprecated. Use TaurusLed instead
+from taurus.qt.qtgui.panel.taurusform import TaurusForm
+from taurus.qt.qtgui.panel.taurusform import TaurusCommandsForm
+
+###############################################################################
+# TaurusDevicePanel (from Vacca)
+
+# Variables that control TaurusDevicePanel shape
+
+STATUS_HEIGHT=170
+SPLIT_SIZES=[15,65,20]
+IMAGE_SIZE=(200,100) #(width,height)
+
+# Helper methods
+
+def matchCl(m,k):
+ return re.match(m.lower(),k.lower())
+
+def searchCl(m,k):
+ if m.startswith('^') or m.startswith('(^') or '(?!^' in m: return matchCl(m,k)
+ return re.search(m.lower(),k.lower())
+
+def get_regexp_dict(dct,key,default=None):
+ for k,v in dct.items(): #Trying regular expression match
+ if matchCl(k,key):
+ return v
+ for k,v in dct.items(): #If failed, trying if key is contained
+ if k.lower() in key.lower():
+ return v
+ if default is not None: return default
+ else: raise Exception('KeyNotFound:%s'%k)
+
+def get_eqtype(dev):
+ ''' It extracts the eqtype from a device name like domain/family/eqtype-serial'''
+ try: eq = str(dev).split('/')[-1].split('-',1)[0].upper()
+ except: eq = ''
+ return eq
+
+def str_to_filter(seq):
+ try: f = eval(seq)
+ except: f = seq
+ if isinstance(f,basestring): return {'.*':[f]}
+ elif isinstance(f,list): return {'.*':f}
+ else: return f
+
+#Stacked palette
+def get_White_palette():
+ palette = Qt.QPalette()
+
+ brush = Qt.QBrush(Qt.QColor(255,255,255))
+ brush.setStyle(Qt.Qt.SolidPattern)
+ palette.setBrush(Qt.QPalette.Active,Qt.QPalette.Base,brush)
+
+ brush = Qt.QBrush(Qt.QColor(255,255,255))
+ brush.setStyle(Qt.Qt.SolidPattern)
+ palette.setBrush(Qt.QPalette.Active,Qt.QPalette.Window,brush)
+
+ brush = Qt.QBrush(Qt.QColor(255,255,255))
+ brush.setStyle(Qt.Qt.SolidPattern)
+ palette.setBrush(Qt.QPalette.Inactive,Qt.QPalette.Base,brush)
+
+ brush = Qt.QBrush(Qt.QColor(255,255,255))
+ brush.setStyle(Qt.Qt.SolidPattern)
+ palette.setBrush(Qt.QPalette.Inactive,Qt.QPalette.Window,brush)
+
+ brush = Qt.QBrush(Qt.QColor(255,255,255))
+ brush.setStyle(Qt.Qt.SolidPattern)
+ palette.setBrush(Qt.QPalette.Disabled,Qt.QPalette.Base,brush)
+
+ brush = Qt.QBrush(Qt.QColor(255,255,255))
+ brush.setStyle(Qt.Qt.SolidPattern)
+ palette.setBrush(Qt.QPalette.Disabled,Qt.QPalette.Window,brush)
+ return palette
+
+# TaurusDevicePanel class
+
+class TaurusDevicePanel(TaurusWidget):
+
+ READ_ONLY = False
+ _attribute_filter = {} #A dictionary like {device_regexp:[attribute_regexps]}
+ _command_filter = {} #A dictionary like {device_regexp:[(command_regexp,default_args)]}
+ _icon_map = {} #A dictionary like {device_regexp:pixmap_url}
+
+ @classmethod
+ def setIconMap(klass,filters):
+ """A dictionary like {device_regexp:pixmap_url}"""
+ klass._icon_map = filters
+
+ @classmethod
+ def getIconMap(klass):
+ return klass._icon_map
+
+ @classmethod
+ def setAttributeFilters(klass,filters):
+ """
+ It will set the attribute filters
+ filters will be like: {device_regexp:[attribute_regexps]}
+ example: {'.*/VGCT-.*': ['ChannelState','p[0-9]']}
+ """
+ klass._attribute_filter.update(filters)
+
+ @classmethod
+ def getAttributeFilters(klass):
+ return klass._attribute_filter
+
+ @classmethod
+ def setCommandFilters(klass,filters):
+ """
+ It will set the command filters
+ filters will be like: {device_regexp:[command_regexps]}
+ example: {'.*/IPCT-.*': (
+ ('setmode',('SERIAL','LOCAL','STEP','FIXED','START','PROTECT')),
+ ('onhv1',()), ('offhv1',()), ('onhv2',()), ('offhv2',()),
+ ('sendcommand',())
+ ),}
+ """
+ klass._command_filter.update(filters)
+
+ @classmethod
+ def getCommandFilters(klass):
+ return klass._command_filter
+
+ ###########################################################################
+
+ def __init__(self,parent=None,model=None,palette=None,bound=True):
+ TaurusWidget.__init__(self,parent)
+ if palette: self.setPalette(palette)
+ self.setLayout(Qt.QGridLayout())
+ self.bound = bound
+ self._dups = []
+
+ self.setWindowTitle('TaurusDevicePanel')
+ self._label = Qt.QLabel()
+ self._label.font().setBold(True)
+
+ self._stateframe = TaurusWidget(self)
+ self._stateframe.setLayout(Qt.QGridLayout())
+ self._stateframe.layout().addWidget(Qt.QLabel('State'),0,0,Qt.Qt.AlignCenter)
+ self._statelabel = LABEL_CLASS(self._stateframe)
+ self._statelabel.setMinimumWidth(100)
+ self._statelabel.setShowQuality(False)
+ self._statelabel.setShowState(True)
+ self._stateframe.layout().addWidget(self._statelabel,0,1,Qt.Qt.AlignCenter)
+ self._state = LED_CLASS(self._stateframe)
+ self._state.setShowQuality(False)
+ self._stateframe.layout().addWidget(self._state,0,2,Qt.Qt.AlignCenter)
+
+ self._statusframe = Qt.QScrollArea(self)
+ self._status = LABEL_CLASS(self._statusframe)
+ self._status.setShowQuality(False)
+ self._status.setAlignment(Qt.Qt.AlignLeft)
+ self._status.setFixedHeight(2000)
+ self._status.setFixedWidth(5000)
+ #self._statusframe.setFixedHeight(STATUS_HEIGHT)
+ self._statusframe.setHorizontalScrollBarPolicy(Qt.Qt.ScrollBarAlwaysOn)
+ self._statusframe.setVerticalScrollBarPolicy(Qt.Qt.ScrollBarAlwaysOn)
+ self._statusframe.setWidget(self._status)
+ self._statusframe.setPalette(get_White_palette())
+
+ self._attrsframe = Qt.QTabWidget(self)
+
+ self._splitter = Qt.QSplitter(Qt.Qt.Vertical,self) ##Horizontal will not allow to show labels of attributes!
+
+ self._attrs,self._comms = None,None
+
+ self.layout().addWidget(self._splitter,0,0)
+ self._header = Qt.QFrame()
+ self._header.setFixedHeight(1.1*IMAGE_SIZE[1])
+ self._header.setLayout(Qt.QGridLayout())
+
+ self._dup = Qt.QPushButton()
+ qpixmap = taurus.qt.qtgui.resource.getPixmap(':/actions/window-new.svg')
+ self._dup.setIcon(Qt.QIcon(qpixmap))
+ self._dup.setIconSize(Qt.QSize(15,15))
+ self.connect(self._dup,Qt.SIGNAL("pressed()"),self.duplicate)
+
+ self._image = Qt.QLabel()
+
+ self._header.layout().addWidget(self._image,0,0,2,1,Qt.Qt.AlignCenter)
+ self._header.layout().addWidget(self._label,0,1,Qt.Qt.AlignLeft)
+ self._header.layout().addWidget(self._stateframe,1,1,1,2,Qt.Qt.AlignLeft)
+ self._header.layout().addWidget(self._dup,0,2,Qt.Qt.AlignRight)
+
+ self._splitter.insertWidget(0,self._header)
+ self._splitter.insertWidget(1,self._attrsframe)
+ self._splitter.insertWidget(2,self._statusframe)
+ self._splitter.setSizes(SPLIT_SIZES)
+ [self._splitter.setStretchFactor(i,v) for i,v in enumerate(SPLIT_SIZES)]
+ self._splitter.setCollapsible(0,False)
+ self._splitter.setCollapsible(1,False)
+
+ if model: self.setModel(model)
+
+ def loadConfigFile(self,ifile=None):
+ self.info('In TaurusDevicePanel.loadConfigFile(%s)'%ifile)
+ if isinstance(ifile,file) or isinstance(ifile,str) and not ifile.endswith('.py'):
+ TaurusWidget.loadConfigFile(self,ifile)
+ else:
+ from imp import load_source
+ config_file = load_source('config_file',ifile)
+ af,cf,im = [getattr(config_file,x,None) for x in ('AttributeFilters','CommandFilters','IconMap')]
+ if af is not None: self.setAttributeFilters(af)
+ if cf is not None: self.setCommandFilters(cf)
+ if im is not None: self.setIconMap(im)
+ self.debug('AttributeFilters are:\n%s'%self.getAttributeFilters())
+
+ def duplicate(self):
+ self._dups.append(TaurusDevicePanel(bound=False))
+ self._dups[-1].setModel(self.getModel())
+ self._dups[-1].show()
+
+ @Qt.pyqtSignature("setModel(QString)")
+ def setModel(self,model,pixmap=None):
+ model,modelclass,raw = str(model).strip(),'',model
+ if model:
+ model = model and model.split()[0] or ''
+ modelclass = taurus.Factory().findObjectClass(model)
+ self.trace('In TaurusDevicePanel.setModel(%s(%s),%s)'%(raw,modelclass,pixmap))
+ if model == self.getModel():
+ return
+ elif raw is None or not model or not modelclass:
+ if self.getModel(): self.detach()
+ return
+ elif issubclass(modelclass,taurus.core.TaurusAttribute):
+ #if model.lower().endswith('/state'):
+ model = model.rsplit('/',1)[0]
+ elif not issubclass(modelclass,taurus.core.TaurusDevice):
+ self.warning('TaurusDevicePanel accepts only Device models')
+ return
+ try:
+ if self.getModel(): self.detach()
+ taurus.Device(model).ping()
+ TaurusWidget.setModel(self,model)
+ self.setWindowTitle(str(model).upper())
+ model = self.getModel()
+ self._label.setText(model.upper())
+ font = self._label.font()
+ font.setPointSize(15)
+ self._label.setFont(font)
+ if pixmap is None and self.getIconMap():
+ for k,v in self.getIconMap().items():
+ if searchCl(k,model):
+ pixmap = v
+ if pixmap is not None:
+ #print 'Pixmap is %s'%pixmap
+ qpixmap = Qt.QPixmap(pixmap)
+ if qpixmap.height()>.9*IMAGE_SIZE[1]: qpixmap=qpixmap.scaledToHeight(.9*IMAGE_SIZE[1])
+ if qpixmap.width()>.9*IMAGE_SIZE[0]: qpixmap=qpixmap.scaledToWidth(.9*IMAGE_SIZE[0])
+ else:
+ qpixmap = taurus.qt.qtgui.resource.getPixmap(':/logo.png')
+
+ self._image.setPixmap(qpixmap)
+ self._state.setModel(model+'/state')
+ if hasattr(self,'_statelabel'): self._statelabel.setModel(model+'/state')
+ self._status.setModel(model+'/status')
+ try:
+ self._attrsframe.clear()
+ filters = get_regexp_dict(TaurusDevicePanel._attribute_filter,model,['.*'])
+ if hasattr(filters,'keys'): filters = filters.items() #Dictionary!
+ if filters and isinstance(filters[0],(list,tuple)): #Mapping
+ self._attrs = []
+ for tab,attrs in filters:
+ self._attrs.append(self.get_attrs_form(device=model,filters=attrs,parent=self))
+ self._attrsframe.addTab(self._attrs[-1],tab)
+ else:
+ if self._attrs and isinstance(self._attrs,list): self._attrs = self._attrs[0]
+ self._attrs = self.get_attrs_form(device=model,form=self._attrs,filters=filters,parent=self)
+ if self._attrs: self._attrsframe.addTab(self._attrs,'Attributes')
+ if not TaurusDevicePanel.READ_ONLY:
+ self._comms = self.get_comms_form(model,self._comms,self)
+ if self._comms: self._attrsframe.addTab(self._comms,'Commands')
+ if SPLIT_SIZES: self._splitter.setSizes(SPLIT_SIZES)
+ except:
+ self.warning( traceback.format_exc())
+ qmsg = Qt.QMessageBox(Qt.QMessageBox.Critical,'%s Error'%model,'%s not available'%model,Qt.QMessageBox.Ok,self)
+ qmsg.setDetailedText(traceback.format_exc())
+ qmsg.show()
+ except:
+ self.warning(traceback.format_exc())
+ qmsg = Qt.QMessageBox(Qt.QMessageBox.Critical,'%s Error'%model,'%s not available'%model,Qt.QMessageBox.Ok,self)
+ qmsg.show()
+ self.setWindowTitle(self.getModel())
+ return
+
+ def detach(self):
+ self.trace('In TaurusDevicePanel(%s).detach()'%self.getModel())
+ _detached = []
+ def detach_recursive(obj):
+ if obj in _detached: return
+ if isinstance(obj,taurus.qt.qtgui.container.TaurusBaseContainer):
+ for t in obj.taurusChildren():
+ detach_recursive(t)
+ if obj is not self and isinstance(obj,taurus.qt.qtgui.base.TaurusBaseWidget):
+ try:
+ if getattr(obj,'model',None):
+ #self.debug('detaching %s from %s'%(obj,obj.model))
+ obj.setModel([] if isinstance(obj,TaurusForm) else '')
+ except:
+ self.warning('detach of %s failed!'%obj)
+ self.warning(traceback.format_exc())
+ _detached.append(obj)
+ detach_recursive(self)
+
+ def get_attrs_form(self,device,form=None,filters=None,parent=None):
+ filters = filters or get_regexp_dict(TaurusDevicePanel._attribute_filter,device,['.*'])
+ self.trace( 'In TaurusDevicePanel.get_attrs_form(%s,%s)'%(device,filters))
+ allattrs = sorted(str(a) for a in taurus.Device(device).get_attribute_list() if str(a).lower() not in ('state','status'))
+ attrs = []
+ for a in filters:
+ for t in allattrs:
+ if a and searchCl(a.strip(),t.strip()):
+ aname = '%s/%s' % (device,t)
+ if not aname in attrs:
+ attrs.append(aname)
+ if attrs:
+ #self.trace( 'Matching attributes are: %s' % str(attrs)[:100])
+ if form is None: form = TaurusForm(parent)
+ elif hasattr(form,'setModel'): form.setModel([])
+ ##Configuring the TauForm:
+ form.setWithButtons(False)
+ form.setWindowTitle(device)
+ try: form.setModel(attrs)
+ except Exception: self.warning('TaurusDevicePanel.ERROR: Unable to setModel for TaurusDevicePanel.attrs_form!!: %s'%traceback.format_exc())
+ return form
+ else: return None
+
+ def get_comms_form(self,device,form=None,parent=None):
+ self.trace( 'In TaurusDevicePanel.get_comms_form(%s)'%device)
+ params = get_regexp_dict(TaurusDevicePanel._command_filter,device,[])
+ if TaurusDevicePanel._command_filter and not params: #If filters are defined only listed devices will show commands
+ self.debug('TaurusDevicePanel.get_comms_form(%s): By default an unknown device type will display no commands'% device)
+ return None
+ if not form:
+ form = TaurusCommandsForm(parent)
+ elif hasattr(form,'setModel'):
+ form.setModel('')
+ try:
+ form.setModel(device)
+ if params:
+ form.setSortKey(lambda x,vals=[s[0].lower() for s in params]: vals.index(x.cmd_name.lower()) if str(x.cmd_name).lower() in vals else 100)
+ form.setViewFilters([lambda c: str(c.cmd_name).lower() not in ('state','status') and any(searchCl(s[0],str(c.cmd_name)) for s in params)])
+ form.setDefaultParameters(dict((k,v) for k,v in (params if not hasattr(params,'items') else params.items()) if v))
+ for wid in form._cmdWidgets:
+ if not hasattr(wid,'getCommand') or not hasattr(wid,'setDangerMessage'): continue
+ if re.match('.*(on|off|init|open|close).*',str(wid.getCommand().lower())):
+ wid.setDangerMessage('This action may affect other systems!')
+ #form._splitter.setStretchFactor(1,70)
+ #form._splitter.setStretchFactor(0,30)
+ form._splitter.setSizes([80,20])
+ except Exception:
+ self.warning('Unable to setModel for TaurusDevicePanel.comms_form!!: %s'%traceback.format_exc())
+ return form
+
def filterNonExported(obj):
if not isinstance(obj,taurus.core.TaurusDevInfo) or obj.exported():
return obj
return None
-
+
+
class TaurusDevPanel(TaurusMainWindow):
'''
TaurusDevPanel is a Taurus Application inspired in Jive and Atk Panel.
@@ -128,6 +488,7 @@ class TaurusDevPanel(TaurusMainWindow):
#try to connect with the device
self.setModel(devname)
dev = self.getModelObj()
+ dev.state()
state = dev.getSWState()
#test the connection
if state == taurus.core.TaurusSWDevState.Running:
@@ -149,7 +510,50 @@ class TaurusDevPanel(TaurusMainWindow):
ret['module'] = 'taurus.qt.qtgui.panel'
return ret
+#===============================================================================
+# Launchers
+#===============================================================================
+
+def TaurusDevicePanelMain():
+ '''A launcher for TaurusDevicePanel.'''
+ #!/usr/bin/python
+ import sys
+ from taurus.qt.qtgui.application import TaurusApplication
+ from taurus.core.util import argparse
+ parser = argparse.get_taurus_parser()
+ parser.set_usage("%prog [options] devname [attrs]")
+ parser.set_description("Taurus Application inspired in Jive and Atk Panel")
+ parser.add_option("", "--config-file", dest="config_file", default=None,
+ help="launch a wizard for creating a new TaurusGUI application")
+
+ app = TaurusApplication(cmd_line_parser=parser,app_name="TaurusDevicePanel",
+ app_version=taurus.Release.version)
+ args = app.get_command_line_args()
+ options = app.get_command_line_options()
+
+ app.setLogLevel(taurus.Debug)
+
+ w = TaurusDevicePanel()
+ w.show()
+ if options.tango_host is None:
+ options.tango_host = taurus.Database().getNormalName()
+ try: w.setTangoHost(options.tango_host)
+ except: pass
+
+ if options.config_file is not None:
+ w.loadConfigFile(options.config_file)
+ else:
+ w.setAttributeFilters({args[0]:args[1:]})
+
+ if len(args)<1:
+ parser.print_help() #@todo use modelchooser instead of printing the help
+ return
+ w.setModel(args[0])
+
+ sys.exit(app.exec_())
+
+
def TaurusPanelMain():
'''A launcher for TaurusPanel.'''
## NOTE: DON'T PUT TEST CODE HERE.
@@ -161,6 +565,7 @@ def TaurusPanelMain():
parser = argparse.get_taurus_parser()
parser.set_usage("%prog [options] [devname]")
parser.set_description("Taurus Application inspired in Jive and Atk Panel")
+
app = TaurusApplication(cmd_line_parser=parser,app_name="tauruspanel",
app_version=taurus.Release.version)
args = app.get_command_line_args()
@@ -171,12 +576,17 @@ def TaurusPanelMain():
if options.tango_host is None:
options.tango_host = taurus.Database().getNormalName()
w.setTangoHost(options.tango_host)
+
if len(args) == 1:
w.setDevice(args[0])
w.show()
- sys.exit(app.exec_())
+ sys.exit(app.exec_())
+
+
+
+###############################################################################
if __name__ == "__main__":
- TaurusPanelMain()
\ No newline at end of file
+ TaurusDevicePanelMain()
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/panel/taurusfilterpanel.py b/lib/taurus/qt/qtgui/panel/taurusfilterpanel.py
index 495f985..b45e99d 100644
--- a/lib/taurus/qt/qtgui/panel/taurusfilterpanel.py
+++ b/lib/taurus/qt/qtgui/panel/taurusfilterpanel.py
@@ -254,7 +254,7 @@ class TaurusFilterPanelOld1(Qt.QWidget, taurus.qt.qtgui.base.TaurusBaseWidget):
g_layout = self.layout()
header = g_layout.itemAt(0).widget()
headerCombo = header.layout().itemAt(1).widget()
- return headerCombo.itemData(headerCombo.currentIndex()).toPyObject()
+ return Qt.from_qvariant(headerCombo.itemData(headerCombo.currentIndex()))
def calculate(self):
db = self.getModelObj()
@@ -269,7 +269,7 @@ class TaurusFilterPanelOld1(Qt.QWidget, taurus.qt.qtgui.base.TaurusBaseWidget):
comboBox = field_layout.itemAt(1).widget()
edit = field_layout.itemAt(2).widget()
- type = comboBox.itemData(comboBox.currentIndex()).toPyObject()
+ type = Qt.from_qvariant(comboBox.itemData(comboBox.currentIndex()))
expr = str(edit.text())
f = getFilter(type, expr)
filters.append(f)
diff --git a/lib/taurus/qt/qtgui/panel/taurusform.py b/lib/taurus/qt/qtgui/panel/taurusform.py
index 514f002..98d2739 100644
--- a/lib/taurus/qt/qtgui/panel/taurusform.py
+++ b/lib/taurus/qt/qtgui/panel/taurusform.py
@@ -69,6 +69,14 @@ class TaurusForm(TaurusWidget):
can be changed and specific mappings can be defined using the
:meth:`setCustomWidgetMap` method.
+ Item objects can be accessed by index using a list-like notation::
+
+ form = TaurusForm()
+ form.model = ['sys/tg_test/1'+a for a in ('short_image','/float_scalar','/double_scalar')]
+ form[0].labelConfig = 'dev_alias'
+ form[-1].writeWidgetClass = 'TaurusWheelEdit'
+ print(len(form)) # --> outputs '3' (the length of the form is the number of items)
+
By default, the form provides global Apply and Cancel buttons.
You can also see some code that exemplifies the use of TaurusForm in :ref:`Taurus
@@ -87,7 +95,6 @@ class TaurusForm(TaurusWidget):
self._model = []
self._children = []
self.setFormWidget(formWidget)
- self._withButtons = withButtons
self.setLayout(Qt.QVBoxLayout())
@@ -104,7 +111,6 @@ class TaurusForm(TaurusWidget):
self.layout().addWidget(self.buttonBox)
self._connectButtons()
- self._manageButtonBox()
#Actions (they automatically populate the context menu)
@@ -114,8 +120,29 @@ class TaurusForm(TaurusWidget):
self.addAction(self.chooseModelsAction)
self.connect(self.chooseModelsAction, Qt.SIGNAL("triggered()"), self.chooseModels)
+ self.showButtonsAction = Qt.QAction('Show Buttons', self)
+ self.showButtonsAction.setCheckable(True)
+ self.addAction(self.showButtonsAction)
+ self.connect(self.showButtonsAction, Qt.SIGNAL("triggered(bool)"), self.setWithButtons)
+ self.setWithButtons(withButtons)
+
+ self.changeLabelsAction = Qt.QAction('Change labels (all items)', self)
+ self.addAction(self.changeLabelsAction)
+ self.connect(self.changeLabelsAction, Qt.SIGNAL("triggered()"), self.onChangeLabelsAction)
+
self.resetModifiableByUser()
self.setSupportedMimeTypes([TAURUS_MODEL_LIST_MIME_TYPE, TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE, TAURUS_MODEL_MIME_TYPE, 'text/plain'])
+
+ #properties
+ self.registerConfigProperty(self.isWithButtons, self.setWithButtons, 'withButtons')
+
+ def __getitem__(self, key):
+ '''provides a list-like interface: items of the form can be accessed using slice notation'''
+ return self.getItemByIndex(key)
+
+ def __len__(self):
+ '''returns the number of items contained by the form'''
+ return len(self.getItems())
def _splitModel(self, modelNames):
'''convert str to list if needed (commas and whitespace are considered as separators)'''
@@ -158,8 +185,24 @@ class TaurusForm(TaurusWidget):
if self.__modelChooser is None:
self.__modelChooser = TaurusModelChooser()
self.connect(self.__modelChooser, Qt.SIGNAL("updateModels"), self.setModel)
- self.__modelChooser.setWindowTitle("%s - Model Chooser"%unicode(self.windowTitle()))
- self.__modelChooser.setListedModels(self.getModel())
+ self.__modelChooser.setWindowTitle("%s - Model Chooser"%unicode(self.windowTitle()))
+
+ models_and_labels = []
+ models = [m.lower() for m in self.getModel()]
+ indexdict = {}
+ for m in models:
+ indexdict[m]=indexdict.get(m,-1)+1
+ item = self.getItemByModel(m,indexdict[m])
+ if item is None:
+ label = None
+ else:
+ try:
+ label = str(item.labelWidget().text()) #this assumes that the readwidget is a QLabel subclass (or something that has text())
+ except:
+ label = None
+ models_and_labels.append((m,label))
+
+ self.__modelChooser.setListedModels(models_and_labels)
self.__modelChooser.show()
self.__modelChooser.raise_()
@@ -234,7 +277,11 @@ class TaurusForm(TaurusWidget):
obj=taurus.Attribute(model) #if it is not an attribute, it will get an exception here
return self._defaultFormWidget,(),{}
except:
- obj=taurus.Device(model)
+ try:
+ obj=taurus.Device(model)
+ except:
+ self.warning('Cannot handle model "%s". Using default widget.'%(model))
+ return self._defaultFormWidget,(),{}
try:
key = obj.getHWObj().info().dev_class
except:
@@ -280,11 +327,11 @@ class TaurusForm(TaurusWidget):
def setWithButtons(self, trueFalse):
self._withButtons = trueFalse
- self._manageButtonBox()
+ self.buttonBox.setVisible(self._withButtons)
+ self.showButtonsAction.setChecked(self._withButtons)
def resetWithButtons(self):
- self._withButtons = True
- self._manageButtonBox()
+ self.setWithButtons(True)
def dropEvent(self, event):
'''reimplemented to support dropping of modelnames in forms'''
@@ -306,6 +353,8 @@ class TaurusForm(TaurusWidget):
'''
TaurusWidget.setModifiableByUser(self, modifiable)
self.chooseModelsAction.setEnabled(modifiable)
+ self.showButtonsAction.setEnabled(modifiable)
+ self.changeLabelsAction.setEnabled(modifiable)
for item in self.getItems():
try:
item.setModifiableByUser(modifiable)
@@ -380,11 +429,24 @@ class TaurusForm(TaurusWidget):
'''returns a list of the objects that have been created as childs of the form'''
return self._children
- def _manageButtonBox(self):
- if self.isWithButtons():
- self.buttonBox.setVisible(True)
- else:
- self.buttonBox.setVisible(False)
+# def _manageButtonBox(self):
+# if self.isWithButtons():
+# self.buttonBox.setVisible(True)
+# else:
+# self.buttonBox.setVisible(False)
+
+ def onChangeLabelsAction(self):
+ '''changes the labelConfig of all its items'''
+ keys = ['label', 'attr_name', 'attr_fullname', 'dev_alias', 'dev_name', 'dev_fullname']
+
+ msg = 'Choose new source for the label. \n'+\
+ 'You can also write a more complex text\n'+\
+ 'using any of the proposed sources as a\n'+\
+ 'placeholder by enclosing it in "< >" brackets'
+ labelConfig, ok = Qt.QInputDialog.getItem(self, 'Change Label', msg, keys, 0, True)
+ if ok:
+ for item in self.getItems():
+ item.labelConfig=str(labelConfig)
@Qt.pyqtSignature("apply()")
def apply(self):
@@ -419,7 +481,7 @@ class TaurusForm(TaurusWidget):
resetWithButtons)
modifiableByUser = Qt.pyqtProperty("bool", TaurusWidget.isModifiableByUser,
- TaurusWidget.setModifiableByUser,
+ setModifiableByUser,
TaurusWidget.resetModifiableByUser)
@@ -840,7 +902,8 @@ def taurusFormMain():
parser.set_usage("%prog [options] [model1 [model2 ...]]")
parser.set_description("the taurus form panel application")
parser.add_option("--window-name", dest="window_name", default="TaurusForm", help="Name of the window")
-
+ parser.add_option("--config", "--config-file", dest="config_file", default=None,
+ help="use the given config file for initialization")
app = TaurusApplication(cmd_line_parser=parser,
app_name="taurusform",
app_version=taurus.Release.version)
@@ -849,15 +912,27 @@ def taurusFormMain():
dialog = TaurusForm()
dialog.setModifiableByUser(True)
+ dialog.setModelInConfig(True)
dialog.setWindowTitle(options.window_name)
+ saveConfigAction = Qt.QAction("Save current settings...", dialog)
+ saveConfigAction.setShortcut(Qt.QKeySequence.Save)
+ dialog.connect(saveConfigAction, Qt.SIGNAL("triggered()"), dialog.saveConfigFile)
+ dialog.addAction(saveConfigAction)
+
+ loadConfigAction = Qt.QAction("&Retrieve saved settings...", dialog)
+ loadConfigAction.setShortcut(Qt.QKeySequence.Open)
+ dialog.connect(loadConfigAction, Qt.SIGNAL("triggered()"), dialog.loadConfigFile)
+ dialog.addAction(loadConfigAction)
#set the default map for this installation
from taurus.TaurusCustomSettings import T_FORM_CUSTOM_WIDGET_MAP
dialog.setCustomWidgetMap(T_FORM_CUSTOM_WIDGET_MAP)
#set a model list from the command line or launch the chooser
- if len(args)>0:
+ if options.config_file is not None:
+ dialog.loadConfigFile(options.config_file)
+ elif len(args)>0:
models=args
dialog.setModel(models)
else:
diff --git a/lib/taurus/qt/qtgui/panel/taurusinputpanel.py b/lib/taurus/qt/qtgui/panel/taurusinputpanel.py
new file mode 100644
index 0000000..bda0c2a
--- /dev/null
+++ b/lib/taurus/qt/qtgui/panel/taurusinputpanel.py
@@ -0,0 +1,413 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""This module provides an Input panel (usually used inside a TaurusDialog)"""
+
+__all__ = ["TaurusInputPanel"]
+
+__docformat__ = 'restructuredtext'
+
+import sys
+import collections
+
+from taurus.qt import Qt
+import ui.ui_TaurusInputPanel
+
+
+class TaurusInputPanel(Qt.QWidget):
+ """A panel design to get an input from the user.
+
+ The input_data is a dictionary which contains information on how to build
+ the input dialog. It **must** contains the following keys:
+
+ - *prompt* <str>: message to be displayed
+
+ The following are optional keys (and their corresponding default values):
+
+ - *title* <str> (doesn't have default value)
+ - *key* <str> (doesn't have default value):
+ a label to be presented left to the input box represeting the label
+ - *unit* <str> (doesn't have default value):
+ a label to be presented right to the input box representing the units
+ - *data_type* <str or sequence> ('String'):
+ type of data to be requested. Standard
+ accepted data types are 'String', 'Integer', 'Float', 'Boolean',
+ 'Text'. A list of elements will be interpreted as a selection.
+ Default TaurusInputPanel class will interpret any custom data types as
+ 'String' and will display input widget accordingly. Custom
+ data types can be handled differently by supplying a different
+ input_panel_klass.
+ - *minimum* <int/float> (-sys.maxint):
+ minimum value (makes sence when data_type is 'Integer' or 'Float')
+ - *maximum* <int/float> (sys.maxint):
+ maximum value (makes sence when data_type is 'Integer' or 'Float')
+ - *step* <int/float> (1):
+ step size value (makes sence when data_type is 'Integer' or 'Float')
+ - *decimals* <int> (1):
+ number of decimal places to show (makes sence when data_type is
+ 'Float')
+ - *default_value* <obj> (doesn't have default value):
+ default value
+ - *allow_multiple* <bool> (False):
+ allow more than one value to be selected (makes sence when data_type
+ is a sequence of possibilities)
+
+
+ Example::
+
+ app = Qt.QApplication([])
+
+ class Listener(object):
+ def on_accept(self):
+ print "user selected", self.panel.value()
+
+ d = dict(prompt="What's your favourite car brand?",
+ data_type=["Mazda", "Skoda", "Citroen", "Mercedes", "Audi", "Ferrari"],
+ default_value="Mercedes")
+ w = TaurusInputPanel(d)
+ l = Listener()
+ l.panel = w
+ w.connect(w.buttonBox(), Qt.SIGNAL("accepted()"), l.on_accept)
+ w.show()
+ app.exec_()
+ """
+ def __init__(self, input_data, parent=None):
+ Qt.QWidget.__init__(self, parent)
+ self._input_data = input_data
+ self._ui = _ui = ui.ui_TaurusInputPanel.Ui_TaurusInputPanel()
+ _ui.setupUi(self)
+ self.fill_main_panel(_ui.inputPanel, input_data)
+
+ def fill_main_panel(self, panel, input_data):
+ layout = Qt.QVBoxLayout()
+ layout.setContentsMargins(0, 0, 0 ,0)
+ panel.setLayout(layout)
+ if isinstance(input_data, collections.Mapping):
+ single_panel, getter = self.create_single_input_panel(input_data)
+ layout.addWidget(single_panel)
+ self.value = getter
+ if 'title' in input_data:
+ self.setWindowTitle(input_data['title'])
+
+ def create_single_input_panel(self, input_data):
+ style = Qt.QApplication.instance().style()
+ icon = style.standardIcon(Qt.QStyle.SP_MessageBoxQuestion)
+ self.setIconPixmap(icon.pixmap(64))
+
+ self.setText(input_data['prompt'])
+
+ data_type = input_data.get('data_type', 'String')
+ is_seq = not isinstance(data_type, (str, unicode)) and \
+ isinstance(data_type, collections.Sequence)
+ if is_seq:
+ panel, getter = self.create_selection_panel(input_data)
+ else:
+ data_type_l = data_type.lower()
+ creator = getattr(self, "create_" + data_type_l + "_panel", self.create_custom_panel)
+ if creator:
+ panel, getter = creator(input_data)
+
+ if panel is None:
+ panel = Qt.QLabel("Cannot create widget for data type '%s'" % data_type)
+ getter = lambda : None
+ return panel, getter
+
+ def create_custom_panel(self, input_data):
+ return self.create_string_panel(input_data)
+
+ def create_selection_panel(self, input_data):
+ allow_multiple = input_data.get('allow_multiple', False)
+
+ if allow_multiple:
+ return self._create_multi_selection_panel(input_data)
+ else:
+ return self._create_single_selection_panel(input_data)
+
+ def _create_single_selection_panel(self, input_data):
+ items = list(map(str, input_data['data_type']))
+ if len(items) > 5:
+ return self._create_combobox_panel(input_data)
+ return self._create_radiobutton_panel(input_data)
+
+ def _create_combobox_panel(self, input_data):
+ panel = self._create_simple_panel(input_data)
+ layout = panel.layout()
+ self._ui.inputWidget = combobox = Qt.QComboBox()
+ items = input_data['data_type']
+ for item in items:
+ is_seq = not isinstance(item, (str, unicode)) and \
+ isinstance(item, collections.Sequence)
+ if is_seq:
+ text, userData = item
+ else:
+ text, userData = str(item), item
+ combobox.addItem(text, userData)
+ layout.addWidget(combobox, 0, 1)
+ return panel, self._get_combobox_value
+
+ def _get_combobox_value(self):
+ combo = self._ui.inputWidget
+ return Qt.from_qvariant(combo.itemData(combo.currentIndex()))
+
+ def _create_radiobutton_panel(self, input_data):
+ panel = self._create_group_panel(input_data)
+ layout = panel.layout()
+ items = input_data['data_type']
+ default_value = input_data.get('default_value')
+ self._ui.inputWidget = buttongroup = Qt.QButtonGroup()
+ buttongroup.setExclusive(True)
+ for item in items:
+ is_seq = not isinstance(item, (str, unicode)) and \
+ isinstance(item, collections.Sequence)
+ if is_seq:
+ text, userData = item
+ else:
+ text, userData = str(item), item
+ button = Qt.QRadioButton(text)
+ button._value = userData
+ if default_value == userData:
+ button.setChecked(True)
+ buttongroup.addButton(button)
+ layout.addWidget(button)
+ return panel, self._get_radiobutton_value
+
+ def _get_radiobutton_value(self):
+ buttongroup = self._ui.inputWidget
+ button = buttongroup.checkedButton()
+ if button is not None:
+ return button._value
+
+ def _create_multi_selection_panel(self, input_data):
+ panel = self._create_group_panel(input_data)
+ layout = panel.layout()
+ items = input_data['data_type']
+ default_value = input_data.get('default_value')
+ if default_value is None:
+ default_value = ()
+ dft_is_seq = not isinstance(default_value, (str, unicode)) and \
+ isinstance(default_value, collections.Sequence)
+ if not dft_is_seq:
+ default_value = default_value,
+
+ self._ui.inputWidget = listwidget = Qt.QListWidget()
+ listwidget.setSelectionMode(Qt.QAbstractItemView.MultiSelection)
+
+ for item in items:
+ is_seq = not isinstance(item, (str, unicode)) and \
+ isinstance(item, collections.Sequence)
+ if is_seq:
+ text, userData = item
+ else:
+ text, userData = str(item), item
+ item_widget = Qt.QListWidgetItem(text, listwidget)
+ item_widget.setData(Qt.Qt.UserRole, Qt.to_qvariant(userData))
+ if userData in default_value:
+ item_widget.setSelected(True)
+ layout.addWidget(listwidget)
+ return panel, self._get_multi_selection_value
+
+ def _get_multi_selection_value(self):
+ listwidget = self._ui.inputWidget
+ return [ Qt.from_qvariant(item.data(Qt.Qt.UserRole)) for item in listwidget.selectedItems() ]
+
+ def _create_group_panel(self, input_data):
+ title = input_data.get('key', '')
+ unit = input_data.get('unit', '')
+ if unit:
+ title += "(" + unit + ")"
+ panel = Qt.QGroupBox(title)
+ layout = Qt.QVBoxLayout()
+ panel.setLayout(layout)
+ self._ui.inputLabel = Qt.QLabel()
+ self._ui.unitLabel = Qt.QLabel()
+ return panel
+
+ def _create_simple_panel(self, input_data):
+ panel = Qt.QWidget()
+ key = input_data.get('key', '')
+ if key:
+ key += ":"
+ unit = input_data.get('unit', '')
+ layout = Qt.QGridLayout()
+ panel.setLayout(layout)
+ self._ui.inputLabel = label = Qt.QLabel(key)
+ layout.addWidget(label, 0, 0)
+ self._ui.unitLabel = unit = Qt.QLabel(unit)
+ layout.addWidget(unit, 0, 2)
+ layout.setColumnStretch(0, 0)
+ layout.setColumnStretch(1, 1)
+ layout.setColumnStretch(2, 0)
+ return panel
+
+ def create_integer_panel(self, input_data):
+ panel = self._create_simple_panel(input_data)
+ minimum = input_data.get('minimum', -sys.maxint)
+ maximum = input_data.get('maximum', sys.maxint)
+ step = input_data.get('step', 1)
+ layout = panel.layout()
+ self._ui.inputWidget = spinbox = Qt.QSpinBox()
+ spinbox.setMinimum(minimum)
+ spinbox.setMaximum(maximum)
+ spinbox.setSingleStep(step)
+ if 'default_value' in input_data:
+ spinbox.setValue(input_data['default_value'])
+ layout.addWidget(spinbox, 0, 1)
+ return panel, self._get_integer_value
+
+ def _get_integer_value(self):
+ return int(self._ui.inputWidget.value())
+
+ def create_float_panel(self, input_data):
+ panel = self._create_simple_panel(input_data)
+ minimum = input_data.get('minimum', -sys.maxint)
+ maximum = input_data.get('maximum', sys.maxint)
+ step = input_data.get('step', 1)
+ decimals = input_data.get('decimals', 1)
+ layout = panel.layout()
+ self._ui.inputWidget = spinbox = Qt.QDoubleSpinBox()
+ spinbox.setMinimum(minimum)
+ spinbox.setMaximum(maximum)
+ spinbox.setSingleStep(step)
+ spinbox.setDecimals(decimals)
+ if 'default_value' in input_data:
+ spinbox.setValue(input_data['default_value'])
+ layout.addWidget(spinbox, 0, 1)
+ return panel, self._get_float_value
+
+ def _get_float_value(self):
+ return float(self._ui.inputWidget.value())
+
+ def create_string_panel(self, input_data):
+ panel = self._create_simple_panel(input_data)
+ layout = panel.layout()
+ self._ui.inputWidget = lineedit = Qt.QLineEdit()
+ if 'default_value' in input_data:
+ lineedit.setText(str(input_data['default_value']))
+ lineedit.selectAll()
+ layout.addWidget(lineedit, 0, 1)
+ return panel, self._get_string_value
+
+ def _get_string_value(self):
+ return str(self._ui.inputWidget.text())
+
+ def create_text_panel(self, input_data):
+ panel = self._create_group_panel(input_data)
+ layout = panel.layout()
+ self._ui.inputWidget = textedit = Qt.QTextEdit()
+ textedit.setAcceptRichText(False)
+ if 'default_value' in input_data:
+ textedit.setPlainText(str(input_data['default_value']))
+ textedit.selectAll()
+ layout.addWidget(textedit)
+ return panel, self._get_text_value
+
+ def _get_text_value(self):
+ return str(self._ui.inputWidget.toPlainText())
+
+ def create_boolean_panel(self, input_data):
+ panel = self._create_simple_panel(input_data)
+ layout = panel.layout()
+ self._ui.inputWidget = checkbox = Qt.QCheckBox()
+ value = input_data.get('default_value', False)
+ checkbox.setChecked(value)
+ layout.addWidget(checkbox, 0, 1)
+ return panel, self._get_boolean_value
+
+ def _get_boolean_value(self):
+ return self._ui.inputWidget.checkState() == Qt.Qt.Checked
+
+ def inputPanel(self):
+ return self._ui.inputPanel
+
+ def buttonBox(self):
+ """Returns the button box from this panel
+
+ :return: the button box from this panel
+ :rtype: PyQt4.Qt.QDialogButtonBox"""
+ return self._ui.buttonBox
+
+ def addButton(self, button, role=Qt.QDialogButtonBox.ActionRole):
+ """Adds the given button with the given to the button box
+
+ :param button: the button to be added
+ :type button: PyQt4.QtGui.QPushButton
+ :param role: button role
+ :type role: PyQt4.Qt.QDialogButtonBox.ButtonRole"""
+ self._ui.buttonBox.addButton(button, role)
+
+ def setIconPixmap(self, pixmap):
+ """Sets the icon to the dialog
+
+ :param pixmap: the icon pixmap
+ :type pixmap: PyQt4.Qt.QPixmap"""
+ self._ui.iconLabel.setPixmap(pixmap)
+
+ def setText(self, text):
+ """Sets the text of this panel
+
+ :param text: the new text
+ :type text: str"""
+ self._ui.textLabel.setText(text)
+
+ def getText(self):
+ """Returns the current text of this panel
+
+ :return: the text for this panel
+ :rtype: str"""
+ return self._ui.textLabel.text()
+
+ def setInputFocus(self):
+ inputWidget = self._ui.inputWidget
+ if not inputWidget:
+ return
+ if isinstance(inputWidget, Qt.QWidget):
+ inputWidget.setFocus()
+ elif isinstance(inputWidget, Qt.QButtonGroup):
+ bid = inputWidget.checkedId()
+ if bid < 0:
+ button = inputWidget.buttons()[0]
+ else:
+ button = inputWidget.button(bid)
+ button.setFocus()
+
+def main():
+ app = Qt.QApplication([])
+
+ class Listener(object):
+ def on_accept(self):
+ print "user selected", self.panel.value()
+
+ d = dict(prompt="What's your favourite car brand?",
+ data_type=["Mazda", "Skoda", "Citroen", "Mercedes", "Audi", "Ferrari"],
+ default_value="Mercedes")
+ w = TaurusInputPanel(d)
+ l = Listener()
+ l.panel = w
+ w.connect(w.buttonBox(), Qt.SIGNAL("accepted()"), l.on_accept)
+ w.show()
+ app.exec_()
+
+if __name__ == "__main__":
+ main()
diff --git a/lib/taurus/qt/qtgui/panel/taurusmessagepanel.py b/lib/taurus/qt/qtgui/panel/taurusmessagepanel.py
index 313322d..4dcbfb3 100644
--- a/lib/taurus/qt/qtgui/panel/taurusmessagepanel.py
+++ b/lib/taurus/qt/qtgui/panel/taurusmessagepanel.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -38,7 +38,7 @@ try:
import pygments
from pygments import highlight
from pygments.formatters import HtmlFormatter
- from pygments.lexers import PythonLexer, PythonTracebackLexer
+ from pygments.lexers import PythonTracebackLexer
except:
pygments = None
@@ -53,21 +53,21 @@ import ui.ui_TaurusMessagePanel
class TaurusMessageErrorHandler(object):
- """This class is designed to handle a generic error into a
+ """This class is designed to handle a generic error into a
:class:`TaurusMessagePanel`"""
-
+
def __init__(self, msgbox):
self._msgbox = msgbox
msgbox.setWindowTitle("Taurus Error")
self.setError(*msgbox.getError())
-
+
def setError(self, err_type=None, err_value=None, err_traceback=None):
"""Translates the given error object into an HTML string and places it
in the message panel
-
+
:param error: an error object (typically an exception object)
:type error: object"""
-
+
msgbox = self._msgbox
error = "".join(traceback.format_exception_only(err_type, err_value))
msgbox.setText(error)
@@ -93,16 +93,16 @@ class TaurusMessageErrorHandler(object):
class TangoMessageErrorHandler(TaurusMessageErrorHandler):
- """This class is designed to handle :class:`PyTango.DevFailed` error into
+ """This class is designed to handle :class:`PyTango.DevFailed` error into
a :class:`TaurusMessagePanel`"""
-
+
def setError(self, err_type=None, err_value=None, err_traceback=None):
"""Translates the given error object into an HTML string and places it
it the message panel
-
+
:param error: an error object (typically an exception object)
:type error: object"""
-
+
msgbox = self._msgbox
html_orig = '<html><head><style type="text/css">{style}</style>' \
'</head><body>'
@@ -135,14 +135,14 @@ class TangoMessageErrorHandler(TaurusMessageErrorHandler):
class MacroServerMessageErrorHandler(TaurusMessageErrorHandler):
-
+
def setError(self, err_type=None, err_value=None, err_traceback=None):
"""Translates the given error object into an HTML string and places it
in the message panel
-
+
:param error: an error object (typically an exception object)
:type error: object"""
-
+
msgbox = self._msgbox
msgbox.setText(err_value)
msg = "<html><body><pre>%s</pre></body></html>" % err_value
@@ -185,16 +185,15 @@ def get_report_handlers():
global _REPORT_HANDLERS
if not _REPORT_HANDLERS is None:
return _REPORT_HANDLERS
-
+
import os.path
- import sys
import functools
import inspect
-
+
this = os.path.abspath(__file__)
report_path = os.path.join(os.path.dirname(this), "report")
sys.path.insert(0, report_path)
-
+
_REPORT_HANDLERS = {}
try:
for elem in os.listdir(report_path):
@@ -212,7 +211,7 @@ def get_report_handlers():
_REPORT_HANDLERS[name] = obj
finally:
sys.path.pop(0)
-
+
return _REPORT_HANDLERS
_REPORT = """\
@@ -231,7 +230,7 @@ An error occured in '{appName} {appVersion}' on {time}
class TaurusMessagePanel(Qt.QWidget):
"""A panel intended to display a taurus error.
Example::
-
+
dev = taurus.Device("sys/tg_test/1")
try:
print dev.read_attribute("throw_exception")
@@ -242,18 +241,18 @@ class TaurusMessagePanel(Qt.QWidget):
You can show the error outside the exception handling code. If you do this,
you should keep a record of the exception information as given by
:func:`sys.exc_info`::
-
+
dev = taurus.Device("sys/tg_test/1")
exc_info = None
try:
print dev.read_attribute("throw_exception")
except PyTango.DevFailed, df:
exc_info = sys.exc_info()
-
+
if exc_info:
msgbox = TaurusMessagePanel(*exc_info)
msgbox.show()"""
-
+
def __init__(self, err_type=None, err_value=None, err_traceback=None, parent=None, designMode=False):
Qt.QWidget.__init__(self, parent)
self._exc_info = err_type, err_value, err_traceback
@@ -263,28 +262,28 @@ class TaurusMessagePanel(Qt.QWidget):
_ui.checkBox.setVisible(False)
_ui.checkBox.setCheckState(Qt.Qt.Unchecked)
self._initReportCombo()
-
+
Qt.QObject.connect(_ui.showDetailsButton, Qt.SIGNAL("toggled(bool)"), self._onShowDetails)
Qt.QObject.connect(_ui.reportComboBox, Qt.SIGNAL("activated(int)"), self._onReportTriggered)
-
+
pixmap = getThemePixmap("emblem-important")
self.setIconPixmap(pixmap)
-
+
if err_value is not None:
self.setError(*self._exc_info)
self.adjustSize()
-
+
def _initReportCombo(self):
report_handlers = get_report_handlers()
combo = self.reportComboBox()
for name, report_handler in report_handlers.items():
name = Qt.QVariant(name)
combo.addItem(report_handler.Label, name)
-
+
def _onReportTriggered(self, index):
report_handlers = get_report_handlers()
combo = self.reportComboBox()
- name = str(combo.itemData(index).toString())
+ name = Qt.from_qvariant(combo.itemData(index), str)
report_handler = report_handlers[name]
report = report_handler(self)
app = Qt.QApplication.instance()
@@ -295,7 +294,7 @@ class TaurusMessagePanel(Qt.QWidget):
detail=self.getDetailedText(),
origin=self.getOriginText())
report.report(txt)
-
+
def _onShowDetails(self, show):
self._ui.detailsWidget.setVisible(show)
if show:
@@ -305,141 +304,141 @@ class TaurusMessagePanel(Qt.QWidget):
self._ui.showDetailsButton.setText(text)
self.adjustSize()
self.emit(Qt.SIGNAL("toggledDetails(bool)"), show)
-
+
def reportComboBox(self):
return self._ui.reportComboBox
-
+
def checkBox(self):
"""Returns the check box from this panel
-
+
:return: the check box from this panel
:rtype: PyQt4.Qt.QCheckBox"""
return self._ui.checkBox
-
+
def checkBoxState(self):
"""Returns the check box state
-
+
:return: the check box state
:rtype: PyQt4.Qt.CheckState"""
return self.checkBox().checkState()
-
+
def checkBoxText(self):
"""Returns the check box text
-
+
:return: the check box text
:rtype: str"""
return str(self.checkBox().text())
-
+
def setCheckBoxText(self, text):
"""Sets the checkbox text.
-
+
:param text: new checkbox text
:type text: str"""
self.checkBox().setText(text)
-
+
def setCheckBoxState(self, state):
"""Sets the checkbox state.
-
+
:param text: new checkbox state
:type text: PyQt4.Qt.CheckState"""
self.checkBox().setCheckState(state)
-
+
def setCheckBoxVisible(self, visible):
"""Sets the checkbox visibility.
-
+
:param visible: True makes checkbox visible, False hides it
:type visible: bool"""
self.checkBox().setVisible(visible)
-
+
def buttonBox(self):
"""Returns the button box from this panel
-
+
:return: the button box from this panel
:rtype: PyQt4.Qt.QDialogButtonBox"""
return self._ui.buttonBox
-
+
def addButton(self, button, role=Qt.QDialogButtonBox.ActionRole):
"""Adds the given button with the given to the button box
-
+
:param button: the button to be added
:type button: PyQt4.QtGui.QPushButton
:param role: button role
:type role: PyQt4.Qt.QDialogButtonBox.ButtonRole"""
self._ui.buttonBox.addButton(button, role)
-
+
def setIconPixmap(self, pixmap):
"""Sets the icon to the dialog
-
+
:param pixmap: the icon pixmap
:type pixmap: PyQt4.Qt.QPixmap"""
self._ui.iconLabel.setPixmap(pixmap)
-
+
def setText(self, text):
"""Sets the text of this panel
-
+
:param text: the new text
:type text: str"""
self._ui.textLabel.setText(text)
-
+
def getText(self):
"""Returns the current text of this panel
-
+
:return: the text for this panel
:rtype: str"""
return self._ui.textLabel.text()
-
+
def setDetailedText(self, text):
"""Sets the detailed text of the dialog
-
+
:param text: the new text
:type text: str"""
self._ui.detailsTextEdit.setPlainText(text)
def setDetailedHtml(self, html):
"""Sets the detailed HTML of the dialog
-
+
:param html: the new HTML text
:type html: str"""
self._ui.detailsTextEdit.setHtml(html)
-
+
def getDetailedText(self):
"""Returns the current detailed text of this panel
-
+
:return: the detailed text for this panel
:rtype: str"""
return self._ui.detailsTextEdit.toPlainText()
def getDetailedHtml(self):
"""Returns the current detailed HTML of this panel
-
+
:return: the detailed HTML for this panel
:rtype: str"""
return self._ui.detailsTextEdit.toHtml()
def setOriginText(self, text):
"""Sets the origin text of the dialog
-
+
:param text: the new text
:type text: str"""
self._ui.originTextEdit.setPlainText(text)
def setOriginHtml(self, html):
"""Sets the origin HTML of the dialog
-
+
:param html: the new HTML text
:type html: str"""
self._ui.originTextEdit.setHtml(html)
-
+
def getOriginText(self):
"""Returns the current origin text of this panel
-
+
:return: the origin text for this panel
:rtype: str"""
return self._ui.originTextEdit.toPlainText()
def getOriginHtml(self):
"""Returns the current origin HTML of this panel
-
+
:return: the origin HTML for this panel
:rtype: str"""
return self._ui.originTextEdit.toHtml()
@@ -447,7 +446,7 @@ class TaurusMessagePanel(Qt.QWidget):
def setError(self, err_type=None, err_value=None, err_traceback=None):
"""Sets the exception object.
Example usage::
-
+
dev = taurus.Device("sys/tg_test/1")
exc_info = None
msgbox = TaurusMessagePanel()
@@ -455,49 +454,49 @@ class TaurusMessagePanel(Qt.QWidget):
print dev.read_attribute("throw_exception")
except PyTango.DevFailed, df:
exc_info = sys.exc_info()
-
+
if exc_info:
msgbox.setError(*exc_info)
msgbox.show()
-
+
:param err_type: the exception type of the exception being handled
(a class object)
:type error: class object
:param err_value: exception object
:type err_value: object
- :param err_traceback: a traceback object which encapsulates the call
+ :param err_traceback: a traceback object which encapsulates the call
stack at the point where the exception originally
occurred
:type err_traceback: traceback"""
i = sys.exc_info()
self._exc_info = [ err_type or i[0], err_value or i[1], err_traceback or i[2] ]
-
+
handler_klass = self.findErrorHandler(self._exc_info[0])
- handler = handler_klass(self)
+ handler_klass(self)
def getError(self):
"""Returns the current exception information of this panel
-
+
:return: the current exception information (same as type as returned by
:func:`sys.exc_info`)
:rtype: tuple<type, value, traceback>"""
return self._exc_info
ErrorHandlers = { PyTango.DevFailed : TangoMessageErrorHandler }
-
+
@classmethod
def registerErrorHandler(klass, err_type, err_handler):
klass.ErrorHandlers[err_type] = err_handler
-
+
@classmethod
def findErrorHandler(klass, err_type):
"""Finds the proper error handler class for the given error
-
+
:param err_type: error class
:type err_type: class object
:return: a message box error handler
:rtype: TaurusMessageBoxErrorHandler class object"""
-
+
for exc, h_klass in klass.ErrorHandlers.items():
if issubclass(err_type, exc):
return h_klass
@@ -530,7 +529,7 @@ class QMessageDialog(Qt.QDialog):
l.addWidget(msgbox)
l.addStretch(1)
self.setLayout(l)
-
+
def py_exc():
"""Shows a python exception in a TaurusMessagePanel"""
@@ -552,10 +551,9 @@ def tg_serv_exc():
"""Shows a tango exception from a server in a TaurusMessagePanel"""
import taurus
dev = taurus.Device("sys/tg_test/1")
- exc_info = None
try:
dev.read_attribute("throw_exception")
- except PyTango.DevFailed, df:
+ except PyTango.DevFailed:
msgbox = TaurusMessagePanel(*sys.exc_info())
QMessageDialog(msgbox).exec_()
except:
@@ -583,7 +581,7 @@ def demo():
panel = Qt.QWidget()
layout = Qt.QVBoxLayout()
panel.setLayout(layout)
-
+
m1 = Qt.QPushButton("Python exception")
layout.addWidget(m1)
Qt.QObject.connect(m1, Qt.SIGNAL("clicked()"), py_exc)
@@ -600,17 +598,17 @@ def demo():
Qt.QObject.connect(m4, Qt.SIGNAL("clicked()"), py_tg_serv_exc)
layout.addWidget(m4)
return panel
-
+
def main():
-
+
import sys
import taurus.qt.qtgui.application
Application = taurus.qt.qtgui.application.TaurusApplication
-
+
app = Application.instance()
owns_app = app is None
-
+
if owns_app:
app = Qt.QApplication([])
app.setApplicationName("Taurus message demo")
@@ -621,8 +619,8 @@ def main():
if owns_app:
sys.exit(app.exec_())
else:
- return panel
+ return w
if __name__ == "__main__":
main()
-
\ No newline at end of file
+
diff --git a/lib/taurus/qt/qtgui/panel/taurusmodellist.py b/lib/taurus/qt/qtgui/panel/taurusmodellist.py
index 52aca2e..cd61974 100644
--- a/lib/taurus/qt/qtgui/panel/taurusmodellist.py
+++ b/lib/taurus/qt/qtgui/panel/taurusmodellist.py
@@ -32,7 +32,6 @@ __all__=['TaurusModelModel','TaurusModelItem', 'TaurusModelList']
import copy
from taurus.qt import Qt
-from PyQt4 import Qwt5
import taurus
from taurus.core import TaurusException, TaurusElementType
from taurus.qt.qtcore.mimetypes import TAURUS_MODEL_LIST_MIME_TYPE, TAURUS_ATTR_MIME_TYPE, TAURUS_MODEL_MIME_TYPE
@@ -155,7 +154,7 @@ class TaurusModelModel(Qt.QAbstractListModel):
if index.isValid() and (0 <= index.row() < self.rowCount()):
row = index.row()
item = self.items[row]
- value = unicode(value.toString())
+ value = Qt.from_qvariant(value, unicode)
if role == Qt.Qt.EditRole:
item.src = value
elif role == Qt.Qt.DisplayRole:
@@ -169,7 +168,7 @@ class TaurusModelModel(Qt.QAbstractListModel):
if position is None or position==-1: position = self.rowCount()
if parentindex is None: parentindex = Qt.QModelIndex()
if items is None:
- slice = [TaurusModelItem() for i in range(rows)]
+ slice = [TaurusModelItem() for i in xrange(rows)]
else:
slice=list(items)
rows = len(slice) #note that the rows parameter is ignored if items is passed
@@ -244,8 +243,9 @@ class TaurusModelModel(Qt.QAbstractListModel):
'''reimplemented from :class:`Qt.QAbstractListModel`'''
mimedata = Qt.QAbstractListModel.mimeData(self, indexes)
if len(indexes)==1:
-# mimedata.setData(TAURUS_ATTR_MIME_TYPE, str(self.data(indexes[0]).toString()))
- mimedata.setText(self.data(indexes[0],role=SRC_ROLE).toString())
+# mimedata.setData(TAURUS_ATTR_MIME_TYPE, Qt.from_qvariant(self.data(indexes[0]), str)))
+ txt = Qt.from_qvariant(self.data(indexes[0],role=SRC_ROLE), str)
+ mimedata.setText(txt)
return mimedata
#mimedata.setData()
@@ -254,7 +254,7 @@ class TaurusModelList(Qt.QListView):
'''A list view widget to display and manage a list of models
Tries to identify the type of model and show the state of the device/attr
- associated qith it. It also allows drag and drop of models and sorting.
+ associated with it. It also allows drag and drop of models and sorting.
'''
def __init__(self, parent=None, items=None, designMode=False):
@@ -294,8 +294,8 @@ class TaurusModelList(Qt.QListView):
idx = selected[0]
else:
return
- value = str(self._model.data(idx, role=Qt.Qt.DisplayRole).toString())
- src = str(self._model.data(idx, role=SRC_ROLE).toString())
+ value = Qt.from_qvariant(self._model.data(idx, role=Qt.Qt.DisplayRole), str)
+ src = Qt.from_qvariant(self._model.data(idx, role=SRC_ROLE), str)
value,ok = Qt.QInputDialog.getText(self, "Display Value", "Display value for %s?"%src, Qt.QLineEdit.Normal, value)
if not ok:
return
@@ -401,4 +401,4 @@ if __name__ == "__main__":
w = TaurusModelList()
w.addModels(["item%i"%i for i in range(3)]+[TaurusModelItem(src='src1',display='d1')]+[('src2','d2')])
w.show()
- sys.exit(app.exec_())
\ No newline at end of file
+ sys.exit(app.exec_())
diff --git a/lib/taurus/qt/qtgui/panel/taurusvalue.py b/lib/taurus/qt/qtgui/panel/taurusvalue.py
index dfa0f33..1a30320 100644
--- a/lib/taurus/qt/qtgui/panel/taurusvalue.py
+++ b/lib/taurus/qt/qtgui/panel/taurusvalue.py
@@ -49,8 +49,6 @@ from taurus.qt.qtgui.input import TaurusWheelEdit, TaurusValueLineEdit
from taurus.qt.qtgui.button import TaurusLauncherButton
from taurus.qt.qtgui.util import TaurusWidgetFactory
-from taurus.qt.qtgui.resource import getIcon
-
class DefaultTaurusValueCheckBox(TaurusValueCheckBox):
def __init__(self,*args):
@@ -62,6 +60,13 @@ class DefaultTaurusValueCheckBox(TaurusValueCheckBox):
return None
class DefaultLabelWidget(TaurusLabel):
+ '''
+ The base class used by default for showing the label of a TaurusValue.
+
+ .. note:: It only makes sense to use this class as a part of a TaurusValue,
+ since it assumes that it can get a reference to a TaurusValue via the
+ :meth:`getTaurusValueBuddy` member
+ '''
_dragEnabled=True
@@ -71,7 +76,7 @@ class DefaultLabelWidget(TaurusLabel):
self.setSizePolicy(Qt.QSizePolicy.Preferred,Qt.QSizePolicy.Maximum)
self.setBgRole(None)
self.autoTrim = False
- self.setStyleSheet('border-style: solid; border-width: 1px; border-color: transparent; border-radius: 4px;')
+ self.setStyleSheet('DefaultLabelWidget {border-style: solid; border-width: 1px; border-color: transparent; border-radius: 4px;}')
def setModel(self, model):
if model is None or model=='':
@@ -82,7 +87,7 @@ class DefaultLabelWidget(TaurusLabel):
config = self.taurusValueBuddy().getLabelConfig()
TaurusLabel.setModel(self, model + "?configuration=%s"%config)
elif self.taurusValueBuddy().getModelClass() == taurus.core.TaurusDevice:
- TaurusLabel.setModel(self, model + "/state?configuration=dev_alias")
+ TaurusLabel.setModel(self, model + "/state?configuration=dev_alias")
def sizeHint(self):
return Qt.QSize(Qt.QLabel.sizeHint(self).width(), 18)
@@ -93,10 +98,15 @@ class DefaultLabelWidget(TaurusLabel):
see :meth:`QWidget.contextMenuEvent`"""
menu = Qt.QMenu(self)
menu.addMenu(taurus.qt.qtgui.util.ConfigurationMenu(self.taurusValueBuddy())) #@todo: This should be done more Taurus-ish
+ if hasattr(self.taurusValueBuddy().writeWidget(), 'resetPendingOperations'):
+ r_action = menu.addAction("reset write value",self.taurusValueBuddy().writeWidget().resetPendingOperations)
+ r_action.setEnabled(self.taurusValueBuddy().hasPendingOperations())
if self.taurusValueBuddy().isModifiableByUser():
- cr_action = menu.addAction("Change Read Widget",self.taurusValueBuddy().onChangeReadWidget)
+ menu.addAction("Change label",self.taurusValueBuddy().onChangeLabelConfig)
+ menu.addAction("Change Read Widget",self.taurusValueBuddy().onChangeReadWidget)
cw_action = menu.addAction("Change Write Widget",self.taurusValueBuddy().onChangeWriteWidget)
cw_action.setEnabled(not self.taurusValueBuddy().isReadOnly()) #disable the action if the taurusValue is readonly
+
menu.exec_(event.globalPos())
event.accept()
@@ -114,6 +124,7 @@ class DefaultLabelWidget(TaurusLabel):
def getQtDesignerPluginInfo(cls):
return None
+
class ExpandingLabel(TaurusLabel):
'''just a expanding TaurusLabel'''
def __init__(self,*args):
@@ -125,6 +136,7 @@ class CenteredLed(TaurusLed):
'''just a centered TaurusLed'''
DefaultAlignment = Qt.Qt.AlignHCenter | Qt.Qt.AlignVCenter
+
class DefaultUnitsWidget(TaurusLabel):
def __init__(self,*args):
TaurusLabel.__init__(self,*args)
@@ -143,6 +155,7 @@ class DefaultUnitsWidget(TaurusLabel):
@classmethod
def getQtDesignerPluginInfo(cls):
return None
+
class _AbstractTaurusValueButton(TaurusLauncherButton):
_deleteWidgetOnClose = True
@@ -155,6 +168,7 @@ class _AbstractTaurusValueButton(TaurusLauncherButton):
def getQtDesignerPluginInfo(cls):
return None
+
class TaurusPlotButton(_AbstractTaurusValueButton):
'''A button that launches a TaurusPlot'''
_widgetClassName = 'TaurusPlot'
@@ -186,7 +200,7 @@ class TaurusValuesTableButton_W(TaurusValuesTableButton):
class TaurusDevButton(_AbstractTaurusValueButton):
'''A button that launches a TaurusAttrForm'''
- _widgetClassName = 'TaurusAttrForm'
+ _widgetClassName = 'TaurusDevicePanel'
_icon = ':/places/folder-remote.svg'
_text = 'Show Device'
@@ -263,6 +277,8 @@ class TaurusValue(Qt.QWidget, TaurusBaseWidget):
if parent is not None:
self.setParent(parent)
+ self.registerConfigProperty(self.getLabelConfig, self.setLabelConfig, 'labeConfig')
+
def setVisible(self, visible):
for w in (self.labelWidget(), self.readWidget(), self.writeWidget(), self.unitsWidget(), self.customWidget(), self.extraWidget()):
if w is not None: w.setVisible(visible)
@@ -396,7 +412,7 @@ class TaurusValue(Qt.QWidget, TaurusBaseWidget):
result = [TaurusValuesTableButton, ExpandingLabel]
elif config.isImage():
try:
- from taurus.qt.qtgui.extra_guiqwt import TaurusImageDialog
+ from taurus.qt.qtgui.extra_guiqwt import TaurusImageDialog #unused import but useful to determine if TaurusImageButton should be added
result = [TaurusImageButton, TaurusValuesTableButton, ExpandingLabel]
except ImportError:
result = [TaurusValuesTableButton, ExpandingLabel]
@@ -442,11 +458,20 @@ class TaurusValue(Qt.QWidget, TaurusBaseWidget):
else:
result = [TaurusValueLineEdit, TaurusValueSpinBox, TaurusWheelEdit]
elif config.isSpectrum():
- result = [TaurusArrayEditorButton, TaurusValuesTableButton_W, TaurusValueLineEdit]
+ configType = config.getType()
+ if configType in (PyTango.ArgType.DevDouble, PyTango.ArgType.DevFloat,
+ PyTango.ArgType.DevInt, PyTango.ArgType.DevLong,
+ PyTango.ArgType.DevLong64, PyTango.ArgType.DevShort,
+ PyTango.ArgType.DevULong, PyTango.ArgType.DevULong64,
+ PyTango.ArgType.DevUShort):
+ result = [TaurusArrayEditorButton, TaurusValuesTableButton_W, TaurusValueLineEdit]
+ else:
+ result = [TaurusValuesTableButton_W, TaurusValueLineEdit]
elif config.isImage():
result = [TaurusValuesTableButton_W]
else:
- self.debug('Unsupported attribute type for writting: %s'% str(config.getType()))
+ self.debug('Unsupported attribute type for writing: %s'% str(config.getType()))
+ result = [None]
if returnAll: return result
else: return result[0]
@@ -486,7 +511,23 @@ class TaurusValue(Qt.QWidget, TaurusBaseWidget):
values are widgets to be used
'''
return self._customWidgetMap
-
+
+ def onChangeLabelConfig(self):
+ keys = ['label', 'attr_name', 'attr_fullname', 'dev_alias', 'dev_name', 'dev_fullname']
+ try:
+ current = keys.index(self.labelConfig)
+ except:
+ current= len(keys)
+ keys.append(self.labelConfig)
+
+ msg = 'Choose new source for the label. \n'+\
+ 'You can also write a more complex text\n'+\
+ 'using any of the proposed sources as a\n'+\
+ 'placeholder by enclosing it in "< >" brackets'
+ labelConfig, ok = Qt.QInputDialog.getItem(self, 'Change Label', msg, keys, current, True)
+ if ok:
+ self.labelConfig=str(labelConfig)
+
def onChangeReadWidget(self):
classnames = ['None', 'Auto']+[c.__name__ for c in self.getDefaultReadWidgetClass(returnAll=True)]
cname, ok = Qt.QInputDialog.getItem(self, 'Change Read Widget', 'Choose a new read widget class', classnames, 1, True)
@@ -919,23 +960,22 @@ class TaurusValue(Qt.QWidget, TaurusBaseWidget):
pass
def hasPendingOperations(self):
- '''self.getPendingOperations will alwaysd return an empty list, but still
+ '''self.getPendingOperations will always return an empty list, but still
self.hasPendingOperations will look at the writeWidget's operations.
If you want to ask the TaurusValue for its pending operations, call
self.writeWidget().getPendingOperations()'''
w = self.writeWidget()
- if w is None: return []
+ if w is None: return False
return w.hasPendingOperations()
-
-
+
def updatePendingOpsStyle(self):
if self._labelWidget is None: return
if self.hasPendingOperations():
self._labelWidget.setStyleSheet(
- 'border-style: solid ; border-width: 1px; border-color: blue; color: blue; border-radius:4px;')
+ '%s {border-style: solid ; border-width: 1px; border-color: blue; color: blue; border-radius:4px;}'%self._labelWidget.__class__.__name__)
else:
self._labelWidget.setStyleSheet(
- 'border-style: solid; border-width: 1px; border-color: transparent; color: black; border-radius:4px;')
+ '%s {border-style: solid; border-width: 1px; border-color: transparent; color: black; border-radius:4px;}'%self._labelWidget.__class__.__name__)
def getLabelConfig(self):
return self._labelConfig
@@ -970,6 +1010,7 @@ class TaurusValue(Qt.QWidget, TaurusBaseWidget):
allowWrite = Qt.pyqtProperty("bool", getAllowWrite, setAllowWrite, resetAllowWrite)
modifiableByUser = Qt.pyqtProperty("bool", TaurusBaseWidget.isModifiableByUser, TaurusBaseWidget.setModifiableByUser, TaurusBaseWidget.resetModifiableByUser)
+
class TaurusValuesFrame(TaurusFrame):
'''This is a container specialized into containing TaurusValue widgets.
It should be used Only for TaurusValues'''
@@ -1053,15 +1094,16 @@ if __name__ == "__main__":
#models=['bl97/pc/dummy-01/CurrentSetpoint','bl97/pc/dummy-02/Current','bl97/pc/dummy-02/RemoteMode','bl97/pysignalsimulator/1/value1']
#models=['bl97/pc/dummy-01/CurrentSetpoint','bl97/pc/dummy-02/RemoteMode']
#models=['sys/tg_test/1/state','sys/tg_test/1/status','sys/tg_test/1/short_scalar','sys/tg_test/1']
- #models = ['sys/tg_test/1']+['sys/tg_test/1/%s_spectrum'%s for s in ('float','short','string','long','boolean') ]
- #container.setModel(models)
+ #models = ['sys/tg_test/1']+['sys/tg_test/1/%s_scalar'%s for s in ('float','short','string','long','boolean') ]
+ #models = ['sys/tg_test/1/float_scalar','sys/tg_test/1/double_scalar']
+
+ container.setModel(models)
#container.getTaurusValueByIndex(0).writeWidget().setDangerMessage('BOOO') #uncomment to test the dangerous operation support
#container.getTaurusValueByIndex(0).readWidget().setShowState(True)
#container.getTaurusValueByIndex(0).setWriteWidgetClass(TaurusValueLineEdit)
#container.setModel(models)
-
container.setModifiableByUser(True)
form.show()
diff --git a/lib/taurus/qt/qtgui/taurusgui/ui/PermanentCustomPanelsDlg.ui b/lib/taurus/qt/qtgui/panel/ui/DoubleListDlg.ui
similarity index 51%
rename from lib/taurus/qt/qtgui/taurusgui/ui/PermanentCustomPanelsDlg.ui
rename to lib/taurus/qt/qtgui/panel/ui/DoubleListDlg.ui
index 25c6139..af99ac3 100644
--- a/lib/taurus/qt/qtgui/taurusgui/ui/PermanentCustomPanelsDlg.ui
+++ b/lib/taurus/qt/qtgui/panel/ui/DoubleListDlg.ui
@@ -1,7 +1,8 @@
-<ui version="4.0" >
- <class>PermanentCustomPanelsDlg</class>
- <widget class="QDialog" name="PermanentCustomPanelsDlg" >
- <property name="geometry" >
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DoubleListDlg</class>
+ <widget class="QDialog" name="DoubleListDlg">
+ <property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@@ -9,64 +10,61 @@
<height>426</height>
</rect>
</property>
- <property name="windowTitle" >
- <string>Permanent Panels Dialog</string>
- </property>
- <property name="modal" >
+ <property name="modal">
<bool>true</bool>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_4" >
+ <layout class="QVBoxLayout" name="verticalLayout_4">
<item>
- <widget class="QLabel" name="label" >
- <property name="sizePolicy" >
- <sizepolicy vsizetype="Maximum" hsizetype="Preferred" >
+ <widget class="QLabel" name="mainLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="text" >
- <string>Please select which Custom panels you want to store permanently.</string>
+ <property name="text">
+ <string/>
</property>
- <property name="wordWrap" >
+ <property name="wordWrap">
<bool>true</bool>
</property>
- <property name="margin" >
+ <property name="margin">
<number>6</number>
</property>
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout" >
+ <layout class="QHBoxLayout" name="horizontalLayout">
<item>
- <widget class="QGroupBox" name="groupBox_2" >
- <property name="sizePolicy" >
- <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
+ <widget class="QGroupBox" name="group1">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="title" >
- <string>Temporary Custom Panels</string>
+ <property name="title">
+ <string/>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_2" >
+ <layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <widget class="QListWidget" name="temporaryPanelsList" >
- <property name="dragEnabled" >
+ <widget class="QListWidget" name="list1">
+ <property name="dragEnabled">
<bool>true</bool>
</property>
- <property name="dragDropOverwriteMode" >
+ <property name="dragDropOverwriteMode">
<bool>true</bool>
</property>
- <property name="dragDropMode" >
+ <property name="dragDropMode">
<enum>QAbstractItemView::NoDragDrop</enum>
</property>
- <property name="alternatingRowColors" >
+ <property name="alternatingRowColors">
<bool>true</bool>
</property>
- <property name="selectionMode" >
+ <property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
- <property name="sortingEnabled" >
+ <property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
@@ -75,13 +73,13 @@
</widget>
</item>
<item>
- <layout class="QVBoxLayout" name="verticalLayout_3" >
+ <layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <spacer name="verticalSpacer" >
- <property name="orientation" >
+ <spacer name="verticalSpacer">
+ <property name="orientation">
<enum>Qt::Vertical</enum>
</property>
- <property name="sizeHint" stdset="0" >
+ <property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
@@ -90,31 +88,34 @@
</spacer>
</item>
<item>
- <widget class="QToolButton" name="toPermanentBT" >
- <property name="text" >
+ <widget class="QToolButton" name="to2BT">
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="text">
<string>...</string>
</property>
- <property name="arrowType" >
+ <property name="arrowType">
<enum>Qt::RightArrow</enum>
</property>
</widget>
</item>
<item>
- <widget class="QToolButton" name="toTemporaryBT" >
- <property name="text" >
+ <widget class="QToolButton" name="to1BT">
+ <property name="text">
<string>...</string>
</property>
- <property name="arrowType" >
+ <property name="arrowType">
<enum>Qt::LeftArrow</enum>
</property>
</widget>
</item>
<item>
- <spacer name="verticalSpacer_2" >
- <property name="orientation" >
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
<enum>Qt::Vertical</enum>
</property>
- <property name="sizeHint" stdset="0" >
+ <property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
@@ -125,35 +126,35 @@
</layout>
</item>
<item>
- <widget class="QGroupBox" name="groupBox" >
- <property name="sizePolicy" >
- <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
+ <widget class="QGroupBox" name="group2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="title" >
- <string>Stored Custom Panels</string>
+ <property name="title">
+ <string/>
</property>
- <layout class="QVBoxLayout" name="verticalLayout" >
+ <layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QListWidget" name="permanentPanelsList" >
- <property name="dragEnabled" >
+ <widget class="QListWidget" name="list2">
+ <property name="dragEnabled">
<bool>true</bool>
</property>
- <property name="dragDropOverwriteMode" >
+ <property name="dragDropOverwriteMode">
<bool>true</bool>
</property>
- <property name="dragDropMode" >
+ <property name="dragDropMode">
<enum>QAbstractItemView::NoDragDrop</enum>
</property>
- <property name="alternatingRowColors" >
+ <property name="alternatingRowColors">
<bool>true</bool>
</property>
- <property name="selectionMode" >
+ <property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
- <property name="sortingEnabled" >
+ <property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
@@ -164,11 +165,11 @@
</layout>
</item>
<item>
- <widget class="QDialogButtonBox" name="buttonBox" >
- <property name="orientation" >
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
- <property name="standardButtons" >
+ <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
@@ -180,14 +181,14 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
- <receiver>PermanentCustomPanelsDlg</receiver>
+ <receiver>DoubleListDlg</receiver>
<slot>accept()</slot>
<hints>
- <hint type="sourcelabel" >
+ <hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
- <hint type="destinationlabel" >
+ <hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
@@ -196,14 +197,14 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
- <receiver>PermanentCustomPanelsDlg</receiver>
+ <receiver>DoubleListDlg</receiver>
<slot>reject()</slot>
<hints>
- <hint type="sourcelabel" >
+ <hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
- <hint type="destinationlabel" >
+ <hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
diff --git a/lib/taurus/qt/qtgui/panel/ui/TaurusInputPanel.ui b/lib/taurus/qt/qtgui/panel/ui/TaurusInputPanel.ui
new file mode 100644
index 0000000..82443dd
--- /dev/null
+++ b/lib/taurus/qt/qtgui/panel/ui/TaurusInputPanel.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TaurusInputPanel</class>
+ <widget class="QWidget" name="TaurusInputPanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>549</width>
+ <height>174</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,1,0">
+ <item row="4" column="0" colspan="2">
+ <widget class="QWidget" name="buttonPanelWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0" rowspan="2" colspan="2">
+ <layout class="QHBoxLayout" name="iconTextLayout" stretch="0,1">
+ <item>
+ <widget class="QLabel" name="iconLabel">
+ <property name="text">
+ <string>iconLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="textLabel">
+ <property name="text">
+ <string>textLabel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QWidget" name="inputPanel" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/lib/taurus/qt/qtgui/panel/ui/ui_DoubleListDlg.py b/lib/taurus/qt/qtgui/panel/ui/ui_DoubleListDlg.py
new file mode 100644
index 0000000..2753ad6
--- /dev/null
+++ b/lib/taurus/qt/qtgui/panel/ui/ui_DoubleListDlg.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file '/tmp/tmpgrv08D.ui'
+#
+# Created: Tue May 15 10:29:42 2012
+# by: PyQt4 UI code generator 4.4.3
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt4 import QtCore, QtGui
+
+class Ui_DoubleListDlg(object):
+ def setupUi(self, DoubleListDlg):
+ DoubleListDlg.setObjectName("DoubleListDlg")
+ DoubleListDlg.resize(533, 426)
+ DoubleListDlg.setModal(True)
+ self.verticalLayout_4 = QtGui.QVBoxLayout(DoubleListDlg)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.mainLabel = QtGui.QLabel(DoubleListDlg)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.mainLabel.sizePolicy().hasHeightForWidth())
+ self.mainLabel.setSizePolicy(sizePolicy)
+ self.mainLabel.setWordWrap(True)
+ self.mainLabel.setMargin(6)
+ self.mainLabel.setObjectName("mainLabel")
+ self.verticalLayout_4.addWidget(self.mainLabel)
+ self.horizontalLayout = QtGui.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.group1 = QtGui.QGroupBox(DoubleListDlg)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.group1.sizePolicy().hasHeightForWidth())
+ self.group1.setSizePolicy(sizePolicy)
+ self.group1.setObjectName("group1")
+ self.verticalLayout_2 = QtGui.QVBoxLayout(self.group1)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.list1 = QtGui.QListWidget(self.group1)
+ self.list1.setDragEnabled(True)
+ self.list1.setDragDropOverwriteMode(True)
+ self.list1.setDragDropMode(QtGui.QAbstractItemView.NoDragDrop)
+ self.list1.setAlternatingRowColors(True)
+ self.list1.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
+ self.list1.setObjectName("list1")
+ self.verticalLayout_2.addWidget(self.list1)
+ self.horizontalLayout.addWidget(self.group1)
+ self.verticalLayout_3 = QtGui.QVBoxLayout()
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem)
+ self.to2BT = QtGui.QToolButton(DoubleListDlg)
+ self.to2BT.setArrowType(QtCore.Qt.RightArrow)
+ self.to2BT.setObjectName("to2BT")
+ self.verticalLayout_3.addWidget(self.to2BT)
+ self.to1BT = QtGui.QToolButton(DoubleListDlg)
+ self.to1BT.setArrowType(QtCore.Qt.LeftArrow)
+ self.to1BT.setObjectName("to1BT")
+ self.verticalLayout_3.addWidget(self.to1BT)
+ spacerItem1 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem1)
+ self.horizontalLayout.addLayout(self.verticalLayout_3)
+ self.group2 = QtGui.QGroupBox(DoubleListDlg)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.group2.sizePolicy().hasHeightForWidth())
+ self.group2.setSizePolicy(sizePolicy)
+ self.group2.setObjectName("group2")
+ self.verticalLayout = QtGui.QVBoxLayout(self.group2)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.list2 = QtGui.QListWidget(self.group2)
+ self.list2.setDragEnabled(True)
+ self.list2.setDragDropOverwriteMode(True)
+ self.list2.setDragDropMode(QtGui.QAbstractItemView.NoDragDrop)
+ self.list2.setAlternatingRowColors(True)
+ self.list2.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
+ self.list2.setObjectName("list2")
+ self.verticalLayout.addWidget(self.list2)
+ self.horizontalLayout.addWidget(self.group2)
+ self.verticalLayout_4.addLayout(self.horizontalLayout)
+ self.buttonBox = QtGui.QDialogButtonBox(DoubleListDlg)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout_4.addWidget(self.buttonBox)
+
+ self.retranslateUi(DoubleListDlg)
+ QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), DoubleListDlg.accept)
+ QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), DoubleListDlg.reject)
+ QtCore.QMetaObject.connectSlotsByName(DoubleListDlg)
+
+ def retranslateUi(self, DoubleListDlg):
+ self.list1.setSortingEnabled(True)
+ self.to2BT.setText(QtGui.QApplication.translate("DoubleListDlg", "...", None, QtGui.QApplication.UnicodeUTF8))
+ self.to1BT.setText(QtGui.QApplication.translate("DoubleListDlg", "...", None, QtGui.QApplication.UnicodeUTF8))
+ self.list2.setSortingEnabled(True)
+
diff --git a/lib/taurus/qt/qtgui/panel/ui/ui_TaurusInputPanel.py b/lib/taurus/qt/qtgui/panel/ui/ui_TaurusInputPanel.py
new file mode 100644
index 0000000..5582af6
--- /dev/null
+++ b/lib/taurus/qt/qtgui/panel/ui/ui_TaurusInputPanel.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'TaurusInputPanel.ui'
+#
+# Created: Mon Jul 9 14:25:29 2012
+# by: PyQt4 UI code generator 4.8.3
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt4 import QtCore, QtGui
+
+try:
+ _fromUtf8 = QtCore.QString.fromUtf8
+except AttributeError:
+ _fromUtf8 = lambda s: s
+
+class Ui_TaurusInputPanel(object):
+ def setupUi(self, TaurusInputPanel):
+ TaurusInputPanel.setObjectName(_fromUtf8("TaurusInputPanel"))
+ TaurusInputPanel.resize(549, 174)
+ self.gridLayout = QtGui.QGridLayout(TaurusInputPanel)
+ self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
+ self.buttonPanelWidget = QtGui.QWidget(TaurusInputPanel)
+ self.buttonPanelWidget.setObjectName(_fromUtf8("buttonPanelWidget"))
+ self.horizontalLayout = QtGui.QHBoxLayout(self.buttonPanelWidget)
+ self.horizontalLayout.setMargin(0)
+ self.horizontalLayout.setMargin(0)
+ self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
+ spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.buttonBox = QtGui.QDialogButtonBox(self.buttonPanelWidget)
+ self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
+ self.horizontalLayout.addWidget(self.buttonBox)
+ self.horizontalLayout.setStretch(0, 1)
+ self.gridLayout.addWidget(self.buttonPanelWidget, 4, 0, 1, 2)
+ self.iconTextLayout = QtGui.QHBoxLayout()
+ self.iconTextLayout.setObjectName(_fromUtf8("iconTextLayout"))
+ self.iconLabel = QtGui.QLabel(TaurusInputPanel)
+ self.iconLabel.setObjectName(_fromUtf8("iconLabel"))
+ self.iconTextLayout.addWidget(self.iconLabel)
+ self.textLabel = QtGui.QLabel(TaurusInputPanel)
+ self.textLabel.setObjectName(_fromUtf8("textLabel"))
+ self.iconTextLayout.addWidget(self.textLabel)
+ self.iconTextLayout.setStretch(1, 1)
+ self.gridLayout.addLayout(self.iconTextLayout, 1, 0, 2, 2)
+ self.inputPanel = QtGui.QWidget(TaurusInputPanel)
+ self.inputPanel.setObjectName(_fromUtf8("inputPanel"))
+ self.gridLayout.addWidget(self.inputPanel, 3, 0, 1, 2)
+ self.gridLayout.setRowStretch(3, 1)
+
+ self.retranslateUi(TaurusInputPanel)
+ QtCore.QMetaObject.connectSlotsByName(TaurusInputPanel)
+
+ def retranslateUi(self, TaurusInputPanel):
+ TaurusInputPanel.setWindowTitle(QtGui.QApplication.translate("TaurusInputPanel", "Form", None, QtGui.QApplication.UnicodeUTF8))
+ self.iconLabel.setText(QtGui.QApplication.translate("TaurusInputPanel", "iconLabel", None, QtGui.QApplication.UnicodeUTF8))
+ self.textLabel.setText(QtGui.QApplication.translate("TaurusInputPanel", "textLabel", None, QtGui.QApplication.UnicodeUTF8))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtGui.QApplication(sys.argv)
+ TaurusInputPanel = QtGui.QWidget()
+ ui = Ui_TaurusInputPanel()
+ ui.setupUi(TaurusInputPanel)
+ TaurusInputPanel.show()
+ sys.exit(app.exec_())
+
diff --git a/lib/taurus/qt/qtgui/plot/__init__.py b/lib/taurus/qt/qtgui/plot/__init__.py
index feac5cc..2c5d19e 100644
--- a/lib/taurus/qt/qtgui/plot/__init__.py
+++ b/lib/taurus/qt/qtgui/plot/__init__.py
@@ -39,4 +39,5 @@ from .arrayedit import ArrayEditor
from .taurusarrayedit import TaurusArrayEditor
from .curvesAppearanceChooserDlg import CurveAppearanceProperties, CurvesAppearanceChooser
from .curveprops import CurvePropertiesView
-from .monitor import TaurusMonitorTiny
\ No newline at end of file
+from .monitor import TaurusMonitorTiny
+from .curveStatsDlg import CurveStatsDialog
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/plot/arrayedit.py b/lib/taurus/qt/qtgui/plot/arrayedit.py
index b1180aa..33ccbab 100644
--- a/lib/taurus/qt/qtgui/plot/arrayedit.py
+++ b/lib/taurus/qt/qtgui/plot/arrayedit.py
@@ -29,8 +29,7 @@ arrayedit.py: Widget for editing a spectrum/array via control points
import numpy
-from taurus.qt import Qt
-from PyQt4 import Qwt5
+from taurus.qt import Qt, Qwt5
from curvesAppearanceChooserDlg import CurveAppearanceProperties
import ui.ui_EditCPointsDialog as ui_EditCPointsDialog
diff --git a/lib/taurus/qt/qtgui/plot/curveStatsDlg.py b/lib/taurus/qt/qtgui/plot/curveStatsDlg.py
new file mode 100644
index 0000000..2c71e54
--- /dev/null
+++ b/lib/taurus/qt/qtgui/plot/curveStatsDlg.py
@@ -0,0 +1,316 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""
+curvesAppearanceChooserDlg.py:
+ A Qt dialog for choosing plot appearance (symbols and lines)
+ for a QwtPlot-derived widget (like Taurusplot)
+"""
+
+from taurus.qt import Qt, Qwt5
+from ui.ui_CurveStatsDialog import Ui_CurveStatsDialog
+from datetime import datetime
+from taurus.qt.qtgui.resource import getIcon, getThemeIcon
+
+
+class CurveStatsDialog(Qt.QDialog):
+ """
+ A dialog for configuring and displaying statistics from curves of a plot
+ """
+
+ statColumns = ('points','min', 'max', 'mean', 'std', 'rms')
+ def __init__(self, parent=None):
+ super(CurveStatsDialog,self).__init__(parent)
+ self.ui = Ui_CurveStatsDialog()
+ self.ui.setupUi(self)
+
+ plot = parent
+ xIsTime = plot.getXIsTime()
+ self.ui.minDTE.setVisible(xIsTime)
+ self.ui.maxDTE.setVisible(xIsTime)
+ self.ui.minSB.setVisible(not xIsTime)
+ self.ui.maxSB.setVisible(not xIsTime)
+
+ self.ui.minSB.setMinimum(float('-inf'))
+ self.ui.minSB.setMaximum(float('inf'))
+ self.ui.maxSB.setMinimum(float('-inf'))
+ self.ui.maxSB.setMaximum(float('inf'))
+
+ icon = getIcon(':/actions/system-search.svg')
+ self.ui.selectMinPB.setIcon(icon)
+ self.ui.selectMaxPB.setIcon(icon)
+
+
+ self.refreshCurves()
+
+ cbs = (self.ui.npointsStatCB, self.ui.minStatCB, self.ui.maxStatCB,
+ self.ui.meanStatCB, self.ui.stdStatCB, self.ui.rmsStatCB)
+ self._checkboxToColMap = dict(zip(cbs, xrange(len(self.statColumns))))
+
+ self.minPicker = Qwt5.QwtPlotPicker(Qwt5.QwtPlot.xBottom,
+ Qwt5.QwtPlot.yLeft,
+ Qwt5.QwtPicker.PointSelection,
+ Qwt5.QwtPicker.VLineRubberBand,
+ Qwt5.QwtPicker.AlwaysOn, plot.canvas())
+
+ self.maxPicker = Qwt5.QwtPlotPicker(Qwt5.QwtPlot.xBottom,
+ Qwt5.QwtPlot.yLeft,
+ Qwt5.QwtPicker.PointSelection,
+ Qwt5.QwtPicker.VLineRubberBand,
+ Qwt5.QwtPicker.AlwaysOn, plot.canvas())
+
+ self.minPicker.setEnabled(False)
+ self.maxPicker.setEnabled(False)
+
+ #initialize min and max display
+ xmin = plot.axisScaleDiv(Qwt5.QwtPlot.xBottom).lowerBound()
+ xmax = plot.axisScaleDiv(Qwt5.QwtPlot.xBottom).upperBound()
+
+ self.minMarker = Qwt5.QwtPlotMarker()
+ self.minMarker.setLineStyle(Qwt5.QwtPlotMarker.VLine)
+ self.minMarker.setLinePen(Qt.QPen(Qt.Qt.green, 3))
+ self.minMarker.setXValue(xmin)
+ self.minMarker.attach(plot)
+ self.minMarker.hide()
+
+ self.maxMarker = Qwt5.QwtPlotMarker()
+ self.maxMarker.setLineStyle(Qwt5.QwtPlotMarker.VLine)
+ self.maxMarker.setLinePen(Qt.QPen(Qt.Qt.red, 3))
+ self.maxMarker.setXValue(xmax)
+ self.maxMarker.attach(plot)
+ self.maxMarker.hide()
+
+ if xIsTime:
+ self.ui.minDTE.setDateTime(self._timestamptToQDateTime(xmin))
+ self.ui.maxDTE.setDateTime(self._timestamptToQDateTime(xmax))
+ else:
+ self.ui.minSB.setValue(xmin)
+ self.ui.maxSB.setValue(xmax)
+
+ refreshAction = Qt.QAction(getThemeIcon('view-refresh'), "Refresh available curves", self.ui.statsTW)
+ refreshAction.setShortcut(Qt.Qt.Key_F5)
+ self.connect(refreshAction, Qt.SIGNAL("triggered()"), self.refreshCurves)
+ self.ui.statsTW.addAction(refreshAction)
+
+ #connections
+ for cb in cbs:
+ self.connect(cb, Qt.SIGNAL('toggled(bool)'), self.onStatToggled)
+ self.connect(self.ui.calculatePB, Qt.SIGNAL('clicked()'),self.onCalculate)
+ self.connect(self.ui.selectMinPB, Qt.SIGNAL('clicked()'),self.onSelectMin)
+ self.connect(self.ui.selectMaxPB, Qt.SIGNAL('clicked()'),self.onSelectMax)
+ self.connect(self.minPicker, Qt.SIGNAL('selected(QwtDoublePoint)'), self.minSelected)
+ self.connect(self.maxPicker, Qt.SIGNAL('selected(QwtDoublePoint)'), self.maxSelected)
+ self.connect(self.ui.minSB, Qt.SIGNAL('valueChanged(double)'), self.onMinChanged)
+ self.connect(self.ui.minDTE, Qt.SIGNAL('dateTimeChanged(QDateTime)'), self.onMinChanged)
+ self.connect(self.ui.maxSB, Qt.SIGNAL('valueChanged(double)'), self.onMaxChanged)
+ self.connect(self.ui.maxDTE, Qt.SIGNAL('dateTimeChanged(QDateTime)'), self.onMaxChanged)
+ self.connect(self.ui.minCB, Qt.SIGNAL('toggled(bool)'), self.onMinChanged)
+ self.connect(self.ui.maxCB, Qt.SIGNAL('toggled(bool)'), self.onMaxChanged)
+
+ def _timestamptToQDateTime(self, ts):
+ dt = datetime.fromtimestamp(ts)
+ return Qt.QDateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond/1000)
+
+ def _QDateTimeToTimestamp(self, qdt):
+ return qdt.toTime_t() + qdt.time().msec()/1000.
+
+ def onSelectMin(self):
+ '''slot called when the user clicks on the selectMin button'''
+ plot = self.parent()
+ plot.getZoomers()[0].setEnabled(False)
+ plot.canvas().setCursor(Qt.Qt.SplitHCursor)
+ self.minPicker.setEnabled(True)
+ self.hide()
+
+ def minSelected(self, pos):
+ '''slot called when the user has selected a min value from the plot'''
+ self.show()
+ self.ui.minCB.setChecked(True)
+ self.minPicker.setEnabled(False)
+ plot = self.parent()
+ xmin = pos.x()
+ if plot.getXIsTime():
+ self.ui.minDTE.setDateTime(self._timestamptToQDateTime(xmin)) #this triggers a call to onMinChanged()
+ else:
+ self.ui.minSB.setValue(xmin) #this triggers a call to onMinChanged()
+ self.restorePlot(keepMarkers=True)
+
+ def onMinChanged(self, x):
+ '''slot called when the min value is changed'''
+ plot = self.parent()
+ if isinstance(x, bool):
+ self.minMarker.setVisible(x)
+ else:
+ if isinstance(x, Qt.QDateTime):
+ x = self._QDateTimeToTimestamp(x)
+ self.minMarker.setXValue(x)
+ plot.replot()
+
+ def onSelectMax(self):
+ '''slot called when the user clicks on the selectMax button'''
+ self.minPicker.setEnabled(False)
+ plot = self.parent()
+ plot.getZoomers()[0].setEnabled(False)
+ plot.canvas().setCursor(Qt.Qt.SplitHCursor)
+ self.maxPicker.setEnabled(True)
+ self.hide()
+
+ def maxSelected(self, pos):
+ '''slot called when the user has selected a min value from the plot'''
+ self.show()
+ self.ui.maxCB.setChecked(True)
+ self.maxPicker.setEnabled(False)
+ plot = self.parent()
+ xmax = pos.x()
+ if plot.getXIsTime():
+ self.ui.maxDTE.setDateTime(self._timestamptToQDateTime(xmax)) #this triggers a call to onMaxChanged()
+ else:
+ self.ui.maxSB.setValue(xmax) #this triggers a call to onMaxChanged()
+ self.restorePlot(keepMarkers=True)
+
+ def onMaxChanged(self, x):
+ '''slot called when the max value is changed'''
+ plot = self.parent()
+ if isinstance(x, bool):
+ self.maxMarker.setVisible(x)
+ else:
+ if isinstance(x, Qt.QDateTime):
+ x = self._QDateTimeToTimestamp(x)
+ self.maxMarker.setXValue(x)
+ plot.replot()
+
+ def onStatToggled(self, checked):
+ '''slot called when any of the stat checkboxes is toggled'''
+ cb = self.sender()
+ col = self._checkboxToColMap[cb]
+ if checked:
+ self.ui.statsTW.showColumn(col)
+ else:
+ self.ui.statsTW.hideColumn(col)
+
+ def refreshCurves(self):
+ '''resets the table re-reading the curves from the plot'''
+ plot = self.parent()
+ self.ui.statsTW.clear()
+ self.ui.statsTW.setColumnCount(len(self.statColumns))
+ self.ui.statsTW.setHorizontalHeaderLabels(self.statColumns)
+ self.curveNames = plot.getCurveNamesSorted()
+ self.ui.statsTW.setRowCount(len(self.curveNames))
+ for row,name in enumerate(self.curveNames):
+ title = plot.getCurveTitle(name)
+ item = Qt.QTableWidgetItem(title)
+ self.ui.statsTW.setVerticalHeaderItem(row, item)
+
+ def getSelectedRows(self):
+ '''returns a list of row numbers corresponding to the selected rows of the table'''
+ selected = []
+ for rg in self.ui.statsTW.selectedRanges():
+ for row in xrange(rg.topRow(), rg.topRow()+rg.rowCount()):
+ selected.append(row)
+ return selected
+
+ def onCalculate(self):
+ '''
+ slot called when the calculate button is pressed. Performs the
+ calculation of stats in the current limits for the currently selected
+ curves (or for all if none selected) and fills the table.
+ '''
+ plot = self.parent()
+ table = self.ui.statsTW
+
+ xmin,xmax = None,None
+ if self.ui.minCB.isChecked():
+ if plot.getXIsTime():
+ xmin = self.ui.minDTE.dateTime().toTime_t() + self.ui.minDTE.time().msec()/1000.
+ else:
+ xmin = self.ui.minSB.value()
+ if self.ui.maxCB.isChecked():
+ if plot.getXIsTime():
+ xmax = self.ui.maxDTE.dateTime().toTime_t() + self.ui.maxDTE.time().msec()/1000.
+ else:
+ xmax = self.ui.maxSB.value()
+ limits = xmin,xmax
+
+ selectedRows = self.getSelectedRows()
+ if len(selectedRows) == 0:
+ selectedRows = range(len(self.curveNames))
+ selectedCurves = [self.curveNames[i] for i in selectedRows]
+ statsdict = plot.getCurveStats(limits=limits, curveNames=selectedCurves)
+
+ for row, name in zip(selectedRows,selectedCurves):
+ stats = statsdict.get(name, {})
+
+ minxy = stats.get('min')
+ if minxy is None:
+ text = "---"
+ elif plot.xIsTime:
+ text = "t=%s \ny=%g"%(datetime.fromtimestamp(minxy[0]).isoformat(),minxy[1])
+ else:
+ text = "x=%g \ny=%g"%minxy
+ table.setItem(row,self.statColumns.index('min'), Qt.QTableWidgetItem(text))
+
+ maxxy = stats.get('max')
+ if maxxy is None:
+ text = "---"
+ elif plot.xIsTime:
+ text = "t=%s \ny=%g"%(datetime.fromtimestamp(maxxy[0]).isoformat(),maxxy[1])
+ else:
+ text = "x=%g \ny=%g"%maxxy
+ table.setItem(row,self.statColumns.index('max'), Qt.QTableWidgetItem(text))
+
+ for s in ('points', 'mean', 'std', 'rms'):
+ r = stats.get(s)
+ if r is None:
+ text = "---"
+ else:
+ text = "%g"%r
+ table.setItem(row, self.statColumns.index(s), Qt.QTableWidgetItem(text))
+ table.resizeColumnsToContents()
+
+ def restorePlot(self, keepMarkers=False):
+ '''leaves the parent plot in its original state'''
+ plot = self.parent()
+ plot.canvas().setCursor(Qt.Qt.CrossCursor)
+ plot.getZoomers()[0].setEnabled(plot.getAllowZoomers())
+ if not keepMarkers:
+ self.minMarker.detach()
+ self.maxMarker.detach()
+ plot.replot()
+
+ def closeEvent(self, event):
+ '''See :meth:`Qwidget.closeEvent`'''
+ self.restorePlot()
+ self.emit(Qt.SIGNAL('closed'))
+
+ def showEvent(self, event):
+ '''See :meth:`Qwidget.showEvent`'''
+ plot = self.parent()
+ self.minMarker.attach(plot)
+ self.maxMarker.attach(plot)
+ plot.replot()
+
+
+
+
diff --git a/lib/taurus/qt/qtgui/plot/curveprops.py b/lib/taurus/qt/qtgui/plot/curveprops.py
index dcc30f4..14d5183 100644
--- a/lib/taurus/qt/qtgui/plot/curveprops.py
+++ b/lib/taurus/qt/qtgui/plot/curveprops.py
@@ -31,8 +31,7 @@ __all__=['CurveConf','CurvesTableModel','ExtendedSelectionModel','CurvePropertie
import copy, re
-from taurus.qt import Qt
-from PyQt4 import Qwt5
+from taurus.qt import Qt, Qwt5
import taurus
import taurus.core
from taurus.qt.qtgui.resource import getThemeIcon
@@ -241,7 +240,7 @@ class CurvesTableModel(Qt.QAbstractTableModel):
self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),self.index(row,0), self.index(row,self.ncolumns-1))
else:
column = index.column()
- value = unicode(value.toString())
+ value = Qt.from_qvariant(value, unicode)
if column == X: curve.x.setSrc(value)
elif column == Y: curve.y.setSrc(value)
elif column == TITLE: curve.title = value
@@ -303,8 +302,10 @@ class CurvesTableModel(Qt.QAbstractTableModel):
def mimeData(self, indexes):
mimedata = Qt.QAbstractTableModel.mimeData(self, indexes)
if len(indexes)==1:
-# mimedata.setData(TAURUS_ATTR_MIME_TYPE, str(self.data(indexes[0]).toString()))
- mimedata.setText(self.data(indexes[0],role=SRC_ROLE).toString())
+# txt = Qt.from_qvariant(self.data(indexes[0], str)
+# mimedata.setData(TAURUS_ATTR_MIME_TYPE, txt)
+ txt = Qt.from_qvariant(self.data(indexes[0], role=SRC_ROLE), str)
+ mimedata.setText(txt)
return mimedata
#mimedata.setData()
@@ -578,4 +579,4 @@ def main():
if __name__ == "__main__":
import sys
- main()
\ No newline at end of file
+ main()
diff --git a/lib/taurus/qt/qtgui/plot/curvesAppearanceChooserDlg.py b/lib/taurus/qt/qtgui/plot/curvesAppearanceChooserDlg.py
index eae8f2f..40f58b1 100644
--- a/lib/taurus/qt/qtgui/plot/curvesAppearanceChooserDlg.py
+++ b/lib/taurus/qt/qtgui/plot/curvesAppearanceChooserDlg.py
@@ -29,8 +29,7 @@ curvesAppearanceChooserDlg.py:
for a QwtPlot-derived widget (like Taurusplot)
"""
-from taurus.qt import Qt
-from PyQt4 import Qwt5
+from taurus.qt import Qt, Qwt5
from ui import ui_curvesAppearanceChooser
from taurus.qt.qtgui.resource import getIcon
from taurus.core.util import CaselessDict
@@ -159,7 +158,7 @@ class CurvesAppearanceChooser(Qt.QWidget,ui_curvesAppearanceChooser.Ui_curvesApp
def onItemChanged(self, item):
'''slot used when an item data has changed'''
- name = str(item.data(self.NAME_ROLE).toString())
+ name = Qt.from_qvariant(item.data(self.NAME_ROLE), str)
previousTitle = self.curvePropDict[name].title
currentTitle = item.text()
if previousTitle!=currentTitle:
@@ -188,7 +187,7 @@ class CurvesAppearanceChooser(Qt.QWidget,ui_curvesAppearanceChooser.Ui_curvesApp
:return: (string_list) the names of the selected curves
'''
- return [str(item.data(self.NAME_ROLE).toString()) for item in self.curvesLW.selectedItems()]
+ return [Qt.from_qvariant(item.data(self.NAME_ROLE), str) for item in self.curvesLW.selectedItems()]
def showProperties(self,prop=None):
diff --git a/lib/taurus/qt/qtgui/plot/monitor.py b/lib/taurus/qt/qtgui/plot/monitor.py
index e898cc3..7f37b02 100644
--- a/lib/taurus/qt/qtgui/plot/monitor.py
+++ b/lib/taurus/qt/qtgui/plot/monitor.py
@@ -28,9 +28,8 @@ monitor.py: Specialized mini-trend widget to monitor some scalar value
"""
from taurus.qt import Qt
-from PyQt4 import Qwt5
from taurus.qt.qtgui.plot import TaurusTrend
-import datetime
+
class TaurusMonitorTiny(TaurusTrend):
'''
diff --git a/lib/taurus/qt/qtgui/plot/qwtdialog.py b/lib/taurus/qt/qtgui/plot/qwtdialog.py
index 2fc237e..40e2f00 100644
--- a/lib/taurus/qt/qtgui/plot/qwtdialog.py
+++ b/lib/taurus/qt/qtgui/plot/qwtdialog.py
@@ -27,8 +27,7 @@
qwtdialog.py: Dialogs for Taurusplot
"""
-from taurus.qt import Qt
-from PyQt4 import Qwt5
+from taurus.qt import Qt, Qwt5
import time
from ui.ui_TaurusPlotConfigDialog import Ui_TaurusPlotConfigDialog
@@ -186,11 +185,11 @@ class TaurusPlotConfigDialog(Qt.QDialog):
# self.connect(self.ui.y2EditMax, Qt.SIGNAL("returnPressed()"),self.apply)
self.connect(self.ui.xModeComboBox, Qt.SIGNAL("currentIndexChanged(const QString&)"),self.modeComboChanged)
self.connect(self.ui.xDynScaleCheckBox, Qt.SIGNAL("toggled (bool)"),self.setXDynScale)
- self.connect(self.ui.xRangeCB, Qt.SIGNAL("currentIndexChanged(const QString&)"),self.apply)
+ #self.connect(self.ui.xRangeCB, Qt.SIGNAL("currentIndexChanged(const QString&)"),self.apply)
self.connect(self.ui.y1ModeComboBox, Qt.SIGNAL("currentIndexChanged(const QString&)"),self.modeComboChanged)
self.connect(self.ui.y2ModeComboBox, Qt.SIGNAL("currentIndexChanged(const QString&)"),self.modeComboChanged)
self.connect(self.ui.peaksComboBox, Qt.SIGNAL("currentIndexChanged(int)"),self.peaksComboChanged)
- self.connect(self.curvesAppearanceChooser, Qt.SIGNAL("controlChanged"),self.apply) #"autoapply" mode for *all* the curve appearance controls
+ #self.connect(self.curvesAppearanceChooser, Qt.SIGNAL("controlChanged"),self.apply) #"autoapply" mode for *all* the curve appearance controls
self.connect(self.curvesAppearanceChooser.assignToY1BT, Qt.SIGNAL("clicked()"),self.setCurvesYAxis)
self.connect(self.curvesAppearanceChooser.assignToY2BT, Qt.SIGNAL("clicked()"),self.setCurvesYAxis)
self.connect(self.curvesAppearanceChooser.bckgndBT, Qt.SIGNAL("clicked()"),self.changeBackgroundColor)
@@ -204,19 +203,19 @@ class TaurusPlotConfigDialog(Qt.QDialog):
xMin = self.parent.axisScaleDiv(Qwt5.QwtPlot.xBottom).lowerBound()
xMax = self.parent.axisScaleDiv(Qwt5.QwtPlot.xBottom).upperBound()
if self.parent.getXIsTime():
- self.ui.xEditMin.setText(Qt.QString(time.strftime('%Y/%m/%d %H:%M:%S',time.localtime(int(xMin)))))
- self.ui.xEditMax.setText(Qt.QString(time.strftime('%Y/%m/%d %H:%M:%S',time.localtime(int(xMax)))))
+ self.ui.xEditMin.setText(time.strftime('%Y/%m/%d %H:%M:%S',time.localtime(int(xMin))))
+ self.ui.xEditMax.setText(time.strftime('%Y/%m/%d %H:%M:%S',time.localtime(int(xMax))))
else:
- self.ui.xEditMin.setText(Qt.QString.number(xMin))
- self.ui.xEditMax.setText(Qt.QString.number(xMax))
+ self.ui.xEditMin.setText(str(xMin))
+ self.ui.xEditMax.setText(str(xMax))
def _populateYAxisScales(self, axis=None):
if axis is None or axis==Qwt5.QwtPlot.yLeft:
- self.ui.y1EditMin.setText(Qt.QString.number(self.parent.axisScaleDiv(Qwt5.QwtPlot.yLeft).lowerBound()))
- self.ui.y1EditMax.setText(Qt.QString.number(self.parent.axisScaleDiv(Qwt5.QwtPlot.yLeft).upperBound()))
+ self.ui.y1EditMin.setText(str(self.parent.axisScaleDiv(Qwt5.QwtPlot.yLeft).lowerBound()))
+ self.ui.y1EditMax.setText(str(self.parent.axisScaleDiv(Qwt5.QwtPlot.yLeft).upperBound()))
if axis is None or axis==Qwt5.QwtPlot.yRight:
- self.ui.y2EditMin.setText(Qt.QString.number(self.parent.axisScaleDiv(Qwt5.QwtPlot.yRight).lowerBound()))
- self.ui.y2EditMax.setText(Qt.QString.number(self.parent.axisScaleDiv(Qwt5.QwtPlot.yRight).upperBound()))
+ self.ui.y2EditMin.setText(str(self.parent.axisScaleDiv(Qwt5.QwtPlot.yRight).lowerBound()))
+ self.ui.y2EditMax.setText(str(self.parent.axisScaleDiv(Qwt5.QwtPlot.yRight).upperBound()))
def deltatime2str(self,dt, fuzzy=False):
'''converts a time diff in secs to a string.
@@ -316,7 +315,10 @@ class TaurusPlotConfigDialog(Qt.QDialog):
rg = abs(self.str2deltatime(str(self.ui.xRangeCB.currentText())))
ok1= (rg is not None)
else:
- rg,ok1=self.ui.xRangeCB.currentText().toDouble()
+ try:
+ rg, ok1 = float(self.ui.xRangeCB.currentText()), True
+ except:
+ rg, ok1 = 0.0, False
xMin=xMax-rg
if not ok1 or (xMin >= xMax):
Qt.QMessageBox.warning(self, "Invalid parameters for X axis range")
@@ -328,8 +330,14 @@ class TaurusPlotConfigDialog(Qt.QDialog):
ok1= (xMin is not None)
ok2= (xMax is not None)
else: #XY mode
- xMin,ok1 = self.ui.xEditMin.text().toDouble()
- xMax,ok2 = self.ui.xEditMax.text().toDouble()
+ try:
+ xMin, ok1 = float(self.ui.xEditMin.text()), True
+ except:
+ xMin, ok1 = 0.0, False
+ try:
+ xMax, ok2 = float(self.ui.xEditMax.text()), True
+ except:
+ xMax, ok2 = 0.0, False
if not (ok1 and ok2) or (xMin >= xMax):
Qt.QMessageBox.warning(self, "Invalid parameters for X axis",
"Min value must be strictly less than max value")
@@ -337,8 +345,14 @@ class TaurusPlotConfigDialog(Qt.QDialog):
#Validating Y1 axis values
if self.ui.y1AutoGroupBox.isChecked():
- y1Min,ok1=self.ui.y1EditMin.text().toDouble()
- y1Max,ok2=self.ui.y1EditMax.text().toDouble()
+ try:
+ y1Min, ok1 = float(self.ui.y1EditMin.text()), True
+ except:
+ y1Min, ok1 = 0.0, False
+ try:
+ y1Max, ok2 = float(self.ui.y1EditMax.text()), True
+ except:
+ y1Max, ok2 = 0.0, False
if not(ok1 and ok2) or (y1Min >= y1Max):
Qt.QMessageBox.warning(self, "Invalid parameters for Y1 axis",
"Min value must be strictly less than max value")
@@ -346,8 +360,14 @@ class TaurusPlotConfigDialog(Qt.QDialog):
#Validating Y2 axis values
if self.ui.y2AutoGroupBox.isChecked():
- y2Min,ok1=self.ui.y2EditMin.text().toDouble()
- y2Max,ok2=self.ui.y2EditMax.text().toDouble()
+ try:
+ y2Min, ok1 = float(self.ui.y2EditMin.text()), True
+ except:
+ y2Min, ok1 = 0.0, False
+ try:
+ y2Max, ok2 = float(self.ui.y2EditMax.text()), True
+ except:
+ y2Max, ok2 = 0.0, False
if not (ok1 and ok2) or (y2Min >= y2Max):
Qt.QMessageBox.warning(self, "Invalid parameters for Y2 axis",
"Min value must be strictly less than max value")
@@ -482,4 +502,4 @@ class TaurusPlotConfigDialog(Qt.QDialog):
:param name: (QString) new title
'''
newTitlesDict = self.parent.setCurvesTitle(newTitle, [name])
- self.curvesAppearanceChooser.updateTitles(newTitlesDict)
\ No newline at end of file
+ self.curvesAppearanceChooser.updateTitles(newTitlesDict)
diff --git a/lib/taurus/qt/qtgui/plot/scales.py b/lib/taurus/qt/qtgui/plot/scales.py
index d2bfcc8..c652b80 100644
--- a/lib/taurus/qt/qtgui/plot/scales.py
+++ b/lib/taurus/qt/qtgui/plot/scales.py
@@ -33,8 +33,7 @@ __all__=["DateTimeScaleEngine", "DeltaTimeScaleEngine", "FixedLabelsScaleEngine"
import numpy
from datetime import datetime, timedelta
from time import mktime
-from taurus.qt import Qt
-from PyQt4 import Qwt5
+from taurus.qt import Qt, Qwt5
def _getDefaultAxisLabelsAlignment(axis, rotation):
diff --git a/lib/taurus/qt/qtgui/plot/taurusplot.py b/lib/taurus/qt/qtgui/plot/taurusplot.py
index 4fe7629..1c258a5 100644
--- a/lib/taurus/qt/qtgui/plot/taurusplot.py
+++ b/lib/taurus/qt/qtgui/plot/taurusplot.py
@@ -33,8 +33,7 @@ import copy
from datetime import datetime
import time
import numpy
-from taurus.qt import Qt
-from PyQt4 import Qwt5
+from taurus.qt import Qt, Qwt5
import PyTango
@@ -44,8 +43,6 @@ from taurus.qt.qtcore.mimetypes import TAURUS_MODEL_LIST_MIME_TYPE, TAURUS_ATTR_
from taurus.qt.qtgui.base import TaurusBaseComponent, TaurusBaseWidget
from taurus.qt.qtgui.plot import TaurusPlotConfigDialog, FancyScaleDraw,\
DateTimeScaleEngine, FixedLabelsScaleEngine, FixedLabelsScaleDraw
-from taurus.qt.qtgui.panel import QDataExportDialog, QRawDataWidget
-from taurus.qt.qtgui.panel import TaurusModelChooser
from curvesAppearanceChooserDlg import CurveAppearanceProperties
def isodatestr2float(s, sep='_'):
@@ -238,7 +235,7 @@ class TaurusCurve(Qwt5.QwtPlotCurve, TaurusBaseComponent):
information about which TaurusCurves are attached. Therefore the programmer
should never attach/detach a TaurusCurve manually.
'''
-
+ droppedEventsWarning = 3 #number of dropped events before issuing a warning
def __init__(self, name, xname=None, parent = None, rawData=None, optimized=False):
Qwt5.QwtPlotCurve.__init__(self)
@@ -248,6 +245,7 @@ class TaurusCurve(Qwt5.QwtPlotCurve, TaurusBaseComponent):
self._yValues = None
self._showMaxPeak = False
self._showMinPeak = False
+ #self._markerFormatter = self.defaultMarkerFormatter
self._filteredWhenLog = True
self._history = []
self._titleText = '<label>'
@@ -257,6 +255,7 @@ class TaurusCurve(Qwt5.QwtPlotCurve, TaurusBaseComponent):
self._minPeakMarker = TaurusCurveMarker(name, self)
self.__curveName = name
self.isRawData= not(rawData is None)
+ self.droppedEventsCount = 0
if optimized:
self.setPaintAttribute(self.PaintFiltered, True)
self.setPaintAttribute(self.ClipPolygons, True)
@@ -276,6 +275,47 @@ class TaurusCurve(Qwt5.QwtPlotCurve, TaurusBaseComponent):
self.error("Problems when adding curve " + str(name))
self.traceback()
+
+# @staticmethod
+# def defaultMarkerFormatter(self,curve,label,i,x,y,xIsTime):
+# """
+# Returns the text to be shown in plot tooltips/markers.
+# :param curve: the name of the curve
+# :param label: the label to be displayed
+# :param i: the index of the point in the curve
+# :param x: x axis position
+# :param y: y axis position
+# :param xIsTime: To adapt format to time if needed
+# :return: (str)
+# """
+# #@todo: Check: is this method ever called??? It seems it is not since it is buggy and we don't see problems
+# if self.getXIsTime():
+# infotxt = "'%s'[%i]:\n\t (t=%s, y=%.3g)"%(pickedCurveName,pickedIndex,datetime.fromtimestamp(picked.x()).ctime(),picked.y())
+# else:
+# infotxt = "'%s'[%i]:\n\t (x=%.3g, y=%.3g)"%(pickedCurveName,pickedIndex,picked.x(),picked.y())
+# return infotxt
+#
+# def setMarkerFormatter(self,formatter):
+# """
+# Sets formatter method for plot tooltips/markers.
+# The method must have at least 4 arguments:
+# :param curve: the name of the curve
+# :param label: the label to be displayed
+# :param i: the index of the point in the curve
+# :param x: x axis position
+# :param y: y axis position
+# :param xIsTime: To adapt format to time if needed
+# """
+# self._markerFormatter = formatter
+#
+# def markerFormatter(self):
+# """
+# Returns the method used to format plot tooltips
+#
+# :return: (function)
+# """
+# return self._formatter
+
def getCurveName(self):
'''Returns the name of the curve (in the case of non RawDataCurves, it
is the same as the model name)
@@ -502,7 +542,7 @@ class TaurusCurve(Qwt5.QwtPlotCurve, TaurusBaseComponent):
if model is None:
self._xValues = numpy.zeros(0)
self._yValues = numpy.zeros(0)
- self._signalGen.emit(Qt.SIGNAL("dataChanged(const QString &)"), Qt.QString(self.getModel()))
+ self._signalGen.emit(Qt.SIGNAL("dataChanged(const QString &)"), str(self.getModel()))
return
if evt_type == taurus.core.TaurusEventType.Config:
@@ -510,17 +550,31 @@ class TaurusCurve(Qwt5.QwtPlotCurve, TaurusBaseComponent):
value = val if isinstance(val, (PyTango.DeviceAttribute, taurus.core.taurusbasetypes.TaurusAttrValue)) else self.getModelValueObj()
if not isinstance(value, (PyTango.DeviceAttribute, taurus.core.taurusbasetypes.TaurusAttrValue)):
- self.debug("Could not get DeviceAttribute value for this event. Dropping")
+ self._onDroppedEvent(reason="Could not get DeviceAttribute value")
return
-
try:
self.setXYFromModel(value)
except Exception, e:
- self.debug("Droping event. Reason %s", str(e))
-
+ self._onDroppedEvent(reason=str(e))
+ return
self._updateMarkers()
- self._signalGen.emit(Qt.SIGNAL("dataChanged(const QString &)"), Qt.QString(self.getModel()))
-
+ self._signalGen.emit(Qt.SIGNAL("dataChanged(const QString &)"), str(self.getModel()))
+
+ def _onDroppedEvent(self, reason='Unknown'):
+ '''inform the user about a dropped event
+
+ :param reason: (str) The reason of the drop
+ '''
+ self.debug("Droping event. Reason %s", reason)
+ self.droppedEventsCount += 1
+ if self.droppedEventsCount == self.droppedEventsWarning:
+ msg = ('At least %i events from model "%s" have being dropped. This attribute may have problems\n' +
+ 'Future occurrences will be silently ignored')%(self.droppedEventsWarning, self.modelName)
+ self.warning(msg)
+ p = self.plot()
+ if p:
+ Qt.QMessageBox.warning(p, "Errors in curve %s"%self.titleText(compiled=True), msg, Qt.QMessageBox.Ok)
+
def _updateMarkers(self):
'''updates min & max markers if needed'''
if self.isVisible():
@@ -733,6 +787,74 @@ class TaurusCurve(Qwt5.QwtPlotCurve, TaurusBaseComponent):
'''see :meth:`TaurusBaseComponent.isReadOnly`'''
return True
+ def getStats(self, limits=None, inclusive=(True,True), imin=None, imax=None):
+ '''
+ returns a dict containing several descriptive statistics of a region of
+ the curve defined by the limits given by the keyword arguments. It also
+ contains a copy of the data in the considered region. The keys of the
+ returned dictionary correspond to:
+
+ -'x' : the abscissas for the considered points (numpy.array)
+ -'y' : the ordinates for the considered points (numpy.array)
+ -'points': number of considered points (int)
+ -'min' : (x,y) pair of the minimum of the curve (float,float)
+ -'max' : (x,y) pair of the maximum of the curve (float,float)
+ -'mean' : arithmetic average of y (float)
+ -'std' : (biased)standard deviation of y (float)
+ -'rms' : root mean square of y (float)
+
+ Note that some of the values may be None if that cannot be computed.
+
+ :param limits: (None or tuple<float,float>) tuple containing (min,max) limits.
+ Points of the curve whose abscisa value is outside of
+ these limits are ignored. If None is passed, the limit is not enforced
+ :param inclusive: (tuple<bool,bool>). A tuple consisting of the (lower flag, upper flag).
+ These flags determine whether values exactly equal to the lower or
+ upper limits are included. The default value is (True, True).
+ :param imin: (int) lowest index to be considered. If None is given,
+ the limit is not enforced
+ :param imax: (int) higest index to be considered. If None is given,
+ the limit is not enforced
+
+ :return: (dict) A dict containing the stats.
+ '''
+
+ data = self.data()
+ if imin is None: imin = 0
+ if imax is None: imax = data.size()
+
+ x = numpy.array([data.x(i) for i in xrange(imin, imax)])
+ y = numpy.array([data.y(i) for i in xrange(imin, imax)])
+
+ if limits is not None:
+ xmin,xmax = limits
+ if xmax is None: xmax=numpy.inf
+ if inclusive:
+ mask = (x>=xmin) * (x<=xmax)
+ else:
+ mask = (x>xmin) * (x<xmax)
+ x = x[mask]
+ y = y[mask]
+
+ ret = {'x' : x,
+ 'y' : y,
+ 'points': x.size,
+ 'min' : None,
+ 'max' : None,
+ 'mean' : None,
+ 'std' : None,
+ 'rms' : None}
+
+ if x.size > 0:
+ argmin = y.argmin()
+ argmax = y.argmax()
+ ret.update({'min' : (x[argmin],y[argmin]),
+ 'max' : (x[argmax],y[argmax]),
+ 'mean' : y.mean(),
+ 'std' : y.std(),
+ 'rms' : numpy.sqrt(numpy.mean(y**2))})
+ return ret
+
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Methods necessary to show/hide peak values
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
@@ -763,7 +885,6 @@ class TaurusCurve(Qwt5.QwtPlotCurve, TaurusBaseComponent):
return self.yAxis()
-
#class TaurusPlot(Qwt5.QwtPlot, Logger):
class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
'''
@@ -825,10 +946,6 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self._supportedConfigVersions = ["tpc-1","tpc-1.1"] #the latest element of this list is considered the current version
# Logger.__init__(self)
# Qwt5.QwtPlot.__init__(self, parent)
-
- #The class to be used for the data export dialog (for allowing customization the data export dialog)
- #See the exportAscii method for details on how the dialog widget will be instantiated.
- self.exportDlgClass = QDataExportDialog
#dictionary for default axes naming
self._axesnames = {Qwt5.QwtPlot.xBottom:'X',Qwt5.QwtPlot.xTop:'X2',
@@ -884,14 +1001,22 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
z.setMaxStackDepth(self._max_zoom_stack)
z.setKeyPattern(z.KeyHome, Qt.Qt.Key_unknown) #this disables the escape key for going to the top of the zoom stack (we use escape via an action for autoscaling)
- # picker
- self._picker=Qwt5.QwtPicker(self.canvas())
-# self._picker = TaurusPicker(self.canvas())
- self._picker.setSelectionFlags(Qwt5.QwtPicker.PointSelection )
+ # point picker
+ self._pointPicker = Qwt5.QwtPicker(self.canvas())
+ self._pointPicker.setSelectionFlags(Qwt5.QwtPicker.PointSelection )
- self._pickedMarker=TaurusCurveMarker("Picked", labelOpacity=0.8)
- self._pickedCurveName=""
- self.connect(self._picker, Qt.SIGNAL('selected(QwtPolygon)'), self.pickDataPoint)
+ self._pickedMarker = TaurusCurveMarker("Picked", labelOpacity=0.8)
+ self._pickedCurveName = ""
+ self.connect(self._pointPicker, Qt.SIGNAL('selected(QwtPolygon)'), self.pickDataPoint)
+
+ #xRegion picker
+ self._xRegionPicker = Qwt5.QwtPlotPicker(Qwt5.QwtPlot.xBottom,
+ Qwt5.QwtPlot.yLeft,
+ Qwt5.QwtPicker.PointSelection,
+ Qwt5.QwtPicker.VLineRubberBand,
+ Qwt5.QwtPicker.AlwaysOn, self.canvas())
+ self._xRegionPicker.setEnabled(False)
+ self.connect(self._xRegionPicker, Qt.SIGNAL('selected(QwtDoublePoint)'), self._onXRegionEvent)
# magnifier
self._magnifier = Qwt5.QwtPlotMagnifier(self.canvas())
@@ -935,9 +1060,13 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self._dataInspectorAction = Qt.QAction("Data &Inspector mode", None)
self._dataInspectorAction.setShortcut(Qt.Qt.Key_I)
self._dataInspectorAction.setCheckable(True)
- self._dataInspectorAction.setChecked(self._picker.isEnabled())
+ self._dataInspectorAction.setChecked(self._pointPicker.isEnabled())
self.connect(self._dataInspectorAction, Qt.SIGNAL("toggled(bool)"), self.toggleDataInspectorMode)
+ self._curveStatsAction = Qt.QAction("Calculate statistics", None)
+ self._curveStatsAction.setShortcut(Qt.Qt.Key_S)
+ self.connect(self._curveStatsAction, Qt.SIGNAL("triggered()"), self.onCurveStatsAction)
+
self._pauseAction = Qt.QAction("&Pause", None)
self._pauseAction.setShortcuts([Qt.Qt.Key_P,Qt.Qt.Key_Pause])
self._pauseAction.setCheckable(True)
@@ -1002,7 +1131,7 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self._toggleZoomAxisAction, self._configDialogAction, self._inputDataAction,
self._saveConfigAction, self._loadConfigAction, self._showLegendAction,
self._showMaxAction, self._showMinAction, self._printAction, self._exportPdfAction,
- self._exportAsciiAction, self._setCurvesTitleAction):
+ self._exportAsciiAction, self._setCurvesTitleAction, self._curveStatsAction):
action.setShortcutContext(Qt.Qt.WidgetShortcut) #this is needed to avoid ambiguity when more than one TaurusPlot is used in the same window
self.canvas().addAction(action) #because of the line above, we must add the actions to the widget that gets the focus (the canvas instead of self)
@@ -1030,6 +1159,24 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
# self.info('Dropped data is invalid (%s)'%repr(modelname))
# return
+ def getCurveTitle(self, curvename):
+ '''return the current title associated to a given curve name
+
+ :param curvename: (str) the name of the curve
+
+ :return:(str)
+ '''
+ self.curves_lock.acquire()
+ try:
+ curve = self.getCurve(curvename)
+ if curve is None:
+ title = None
+ else:
+ title = unicode(curve.title().text())
+ finally:
+ self.curves_lock.release()
+ return title
+
def getCurveNames(self):
'''returns the names of all TaurusCurves attached to the plot (in arbitrary
order, if you need a sorted list, see :meth:`getCurveNamesSorted`).
@@ -1445,14 +1592,16 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self.setAxisScale(Qwt5.QwtPlot.xBottom, min, max)
finally:
self.curves_lock.release()
- self.emit(Qt.SIGNAL("dataChanged(const QString &)"), Qt.QString(name))
+ self.emit(Qt.SIGNAL("dataChanged(const QString &)"), str(name))
self.replot()
def attachRawData(self, rawdata, properties=None, id=None):
"""attaches a curve to the plot formed from raw data that comes in a dict
:param rawdata: (dict) A dictionary defining a rawdata curve. It has the
- following structure:
+ following structure (all keys are optional, but either
+ "y" or "f(x)" must be present. Also, the value of x, y
+ and f(x) can be None):
{"title":<str>, "x":list<float>, "y":list<float>,
"f(x)": <str (an expression to evaluate on the x values)>}
@@ -1481,21 +1630,24 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self.warning('The use of "name" (=%s) for attaching rawdata is deprecated. Use "title" instead'%rawdata["name"])
rawdata["title"] = rawdata["name"]
- y=rawdata.get("y",None)
- fx=rawdata.get("f(x)",None)
+ y = rawdata.get("y",None)
+ fx = rawdata.get("f(x)",None)
+ x = rawdata.get("x",None)
if fx is None:
- if y is None: raise ValueError() #There must be either f(x) or y. TODO: deal with this exception properly.
- title=str(rawdata.get("title","rawdata"))
- x=numpy.array(rawdata.get("x",numpy.arange(len(y)))) #if no x is given, the indices will be used
+ if y is None: raise ValueError('Either "f(x)" or "y" keys must be present')
+ title = str(rawdata.get("title","rawdata"))
+ if x is None:
+ x = numpy.arange(len(y)) #if no x is given, the indices will be used
+ else:
+ x = numpy.array(x)
else:
- if y is not None: raise ValueError() #We do not want both y and f(x) being passed. TODO: deal with this exception properly.
- title=str(rawdata.get("title",fx))
- x=rawdata.get("x",None)
+ if y is not None: raise ValueError('only one of "f(x)" or "y" keys can be present')
if x is None: raise ValueError('Missing "x" values') #we need x values in which to evaluate
- x=numpy.array(x)
- sev=SafeEvaluator({'x':x})
+ title = str(rawdata.get("title",fx))
+ x = numpy.array(x)
+ sev = SafeEvaluator({'x':x})
try:
- y=sev.eval(fx)
+ y = sev.eval(fx)
except:
self.warning("the function '%s' could not be evaluated (skipping)"%title) #TODO: deal with this exception properly.
return
@@ -1513,7 +1665,7 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self.curves_lock.acquire()
try:
if self.curves.has_key(name):
- curve=self.curves.get(name)
+ curve = self.curves.get(name)
if curve.isRawData:
self.detachRawData(name)
else:
@@ -1578,10 +1730,11 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self.detachRawData(name)
return names
- def getCurveData(self,curvename):
+ def getCurveData(self,curvename, numpy=False):
"""returns the data in the curve as two lists (x,y) of values
:param curvename: (str) the curve name
+ :param numpy: (bool) if True, the result is returned as numpy arrays instead of lists
:return: (tuple<list,list>) tuple of two lists (x,y) containing the curve data
"""
@@ -1596,6 +1749,8 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
raise KeyError()
finally:
self.curves_lock.release()
+ if numpy:
+ x,y = numpy.array(x), numpy.array(y)
return x,y
def updateCurves(self, names):
@@ -1818,7 +1973,7 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
menu.addSeparator()
exportSubMenu=menu.addMenu("&Export && Print")
-
+ menu.addAction(self._curveStatsAction)
exportSubMenu.addAction(self._printAction)
exportSubMenu.addAction(self._exportPdfAction)
exportSubMenu.addAction(self._exportAsciiAction)
@@ -2087,8 +2242,8 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
"""
import cPickle as pickle
if ofile is None:
- ofile = Qt.QFileDialog.getSaveFileName( self, 'Save Taurusplot Configuration', 'TaurusplotConfig.pck', 'TaurusPlot Curve Properties File (*.pck)')
- if ofile.isEmpty(): return
+ ofile = str(Qt.QFileDialog.getSaveFileName( self, 'Save Taurusplot Configuration', 'TaurusplotConfig.pck', 'TaurusPlot Curve Properties File (*.pck)'))
+ if not ofile: return
if not isinstance(ofile,file): ofile=open(ofile,'w')
configdict=self.createConfig(curvenames=curvenames)
self.info("Saving current settings in '%s'"%ofile.name)
@@ -2104,8 +2259,8 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
"""
import cPickle as pickle
if ifile is None:
- ifile = Qt.QFileDialog.getOpenFileName( self, 'Load Taurusplot Configuration', '', 'TaurusPlot Curve Properties File (*.pck)')
- if ifile.isEmpty(): return
+ ifile = str(Qt.QFileDialog.getOpenFileName( self, 'Load Taurusplot Configuration', '', 'TaurusPlot Curve Properties File (*.pck)'))
+ if not ifile: return
if not isinstance(ifile,file): ifile=open(ifile,'r')
configdict=pickle.load(ifile)
self.applyConfig(configdict)
@@ -2137,8 +2292,27 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
def autoScaleAllAxes(self):
'''Optimized autoscale of whole plot'''
+ minX=float('inf')
+ maxX=float('-inf')
+ if self.getXDynScale():
+ originalXRange = self.getXAxisRange()
+ self.curves_lock.acquire()
+ try:
+ for c in self.curves.values():
+ if c.minXValue() < minX:
+ minX = c.minXValue()
+ if c.maxXValue() > maxX:
+ maxX = c.maxXValue()
+ if minX!=maxX:
+ break
+ finally:
+ self.curves_lock.release()
+
for axis in range(Qwt5.QwtPlot.axisCnt):
- Qwt5.QwtPlot.setAxisAutoScale(self, axis)
+ if axis == Qwt5.QwtPlot.xBottom and minX==maxX:
+ Qwt5.QwtPlot.setAxisScale(self, axis, minX-0.5*originalXRange, minX+0.5*originalXRange)
+ else:
+ Qwt5.QwtPlot.setAxisAutoScale(self, axis)
self.replot()
#Update the zoom stacks
self._zoomer1.setZoomBase()
@@ -2236,15 +2410,17 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
exported. If None given, the user will be prompted for
a file name.
"""
- fileName = (fileName is not None and Qt.QString(fileName)) or Qt.QFileDialog.getSaveFileName( self, 'Export File Name', 'plot.pdf', 'PDF Documents (*.pdf)')
- if not fileName.isEmpty():
+ if fileName is None:
+ fileName = Qt.QFileDialog.getSaveFileName( self, 'Export File Name', 'plot.pdf', 'PDF Documents (*.pdf)')
+ fileName = str(fileName)
+ if fileName:
try:
- f = open(str(fileName),'w') #check if the file is actually writable
+ f = open(fileName,'w') #check if the file is actually writable
f.close()
except:
- self.error("Can't write to '%s'"%str(fileName))
+ self.error("Can't write to '%s'"%fileName)
Qt.QMessageBox.warning(self, "File Error",
- "Can't write to\n'%s'"%str(fileName),
+ "Can't write to\n'%s'"%fileName,
Qt.QMessageBox.Ok)
return
printer = Qt.QPrinter()
@@ -2262,9 +2438,9 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
printer.setCreator('TaurusPlot')
printer.setOrientation(Qt.QPrinter.Landscape)
printer.setColorMode(Qt.QPrinter.Color)
- docName = self.title().text()
- if not docName.isEmpty():
- docName.replace(Qt.QRegExp(Qt.QString.fromLatin1('\n')), self.tr(' -- '))
+ docName = str(self.title().text())
+ if docName:
+ docName.replace('\n', ' -- ')
printer.setDocName(docName)
dialog = Qt.QPrintDialog(printer)
if dialog.exec_():
@@ -2290,7 +2466,11 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
frozendata[k]=self.getCurveData(k)
finally:
self.curves_lock.release()
- dialog = self.exportDlgClass(parent=self, datadict=frozendata)
+ klass = getattr(self,'exportDlgClass', None)
+ if klass is None:
+ from taurus.qt.qtgui.panel import QDataExportDialog
+ klass = QDataExportDialog
+ dialog = klass(parent=self, datadict=frozendata)
dialog.setXIsTime(self.getXIsTime())
return dialog.exec_()
@@ -2355,9 +2535,11 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
from files
'''
if self.DataImportDlg is None:
+ from taurus.qt.qtgui.panel import TaurusModelChooser
self.DataImportDlg = Qt.QDialog(self)
self.DataImportDlg.setWindowTitle("%s - Import Data"%(str(self.windowTitle())))
self.DataImportDlg.modelChooser = TaurusModelChooser(selectables=[taurus.core.TaurusElementType.Attribute])
+ from taurus.qt.qtgui.panel import QRawDataWidget
self.DataImportDlg.rawDataChooser = QRawDataWidget()
tabs=Qt.QTabWidget()
@@ -2370,7 +2552,9 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self.connect(self.DataImportDlg.rawDataChooser, Qt.SIGNAL("ReadFromFiles"), self.readFromFiles)
self.connect(self.DataImportDlg.rawDataChooser, Qt.SIGNAL("AddCurve"), self.attachRawData)
- self.DataImportDlg.modelChooser.setListedModels(self._modelNames)
+ models_and_display = [(m,self.getCurveTitle(m.split('|')[-1])) for m in self._modelNames]
+
+ self.DataImportDlg.modelChooser.setListedModels(models_and_display)
self.DataImportDlg.show()
def readFromFiles(self, xcol, skiprows):
@@ -2539,16 +2723,16 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self._pickedMarker.attach(self)
self._pickedCurveName=pickedCurveName
self._pickedMarker.pickedIndex=pickedIndex
+ pickedCurveTitle = self.getCurveTitle(pickedCurveName)
self.replot()
label=self._pickedMarker.label()
if self.getXIsTime():
- infotxt = "'%s'[%i]:\n\t (t=%s, y=%.3g)"%(pickedCurveName,pickedIndex,datetime.fromtimestamp(picked.x()).ctime(),picked.y())
+ infotxt = "'%s'[%i]:\n\t (t=%s, y=%.3g)"%(pickedCurveTitle,pickedIndex,datetime.fromtimestamp(picked.x()).ctime(),picked.y())
else:
- infotxt = "'%s'[%i]:\n\t (x=%.3g, y=%.3g)"%(pickedCurveName,pickedIndex,picked.x(),picked.y())
+ infotxt = "'%s'[%i]:\n\t (x=%.3g, y=%.3g)"%(pickedCurveTitle,pickedIndex,picked.x(),picked.y())
label.setText(infotxt)
fits = label.textSize().width()<self.size().width()
if fits:
- label.setText(infotxt)
self._pickedMarker.setLabel(Qwt5.QwtText (label))
self._pickedMarker.alignLabel()
self.replot()
@@ -2561,10 +2745,7 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
popup.move(self.pos().x()-popup.size().width(),self.pos().y() )
popup.move(self.pos())
Qt.QTimer.singleShot(5000, popup.hide)
-
-
-
-
+
return picked,pickedCurveName,pickedIndex
def toggleDataInspectorMode(self, enable=None):
@@ -2583,7 +2764,7 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
if enable is None:
enable=not(self._inspectorMode)
- self._picker.setEnabled(enable)#enables/disables the picker
+ self._pointPicker.setEnabled(enable)#enables/disables the picker
self._zoomer.setEnabled(self._allowZoomers and not(enable)) #disables/enables the zoomers
if enable:
@@ -2597,6 +2778,116 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
self._inspectorMode=enable
return self._inspectorMode
+
+ def onCurveStatsAction(self):
+ '''
+ slot for the curveStatsAction. Allows the user to select a range and
+ then shows curve statistics on that range.
+ '''
+ if getattr(self, '_curveStatsDialog',None) is None:
+ from taurus.qt.qtgui.plot import CurveStatsDialog
+ self._curveStatsDialog = CurveStatsDialog(self)
+ self.connect(self._curveStatsDialog, Qt.SIGNAL('closed'),self._onCurveStatsDialogClosed)
+ self.connect(self._curveStatsDialog, Qt.SIGNAL('finished(int)'),self._onCurveStatsDialogClosed)
+ elif not self._curveStatsDialog.isVisible():
+ self._curveStatsDialog.refreshCurves()
+ self._curveStatsAction.setEnabled(False) #it will be reenabed by _onCurveStatsDialogClosed
+ self._curveStatsDialog.show()
+
+ def _onCurveStatsDialogClosed(self, *args):
+ '''slot called when the Curve Stats dialog is closed'''
+ self._curveStatsAction.setEnabled(True)
+
+ def selectXRegion(self, axis=Qwt5.QwtPlot.xBottom, callback=None):
+ '''Changes the input mode to allow the user to select a region of the X axis
+
+ :param axis: (Qwt5.QwtPlot.xBottom or Qwt5.QwtPlot.xTop) on which the
+ region will be defined (Default=Qwt5.QwtPlot.xBottom)
+ :param callback: (method) a function that will be called when the user
+ finishes selecting the region. If None passed (default)
+ nothing is done
+ '''
+ self.__xRegionCallback = callback
+ self._xRegionPicker.setAxis(axis,Qwt5.QwtPlot.yLeft)
+ self._beginXRegionMode()
+
+ def _onXRegionEvent(self, pos):
+ '''slot called when the _xRegionPicker picks a point'''
+ if self.__xRegionEnd is not None:
+ self.debug('Ignoring xRegionEvent. Reason: not-reset region')
+ return
+ x = pos.x()
+ if self.__xRegionStart is None:
+ self.__xRegionStart = x
+ self.__xRegionStartMarker.setXValue(x)
+ self.__xRegionStartMarker.attach(self)
+ self.replot()
+ else:
+ self.__xRegionEnd = x
+ self.__xRegionEndMarker.setXValue(x)
+ self.__xRegionEndMarker.attach(self)
+
+ xmin,xmax = self.__xRegionStart,self.__xRegionEnd
+ if xmin>xmax: xmin,xmax = xmax,xmin
+
+ self.__xRegionCallback((xmin,xmax))
+
+ self.replot()
+ self._endXRegionMode()
+
+ def _beginXRegionMode(self):
+ '''pre-region selection tasks'''
+ self.__xRegionStart = None
+ self.__xRegionEnd = None
+ self.__xRegionStartMarker = Qwt5.QwtPlotMarker()
+ self.__xRegionStartMarker.setLineStyle(Qwt5.QwtPlotMarker.VLine)
+ self.__xRegionStartMarker.setLinePen(Qt.QPen(Qt.Qt.green, 2))
+ self.__xRegionEndMarker = Qwt5.QwtPlotMarker()
+ self.__xRegionEndMarker.setLineStyle(Qwt5.QwtPlotMarker.VLine)
+ self.__xRegionEndMarker.setLinePen(Qt.QPen(Qt.Qt.green, 2))
+
+ self._zoomer.setEnabled(False) #disables the zoomers
+ self.canvas().setCursor(Qt.Qt.SplitHCursor)
+ self._xRegionPicker.setEnabled(True)
+
+ def _endXRegionMode(self):
+ '''post-region selection tasks'''
+ self._xRegionPicker.setEnabled(False)
+ self.canvas().setCursor(Qt.Qt.CrossCursor)
+ self._zoomer.setEnabled(self._allowZoomers)
+ self.__xRegionStartMarker.detach()
+ self.__xRegionEndMarker.detach()
+ self.replot()
+
+ def getCurveStats(self, limits=None, curveNames=None):
+ '''Shows a dialog containing descriptive statistics on curves
+
+ :param limits: (None or tuple<float,float>) tuple containing (min,max) limits.
+ Points of the curve whose abscisa value is outside of
+ these limits are ignored. If None is passed, the limit is not enforced
+ :param curveNames: (seq<str>) sequence of curve names for which
+ statistics are requested. If None passed (default), all curves are
+ considered
+
+ :return: (dict) Returns a dictionary whose keys are the curve names and
+ whose values are the dictionaries returned by
+ :meth:`TaurusCurve.getStats`
+ '''
+ if limits is not None and limits[0] is None and limits[1] is None:
+ limits = None
+ if curveNames is None:
+ curveNames=self.getCurveNamesSorted()
+
+ stats = {}
+ self.curves_lock.acquire()
+ try:
+ for name in curveNames:
+ curve = self.curves.get(name, None)
+ stats[name] = curve.getStats(limits=limits)
+ stats[name]['title'] = unicode(curve.title().text())
+ finally:
+ self.curves_lock.release()
+ return stats
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# QT property definition
@@ -2842,7 +3133,7 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
curves to which the title will be changed (if None given , it will apply
to all the curves except the raw data ones)
- :return: (caselessDict<str,QString>) dictionary with key=curvename and value=newtitle
+ :return: (caselessDict<str,str>) dictionary with key=curvename and value=newtitle
.. seealso:: :meth:`changeCurvesTitlesDialog`,
:meth:`setDefaultCurvesTitle`, :meth:`TaurusCurve.setTitleText`
@@ -2854,7 +3145,7 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
for curveName in curveNamesList:
curve = self.curves.get(curveName)
curve.setTitleText(titletext)
- newTitlesDict[curveName] = curve.title().text()
+ newTitlesDict[curveName] = str(curve.title().text())
self.updateLegend(self.legend())
return newTitlesDict
finally:
@@ -2870,19 +3161,22 @@ class TaurusPlot(Qwt5.QwtPlot, TaurusBaseWidget):
except the raw data ones and it will also be used
as default for newly created ones)
- :return: (caselessDict<str,QString>) dictionary with key=curvename and value=newtitle
+ :return: (caselessDict<str,str>) dictionary with key=curvename and value=newtitle
.. seealso:: :meth:`setCurvesTitle`, :meth:`setDefaultCurvesTitle`
'''
newTitlesDict = None
- titletext, ok = Qt.QInputDialog.getText( self,
- 'New Title for Curves',
- 'New text to be used for the curves.'\
- 'You can use any of the following placeholders:\n'\
- '<label>, <model>, <attr_name>, <attr_fullname>,'\
- '<dev_alias>, <dev_name>, <dev_fullname>, <current_title>',
- Qt.QLineEdit.Normal,
- self._defaultCurvesTitle)
+ placeholders = ['<label>', '<model>', '<attr_name>', '<attr_fullname>',
+ '<dev_alias>', '<dev_name>', '<dev_fullname>', '<current_title>']
+ try:
+ current = placeholders.index(self._defaultCurvesTitle)
+ items = placeholders
+ except:
+ current= len(placeholders)
+ items = placeholders+[self._defaultCurvesTitle]
+
+ msg = 'New text to be used for the curves.\nYou can use any of the following placeholders:\n%s'%", ".join(placeholders)
+ titletext, ok = Qt.QInputDialog.getItem(self, 'New Title for Curves', msg, items, current, True)
if ok:
titletext = str(titletext)
if curveNamesList is None: self.setDefaultCurvesTitle(titletext)
diff --git a/lib/taurus/qt/qtgui/plot/taurusplotconf.py b/lib/taurus/qt/qtgui/plot/taurusplotconf.py
index 2a4531b..1c8b549 100644
--- a/lib/taurus/qt/qtgui/plot/taurusplotconf.py
+++ b/lib/taurus/qt/qtgui/plot/taurusplotconf.py
@@ -32,8 +32,7 @@ __all__ = ['TaurusPlotConfDlg']
raise NotImplementedError('Under Construction!')
import taurus.core
-from taurus.qt import Qt
-from PyQt4 import Qwt5
+from taurus.qt import Qt, Qwt5
from ui.ui_TaurusPlotConf import Ui_TaurusPlotConfDlg
import curveprops
try:
diff --git a/lib/taurus/qt/qtgui/plot/taurustrend.py b/lib/taurus/qt/qtgui/plot/taurustrend.py
index 7d31fb3..0bf4d6d 100644
--- a/lib/taurus/qt/qtgui/plot/taurustrend.py
+++ b/lib/taurus/qt/qtgui/plot/taurustrend.py
@@ -28,14 +28,12 @@ taurustrend.py: Generic trend widget for Taurus
"""
__all__=["ScanTrendsSet", "TaurusTrend", "TaurusTrendsSet"]
-import copy
from datetime import datetime
import time
import numpy
import re
import gc
-from taurus.qt import Qt
-from PyQt4 import Qwt5
+from taurus.qt import Qt, Qwt5
import taurus.core
from taurus.core.util import CaselessDict, CaselessList, ArrayBuffer
@@ -87,12 +85,15 @@ class TaurusTrendsSet(Qt.QObject, TaurusBaseComponent):
update its values)
"""
+ droppedEventsWarning = 3 #number of dropped events before issuing a warning
def __init__(self, name, parent = None, curves=None):
Qt.QObject.__init__(self, parent)
self.call__init__(TaurusBaseComponent, self.__class__.__name__)
self._xBuffer = None
self._yBuffer = None
self.forcedReadingTimer = None
+ self.droppedEventsCount = 0
+ self.compiledTitle = name
try: self._maxBufferSize = self.parent().getMaxDataBufferSize()
except: self._maxBufferSize = TaurusTrend.DEFAULT_MAX_BUFFER_SIZE
if curves is None:
@@ -215,8 +216,9 @@ class TaurusTrendsSet(Qt.QObject, TaurusBaseComponent):
basetitle = basetitle.replace('<dev_full_name>',dev.getFullName() or '---')
if len(self._curves)==1: basetitle = basetitle.replace('<[trend_index]>','')
- else: basetitle = basetitle.replace('<[trend_index]>','[<trend_index>]')
+ else: basetitle = basetitle.replace('<[trend_index]>','[<trend_index>]')
+ self.compiledTitle = basetitle
return basetitle
def addCurve(self, name, curve):
@@ -277,48 +279,44 @@ class TaurusTrendsSet(Qt.QObject, TaurusBaseComponent):
SPECTRUM attribute with dim_x=8. Then the return value will be (X,Y)
where X.shape=(10,) and Y.shape=(10,8); X.dtype = Y.dtype = <dtype('float64')>
'''
- if numpy.isscalar(value.value):
- ntrends=1
- else:
- ntrends=len(value.value)
-
+ if value is not None:
+ v = value.value
+ if numpy.isscalar(v):
+ ntrends = 1
+ else:
+ try:
+ v = float(v)
+ ntrends = 1
+ except:
+ ntrends = len(v)
+ else: ntrends = len(self._curves)
+
if self._xBuffer is None:
- self._xBuffer = ArrayBuffer(numpy.zeros(128, dtype='d'), maxSize=self._maxBufferSize )
+ self._xBuffer = ArrayBuffer(numpy.zeros(min(128,self._maxBufferSize), dtype='d'), maxSize=self._maxBufferSize )
if self._yBuffer is None:
- self._yBuffer = ArrayBuffer(numpy.zeros((128, ntrends),dtype='d'), maxSize=self._maxBufferSize )
-
- self._yBuffer.append(value.value)
+ self._yBuffer = ArrayBuffer(numpy.zeros((min(128,self._maxBufferSize), ntrends),dtype='d'), maxSize=self._maxBufferSize )
+
+ #self.trace('_updateHistory(%s,%s(...))' % (model,type(value.value)))
+ if value is not None: self._yBuffer.append(value.value)
if self.parent().getXIsTime():
#add the timestamp to the x buffer
- self._xBuffer.append(value.time.totime())
+ if value is not None: self._xBuffer.append(value.time.totime())
##Adding archiving values
if self.parent().getUseArchiving():
- if self.parent().getXDynScale() or not self.parent().axisAutoScale(Qwt5.QwtPlot.xBottom): #Do not open a mysql connection for autoscaled plots
- startdate = self.parent().axisScaleDiv(Qwt5.QwtPlot.xBottom).lowerBound()
- stopdate = self._xBuffer[0] #Older value already read
+ #open a mysql connection for online trends or any not autoscaled plots
+ if self.parent().getXDynScale() or not self.parent().axisAutoScale(Qwt5.QwtPlot.xBottom):
try:
- archived = getArchivedTrendValues(self,model,startdate,stopdate)
- if archived is not None and len(archived):
- del(archived[:-self._xBuffer.remainingSize()]) #limit the archived values according to the maximum size of the buffer
- t = numpy.zeros(len(archived), dtype=float)
- y = numpy.zeros((len(archived), ntrends), dtype=float)#self._yBuffer.dtype)
- for i,v in enumerate(archived):
- t[i]=v.time.totime()
- y[i]=v.value
- self._xBuffer.extendLeft(t)
- self._yBuffer.extendLeft(y)
- self.parent().replot()
+ getArchivedTrendValues(self,model,insert=True)
except Exception,e:
import traceback
self.warning('%s: reading from archiving failed: %s'%(datetime.now().isoformat('_'),traceback.format_exc()))
- else:
+ elif value is not None:
#add the event number to the x buffer
try:
self._xBuffer.append(1.+self._xBuffer[-1])
except IndexError: #this will happen when the x buffer is empty
self._xBuffer.append(0)
-
return self._xBuffer.contents(), self._yBuffer.contents()
def clearTrends(self, replot=True):
@@ -348,25 +346,54 @@ class TaurusTrendsSet(Qt.QObject, TaurusBaseComponent):
curves in the set according to the value of the attribute.
For documentation about the parameters of this method, see
- :meth:`TaurusBaseComponent.handleEvent`'''
+ :meth:`TaurusBaseComponent.handleEvent`
+ '''
if evt_type == taurus.core.TaurusEventType.Config:
#self.setTitleText(self._titleText or self.parent().getDefaultCurvesTitle()) #this did not work well (it overwrites custom titles!)
return
+ else:
+ model = evt_src if evt_src is not None else self.getModelObj()
+ if evt_type == taurus.core.TaurusEventType.Error:
+ self._onDroppedEvent(reason='Error event')
+ if not self.parent().getUseArchiving(): return
+ else: value = None
+ elif model is None:
+ self._onDroppedEvent(reason='unknown model')
+ if not self.parent().getUseArchiving(): return
+ else: value = None
+ else:
+ value = evt_value if isinstance(evt_value, (taurus.core.TaurusAttrValue, PyTango.DeviceAttribute)) else self.getModelValueObj()
+ if value is None or value.value is None:
+ self._onDroppedEvent(reason='invalid value')
+ if not self.parent().getUseArchiving(): return
+ else:
+ self._checkDataDimensions(value.value)
+
+ #get the data from the event
+ try:
+ self._xValues, self._yValues = self._updateHistory(model=model or self.getModel(),value=value)
+ except Exception, e:
+ self._onDroppedEvent(reason=str(e))
+ raise
+
+ #assign xvalues and yvalues to each of the curves in self._curves
+ for i,(n,c) in enumerate(self.getCurves()):
+ c._xValues, c._yValues = self._xValues, self._yValues[:,i]
+ c._updateMarkers()
+
+ self.emit(Qt.SIGNAL("dataChanged(const QString &)"), Qt.QString(self.getModel()))
- if evt_type == taurus.core.TaurusEventType.Error:
- return
-
- model = evt_src if evt_src is not None else self.getModelObj()
- if model is None: return
-
- value = evt_value if isinstance(evt_value, (taurus.core.TaurusAttrValue, PyTango.DeviceAttribute)) else self.getModelValueObj()
- if value is None or value.value is None: return
-
- #Check that the data dimensions are consistent with what was plotted before
- if numpy.isscalar(value.value):
+ def _checkDataDimensions(self,value):
+ '''
+ Check that the data dimensions are consistent with what was plotted before
+ '''
+ if value is None:
+ return len(self._curves)
+ try:
+ float(value)
ntrends=1
- else:
- ntrends=len(value.value)
+ except:
+ ntrends=len(value)
if ntrends != len(self._curves):
#clean previous curves
self.clearTrends(replot=False)
@@ -379,17 +406,22 @@ class TaurusTrendsSet(Qt.QObject, TaurusBaseComponent):
self.addCurve(subname, self.parent().curves[subname])
self.setTitleText(self._titleText or self.parent().getDefaultCurvesTitle())
self.parent().autoShowYAxes()
-
- #get the data from the event
- self._xValues, self._yValues = self._updateHistory(model=model,value=value)
-
- #assign xvalues and yvalues to each of the curves in self._curves
- for i,(n,c) in enumerate(self.getCurves()):
- c._xValues, c._yValues = self._xValues, self._yValues[:,i]
- c._updateMarkers()
-
- self.emit(Qt.SIGNAL("dataChanged(const QString &)"), Qt.QString(self.getModel()))
-
+ return ntrends
+
+ def _onDroppedEvent(self, reason='Unknown'):
+ '''inform the user about a dropped event
+
+ :param reason: (str) The reason of the drop
+ '''
+ self.debug("Droping event. Reason %s", reason)
+ self.droppedEventsCount += 1
+ if self.droppedEventsCount == self.droppedEventsWarning:
+ msg = ('At least %i events from model "%s" have being dropped. This attribute may have problems\n' +
+ 'Future occurrences will be silently ignored')%(self.droppedEventsWarning, self.modelName)
+ self.warning(msg)
+ p = self.parent()
+ if p:
+ Qt.QMessageBox.warning(p, "Errors in set %s"%self._titleText, msg, Qt.QMessageBox.Ok)
def isReadOnly(self):
return True
@@ -406,8 +438,10 @@ class TaurusTrendsSet(Qt.QObject, TaurusBaseComponent):
:param maxSize: (int) the maximum limit
'''
- self._xBuffer.setMaxSize(maxSize)
- self._yBuffer.setMaxSize(maxSize)
+ if self._xBuffer is not None:
+ self._xBuffer.setMaxSize(maxSize)
+ if self._yBuffer is not None:
+ self._yBuffer.setMaxSize(maxSize)
self._maxBufferSize = maxSize
def maxDataBufferSize(self):
@@ -527,7 +561,7 @@ class ScanTrendsSet(TaurusTrendsSet):
if packet is None:
self.debug('Ignoring empty scan data packet')
return
- id,packet = packet
+ pkgid,packet = packet
pcktype = packet.get("type","__UNKNOWN_PCK_TYPE__")
if pcktype == "data_desc":
self._dataDescReceived(packet["data"])
@@ -599,7 +633,7 @@ class ScanTrendsSet(TaurusTrendsSet):
if self._xDataKey is None or self._xDataKey == "<mov>": #@todo use a standard key for <mov> and <idx>
try:
self._autoXDataKey = datadesc['ref_moveables'][0]
- except KeyError, IndexError:
+ except (KeyError, IndexError):
self._autoXDataKey = self.DEFAULT_X_DATA_KEY
elif self._xDataKey == "<idx>":
self._autoXDataKey = 'point_nb'
@@ -639,7 +673,7 @@ class ScanTrendsSet(TaurusTrendsSet):
name = dd["name"]
if name not in self._curves and self._plotablesFilter(name) and name != self._autoXDataKey:
rawdata["title"] = dd["label"]
- curve = self.parent().attachRawData(rawdata)
+ curve = self.parent().attachRawData(rawdata,id=name)
prop = curve.getAppearanceProperties()
prop.sColor = prop.lColor
prop.sStyle = Qwt5.QwtSymbol.Ellipse
@@ -791,11 +825,62 @@ class TaurusTrend(TaurusPlot):
self.connect(self._setForcedReadingPeriodAction, Qt.SIGNAL("triggered()"), self.setForcedReadingPeriod)
self._clearBuffersAction = Qt.QAction("Clear Buffers", None)
self.connect(self._clearBuffersAction, Qt.SIGNAL("triggered()"), self.clearBuffers)
- self._autoClearOnScanAction = Qt.QAction("Auto Clear on new Scans", None)
+ self._setMaxBufferSizeAction = Qt.QAction("Change buffers size...", None)
+ self.connect(self._setMaxBufferSizeAction, Qt.SIGNAL("triggered()"), self.setMaxDataBufferSize)
+ self._autoClearOnScanAction = Qt.QAction("Auto-clear on new scans", None)
self._autoClearOnScanAction.setCheckable(True)
self._autoClearOnScanAction.setChecked(True)
self.connect(self._autoClearOnScanAction, Qt.SIGNAL("toggled(bool)"), self._onAutoClearOnScanAction)
+ def isTimerNeeded(self, checkMinimized=True):
+ '''checks if it makes sense to activate the replot timer.
+ The following conditions must be met:
+
+ - the replot timer must exist
+ - the area of the plot must be non-zero
+ - at least one trendset must be attached
+ - the plot should be visible
+ - the plot should not be minimized (unless checkMinimized=False)
+
+ :param checkMinimized: (bool) whether to include the check of minimized (True by default)
+
+ :return: (bool)
+ '''
+ return self._replotTimer is not None and \
+ not self.size().isEmpty() and \
+ bool(len(self.trendSets)) and \
+ self.isVisible() and \
+ not (checkMinimized and self.isMinimized())
+
+ def showEvent(self, event):
+ '''reimplemented from :meth:`TaurusPlot.showEvent` so that
+ the replot timer is active only when needed'''
+ TaurusPlot.showEvent(self, event)
+ if self.isTimerNeeded(checkMinimized=False):
+ self.debug('(re)starting the timer (in showEvent)')
+ self._replotTimer.start()
+
+ def hideEvent(self, event):
+ '''reimplemented from :meth:`TaurusPlot.showEvent` so that
+ the replot timer is active only when needed'''
+ TaurusPlot.hideEvent(self, event)
+ if self._replotTimer is not None:
+ self.debug('stopping the timer (in hideEvent)')
+ self._replotTimer.stop()
+
+ def resizeEvent(self,event):
+ '''reimplemented from :meth:`TaurusPlot.resizeEvent` so that
+ the replot timer is active only when needed'''
+ TaurusPlot.resizeEvent(self, event)
+ if event.oldSize().isEmpty(): #do further checks only if previous size was 0
+ if self.isTimerNeeded():
+ self.debug('(re)starting the timer (in resizeEvent)')
+ self._replotTimer.start()
+ else:
+ if self._replotTimer is not None:
+ self.debug('stopping the timer (in resizeEvent)')
+ self._replotTimer.stop()
+
def setXIsTime(self, enable, axis=Qwt5.QwtPlot.xBottom):
'''Reimplemented from :meth:`TaurusPlot.setXIsTime`'''
#set a reasonable scale
@@ -953,6 +1038,7 @@ class TaurusTrend(TaurusPlot):
if len(del_sets) == len(self.trendSets):
self._curvePens.setCurrentIndex(0)
+ #update new/existing trendsets
for name in names:
name = str(name)
if "|" in name: raise ValueError('composed ("X|Y") models are not supported by TaurusTrend')
@@ -984,6 +1070,15 @@ class TaurusTrend(TaurusPlot):
self.showLegend(len(self.curves) > 1, forever=False)
self.replot()
+ #keep the replotting timer active only if there is something to refresh
+ if self.isTimerNeeded():
+ self.debug('(re)starting the timer (in updateCurves)')
+ self._replotTimer.start()
+ else:
+ if self._replotTimer is not None:
+ self.debug('stopping the timer (in updateCurves)')
+ self._replotTimer.stop()
+
finally:
self.curves_lock.release()
@@ -1012,6 +1107,40 @@ class TaurusTrend(TaurusPlot):
self.curves_lock.release()
return ret
+ def getCurveTitle(self, name, index=None):
+ '''reimplemented from :class:`TaurusPlot`.
+ Returns the title of a curve from a trendset
+
+ :param name: (str) The name of the trendset. If the name is not a known
+ trendset name and index is None, we will try with tsetname and
+ index obtained from parsing the given name (assuming the
+ format '<tsetname>[<index>]').
+ :param index: (int or None) the index of the curve in the trend set.
+ If None is passed, it returns the base title of the trendset
+
+ :return: (str) the title
+ '''
+ self.curves_lock.acquire()
+ try:
+ tset = self.trendSets.get(name)
+ if tset is None: #name not found...
+ if index is None: # maybe name was actually a curve name including the index?
+ match = re.match(r'^(.*)\[([0-9]+)\]$', name)
+ if match:
+ name,index = match.groups()
+ index = int(index)
+ return self.getCurveTitle(name, index=index) #recursive call with parsed tsetname and index
+ return None
+ if index is None:
+ if len(tset) == 1:
+ index = 0
+ else:
+ return tset.compiledTitle
+ title = unicode(tset[index].title().text())
+ finally:
+ self.curves_lock.release()
+ return title
+
def changeCurvesTitlesDialog(self, curveNamesList=None):
'''Shows a dialog to set the curves titles (it will change the current
curves titles and the default curves titles)
@@ -1029,15 +1158,20 @@ class TaurusTrend(TaurusPlot):
.. seealso:: :meth:`setCurvesTitle`, :meth:`setDefaultCurvesTitle`
'''
newTitlesDict = None
- titletext, ok = Qt.QInputDialog.getText( self,
- 'New Title for Curves',
- 'New text to be used for the curves.'\
- 'You can use any of the following placeholders:\n'\
- '<label>, <model>, <attr_name>, <attr_fullname>,'\
- '<dev_alias>, <dev_name>, <dev_fullname>,' \
- '<current_title>, <trend_index>, <[trend_index]>',
- Qt.QLineEdit.Normal,
- self._defaultCurvesTitle)
+
+ placeholders = ['<label>', '<model>', '<attr_name>', '<attr_fullname>',
+ '<dev_alias>', '<dev_name>', '<dev_fullname>', '<current_title>',
+ '<trend_index>', '<[trend_index]>']
+ try:
+ current = placeholders.index(self._defaultCurvesTitle)
+ items = placeholders
+ except:
+ current= len(placeholders)
+ items = placeholders+[self._defaultCurvesTitle]
+
+ msg = 'New text to be used for the curves.\nYou can use any of the following placeholders:\n%s'%", ".join(placeholders)
+ titletext, ok = Qt.QInputDialog.getItem(self, 'New Title for Curves', msg, items, current, True)
+
if ok:
titletext = str(titletext)
if curveNamesList is None:
@@ -1113,7 +1247,7 @@ class TaurusTrend(TaurusPlot):
def doReplot(self):
'''calls :meth:`replot` only if there is new data to be plotted'''
- self.debug('Replotting? %s',self._dirtyPlot)
+ #self.trace('Replotting? %s',self._dirtyPlot)
if self._dirtyPlot:
self.replot()
self._dirtyPlot = False
@@ -1135,8 +1269,13 @@ class TaurusTrend(TaurusPlot):
currmin, currmax = sdiv.lowerBound(), sdiv.upperBound()
plot_refresh = int(1000*(currmax-currmin)/width)
plot_refresh = min((max((plot_refresh,250)),1800000)) #enforce limits
- self._replotTimer.start(plot_refresh)
- self.debug('New replot period is %1.2f seconds',(plot_refresh/1000.))
+ if self._replotTimer.interval() != plot_refresh:
+ #note: calling QTimer.setInterval() very often seems to eventually trigger some bug
+ # that stops the timer from emitting the timeout signal. We avoid this by
+ # calling setInterval only when really needed.
+ self._replotTimer.setInterval(plot_refresh)
+ self.debug('New replot period is %1.2f seconds',(plot_refresh/1000.))
+
else:
self.warning('rescheduleReplot() called but X axis is not in time mode')
@@ -1176,6 +1315,7 @@ class TaurusTrend(TaurusPlot):
rawdatadict = CaselessDict(configdict["RawData"])
miscdict = CaselessDict(configdict["Misc"])
miscdict["ForcedReadingPeriod"] = self.getForcedReadingPeriod()
+ miscdict["MaxBufferSize"] = self.getMaxDataBufferSize()
self.curves_lock.acquire()
try:
for tsname,ts in self.trendSets.iteritems():
@@ -1198,6 +1338,10 @@ class TaurusTrend(TaurusPlot):
.. seealso:: :meth:`createConfig`
"""
if not self.checkConfigVersion(configdict): return
+ #set the max Buffer data size (we do it before ataching the curves to avoid useless reallocations of buffers)
+ maxBufferSize = configdict["Misc"].get("MaxBufferSize")
+ if maxBufferSize is not None:
+ self.setMaxDataBufferSize(maxBufferSize)
#attach the curves
for rd in configdict["RawData"].values(): self.attachRawData(rd)
models = configdict.get("model",configdict["TrendSets"].values()) #for backwards compatibility, if the ordered list of models is not stored, it uses the unsorted dict values
@@ -1215,7 +1359,7 @@ class TaurusTrend(TaurusPlot):
forcedreadingperiod = configdict["Misc"].get("ForcedReadingPeriod")
if forcedreadingperiod is not None:
self.setForcedReadingPeriod(forcedreadingperiod)
-
+
@classmethod
def getQtDesignerPluginInfo(cls):
"""Returns pertinent information in order to be able to build a valid
@@ -1301,18 +1445,20 @@ class TaurusTrend(TaurusPlot):
def _scaleChangeWarning(self):
'''slot that may be called when the x axis changes the scale'''
sdiv = self.axisScaleDiv(self.xBottom)
- min = sdiv.lowerBound()
- if min < self._archivingWarningThresshold:
+ smin = sdiv.lowerBound()
+ if smin < self._archivingWarningThresshold:
self.showArchivingWarning()
- self._archivingWarningThresshold = min-2*sdiv.range() #lower the thresshold by twice the current range
+ self._archivingWarningThresshold = smin-2*sdiv.range() #lower the thresshold by twice the current range
def showArchivingWarning(self):
'''shows a dialog warning of the potential isuues with
archiving performance. It offers the user to disable archiving retrieval'''
#stop the scale change notification temporally (to avoid duplicate warnings)
+ self.setUseArchiving(False)
self.disconnect(self.axisWidget(self.xBottom), Qt.SIGNAL("scaleDivChanged ()"), self._scaleChangeWarning)
#show a dialog
dlg = Qt.QDialog(self)
+ dlg.setModal(True)
dlg.setLayout(Qt.QVBoxLayout())
dlg.setWindowTitle('Archiving warning')
msg = 'Archiving retrieval is enabled.\n'+\
@@ -1332,20 +1478,44 @@ class TaurusTrend(TaurusPlot):
if dlg.result() == dlg.Accepted:
self.setUseArchiving(False)
#restore the scale change notification only if the user chose to keep archiving AND did not want to disable warnings
- elif not rememberCB.isChecked():
- self.connect(self.axisWidget(self.xBottom), Qt.SIGNAL("scaleDivChanged ()"), self._scaleChangeWarning)
+ else:
+ self.setUseArchiving(True)
+ if not rememberCB.isChecked():
+ self.connect(self.axisWidget(self.xBottom), Qt.SIGNAL("scaleDivChanged ()"), self._scaleChangeWarning)
- def setMaxDataBufferSize(self, maxSize):
+ def setMaxDataBufferSize(self, maxSize=None):
'''sets the maximum number of events that can be plotted in the trends
- :param maxSize: (int) the maximum limit
+ :param maxSize: (int or None) the maximum limit. If None is passed,
+ the user is prompted for a value.
.. seealso:: :meth:`TaurusTrendSet.setMaxDataBufferSize`
'''
+ if maxSize is None:
+ maxSize = self._maxDataBufferSize
+ try: #API changed in QInputDialog since Qt4.4
+ qgetint = Qt.QInputDialog.getInt
+ except AttributeError:
+ qgetint = Qt.QInputDialog.getInteger
+ maxSize,ok = qgetint(self, 'New buffer data size',
+ 'Enter the number of points to be kept in memory for each curve',
+ maxSize, 2, 10000000, 1000)
+ if not ok:
+ return
+
+ choiceOnClear = None
+
self.curves_lock.acquire()
try:
- for ts in self.trendSets.itervalues():
- ts.setMaxDataBufferSize(maxSize)
+ for n,ts in self.trendSets.iteritems():
+ try:
+ ts.setMaxDataBufferSize(maxSize)
+ except ValueError:
+ if choiceOnClear is None:
+ choiceOnClear = Qt.QMessageBox.question(self, "Clear buffers?", "Clear the curves that contain too many points for the selected buffer size?", Qt.QMessageBox.No|Qt.QMessageBox.Yes)
+ if choiceOnClear == Qt.QMessageBox.Yes:
+ ts.clearTrends(replot=False)
+ ts.setMaxDataBufferSize(maxSize)
finally:
self.curves_lock.release()
self._maxDataBufferSize = maxSize
@@ -1367,6 +1537,7 @@ class TaurusTrend(TaurusPlot):
menu.insertAction(self._setCurvesTitleAction, self._useArchivingAction)
menu.insertAction(self._setCurvesTitleAction, self._usePollingBufferAction)
menu.insertAction(self._setCurvesTitleAction, self._setForcedReadingPeriodAction)
+ menu.insertAction(self._setCurvesTitleAction, self._setMaxBufferSizeAction)
menu.insertAction(self._setCurvesTitleAction, self._clearBuffersAction)
if self.__qdoorname is not None:
menu.insertAction(self._setCurvesTitleAction, self._autoClearOnScanAction)
@@ -1376,7 +1547,7 @@ class TaurusTrend(TaurusPlot):
''' see :meth:`TaurusPlot._axisContextMenu` '''
menu = TaurusPlot._axisContextMenu(self, axis=axis)
if axis in (Qwt5.QwtPlot.xBottom, Qwt5.QwtPlot.xTop) and self.__qdoorname is not None:
- changeXDataKeyAction = menu.addAction('Source of X values...', self.onChangeXDataKeyAction)
+ menu.addAction('Source of X values...', self.onChangeXDataKeyAction)
return menu
def onChangeXDataKeyAction(self):
@@ -1451,7 +1622,7 @@ class TaurusTrend(TaurusPlot):
else:
self.curves_lock.acquire()
try:
- return self.trendSets[name].getForcedReadingPeriod()
+ return self.trendSets[tsetname].getForcedReadingPeriod()
finally:
self.curves_lock.release()
@@ -1493,6 +1664,24 @@ class TaurusTrend(TaurusPlot):
scrollstep = Qt.pyqtProperty("double", getScrollStep, setScrollStep, resetScrollStep)
forcedReadingPeriod = Qt.pyqtProperty("int", getForcedReadingPeriod, setForcedReadingPeriod, resetForcedReadingPeriod)
+
+
+def test():
+ import sys
+ import taurus.qt.qtgui.application
+ app = taurus.qt.qtgui.application.TaurusApplication()
+ w=Qt.QWidget()
+ w.setLayout(Qt.QVBoxLayout())
+ s=Qt.QSplitter()
+ w.layout().addWidget(s)
+ t=TaurusTrend()
+ l=Qt.QLabel('asdasdasdasdasd')
+ s.addWidget(l)
+ s.addWidget(t)
+ s.setSizes([1,0])
+ w.show()
+ t.setModel(['bl97/pc/dummy-01/voltage'])
+ sys.exit(app.exec_())
def main():
import sys
diff --git a/lib/taurus/qt/qtgui/plot/ui/CurveStatsDialog.ui b/lib/taurus/qt/qtgui/plot/ui/CurveStatsDialog.ui
new file mode 100644
index 0000000..bfa3719
--- /dev/null
+++ b/lib/taurus/qt/qtgui/plot/ui/CurveStatsDialog.ui
@@ -0,0 +1,341 @@
+<ui version="4.0" >
+ <class>CurveStatsDialog</class>
+ <widget class="QDialog" name="CurveStatsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>723</width>
+ <height>382</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Curve Stats Dialog</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2" >
+ <item row="0" column="0" >
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>X limits</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <widget class="QCheckBox" name="minCB" >
+ <property name="text" >
+ <string>min</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="minSB" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="decimals" >
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDateTimeEdit" name="minDTE" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentSection" >
+ <enum>QDateTimeEdit::YearSection</enum>
+ </property>
+ <property name="displayFormat" >
+ <string>yyyy-MM-ddThh:mm:ss.zzz</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="selectMinPB" >
+ <property name="enabled" >
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" >
+ <string>Select from plot</string>
+ </property>
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" >
+ <item>
+ <widget class="QCheckBox" name="maxCB" >
+ <property name="text" >
+ <string>max</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="maxSB" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="decimals" >
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDateTimeEdit" name="maxDTE" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentSection" >
+ <enum>QDateTimeEdit::YearSection</enum>
+ </property>
+ <property name="displayFormat" >
+ <string>yyyy-MM-ddThh:mm:ss.zzz</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="selectMaxPB" >
+ <property name="enabled" >
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" >
+ <string>Select from plot</string>
+ </property>
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="title" >
+ <string>Stats</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" >
+ <widget class="QCheckBox" name="npointsStatCB" >
+ <property name="toolTip" >
+ <string>Number of points considered for the statistical analysis</string>
+ </property>
+ <property name="text" >
+ <string>#points</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QCheckBox" name="minStatCB" >
+ <property name="toolTip" >
+ <string>x and y values for the minimum of the curve in the considered range</string>
+ </property>
+ <property name="text" >
+ <string>min</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" >
+ <widget class="QCheckBox" name="stdStatCB" >
+ <property name="toolTip" >
+ <string>biased standard deviation:
+std = sqrt(mean(abs(y - y.mean())**2))</string>
+ </property>
+ <property name="text" >
+ <string>std</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QCheckBox" name="meanStatCB" >
+ <property name="toolTip" >
+ <string>arithmetic mean</string>
+ </property>
+ <property name="text" >
+ <string>mean</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QCheckBox" name="maxStatCB" >
+ <property name="toolTip" >
+ <string>x and y values for the maximum of the curve in the considered range</string>
+ </property>
+ <property name="text" >
+ <string>max</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QCheckBox" name="rmsStatCB" >
+ <property name="toolTip" >
+ <string>Root Mean Square:
+rms=sqrt(mean(y**2))</string>
+ </property>
+ <property name="text" >
+ <string>rms</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2" >
+ <widget class="QTableWidget" name="statsTW" >
+ <property name="contextMenuPolicy" >
+ <enum>Qt::ActionsContextMenu</enum>
+ </property>
+ <property name="toolTip" >
+ <string>select the curve names for which statistics will be calculated</string>
+ </property>
+ <property name="selectionBehavior" >
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2" >
+ <layout class="QHBoxLayout" name="horizontalLayout_4" >
+ <item>
+ <spacer name="horizontalSpacer_2" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="calculatePB" >
+ <property name="toolTip" >
+ <string>(re) calculate statistics for the selected curves (or for all curves if None is selected)</string>
+ </property>
+ <property name="text" >
+ <string>(re)calculate</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>minCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>minSB</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>84</x>
+ <y>45</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>132</x>
+ <y>45</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>minCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>minDTE</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>73</x>
+ <y>60</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>221</x>
+ <y>62</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>maxCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>maxSB</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>37</x>
+ <y>83</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>122</x>
+ <y>91</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>maxCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>maxDTE</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>71</x>
+ <y>84</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>242</x>
+ <y>96</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/lib/taurus/qt/qtgui/plot/ui/ui_CurveStatsDialog.py b/lib/taurus/qt/qtgui/plot/ui/ui_CurveStatsDialog.py
new file mode 100644
index 0000000..e6bf21f
--- /dev/null
+++ b/lib/taurus/qt/qtgui/plot/ui/ui_CurveStatsDialog.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file '/tmp/tmpCPhivc.ui'
+#
+# Created: Wed Jan 16 09:11:13 2013
+# by: PyQt4 UI code generator 4.4.3
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt4 import QtCore, QtGui
+
+class Ui_CurveStatsDialog(object):
+ def setupUi(self, CurveStatsDialog):
+ CurveStatsDialog.setObjectName("CurveStatsDialog")
+ CurveStatsDialog.resize(723, 382)
+ self.gridLayout_2 = QtGui.QGridLayout(CurveStatsDialog)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.groupBox = QtGui.QGroupBox(CurveStatsDialog)
+ self.groupBox.setObjectName("groupBox")
+ self.verticalLayout = QtGui.QVBoxLayout(self.groupBox)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.horizontalLayout = QtGui.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.minCB = QtGui.QCheckBox(self.groupBox)
+ self.minCB.setObjectName("minCB")
+ self.horizontalLayout.addWidget(self.minCB)
+ self.minSB = QtGui.QDoubleSpinBox(self.groupBox)
+ self.minSB.setEnabled(False)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.minSB.sizePolicy().hasHeightForWidth())
+ self.minSB.setSizePolicy(sizePolicy)
+ self.minSB.setDecimals(5)
+ self.minSB.setObjectName("minSB")
+ self.horizontalLayout.addWidget(self.minSB)
+ self.minDTE = QtGui.QDateTimeEdit(self.groupBox)
+ self.minDTE.setEnabled(False)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.minDTE.sizePolicy().hasHeightForWidth())
+ self.minDTE.setSizePolicy(sizePolicy)
+ self.minDTE.setCurrentSection(QtGui.QDateTimeEdit.YearSection)
+ self.minDTE.setObjectName("minDTE")
+ self.horizontalLayout.addWidget(self.minDTE)
+ self.selectMinPB = QtGui.QToolButton(self.groupBox)
+ self.selectMinPB.setEnabled(True)
+ self.selectMinPB.setObjectName("selectMinPB")
+ self.horizontalLayout.addWidget(self.selectMinPB)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ self.horizontalLayout_2 = QtGui.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.maxCB = QtGui.QCheckBox(self.groupBox)
+ self.maxCB.setObjectName("maxCB")
+ self.horizontalLayout_2.addWidget(self.maxCB)
+ self.maxSB = QtGui.QDoubleSpinBox(self.groupBox)
+ self.maxSB.setEnabled(False)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.maxSB.sizePolicy().hasHeightForWidth())
+ self.maxSB.setSizePolicy(sizePolicy)
+ self.maxSB.setDecimals(5)
+ self.maxSB.setObjectName("maxSB")
+ self.horizontalLayout_2.addWidget(self.maxSB)
+ self.maxDTE = QtGui.QDateTimeEdit(self.groupBox)
+ self.maxDTE.setEnabled(False)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.maxDTE.sizePolicy().hasHeightForWidth())
+ self.maxDTE.setSizePolicy(sizePolicy)
+ self.maxDTE.setCurrentSection(QtGui.QDateTimeEdit.YearSection)
+ self.maxDTE.setObjectName("maxDTE")
+ self.horizontalLayout_2.addWidget(self.maxDTE)
+ self.selectMaxPB = QtGui.QToolButton(self.groupBox)
+ self.selectMaxPB.setEnabled(True)
+ self.selectMaxPB.setObjectName("selectMaxPB")
+ self.horizontalLayout_2.addWidget(self.selectMaxPB)
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+ self.gridLayout_2.addWidget(self.groupBox, 0, 0, 1, 1)
+ self.groupBox_2 = QtGui.QGroupBox(CurveStatsDialog)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.gridLayout = QtGui.QGridLayout(self.groupBox_2)
+ self.gridLayout.setObjectName("gridLayout")
+ self.npointsStatCB = QtGui.QCheckBox(self.groupBox_2)
+ self.npointsStatCB.setChecked(True)
+ self.npointsStatCB.setObjectName("npointsStatCB")
+ self.gridLayout.addWidget(self.npointsStatCB, 0, 0, 1, 1)
+ self.minStatCB = QtGui.QCheckBox(self.groupBox_2)
+ self.minStatCB.setChecked(True)
+ self.minStatCB.setObjectName("minStatCB")
+ self.gridLayout.addWidget(self.minStatCB, 0, 1, 1, 1)
+ self.stdStatCB = QtGui.QCheckBox(self.groupBox_2)
+ self.stdStatCB.setChecked(True)
+ self.stdStatCB.setObjectName("stdStatCB")
+ self.gridLayout.addWidget(self.stdStatCB, 0, 2, 1, 1)
+ self.meanStatCB = QtGui.QCheckBox(self.groupBox_2)
+ self.meanStatCB.setChecked(True)
+ self.meanStatCB.setObjectName("meanStatCB")
+ self.gridLayout.addWidget(self.meanStatCB, 1, 0, 1, 1)
+ self.maxStatCB = QtGui.QCheckBox(self.groupBox_2)
+ self.maxStatCB.setChecked(True)
+ self.maxStatCB.setObjectName("maxStatCB")
+ self.gridLayout.addWidget(self.maxStatCB, 1, 1, 1, 1)
+ self.rmsStatCB = QtGui.QCheckBox(self.groupBox_2)
+ self.rmsStatCB.setChecked(True)
+ self.rmsStatCB.setObjectName("rmsStatCB")
+ self.gridLayout.addWidget(self.rmsStatCB, 1, 2, 1, 1)
+ self.gridLayout_2.addWidget(self.groupBox_2, 0, 1, 1, 1)
+ self.statsTW = QtGui.QTableWidget(CurveStatsDialog)
+ self.statsTW.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
+ self.statsTW.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+ self.statsTW.setObjectName("statsTW")
+ self.statsTW.setColumnCount(0)
+ self.statsTW.setRowCount(0)
+ self.gridLayout_2.addWidget(self.statsTW, 1, 0, 1, 2)
+ self.horizontalLayout_4 = QtGui.QHBoxLayout()
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
+ spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
+ self.horizontalLayout_4.addItem(spacerItem)
+ self.calculatePB = QtGui.QPushButton(CurveStatsDialog)
+ self.calculatePB.setObjectName("calculatePB")
+ self.horizontalLayout_4.addWidget(self.calculatePB)
+ self.gridLayout_2.addLayout(self.horizontalLayout_4, 2, 0, 1, 2)
+
+ self.retranslateUi(CurveStatsDialog)
+ QtCore.QObject.connect(self.minCB, QtCore.SIGNAL("toggled(bool)"), self.minSB.setEnabled)
+ QtCore.QObject.connect(self.minCB, QtCore.SIGNAL("toggled(bool)"), self.minDTE.setEnabled)
+ QtCore.QObject.connect(self.maxCB, QtCore.SIGNAL("toggled(bool)"), self.maxSB.setEnabled)
+ QtCore.QObject.connect(self.maxCB, QtCore.SIGNAL("toggled(bool)"), self.maxDTE.setEnabled)
+ QtCore.QMetaObject.connectSlotsByName(CurveStatsDialog)
+
+ def retranslateUi(self, CurveStatsDialog):
+ CurveStatsDialog.setWindowTitle(QtGui.QApplication.translate("CurveStatsDialog", "Curve Stats Dialog", None, QtGui.QApplication.UnicodeUTF8))
+ self.groupBox.setTitle(QtGui.QApplication.translate("CurveStatsDialog", "X limits", None, QtGui.QApplication.UnicodeUTF8))
+ self.minCB.setText(QtGui.QApplication.translate("CurveStatsDialog", "min", None, QtGui.QApplication.UnicodeUTF8))
+ self.minDTE.setDisplayFormat(QtGui.QApplication.translate("CurveStatsDialog", "yyyy-MM-ddThh:mm:ss.zzz", None, QtGui.QApplication.UnicodeUTF8))
+ self.selectMinPB.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "Select from plot", None, QtGui.QApplication.UnicodeUTF8))
+ self.selectMinPB.setText(QtGui.QApplication.translate("CurveStatsDialog", "...", None, QtGui.QApplication.UnicodeUTF8))
+ self.maxCB.setText(QtGui.QApplication.translate("CurveStatsDialog", "max", None, QtGui.QApplication.UnicodeUTF8))
+ self.maxDTE.setDisplayFormat(QtGui.QApplication.translate("CurveStatsDialog", "yyyy-MM-ddThh:mm:ss.zzz", None, QtGui.QApplication.UnicodeUTF8))
+ self.selectMaxPB.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "Select from plot", None, QtGui.QApplication.UnicodeUTF8))
+ self.selectMaxPB.setText(QtGui.QApplication.translate("CurveStatsDialog", "...", None, QtGui.QApplication.UnicodeUTF8))
+ self.groupBox_2.setTitle(QtGui.QApplication.translate("CurveStatsDialog", "Stats", None, QtGui.QApplication.UnicodeUTF8))
+ self.npointsStatCB.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "Number of points considered for the statistical analysis", None, QtGui.QApplication.UnicodeUTF8))
+ self.npointsStatCB.setText(QtGui.QApplication.translate("CurveStatsDialog", "#points", None, QtGui.QApplication.UnicodeUTF8))
+ self.minStatCB.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "x and y values for the minimum of the curve in the considered range", None, QtGui.QApplication.UnicodeUTF8))
+ self.minStatCB.setText(QtGui.QApplication.translate("CurveStatsDialog", "min", None, QtGui.QApplication.UnicodeUTF8))
+ self.stdStatCB.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "biased standard deviation:\n"
+"std = sqrt(mean(abs(y - y.mean())**2))", None, QtGui.QApplication.UnicodeUTF8))
+ self.stdStatCB.setText(QtGui.QApplication.translate("CurveStatsDialog", "std", None, QtGui.QApplication.UnicodeUTF8))
+ self.meanStatCB.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "arithmetic mean", None, QtGui.QApplication.UnicodeUTF8))
+ self.meanStatCB.setText(QtGui.QApplication.translate("CurveStatsDialog", "mean", None, QtGui.QApplication.UnicodeUTF8))
+ self.maxStatCB.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "x and y values for the maximum of the curve in the considered range", None, QtGui.QApplication.UnicodeUTF8))
+ self.maxStatCB.setText(QtGui.QApplication.translate("CurveStatsDialog", "max", None, QtGui.QApplication.UnicodeUTF8))
+ self.rmsStatCB.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "Root Mean Square:\n"
+"rms=sqrt(mean(y**2))", None, QtGui.QApplication.UnicodeUTF8))
+ self.rmsStatCB.setText(QtGui.QApplication.translate("CurveStatsDialog", "rms", None, QtGui.QApplication.UnicodeUTF8))
+ self.statsTW.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "select the curve names for which statistics will be calculated", None, QtGui.QApplication.UnicodeUTF8))
+ self.calculatePB.setToolTip(QtGui.QApplication.translate("CurveStatsDialog", "(re) calculate statistics for the selected curves (or for all curves if None is selected)", None, QtGui.QApplication.UnicodeUTF8))
+ self.calculatePB.setText(QtGui.QApplication.translate("CurveStatsDialog", "(re)calculate", None, QtGui.QApplication.UnicodeUTF8))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtGui.QApplication(sys.argv)
+ CurveStatsDialog = QtGui.QDialog()
+ ui = Ui_CurveStatsDialog()
+ ui.setupUi(CurveStatsDialog)
+ CurveStatsDialog.show()
+ sys.exit(app.exec_())
+
diff --git a/lib/taurus/qt/qtgui/resource/large/TaurusSplash.png b/lib/taurus/qt/qtgui/resource/large/TaurusSplash.png
new file mode 100644
index 0000000..e39062b
Binary files /dev/null and b/lib/taurus/qt/qtgui/resource/large/TaurusSplash.png differ
diff --git a/lib/taurus/qt/qtgui/resource/large/snapshot/TaurusDevicePanel.png b/lib/taurus/qt/qtgui/resource/large/snapshot/TaurusDevicePanel.png
new file mode 100644
index 0000000..b969168
Binary files /dev/null and b/lib/taurus/qt/qtgui/resource/large/snapshot/TaurusDevicePanel.png differ
diff --git a/lib/taurus/qt/qtgui/resource/large/snapshot/TaurusJDrawSynopticsView.png b/lib/taurus/qt/qtgui/resource/large/snapshot/TaurusJDrawSynopticsView.png
new file mode 100644
index 0000000..97119d5
Binary files /dev/null and b/lib/taurus/qt/qtgui/resource/large/snapshot/TaurusJDrawSynopticsView.png differ
diff --git a/lib/taurus/qt/qtgui/resource/taurus_resource_utils.py b/lib/taurus/qt/qtgui/resource/taurus_resource_utils.py
index 5976544..f040765 100644
--- a/lib/taurus/qt/qtgui/resource/taurus_resource_utils.py
+++ b/lib/taurus/qt/qtgui/resource/taurus_resource_utils.py
@@ -95,19 +95,23 @@ def __init_theme_members():
res_dir = os.path.dirname(os.path.abspath(__file__))
theme_icon_dir = os.path.join(res_dir, "tango-icons")
members = {}
- for d in os.listdir(theme_icon_dir):
- abs_dir = os.path.join(theme_icon_dir, d)
- if d[0] == "." or not os.path.isdir(abs_dir):
- continue
- elems = []
- for elem in os.listdir(abs_dir):
- abs_elem = os.path.join(abs_dir, elem)
- idx = elem.rfind(".svg")
- if elem[0] == "." or idx < 0 or not os.path.isfile(abs_elem):
+ if os.path.isdir(theme_icon_dir):
+ for d in os.listdir(theme_icon_dir):
+ abs_dir = os.path.join(theme_icon_dir, d)
+ if d[0] == "." or not os.path.isdir(abs_dir):
continue
- elems.append(elem[:idx])
- members[d] = elems
-
+ elems = []
+ for elem in os.listdir(abs_dir):
+ abs_elem = os.path.join(abs_dir, elem)
+ idx = elem.rfind(".svg")
+ if elem[0] == "." or idx < 0 or not os.path.isfile(abs_elem):
+ continue
+ elems.append(elem[:idx])
+ members[d] = elems
+ else:
+ __LOGGER.warning('Cannot find dir "%s". Tango-Icon fallback for themed icons will not work',theme_icon_dir)
+ __LOGGER.debug('Please report this error with the following info:\n Theme_Capacity:%s , Qt=%s, PyQt=%s',__THEME_CAPACITY, Qt.QT_VERSION_STR, Qt.PYQT_VERSION_STR)
+
__THEME_MEMBERS = members
__INITIALIZED_THEME = True
@@ -168,13 +172,17 @@ def getThemePixmap(key, size=None):
:return: (PyQt4.QtGui.QPixmap) a PyQt4.QtGui.QPixmap for the given key and size"""
global __THEME_CAPACITY
- if __THEME_CAPACITY and Qt.QIcon.hasThemeIcon(key):
- size = size or 48
- return Qt.QIcon.fromTheme(key).pixmap(size, size)
-
+ global __LOGGER
+ if __THEME_CAPACITY:
+ if Qt.QIcon.hasThemeIcon(key):
+ size = size or 48
+ return Qt.QIcon.fromTheme(key).pixmap(size, size)
+ else:
+ __LOGGER.debug('Theme pixmap "%s" not supported. Trying to provide a fallback...',key)
for member, items in getThemeMembers().items():
if not key in items: continue
return getPixmap(":/%s/%s.svg" % (member, key), size)
+ __LOGGER.debug('Theme pixmap "%s" not supported.', key)
return Qt.QPixmap()
def getThemeIcon(key):
@@ -192,12 +200,16 @@ def getThemeIcon(key):
:return: (PyQt4.QtGui.QIcon) a PyQt4.QtGui.QIcon for the given key"""
global __THEME_CAPACITY
- if __THEME_CAPACITY and Qt.QIcon.hasThemeIcon(key):
- return Qt.QIcon.fromTheme(key)
-
+ global __LOGGER
+ if __THEME_CAPACITY:
+ if Qt.QIcon.hasThemeIcon(key):
+ return Qt.QIcon.fromTheme(key)
+ else:
+ __LOGGER.debug('Theme icon "%s" not supported. Trying to provide a fallback...',key)
for member, items in getThemeMembers().items():
if not key in items: continue
return Qt.QIcon(":/%s/%s.svg" % (member, key))
+ __LOGGER.debug('Theme icon "%s" not supported.', key)
return Qt.QIcon()
def getStandardIcon(key, widget=None):
diff --git a/lib/taurus/qt/qtgui/shell/spockshell.py b/lib/taurus/qt/qtgui/shell/spockshell.py
deleted file mode 100644
index bd851f4..0000000
--- a/lib/taurus/qt/qtgui/shell/spockshell.py
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/env python
-
-#############################################################################
-##
-## This file is part of Taurus, a Tango User Interface Library
-##
-## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
-##
-## 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/>.
-##
-#############################################################################
-
-"""This module contains a spock shell widget. The widget starts an ipython
-interpreter in a subprocess with the spock profile"""
-
-__all__ = ["SpockShellWidget", "SpockShell"]
-
-__docformat__ = 'restructuredtext'
-
-from taurusshell import *
-
-#-------------------------------------------------------------------------------
-# Spock shell. A pure tango shell based on IPython
-#-------------------------------------------------------------------------------
-
-class SpockShellWidget(TaurusPythonShellWidget):
- pass
-
-
-class SpockShell(TaurusPythonShell):
- SHELL_CLASS = SpockShellWidget
-
- def _preprocess_arguments(self, args):
- if not '-p' in args.split(' '):
- args += "-p spock"
- return args
-
- def __init__(self, *args, **kwargs):
- if not kwargs.has_key('ipython'): kwargs['ipython'] = True
- arguments = kwargs.get('arguments')
- if arguments is None:
- arguments = ''
- kwargs['arguments'] = self._preprocess_arguments(arguments)
- if kwargs.has_key('designMode'): kwargs.pop('designMode')
- TaurusPythonShell.__init__(self, *args, **kwargs)
-
- @classmethod
- def getQtDesignerPluginInfo(cls):
- ret = TaurusPythonShell.getQtDesignerPluginInfo()
- ret['icon'] = ":/designer/spockshell.png"
- return ret
-
-def demo():
- #"""Tango shell"""
- from spyderlib.plugins.variableexplorer import VariableExplorer
- settings = VariableExplorer.get_settings()
- shell = SpockShell(stand_alone=settings)
- shell.resize(768,768)
- shell.show()
- return shell
-
-def main():
- import sys
- import taurus.qt.qtgui.application
- Application = taurus.qt.qtgui.application.TaurusApplication
-
- app = Application.instance()
- owns_app = app is None
-
- if owns_app:
- import taurus.core.util.argparse
- parser = taurus.core.util.argparse.get_taurus_parser()
- app = Application(sys.argv, cmd_line_parser=parser,
- app_name="Spock Shell demo", app_version="1.0",
- org_domain="Taurus", org_name="Tango community")
-
- shell = demo()
-
- if owns_app:
- sys.exit(app.exec_())
- else:
- return w
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/shell/taurusshell.py b/lib/taurus/qt/qtgui/shell/taurusshell.py
deleted file mode 100644
index e061971..0000000
--- a/lib/taurus/qt/qtgui/shell/taurusshell.py
+++ /dev/null
@@ -1,376 +0,0 @@
-#!/usr/bin/env python
-
-#############################################################################
-##
-## This file is part of Taurus, a Tango User Interface Library
-##
-## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
-##
-## 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/>.
-##
-#############################################################################
-
-"""This module contains a taurus python shell widget. The widget starts a python
-interpreter in a subprocess and gives a python/IPython shell"""
-
-__all__ = ["TaurusPythonShellWidget", "TaurusPythonShell", "TaurusShell"]
-
-__docformat__ = 'restructuredtext'
-
-import os
-
-from taurus.qt import Qt
-
-try:
- import spyderlib
- if int(spyderlib.__version__.split(".")[0]) < 2:
- raise Exception("TaurusShell needs spyderlib >= 2.0")
-except ImportError:
- raise Exception("TaurusShell needs spyderlib >= 2.0")
-
-import PyTango.ipython
-
-if PyTango.Release.version_info[:3] < (7,1,4):
- raise Exception("TaurusShell needs PyTango >= 7.1.4")
-
-from spyderlib.widgets.externalshell.pythonshell import ExtPythonShellWidget
-from spyderlib.widgets.externalshell.pythonshell import ExternalPythonShell
-from spyderlib.widgets.externalshell import startup
-from spyderlib.utils.qthelpers import create_toolbutton, translate
-from spyderlib.utils.qthelpers import create_action, add_actions
-
-from taurus.qt.qtgui.resource import getIcon, getThemeIcon
-
-# old PyTango (< 7.2) doesn't have get_ipython_profiles function
-if not hasattr(PyTango.ipython, "get_ipython_profiles"):
- def get_ipython_profiles():
- """Helper functions to find ipython profiles"""
- ret = []
- if IPython is None:
- return ret
- ipydir = IPython.iplib.get_ipython_dir()
- if os.path.isdir(ipydir):
- for i in os.listdir(ipydir):
- fullname = os.path.join(ipydir, i)
- if i.startswith("ipy_profile_") and i.endswith(".py"):
- if os.path.isfile(fullname):
- ret.append(i[len("ipy_profile_"):i.rfind(".")])
- return ret
- PyTango.ipython.get_ipython_profiles = get_ipython_profiles
-
-
-try:
- import IPython
- import IPython.iplib
-except:
- IPython = None
-
-try:
- import spocklib
- import spocklib.genutils
-except:
- spocklib = None
-
-class TaurusPythonShellWidget(ExtPythonShellWidget):
- pass
-
-
-class TaurusPythonShell(ExternalPythonShell):
- """A python shell widget"""
-
- SHELL_CLASS = TaurusPythonShellWidget
-
- def __init__(self, *args, **kwargs):
- if not kwargs.has_key('ipython'):
- kwargs['ipython'] = IPython is not None
- ExternalPythonShell.__init__(self, *args, **kwargs)
-
- self.shell.toggle_wrap_mode(True)
- self.shell.set_font(Qt.QFont("Monospace", 8))
- self.start_shell(False)
-
-
- @classmethod
- def getQtDesignerPluginInfo(cls):
- return {
- 'module' : 'taurus.qt.qtgui.shell',
- 'group' : 'Taurus Composite Widgets',
- 'icon' : ":/designer/shell.png",
- 'container' : False
- }
-
-
-class TaurusShellWidget(ExtPythonShellWidget):
- pass
-
-
-class TaurusShell(ExternalPythonShell):
- """A super duper python shell widget"""
-
- SHELL_CLASS = TaurusShellWidget
-
- def __init__(self, *args, **kwargs):
- if not kwargs.has_key('ipython'):
- kwargs['ipython'] = True
- if not kwargs.has_key('stand_alone'):
- import spyderlib.plugins.variableexplorer
- VE = spyderlib.plugins.variableexplorer.VariableExplorer
- kwargs['stand_alone'] = VE.get_settings()
- self.profile_button = None
- self.profile_menu_actions = None
- self.sardana_menu = None
-
- ExternalPythonShell.__init__(self, *args, **kwargs)
-
- self.shell.toggle_wrap_mode(True)
- self.shell.set_font(Qt.QFont("Monospace", 8))
- self.start_shell(False)
-
- def get_toolbar_buttons(self):
- buttons = ExternalPythonShell.get_toolbar_buttons(self)
- if self.profile_button is None:
- profile = self.get_profile_menu()
- self.profile_button = create_toolbutton(self,
- text=translate('ExternalShellBase', "Profile"),
- icon=getIcon(':/categories/user-employee.svg'),
- tip='Select shell profile',
- text_beside_icon=True)
- self.profile_button.setPopupMode(Qt.QToolButton.InstantPopup)
- menu = Qt.QMenu(self)
- add_actions(menu, profile)
- self.profile_button.setMenu(menu)
- buttons.insert(0, self.profile_button)
- return buttons
-
- def get_profile_menu(self):
- if self.profile_menu_actions is None:
- self.profile_menu_actions = []
-
- # ------------------------------------------------------------------
- # IPython items
- # ------------------------------------------------------------------
- ipy_icon = getIcon(':/ipython.png')
- pyconsole_icon = getIcon(':/python-console.png')
- ipy_action = create_action(self, self.tr("IPython"),
- icon=ipy_icon,
- tip='Pure IPython shell',
- data="/IPython",
- triggered=self.on_select_ipython_profile)
-
- if spocklib is None:
- ipy_profiles = PyTango.ipython.get_ipython_profiles()
- else:
- ipy_profiles = spocklib.genutils.get_non_spock_profiles()
- ipy_profiles = sorted(ipy_profiles)
-
- # make sure the ITango profile is not there
- try: ipy_profiles.remove("spock")
- except: pass
-
- ipy_actions = []
- for profile in ipy_profiles:
- action = create_action(self, self.tr(profile),
- icon=pyconsole_icon,
- tip='IPython shell (%s profile)',
- data=profile,
- triggered=self.on_select_ipython_profile)
- ipy_actions.append(action)
- ipy_actions.append(None)
- ipy_actions.append(ipy_action)
- ipy_menu = Qt.QMenu("IPython")
- ipy_menu.setIcon(ipy_icon)
- add_actions(ipy_menu, ipy_actions)
-
- self.profile_menu_actions.append(ipy_menu)
-
- # ------------------------------------------------------------------
- # ITango items
- # ------------------------------------------------------------------
- tango_icon = getIcon(':/tango.png')
- tango_action = create_action(self, self.tr("Tango"),
- icon=tango_icon,
- tip='Tango shell',
- data="/Tango",
- triggered=self.on_select_tango_profile)
-
- self.profile_menu_actions.append(tango_action)
-
- # ------------------------------------------------------------------
- # Sardana items
- # ------------------------------------------------------------------
- if spocklib is not None and self.has_sardana():
- sardana_icon = getIcon(':/categories/user-digital-person.svg')
- sardana_profiles = spocklib.genutils.get_spock_profiles()
- sardana_profiles = sorted(sardana_profiles)
- sardana_actions = []
- for profile in sardana_profiles:
- sardana_action = create_action(self, self.tr(profile),
- icon=sardana_icon,
- tip='Sardana shell (%s profile)' % profile,
- data=profile,
- triggered=self.on_select_sardana_profile)
- sardana_actions.append(sardana_action)
- new_sardana_action = create_action(self, self.tr("New..."),
- icon=getIcon(':/categories/user-other-new.svg'),
- tip='Create a new sardana profile',
- data="_New",
- triggered=self.on_select_new_sardana_profile)
- sardana_actions.append(None) # separator
- sardana_actions.append(new_sardana_action)
-
- self.sardana_menu = Qt.QMenu("Sardana")
- self.sardana_menu.setIcon(sardana_icon)
- add_actions(self.sardana_menu, sardana_actions)
- self.profile_menu_actions.append(self.sardana_menu)
- return self.profile_menu_actions
-
- def restart_shell(self, ask_for_arguments=False):
- p = self.process
- if p is not None and p.state() == Qt.QProcess.Running:
- p.terminate()
- p.waitForFinished(3000)
- self.start_shell(ask_for_arguments=ask_for_arguments)
-
- def _get_default_ipython_startup_file(self):
- startup_file = startup.__file__
- if 'library.zip' in startup_file:
- # py2exe distribution
- from spyderlib.config import DATA_DEV_PATH
- startup_file = os.path.join(DATA_DEV_PATH, "widgets", "externalshell",
- "startup.py")
- return startup_file
-
- def on_select_ipython_profile(self):
- if not self.new_profile_dialog():
- return
- action_name = self.sender().data().toString()
- if action_name == "/IPython":
- self.arguments = ""
- else:
- self.arguments = "-p %s" % action_name
- self.fname = self._get_default_ipython_startup_file()
- self.restart_shell()
-
- def _get_default_tango_startup_file(self):
- return self._get_default_ipython_startup_file()
-
- def on_select_tango_profile(self):
- if not self.new_profile_dialog():
- return
- self.arguments = "-p spock"
- self.fname = self._get_default_tango_startup_file()
- self.restart_shell()
-
- def has_sardana(self):
- return os.path.isdir(self._get_sardana_dir())
-
- def _get_sardana_dir(self):
- d = os.path.dirname(os.path.abspath(__file__))
- return os.path.join(d, "..", "extra_sardana")
-
- def _get_default_sardana_startup_file(self):
- return os.path.join(self._get_sardana_dir(), 'startup.py')
-
- def on_select_sardana_profile(self):
- if not self.new_profile_dialog():
- return
- action_name = self.sender().data().toString()
- self.arguments = "-p %s" % action_name
- self.fname = self._get_default_sardana_startup_file()
- self.restart_shell()
-
- def on_select_new_sardana_profile(self):
- if not self.new_profile_dialog():
- return
- self.fname = self._get_default_sardana_startup_file()
-
- # get profile from user
- if not self.get_arguments():
- self.set_running_state(False)
- if not self.arguments.startswith("-p "):
- self.arguments = "-p %s" % self.arguments
- self.restart_shell()
-
- try:
- args = self.arguments.split()
- for i, arg in enumerate(args):
- if arg == "-p":
- profile = args[i+1]
- except:
- return
-
- sardana_icon = getIcon(':/categories/user-digital-person.svg')
- sardana_action = create_action(self, self.tr(profile),
- icon=sardana_icon,
- tip='Sardana shell (%s profile)' % profile,
- data=profile,
- triggered=self.on_select_sardana_profile)
- actions = self.sardana_menu.actions()
- if len(actions)>0:
- self.sardana_menu.insertAction(actions[0], sardana_action)
- else:
- self.sardana_menu.addAction(sardana_action)
-
- def new_profile_dialog(self):
- return Qt.QMessageBox.warning(self, "Are you sure?",
- "Changing the profile implies restarting the shell.\n" \
- "All local variables will be lost!",
- buttons=Qt.QMessageBox.Yes | Qt.QMessageBox.No,
- defaultButton=Qt.QMessageBox.Yes) == Qt.QMessageBox.Yes
-
- @classmethod
- def getQtDesignerPluginInfo(cls):
- return {
- 'module' : 'taurus.qt.qtgui.shell',
- 'group' : 'Taurus Composite Widgets',
- 'icon' : ":/designer/shell.png",
- 'container' : False
- }
-
-def demo():
- """Shell"""
-
- from spyderlib.plugins.variableexplorer import VariableExplorer
- settings = VariableExplorer.get_settings()
- shell = TaurusShell()
- shell.resize(768,768)
- shell.show()
- return shell
-
-def main():
- import sys
- import taurus.qt.qtgui.application
- Application = taurus.qt.qtgui.application.TaurusApplication
-
- app = Application.instance()
- owns_app = app is None
-
- if owns_app:
- import taurus.core.util.argparse
- parser = taurus.core.util.argparse.get_taurus_parser()
- app = Application(sys.argv, cmd_line_parser=parser,
- app_name="Taurus Shell demo", app_version="1.0",
- org_domain="Taurus", org_name="Tango community")
-
- shell = demo()
-
- if owns_app:
- sys.exit(app.exec_())
- else:
- return w
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/table/__init__.py b/lib/taurus/qt/qtgui/table/__init__.py
index 71f098a..052e4b8 100644
--- a/lib/taurus/qt/qtgui/table/__init__.py
+++ b/lib/taurus/qt/qtgui/table/__init__.py
@@ -33,4 +33,5 @@ from .taurustable import *
from .taurusdbtable import *
from .taurusvaluestable import *
from .taurusdevicepropertytable import *
-from .taurusgrid import *
\ No newline at end of file
+from .taurusgrid import *
+from .qdictionary import *
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/table/qdictionary.py b/lib/taurus/qt/qtgui/table/qdictionary.py
new file mode 100644
index 0000000..c8b841a
--- /dev/null
+++ b/lib/taurus/qt/qtgui/table/qdictionary.py
@@ -0,0 +1,328 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+"""This module provides basic python dictionary/list editor widgets"""
+
+__all__ = ["QDictionaryEditor","QListEditor"]
+
+__docformat__ = 'restructuredtext'
+
+import sys
+import taurus
+import numpy
+from taurus.core.util.containers import SortedDict
+from taurus.qt import Qt
+from taurus.qt.qtgui.container import TaurusBaseContainer,TaurusWidget
+from taurus.qt.qtcore.util.properties import join,djoin
+
+###############################################################################
+# Methods borrowed from fandango modules
+
+def isString(seq):
+ if isinstance(seq,basestring): return True # It matches most python str-like classes
+ if any(s in str(type(seq)).lower() for s in ('vector','array','list',)): return False
+ if 'qstring' == str(type(seq)).lower(): return True # It matches QString
+ return False
+
+def isSequence(seq,INCLUDE_GENERATORS = True):
+ """ It excludes Strings, dictionaries but includes generators"""
+ if any(isinstance(seq,t) for t in (list,set,tuple)):
+ return True
+ if isString(seq):
+ return False
+ if hasattr(seq,'items'):
+ return False
+ if INCLUDE_GENERATORS:
+ if hasattr(seq,'__iter__'):
+ return True
+ elif hasattr(seq,'__len__'):
+ return True
+ return False
+
+def isDictionary(seq):
+ """ It includes dicts and also nested lists """
+ if isinstance(seq,dict): return True
+ if hasattr(seq,'items') or hasattr(seq,'iteritems'): return True
+ if seq and isSequence(seq) and isSequence(seq[0]):
+ if seq[0] and not isSequence(seq[0][0]): return True #First element of tuple must be hashable
+ return False
+
+def dict2array(dct):
+ """ Converts a dictionary in a table of data, lists are unnested columns """
+ data,table = {},[]
+ data['nrows'],data['ncols'] = 0,2 if isDictionary(dct) else 1
+ def expand(d,level):#,nrows=nrows,ncols=ncols):
+ #self.debug('\texpand(%s(%s),%s)'%(type(d),d,level))
+ items = d.items() if isinstance(d,SortedDict) else sorted(d.items() if hasattr(d,'items') else d)
+ for k,v in items:
+ zero = data['nrows']
+ data[(data['nrows'],level)] = k
+ if isDictionary(v):
+ data['ncols']+=1
+ expand(v,level+1)
+ else:
+ if not isSequence(v): v = [v]
+ for t in v:
+ data[(data['nrows'],level+1)] = t
+ data['nrows']+=1
+ #for i in range(zero+1,nrows): data[(i,level)] = None
+ expand(dct,0)
+ [table.append([]) for r in range(data.pop('nrows'))]
+ [table[r].append(None) for c in range(data.pop('ncols')) for r in range(len(table))]
+ for coord,value in data.items(): table[coord[0]][coord[1]] = value
+ return table
+
+def array2dict(table):
+ """ Converts a table in a dictionary of left-to-right nested date, unnested columns are lists"""
+ nrows,ncols = len(table),len(table[0])
+ r,c = 0,0
+ def expand(r,c,end):
+ print('expand(%s,%s,%s)'%(r,c,end))
+ i0,t0 = r,table[r][c]
+ if not t0: return t0
+ if c+1<ncols and (table[r][c+1] or not c):
+ d = {}
+ keys = []
+ new_end = r+1
+ for i in range(r+1,end+1):
+ t = table[i][c] if i<end else None
+ if t or i>=end:
+ keys.append((i0,t0,new_end)) #start,name,stop for each key
+ t0,i0 = t,i
+ new_end = i+1
+ for i,key,new_end in keys:
+ nd = expand(i,c+1,new_end)
+ d[key] = nd if key not in d else djoin(d.get(key),nd)
+ print('expand(%s to %s,%s): %s'%(r,end,c,d))
+ return d
+ else:
+ d = [table[i][c] for i in range(r,end)]
+ print('expand(%s to %s,%s): %s'%(r,end,c,d))
+ return d
+ data = expand(0,0,nrows)
+ return data
+
+###############################################################################
+
+class QBaseDictionaryEditor(Qt.QDialog,TaurusBaseContainer):
+ def __init__(self, parent = None, designMode = None, title = None):
+ self.data = {} #An {(x,y):value} array
+ self.title = title
+ self.dctmodel = SortedDict()
+ self.callback = None
+ self.call__init__wo_kw(Qt.QDialog, parent)
+ self.call__init__(TaurusBaseContainer, type(self).__name__, designMode=designMode)#defineStyle called from here
+
+ @classmethod
+ def main(klass, args=None,title='',modal=False,callback=None):
+ dialog = klass()
+ dialog.setModal(modal)
+ dialog.setCallback(callback)
+ dialog.setModifiableByUser(True)
+ dialog.setWindowTitle(title or self.title or klass.__name__)
+ if args: dialog.setModel(args) #[0] if isSequence(args) else args)
+ dialog.show()
+ return dialog
+
+ def defineStyle(self):
+ self.info('QBaseDictionaryEditor.defineStyle()')
+ #self.setWindowTitle('DictionaryEditor')
+ self.label = Qt.QLabel('Dictionary as a nested tree: {key1:[val1,val2],key2:[val3]')
+ #self.value = Qt.QLabel()
+ self.table = Qt.QTableWidget()#TaurusBaseTable()
+ self.table.horizontalHeader().setStretchLastSection(True)
+ #self.table.verticalHeader().setStretchLastSection(True)
+ self.baccept = Qt.QPushButton('Apply')
+ self.bcancel = Qt.QPushButton('Cancel')
+ self.baddColumn = Qt.QPushButton()
+ self.baddRow = Qt.QPushButton()
+ [(b.setFixedSize(Qt.QSize(20,20)),b.setIcon(taurus.qt.qtgui.resource.getIcon(':/designer/plus.png'))) for b in (self.baddColumn,self.baddRow)]
+ self.setLayout(Qt.QGridLayout())
+ self.layout().addWidget(self.label,0,0,1,5)
+ #self.layout().addWidget(self.value,1,0,1,3)
+ self.layout().addWidget(self.baddColumn,2,5,1,1)
+ self.layout().addWidget(self.table,2,0,5,5)
+ self.layout().addWidget(self.baddRow,7,0,1,1)
+ self.layout().addWidget(self.baccept,8,3,1,1)
+ self.layout().addWidget(self.bcancel,8,4,1,1)
+ self.connect(self.baccept,Qt.SIGNAL("clicked()"),self.save)
+ self.connect(self.bcancel,Qt.SIGNAL("clicked()"),self.close)
+ self.connect(self.baddRow,Qt.SIGNAL("clicked()"),self.addRow)
+ self.connect(self.baddColumn,Qt.SIGNAL("clicked()"),self.addColumn)
+ self.connect(self,Qt.SIGNAL("reject()"),self.close)
+
+ def addRow(self):
+ self.table.setRowCount(self.table.rowCount()+1)
+ self.table.resizeRowsToContents()
+ self.table.update()
+
+ def addColumn(self):
+ self.table.horizontalHeader().setStretchLastSection(False)
+ self.table.setColumnCount(self.table.columnCount()+1)
+ self.table.resizeColumnsToContents()
+ self.table.horizontalHeader().setStretchLastSection(True)
+ self.table.update()
+
+ def setCallback(self,callback):
+ self.callback = callback
+
+ def getCellText(self,row,column):
+ if row>=self.table.rowCount() or column>=self.table.columnCount():
+ v = None
+ else:
+ i = self.table.item(row,column)
+ if i is None: v = i
+ else: v = str(i.text()).strip()
+ self.debug('getCellText(%s,%s): %s'%(row,column,v))
+ return v
+
+ def setCellText(self,row,column,value,bold=False,italic=False):
+ i = self.table.item(row,column) or Qt.QTableWidgetItem()
+ i.setText(str(value if value is not None else ''))
+ if bold or italic:
+ f = i.font()
+ if bold: f.setBold(True)
+ if italic: f.setItalic(True)
+ i.setFont(f)
+ self.table.setItem(row,column,i)
+
+ def setModel(self,model): raise Exception('setModel(self,model)!')
+ def updateStyle(self): raise Exception('updateStyle(self)!')
+ def getValues(self): raise Exception('getValues(self)!')
+ def save(self): raise Exception('save(self)!')
+
+###############################################################################
+
+class QDictionaryEditor(QBaseDictionaryEditor):
+
+ def setModel(self,model):
+ self.info('DictionaryEditor.setModel(%s(%s))'%(type(model),model))
+ self.dctmodel = eval(model) if isString(model) else model
+ TaurusBaseContainer.setModel(self,model) #self.updateStyle() called from the property setter
+
+ def getModelClass(self):
+ return dict
+
+ def updateStyle(self):
+ #self.value.setText(str(self.dctmodel))
+ data = dict2array(self.dctmodel)
+ self.nrows,self.ncols = len(data),len(data[0])
+ self.info(data)
+ self.table.setRowCount(self.nrows)
+ self.table.setColumnCount(self.ncols)
+ for r in range(self.nrows):
+ for c in range(self.ncols):
+ self.setCellText(r,c,data[r][c],bold=(not c),italic=(c==1))
+ self.table.resizeRowsToContents()
+ self.table.resizeColumnsToContents()
+ self.update()
+
+ def getValues(self):
+ nrows,ncols = self.table.rowCount(),self.table.columnCount()
+ table = [[self.getCellText(r,c) for c in range(ncols)] for r in range(nrows)]
+ self.data = array2dict(table) #It returns a SortedDict
+ self.info('getValues(): %s'%str(self.data))
+ return self.data
+
+ def save(self):
+ self.getValues()
+ self.info('DictionaryEditor.save(): %s'%self.data)
+ if self.callback:
+ self.callback(self.data)
+ elif self.dctmodel is None:
+ self.dctmodel = self.data
+ else: #Overwriting dctmodel
+ self.dctmodel.clear()
+ self.dctmodel.update(self.data)
+ if self.callback:
+ self.callback(self.data) #A SortedDict is passed here
+ self.updateStyle()
+
+###############################################################################
+
+class QListEditor(QBaseDictionaryEditor):
+
+ def defineStyle(self):
+ QBaseDictionaryEditor.defineStyle(self)
+ self.table.setColumnCount(1)
+ self.baddColumn.hide()
+
+ def setModel(self,model):
+ self.info('DictionaryEditor.setModel(%s(%s))'%(type(model),model))
+ if isString(model):
+ try: self.dctmodel = list(eval(model)) if any(c in model for c in ('{','[','(')) else [model]
+ except: self.dctmodel = [model]
+ else: self.dctmodel = model
+ TaurusBaseContainer.setModel(self,model) #self.updateStyle() called from the property setter
+
+ def getModelClass(self):
+ return list
+
+ def updateStyle(self):
+ #self.value.setText(str(self.dctmodel))
+ data = list(self.dctmodel)
+ self.nrows,self.ncols = len(data),1
+ self.table.setRowCount(self.nrows)
+ self.table.setColumnCount(self.ncols)
+ for r in range(self.nrows): self.setCellText(r,0,data[r])
+ self.table.resizeRowsToContents()
+ self.table.horizontalHeader().setStretchLastSection(True)
+ self.update()
+
+ def getValues(self):
+ nrows = self.table.rowCount()
+ self.data = [self.getCellText(r,0)for r in range(nrows)]
+ return self.data
+
+ def save(self):
+ self.getValues()
+ self.info('DictionaryEditor.save(%s(%s)): %s'%(type(self.data),self.data,self.callback))
+ if self.callback:
+ self.callback(self.data)
+ elif self.dctmodel is None:
+ self.dctmodel = self.data
+ else: #Overwriting dctmodel
+ if isSequence(self.dctmodel):
+ while len(self.dctmodel):self.dctmodel.pop(0)
+ self.dctmodel.extend(self.data)
+ elif isDictionary(self.dctmodel):
+ self.dctmodel.clear()
+ self.dctmodel = djoin(self.dctmodel,self.data)
+ return self.data
+
+
+###############################################################################
+
+def prepare():
+ from taurus.qt.qtgui.application import TaurusApplication
+ app = TaurusApplication(app_name='DictionaryEditor')
+ args = app.get_command_line_args()
+ return app,args
+
+if __name__ == '__main__':
+ app,args = prepare()
+ dialog = QDictionaryEditor.main(args)
+ sys.exit(app.exec_())
+
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/table/qlogtable.py b/lib/taurus/qt/qtgui/table/qlogtable.py
index 74544cc..e940136 100644
--- a/lib/taurus/qt/qtgui/table/qlogtable.py
+++ b/lib/taurus/qt/qtgui/table/qlogtable.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -30,141 +30,183 @@ __all__ = ["QLoggingTableModel", "QLoggingTable", "QLoggingWidget"]
__docformat__ = 'restructuredtext'
-import operator
import logging
+import logging.handlers
import datetime
+import threading
+import socket
+
+
+import taurus
+from taurus.core.util import Logger
+from taurus.core.util.remotelogmonitor import LogRecordStreamHandler, \
+ LogRecordSocketReceiver
+from taurus.core.util.decorator.memoize import memoized
+
from taurus.qt import Qt
+from taurus.qt.qtgui.model import FilterToolBar
+from taurus.qt.qtgui.util import ActionFactory
+from taurus.qt.qtgui.resource import getThemeIcon
+
+from qtable import QBaseTableWidget
-import taurus.core.util
-from taurus.qt.qtgui.resource import getIcon, getThemeIcon
+LEVEL, TIME, MSG, NAME, ORIGIN = range(5)
+HORIZ_HEADER = 'Level', 'Time', 'Message', 'By', 'Origin'
+
+__LEVEL_BRUSH = {
+ taurus.Trace : (Qt.QBrush(Qt.Qt.lightGray), Qt.QBrush(Qt.Qt.black)),
+ taurus.Debug : (Qt.QBrush(Qt.Qt.green), Qt.QBrush(Qt.Qt.black)),
+ taurus.Info : (Qt.QBrush(Qt.Qt.blue), Qt.QBrush(Qt.Qt.white)),
+ taurus.Warning : (Qt.QBrush(Qt.QColor(255,165,0)), Qt.QBrush(Qt.Qt.black)),
+ taurus.Error : (Qt.QBrush(Qt.Qt.red), Qt.QBrush(Qt.Qt.black)),
+ taurus.Critical : (Qt.QBrush(Qt.QColor(160,32,240)), Qt.QBrush(Qt.Qt.white)),
+}
def getBrushForLevel(level):
- if level <= taurus.Trace : return Qt.QBrush(Qt.Qt.lightGray), Qt.QBrush(Qt.Qt.black)
- elif level <= taurus.Debug : return Qt.QBrush(Qt.Qt.green), Qt.QBrush(Qt.Qt.black)
- elif level <= taurus.Info : return Qt.QBrush(Qt.Qt.blue), Qt.QBrush(Qt.Qt.white)
- elif level <= taurus.Warning : return Qt.QBrush(Qt.QColor(255,165,0)), Qt.QBrush(Qt.Qt.black)
- elif level <= taurus.Error : return Qt.QBrush(Qt.Qt.red), Qt.QBrush(Qt.Qt.black)
- elif level <= taurus.Critical : return Qt.QBrush(Qt.QColor(160,32,240)), Qt.QBrush(Qt.Qt.white)
- return Qt.QBrush(Qt.Qt.FDiagPattern), Qt.QBrush(Qt.Qt.black)
+ elevel = taurus.Trace
+ if level <= taurus.Trace:
+ elevel = taurus.Trace
+ elif level <= taurus.Debug:
+ elevel = taurus.Debug
+ elif level <= taurus.Info:
+ elevel = taurus.Info
+ elif level <= taurus.Warning:
+ elevel = taurus.Warning
+ elif level <= taurus.Error:
+ elevel = taurus.Error
+ elif level <= taurus.Critical:
+ elevel = taurus.Critical
+ return __LEVEL_BRUSH[elevel]
-LEVEL, TYPE, TIME, MSG, NAME, THREAD, LOCALT = range(7)
-HORIZ_HEADER = 'Level','Type','Time','Message','Object','Thread','App time'
-class QLoggingTableModel(Qt.QAbstractTableModel, logging.Handler):
+def _origin_cmp(rec1, rec2):
+ c1 = cmp(rec1.process, rec2.process)
+ if c1 == 0:
+ c2 = cmp(rec1.thread, rec2.thread)
+ if c2 == 0:
+ return cmp(rec1.name, rec2.name)
+ return c2
+ return c1
+
+gethostname = memoized(socket.gethostname)
+
+def _get_record_origin(rec):
+ host = getattr(rec, 'hostName', "?" + gethostname() + "?")
+ procName = getattr(rec, 'processName', "?process?")
+ procID = getattr(rec, 'process', "?PID?")
+ threadName = getattr(rec, 'threadName', "?thread?")
+ threadID = getattr(rec, 'thread', "?threadID?")
+ return host, procName, procID, threadName, threadID
+
+def _get_record_trace(rec):
+ pathname = getattr(rec, 'pathname', '')
+ filename = getattr(rec, 'filename', '')
+ modulename = getattr(rec, 'module', '')
+ funcname = getattr(rec, 'funcName', '')
+ lineno = getattr(rec, 'lineno', '')
+ return pathname, filename, modulename, funcname, lineno
- DftOddRowBrush = Qt.QBrush(Qt.QColor(220,220,220)), Qt.QBrush(Qt.Qt.black)
- DftEvenRowBrush = Qt.QBrush(Qt.QColor(255,255,255)), Qt.QBrush(Qt.Qt.black)
- DftFont = Qt.QFont("Mono", 8)
- DftColSize = Qt.QSize(90, 20), Qt.QSize(50, 20), Qt.QSize(90, 20), \
- Qt.QSize(250, 20), Qt.QSize(130, 20), Qt.QSize(90, 20), \
- Qt.QSize(90, 20)
+def _get_record_origin_str(rec):
+ return "{0}.{1}.{3}".format(*_get_record_origin(rec))
+
+def _get_record_origin_tooltip(rec):
+
+ host, procName, procID, threadName, threadID = _get_record_origin(rec)
+ pathname, filename, modulename, funcname, lineno = _get_record_trace(rec)
+ timestamp = str(datetime.datetime.fromtimestamp(rec.created))
+ bgcolor, fgcolor = map(Qt.QBrush.color, getBrushForLevel(rec.levelno))
+ bgcolor = "#%02x%02x%02x" % (bgcolor.red(), bgcolor.green(), bgcolor.blue())
+ fgcolor = "#%02x%02x%02x" % (fgcolor.red(), fgcolor.green(), fgcolor.blue())
+ return """<html><font face="monospace" size="1">
+<table border="0" cellpadding="0" cellspacing="0">
+<tr><td>Level:</td><td><font color="{level_bgcolor}">{level}</font></td></tr>
+<tr><td>Time:</td><td>{timestamp}</td></tr>
+<tr><td>Message:</td><td>{message}</td></tr>
+<tr><td>By:</td><td>{name}</td></tr>
+<tr><td>Host:</td><td>{host}</td></tr>
+<tr><td>Process:</td><td>{procname}({procID})</td></tr>
+<tr><td>Thread:</td><td>{threadname}({threadID})</td></tr>
+<tr><td>From:</td><td>File pathname({filename}), line {lineno}, in {funcname}</td></tr>
+</table></font></html>
+""".format(level=rec.levelname, level_fgcolor=fgcolor, level_bgcolor=bgcolor,
+ timestamp=timestamp, message=rec.getMessage(),
+ name=rec.name, host=host, procname=procName, procID=procID,
+ threadname=threadName, threadID=threadID,
+ pathname=pathname, filename=filename, funcname=funcname,
+ lineno=lineno)
+
+class QLoggingTableModel(Qt.QAbstractTableModel, logging.Handler):
+
+ DftFont = Qt.QFont("Mono", 8)
+ DftColSize = Qt.QSize(80, 20), Qt.QSize(200, 20), \
+ Qt.QSize(300, 20), Qt.QSize(180, 20), Qt.QSize(240, 20),
+
def __init__(self, capacity=500000, freq=0.25):
super(Qt.QAbstractTableModel, self).__init__()
logging.Handler.__init__(self)
self._capacity = capacity
self._records = []
self._accumulated_records = []
- taurus.core.util.Logger.addRootLogHandler(self)
+ Logger.addRootLogHandler(self)
self.startTimer(freq*1000)
# ---------------------------------
# Qt.QAbstractTableModel overwrite
# ---------------------------------
-
+
def sort(self, column, order = Qt.Qt.AscendingOrder):
if column == LEVEL:
f = lambda a,b: cmp(a.levelno,b.levelno)
- elif column == TYPE:
- def f(a,b):
- if not operator.isMappingType(a) or not operator.isMappingType(b):
- return 0
- return cmp(a.args.get('type','taurus'), b.args.get('type','taurus'))
elif column == TIME:
f = lambda a,b: cmp(a.created,b.created)
elif column == MSG:
f = lambda a,b: cmp(a.msg,b.msg)
elif column == NAME:
f = lambda a,b: cmp(a.name,b.name)
- elif column == THREAD:
- f = lambda a,b: cmp(a.threadName,b.threadName)
- elif column == LOCALT:
- f = lambda a,b: cmp(a.relativeCreated,b.relativeCreated)
+ elif column == ORIGIN:
+ f = _origin_cmp
self._records = sorted(self._records, cmp=f,reverse= order == Qt.Qt.DescendingOrder)
- #self.reset()
-
+
def rowCount(self, index=Qt.QModelIndex()):
return len(self._records)
def columnCount(self, index=Qt.QModelIndex()):
- return 7
+ return len(HORIZ_HEADER)
+
+ def getRecord(self, index):
+ return self._records[index.row()]
def data(self, index, role=Qt.Qt.DisplayRole):
if not index.isValid() or not (0 <= index.row() < len(self._records)):
return Qt.QVariant()
- record = self._records[index.row()]
+ record = self.getRecord(index)
column = index.column()
if role == Qt.Qt.DisplayRole:
if column == LEVEL:
return Qt.QVariant(record.levelname)
- elif column == TYPE:
- try:
- t = record.args['type']
- except:
- t = 'taurus'
- return Qt.QVariant(t)
elif column == TIME:
dt = datetime.datetime.fromtimestamp(record.created)
return Qt.QVariant(str(dt))
- #return Qt.QVariant(dt.strftime("%H:%M:%S"))
+ #return Qt.QVariant(dt.strftime("%Y-%m-%d %H:%m:%S.%f"))
elif column == MSG:
return Qt.QVariant(record.getMessage())
elif column == NAME:
return Qt.QVariant(record.name)
- elif column == THREAD:
- return Qt.QVariant(record.threadName)
- elif column == LOCALT:
- dt = datetime.datetime.fromtimestamp(record.relativeCreated)
- return Qt.QVariant(dt.strftime("%H:%M:%S"))
+ elif column == ORIGIN:
+ return Qt.QVariant(_get_record_origin_str(record))
elif role == Qt.Qt.TextAlignmentRole:
if column in (LEVEL, MSG):
return Qt.QVariant(Qt.Qt.AlignLeft|Qt.Qt.AlignVCenter)
return Qt.QVariant(Qt.Qt.AlignRight|Qt.Qt.AlignVCenter)
elif role == Qt.Qt.BackgroundRole:
if column == LEVEL:
- bg = getBrushForLevel(record.levelno)[0]
- else:
- if index.row() % 2:
- bg = self.DftOddRowBrush[0]
- else:
- bg = self.DftEvenRowBrush[0]
- return Qt.QVariant(bg)
+ return Qt.QVariant(getBrushForLevel(record.levelno)[0])
elif role == Qt.Qt.ForegroundRole:
if column == LEVEL:
- fg = getBrushForLevel(record.levelno)[1]
- else:
- if index.row() % 2:
- fg = self.DftOddRowBrush[1]
- else:
- fg = self.DftEvenRowBrush[1]
- return Qt.QVariant(fg)
+ return Qt.QVariant(getBrushForLevel(record.levelno)[1])
elif role == Qt.Qt.ToolTipRole:
- if column == LEVEL:
- return Qt.QVariant(record.levelname)
- elif column == TYPE:
- return Qt.QVariant("log type")
- elif column == TIME:
- dt = datetime.datetime.fromtimestamp(record.created)
- return Qt.QVariant(str(dt))
- elif column == MSG:
- return Qt.QVariant("log message")
- elif column == NAME:
- return Qt.QVariant("object who recorded the log")
- elif column == THREAD:
- return Qt.QVariant("%s [%s]" % (record.processName, record.threadName))
- elif column == LOCALT:
- dt = datetime.datetime.fromtimestamp(record.relativeCreated)
- return Qt.QVariant(str(dt))
+ return Qt.QVariant(_get_record_origin_tooltip(record))
elif role == Qt.Qt.SizeHintRole:
return self._getSizeHint(column)
#elif role == Qt.Qt.StatusTipRole:
@@ -191,45 +233,42 @@ class QLoggingTableModel(Qt.QAbstractTableModel, logging.Handler):
elif role == Qt.Qt.ToolTipRole:
if section == LEVEL:
return Qt.QVariant("log level")
- elif section == TYPE:
- return Qt.QVariant("log type")
elif section == TIME:
return Qt.QVariant("log time stamp")
elif section == MSG:
return Qt.QVariant("log message")
elif section == NAME:
return Qt.QVariant("object who recorded the log")
- elif section == THREAD:
- return Qt.QVariant("the thread where the log was executed from")
- elif section == LOCALT:
- return Qt.QVariant("application relative log time stamp")
+ elif section == ORIGIN:
+ return Qt.QVariant("the host, process and thread where the "
+ "log was executed from")
if role != Qt.Qt.DisplayRole:
return Qt.QVariant()
if orientation == Qt.Qt.Horizontal:
return Qt.QVariant(HORIZ_HEADER[section])
return Qt.QVariant(int(section+1))
-
+
def insertRows(self, position, rows=1, index=Qt.QModelIndex()):
self.beginInsertRows(Qt.QModelIndex(), position, position+rows-1)
self.endInsertRows()
-
+
def removeRows(self, position, rows=1, index=Qt.QModelIndex()):
self.beginRemoveRows(Qt.QModelIndex(), position, position+rows-1)
self.endRemoveRows()
-
+
#def setData(self, index, value, role=Qt.Qt.DisplayRole):
# pass
-
+
#def flags(self, index)
# pass
-
+
#def insertColumns(self):
# pass
-
+
#def removeColumns(self):
# pass
-
+
# --------------------------
# logging.Handler overwrite
# --------------------------
@@ -249,10 +288,10 @@ class QLoggingTableModel(Qt.QAbstractTableModel, logging.Handler):
start = len(self._records) - self._capacity
self._records = self._records[start:]
self.removeRows(0, start)
-
+
def emit(self, record):
self._accumulated_records.append(record)
-
+
def flush(self):
pass
@@ -262,128 +301,281 @@ class QLoggingTableModel(Qt.QAbstractTableModel, logging.Handler):
logging.Handler.close(self)
-class QLoggingTable(Qt.QTableView):
-
- DftScrollLock = False
+class _LogRecordStreamHandler(LogRecordStreamHandler):
+
+ def handleLogRecord(self, record):
+ self.server.data.get('model').emit(record)
+
+
+class QRemoteLoggingTableModel(QLoggingTableModel):
+
+ def connect_logging(self, host='localhost',
+ port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
+ handler=_LogRecordStreamHandler):
+ self.log_receiver = LogRecordSocketReceiver(host=host, port=port,
+ handler=handler, model=self)
+ self.log_thread = threading.Thread(target=self.log_receiver.serve_until_stopped)
+ self.log_thread.daemon = False
+ self.log_thread.start()
+ def disconnect_logging(self):
+ if not hasattr(self, 'log_receiver') or self.log_receiver is None:
+ return
+ self.log_receiver.stop()
+ self.log_thread.join()
+ del self.log_receiver
+
+
+class QLoggingTable(Qt.QTableView):
"""A Qt table that displays the taurus logging messages"""
- def __init__(self, parent=None, model=None, designMode=False):
- super(QLoggingTable, self).__init__(parent)
- self.setShowGrid(False)
-
- self.resetScrollLock()
- model = model or QLoggingTableModel()
- self.setModel(model)
- hh = self.horizontalHeader()
- hh.setResizeMode(MSG, Qt.QHeaderView.Stretch)
- self.setSortingEnabled(True)
- self.sortByColumn(TIME, Qt.Qt.AscendingOrder)
- if designMode:
- taurus.disableLogOutput()
- for i in xrange(10): taurus.info("Hello world %04d" % i)
-
+
+ scrollLock = False
+
def rowsInserted(self, index, start, end):
- """Overwrite of slot rows inserted to do proper resize and scroll to
- bottom if desired
- """
+ """Overwrite of slot rows inserted to do proper resize and scroll to
+ bottom if desired"""
+ Qt.QTableView.rowsInserted(self, index, start, end)
for i in xrange(start,end+1):
self.resizeRowToContents(i)
if start == 0:
self.resizeColumnsToContents()
- if not self._scrollLock:
+ if not self.scrollLock:
self.scrollToBottom()
def setScrollLock(self, scrollLock):
"""Sets the state for scrollLock"""
- self._scrollLock = scrollLock
-
+ self.scrollLock = scrollLock
+
def getScrollLock(self):
"""Returns wheater or not the scrollLock is active"""
- return self._scrollLock
+ return self.scrollLock
def resetScrollLock(self):
- self.setScrollLock(QLoggingTable.DftScrollLock)
+ self.setScrollLock(QLoggingTable.ScrollLock)
- @classmethod
- def getQtDesignerPluginInfo(cls):
- return {
- 'module' : 'taurus.qt.qtgui.table',
- 'group' : 'Taurus Views',
- 'icon' : ':/designer/table.png',
- 'container' : False }
+class LoggingToolBar(FilterToolBar):
+
+ def __init__(self, view=None, parent=None, designMode=False):
+ FilterToolBar.__init__(self, view=view, parent=parent,
+ designMode=designMode)
+ self.getFilterLineEdit().setToolTip("Quick filter by log name")
- #: Tells wheater the table should scroll automatically to the end each
- #: time a record is added or not
- autoScroll = Qt.pyqtProperty("bool", getScrollLock, setScrollLock, resetScrollLock)
+ self._logLevelComboBox = logLevelComboBox = Qt.QComboBox()
+ levels = "Trace", "Debug", "Info", "Warning", "Error", "Critical"
+ for level in levels:
+ logLevelComboBox.addItem(level, Qt.QVariant(getattr(taurus, level)))
+ logLevelComboBox.setCurrentIndex(0)
+ Qt.QObject.connect(logLevelComboBox,
+ Qt.SIGNAL("currentIndexChanged(int)"),
+ self.onLogLevelChanged)
+ logLevelComboBox.setToolTip("Filter by log level")
+
+ self._filterLevelAction = self.addWidget(logLevelComboBox)
+ self.addSeparator()
+
+ af = ActionFactory()
+ self._scrollLockAction = af.createAction(self, "Refresh",
+ icon=getThemeIcon("system-lock-screen"),
+ tip="Scroll lock",
+ toggled=self.onToggleScrollLock)
+
+ self.addAction(self._scrollLockAction)
+ def onToggleScrollLock(self, yesno):
+ self.emit(Qt.SIGNAL("scrollLockToggled(bool)"), yesno)
-class QLoggingWidget(Qt.QWidget):
+ def onLogLevelChanged(self, index):
+ self.onFilterChanged()
- def __init__(self, parent=None, model=None, designMode=False):
- super(QLoggingWidget, self).__init__(parent)
- self._model = model or QLoggingTableModel()
- self.init(designMode)
-
- def init(self, designMode):
- l = Qt.QGridLayout()
- l.setContentsMargins(0,0,0,0)
- l.setVerticalSpacing(2)
- self.setLayout(l)
-
- table = self._logtable = QLoggingTable(model = self._model, designMode=designMode)
- tb = self._toolbar = Qt.QToolBar("Taurus logger toolbar")
- tb.setFloatable(False)
+ def getLogLevelComboBox(self):
+ return self._logLevelComboBox
- self._scrollLockButton = Qt.QPushButton(getIcon(":/emblems/lock.svg"),"")
- self._scrollLockButton.setCheckable(True)
- self._scrollLockButton.setChecked(table.getScrollLock())
- self._scrollLockButton.setToolTip('Scroll lock')
- self._scrollLockButton.setFlat(True)
- Qt.QObject.connect(self._scrollLockButton, Qt.SIGNAL("toggled(bool)"), table.setScrollLock)
-
- self._updateButton = Qt.QPushButton("Update")
- self._updateButton.setCheckable(True)
- self._levelFilterComboBox = Qt.QComboBox()
- self._levelFilterComboBox.addItems([">=Trace",">=Debug",">=Info",">=Warning",">=Error",">=Critical"])
- tb.addWidget(self._scrollLockButton)
- tb.addWidget(self._updateButton)
- tb.addWidget(self._levelFilterComboBox)
- l.addWidget(tb, 0, 0)
- l.addWidget(table, 1, 0)
- l.setColumnStretch(0,1)
- l.setRowStretch(1,1)
+ def getLogLevel(self):
+ combo = self.getLogLevelComboBox()
+ return Qt.from_qvariant(combo.itemData(combo.currentIndex()))
+
+ def setLogLevel(self, level):
+ combo = self.getLogLevelComboBox()
+ for i in range(combo.count()):
+ l = Qt.from_qvariant(combo.itemData(i))
+ if l == level:
+ combo.setCurrentIndex(i)
+
+
+class QLoggingFilterProxyModel(Qt.QSortFilterProxyModel):
+ """A filter by log record object name"""
+
+ def __init__(self, parent=None):
+ Qt.QSortFilterProxyModel.__init__(self, parent)
+ self._logLevel = taurus.Trace
+
+ # filter configuration
+ self.setFilterCaseSensitivity(Qt.Qt.CaseInsensitive)
+ self.setFilterKeyColumn(0)
+ self.setFilterRole(Qt.Qt.DisplayRole)
+
+ # sort configuration
+ #self.setSortCaseSensitivity(Qt.Qt.CaseInsensitive)
+ #self.setSortRole(Qt.Qt.DisplayRole)
+
+ # general configuration
+
+ def setFilterLogLevel(self, level):
+ self._logLevel = level
+
+ def __getattr__(self, name):
+ return getattr(self.sourceModel(), name)
+
+ def filterAcceptsRow(self, sourceRow, sourceParent):
+ sourceModel = self.sourceModel()
+ idx = sourceModel.index(sourceRow, NAME, sourceParent)
+ record = self.getRecord(idx)
+ if record.levelno < self._logLevel:
+ return False
+ name = str(sourceModel.data(idx))
+ regexp = self.filterRegExp()
+ if regexp.indexIn(name) != -1:
+ return True
+ return False
+
+_W = "Warning: Switching log perspective will erase previous log messages " \
+ "from current perspective!"
+
+class QLoggingWidget(QBaseTableWidget):
+
+ KnownPerspectives = {
+ 'Standard' : {
+ "label" : "Local",
+ "icon" : "computer",
+ "tooltip" : "Local logging.\n" + _W,
+ "model" : [QLoggingFilterProxyModel, QLoggingTableModel,],
+ },
+ 'Remote' : {
+ "label" : "Remote",
+ "icon" : "network-server",
+ "tooltip" : "Monitor remote logs.\n" + _W,
+ "model" : [QLoggingFilterProxyModel, QRemoteLoggingTableModel,],
+ },
+ }
+
+ DftPerspective = 'Standard'
+
+ def __init__(self, parent=None, designMode=False,
+ with_filter_widget=LoggingToolBar,
+ with_selection_widget=True, with_refresh_widget=True,
+ perspective=None, proxy=None):
+ QBaseTableWidget.__init__(self, parent=parent, designMode=designMode,
+ with_filter_widget=with_filter_widget,
+ with_selection_widget=False, with_refresh_widget=False,
+ perspective=perspective, proxy=proxy)
+
+ def createViewWidget(self, klass=None):
+ if klass is None:
+ klass = QLoggingTable
+ view = QBaseTableWidget.createViewWidget(self, klass=klass)
+ hh = view.horizontalHeader()
+ hh.setResizeMode(MSG, Qt.QHeaderView.Stretch)
+ view.setShowGrid(False)
+ view.sortByColumn(TIME, Qt.Qt.AscendingOrder)
+ return view
+
+ def createToolArea(self):
+ tb = QBaseTableWidget.createToolArea(self)
+ filterBar = self.getFilterBar()
+ Qt.QObject.connect(filterBar, Qt.SIGNAL("scrollLockToggled(bool)"),
+ self.onScrollLockToggled)
+ return tb
+
+ def onScrollLockToggled(self, yesno):
+ self.viewWidget().setScrollLock(yesno)
+
+ def onFilterChanged(self, filter):
+ if not self.usesProxyQModel():
+ return
+ proxy_model = self.getQModel()
+ level = self.getFilterBar().getLogLevel()
+ proxy_model.setFilterLogLevel(level)
+ return QBaseTableWidget.onFilterChanged(self, filter)
+
+ def onSwitchPerspective(self, perspective):
+ self.stop_logging()
+ if perspective == "Remote":
+ if hasattr(self, 'hostName') and hasattr(self, 'port'):
+ host, port = self.hostName, self.port
+ else:
+ isValid = False
+ dft = "%s:%d" % (socket.gethostname(), logging.handlers.DEFAULT_TCP_LOGGING_PORT)
+ while not isValid:
+ txt, res = Qt.QInputDialog.getText(self,
+ "Please input remote logging host and port",
+ "Location (<host>:<port>):", Qt.QLineEdit.Normal, dft)
+ if not res:
+ return
+ try:
+ host, port = str(txt).split(":",1)
+ port = int(port)
+ isValid = True
+ except:
+ Qt.QMessageBox.information(self, "Invalid name",
+ "Please type a valid <host>:<port>")
+ ret = QBaseTableWidget.onSwitchPerspective(self, perspective)
+ qmodel = self.getQModel()
+ qmodel.connect_logging(host=host, port=port)
+ else:
+ ret = QBaseTableWidget.onSwitchPerspective(self, perspective)
+ return ret
+
+ def destroy(self, destroyWindow=True, destroySubWindows=True):
+ self.stop_logging()
+ return QBaseTableWidget.destroy(self, destroyWindow, destroySubWindows)
+
+ def stop_logging(self):
+ model = self.getBaseQModel()
+ if hasattr(model, 'disconnect_logging'):
+ model.disconnect_logging()
+
+
@classmethod
def getQtDesignerPluginInfo(cls):
- return {
+ return {
'module' : 'taurus.qt.qtgui.table',
'group' : 'Taurus Views',
'icon' : ':/designer/table.png',
'container' : False }
+
def fill_log():
import time
import random
-
+
for i in xrange(10):
taurus.info("Hello world %04d" % i)
-
+
+ loggers = ["Object%02d" % (i+1) for i in range(10)]
i = 0
while True:
time.sleep(random.random())
+ logger = logging.getLogger(random.choice(loggers))
level = random.randint(taurus.Trace, taurus.Critical)
- taurus.log(level, "EXTRA %04d" % i)
+ logger.log(level, "log message %04d" % i)
i +=1
-
+
def main():
- import sys
-
- app = Qt.QApplication(sys.argv)
-
- w = QLoggingWidget()
+ import taurus.qt.qtgui.application
+ Application = taurus.qt.qtgui.application.TaurusApplication
+
+ app = Application.instance()
+ owns_app = app is None
+
+ if owns_app:
+ app = Application(app_name="Logging demo", app_version="1.0",
+ org_domain="Taurus", org_name="Taurus community")
+
taurus.setLogLevel(taurus.Trace)
taurus.disableLogOutput()
+ w = QLoggingWidget()
taurus.trace("trace message")
taurus.debug("debug message")
@@ -391,15 +583,10 @@ def main():
taurus.warning("Warning message")
taurus.error("error message")
taurus.critical("critical message")
- w.setMinimumSize(1024,400)
- w.setVisible(True)
-
- import threading
- t = threading.Thread(name="Filler", target=fill_log)
- t.daemon = True
- t.start()
-
+ w.setMinimumSize(1200,600)
+ w.show()
app.exec_()
+ w.stop_logging()
if __name__ == '__main__':
main()
diff --git a/lib/taurus/qt/qtgui/table/qtable.py b/lib/taurus/qt/qtgui/table/qtable.py
index 4f5d6a7..cb59607 100644
--- a/lib/taurus/qt/qtgui/table/qtable.py
+++ b/lib/taurus/qt/qtgui/table/qtable.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -37,11 +37,13 @@ class QBaseTableWidget(QBaseModelWidget):
def tableView(self):
return self.viewWidget()
-
- def createViewWidget(self):
- table = Qt.QTableView(self)
+
+ def createViewWidget(self, klass=None):
+ if klass is None:
+ klass = Qt.QTableView
+ table = klass()
table.setSortingEnabled(True)
table.setAlternatingRowColors(True)
table.setSelectionBehavior(Qt.QAbstractItemView.SelectRows)
table.setSelectionMode(Qt.QAbstractItemView.ExtendedSelection)
- return table
\ No newline at end of file
+ return table
diff --git a/lib/taurus/qt/qtgui/table/taurusdevicepropertytable.py b/lib/taurus/qt/qtgui/table/taurusdevicepropertytable.py
index ed81905..ff71316 100644
--- a/lib/taurus/qt/qtgui/table/taurusdevicepropertytable.py
+++ b/lib/taurus/qt/qtgui/table/taurusdevicepropertytable.py
@@ -30,11 +30,9 @@ taurusdevicepropertytable.py:
__all__ = ["TaurusPropTable"]
from taurus.qt import Qt, QtCore, QtGui
-from PyQt4 import Qwt5
from taurus.qt.qtgui.base import TaurusBaseWidget
import taurus.core
import PyTango
-import traceback
class TaurusPropTable(QtGui.QTableWidget, TaurusBaseWidget):
'''
@@ -125,6 +123,7 @@ class TaurusPropTable(QtGui.QTableWidget, TaurusBaseWidget):
self.updateStyle()
self.dev_name = dev_name
+ self.setWindowTitle('%s Properties'%dev_name)
self.resizeColumnsToContents()
self.resizeRowsToContents()
##Signals @todo
@@ -134,6 +133,7 @@ class TaurusPropTable(QtGui.QTableWidget, TaurusBaseWidget):
def defineStyle(self):
""" Defines the initial style for the widget """
+ self.setWindowTitle('Properties')
self.setColumnCount(2)
self.setRowCount(0)
self.setGeometry(QtCore.QRect(0,0,400,500))
@@ -380,4 +380,4 @@ if __name__ == '__main__':
widget.setTable(sys.args)
widget.show()
app.exec_()
-
\ No newline at end of file
+
diff --git a/lib/taurus/qt/qtgui/table/taurusgrid.py b/lib/taurus/qt/qtgui/table/taurusgrid.py
index 79f4e4b..67432e1 100644
--- a/lib/taurus/qt/qtgui/table/taurusgrid.py
+++ b/lib/taurus/qt/qtgui/table/taurusgrid.py
@@ -37,11 +37,13 @@ __docformat__ = 'restructuredtext'
import re
import operator
import traceback
+import Queue
from functools import partial
-from taurus.qt import QtGui, QtCore
+from taurus.qt import Qt, QtGui, QtCore
import taurus
+from taurus.qt.qtcore.util.emitter import modelSetter,TaurusEmitterThread,SingletonWorker,MethodModel
from taurus.core import TaurusManager
from taurus.core.util import Logger
from taurus.qt.qtgui.base import TaurusBaseWidget
@@ -58,15 +60,18 @@ def get_all_models(expressions,limit=1000):
'''
All devices matching expressions must be obtained.
For each device only the good attributes are read.
+
+ It practically equals to fandango.get_matching_attributes; check which is better!
+ Move this method to taurus.core.tango.search
'''
print( 'In TaurusGrid.get_all_models(%s:"%s") ...' % (type(expressions),expressions))
if isinstance(expressions,str):
- if any(re.match(s,expressions) for s in ('\{.*\}','\(.*\)','\[.*\]')):
- #self.debug( 'evaluating expressions ....')
- expressions = list(eval(expressions))
- else:
- #self.debug( 'expressions as string separated by commas ...')
- expressions = expressions.split(',')
+ #if any(re.match(s,expressions) for s in ('\{.*\}','\(.*\)','\[.*\]')):
+ ##self.debug( 'evaluating expressions ....')
+ #expressions = list(eval(expressions))
+ #else:
+ ##self.debug( 'expressions as string separated by commas ...')
+ expressions = expressions.split(',')
elif any(isinstance(expressions,klass) for klass in (QtCore.QStringList,list,tuple,dict)):
#self.debug( 'expressions converted from list ...')
@@ -74,6 +79,7 @@ def get_all_models(expressions,limit=1000):
#self.debug( 'In TaurusGrid.get_all_models(%s:"%s") ...' % (type(expressions),expressions))
taurus_db = taurus.core.TaurusManager().getFactory()().getDatabase()
+ #taurus_db = taurus.Database(os.environ['TANGO_HOST'])
if 'SimulationDatabase' in str(type(taurus_db)):
#self.info( 'Using a simulated database ...')
models = expressions
@@ -99,7 +105,6 @@ def get_all_models(expressions,limit=1000):
for dev in devs:
if any(c in attribute for c in '.*[]()+?'):
if '*' in attribute and '.*' not in attribute: attribute = attribute.replace('*','.*')
- #taurus_dp = taurus.core.TaurusManager().getFactory()().getDevice( 'test/sim/sergi')
try:
#taurus_dp = taurus.core.TaurusDevice(dev)
taurus_dp = taurus.core.TaurusManager().getFactory()().getDevice(dev)
@@ -155,15 +160,10 @@ def get_readwrite_models(expressions,limit=1000):
for dev in devs:
if any(c in attribute for c in '.*[]()+?'):
if '*' in attribute and '.*' not in attribute: attribute = attribute.replace('*','.*')
- #taurus_dp = taurus.core.TaurusManager().getFactory()().getDevice( 'test/sim/sergi')
try:
taurus_dp = taurus.core.TaurusManager().getFactory()().getDevice(dev)
attrs = [att.name for att in taurus_dp.attribute_list_query() if re_match_low(attribute,att.name) and att.isReadOnly()]
targets.extend(dev+'/'+att for att in attrs)
-# for att in attrs:
-# if taurus.core.TaurusManager().getFactory()().getAttribute(dev+'/'+att).isReadOnly:
-# continue
-# targets.extend(dev+'/'+att)
except Exception,e:
pass
else: targets.append(dev+'/'+attribute)
@@ -171,14 +171,6 @@ def get_readwrite_models(expressions,limit=1000):
models = models[:limit]
return models
-def modelSetter(args):
- print '#'*80
- obj,model = args[0],args[1]
- print 'In modelSetter(%s,%s)' % (str(obj),str(model))
- obj.setModel(model)
- print 'Out of modelSetter(%s,%s)' % (str(obj),str(model))
-
-
class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
""" TaurusGrid is a Taurus widget designed to represent a set of attributes distributed in columns and rows.
The Model will be a list with attributes or device names (for devices the State attribute will be shown).
@@ -196,7 +188,7 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
#---------------------------------------------------------------------------
# Write your own code here to define the signals generated by this widget
#
- __pyqtSignals__ = ("modelChanged(const QString &)",)
+ __pyqtSignals__ = ("modelChanged(const QString &)","itemSelected(QString)")
_TAGS = ['DOMAIN','FAMILY','HOST','LEVEL','CLASS','ATTRIBUTE','DEVICE']
@@ -216,28 +208,56 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
self._modelNames = []
self.row_labels = []
self.column_labels = []
- self.defineStyle()
- self._others = False
self._widgets_list = []
+ self._last_selected = None
+ self._show_frames = True
+ self._show_row_frame = True
+ self._show_column_frame = True
+ self._show_others = False
+ self._show_attr_labels = True
+ self._show_attr_units = True
self.hideLabels=False
+
+ self.defineStyle()
self.modelsQueue = Queue.Queue()
- self.modelsThread = TaurusEmitterThread(parent=self,queue=self.modelsQueue,method=modelSetter )
+ #self.modelsThread = TaurusEmitterThread(parent=self,queue=self.modelsQueue,method=modelSetter )
+ self.modelsThread = SingletonWorker(parent=self,name='TaurusGrid',queue=self.modelsQueue,method=modelSetter,cursor=True)
def save(self,filename):
import pickle
- d = {'model':self.filter,'row_labels':self.row_labels,'column_labels':self.column_labels}
+ d = {
+ 'model':self.filter,
+ 'row_labels':self.row_labels,'column_labels':self.column_labels,
+ 'frames':self._show_row_frame or self._show_column_frame,'labels':self._show_attr_labels,
+ 'units':self._show_attr_units,'others':self._show_others
+ }
f = open(filename,'w')
pickle.dump(d,f)
f.close()
- def load(self,filename):
- import pickle
- f = open(filename)
- d = pickle.load(f)
- f.close()
+ def load(self,filename,delayed=False):
+ self.info('In TauGrid.load(%s,%s)'%(filename,delayed))
+ if not isinstance(filename,dict):
+ import pickle
+ f = open(filename)
+ d = pickle.load(f)
+ f.close()
+ else:
+ d = filename
self.setRowLabels(d['row_labels'])
- self.setColumnLabels(d['column_labels'])
- self.setModel(d['model'])
+ self.setColumnLabels(d['column_labels'])
+ #self._show_attr_labels = d.get('labels',True) #self.showAttributeLabels(d.get('labels',True))
+ #self._show_attr_units = d.get('units',True) #self.showAttributeUnits(d.get('units',True))
+ #self._show_others = d.get('others',True)
+ #self._show_row_frame = d.get('frames',True)
+ #self._show_column_frame = d.get('frames',True)
+ self.showAttributeLabels(d.get('labels',True))
+ self.showAttributeUnits(d.get('units',True))
+ self.showOthers(d.get('others',True))
+ self.showRowFrame(d.get('frames',True))
+ self.showColumnFrame(d.get('frames',True))
+
+ self.setModel(d['model'],delayed=d.get('delayed',delayed))
return self._modelNames
def defineStyle(self):
@@ -321,8 +341,10 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
self.debug('In TaurusGrid.updateStyle() ....... It seems never called!!!!')
self.debug('@'*80)
- value = self.getShowText() or ''
- if self._setText: self._setText(value) ##It must be included
+ #It was showing an annoying "True" in the widget
+ #value = self.getShowText() or ''
+ #if self._setText: self._setText(value) ##It must be included
+
#update tooltip
self.setToolTip(self.getFormatedToolTip()) ##It must be included
@@ -330,45 +352,67 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
if hasattr(self,'title_widget'):
if self.title: self.title_widget.show()
else: self.title_widget.hide()
- #if hasattr(self,'table'):
- #self.table.resizeColumnsToContents()
- #self.table.resizeRowsToContents()
-
- if hasattr(self,'modelsThread') and not self.modelsThread.isRunning():
- self.debug('<'*80)
- self.modelsThread.start()#self.modelsThread.IdlePriority)
-
+
self.update()
#---------------------------------------------------------------------------
# Write your own code here for your own widget properties
- @QtCore.pyqtSignature("setModel(QStringList)")
- def setModel(self,model,devsInRows=False):
- '''The model can be initialized as a list of devices or hosts or ...'''
+ def setModel(self,model,devsInRows=False,delayed=False,append=False,load=True):
+ '''The model can be initialized as a list of devices or hosts or dictionary or ...'''
#self.setModelCheck(model) ##It must be included
#differenciate if the model is a RegExp
- model = isinstance(model,(str,QtCore.QString)) and [model] or list(model)
- self.debug('#'*80)
- self.debug('In TaurusGrid.setModel(%s)'%model)
- self.filter = model
- if any('*' in m for m in model):
- model = get_all_models(model)
- self.debug('model was a RegExp, done the query and converted to an attr list')
-
- if not self._modelNames == []:#clean to start from scratch
- self._modelNames = []
- for widget in self._widgets_list:
- del widget
-
- #here we always have the reals model list, even if it comes from a regexp
- self._modelNames = model
- self.debug('In setModel(%s): modelNames are %s'%(model,self._modelNames))
- if devsInRows:
- self.setRowLabels(','.join(set(d.rsplit('/',1)[0] for d in self._modelNames)))
- self.create_widgets_table(self._modelNames)
- self.debug('Out of TaurusGrid.setModel(%s)'%model)
- self.updateStyle()
+ if isinstance(model,dict):
+ self.load(model)
+ else:
+ model = isinstance(model,(str,QtCore.QString)) and [model] or list(model)
+ self.debug('#'*80)
+ self.debug('In TaurusGrid.setModel(%s)'%str(model)[:100])
+
+ self.delayed = delayed
+ self.filter = model
+ if any('*' in m for m in model):
+ model = get_all_models(model)
+ self.debug('model was a RegExp, done the query and converted to an attr list')
+
+ if not self._modelNames == []:#clean to start from scratch
+ for widget in self._widgets_list:
+ del widget
+
+ #here we always have the reals model list, even if it comes from a regexp
+ if append: self._modelNames = self._modelNames+model
+ else: self._modelNames = model
+
+ self.debug(('In TaurusGrid.setModel(...): modelNames are %s'%(self._modelNames))[:100]+'...')
+
+ if load:
+ self.info('In TaurusGrid.setModel(%s,load=True): modelNames are %d'%(str(model)[:100]+'...',len(self._modelNames)))#,self._modelNames))
+ if devsInRows:
+ self.setRowLabels(','.join(set(d.rsplit('/',1)[0] for d in self._modelNames)))
+ self.create_widgets_table(self._modelNames)
+ self.modelsQueue.put((MethodModel(self.showRowFrame),self._show_row_frame))
+ self.modelsQueue.put((MethodModel(self.showColumnFrame),self._show_column_frame))
+ self.modelsQueue.put((MethodModel(self.showOthers),self._show_others))
+ self.modelsQueue.put((MethodModel(self.showAttributeLabels),self._show_attr_labels))
+ self.modelsQueue.put((MethodModel(self.showAttributeUnits),self._show_attr_units))
+ self.updateStyle()
+
+ if not self.delayed:
+ self.info('In setModel(): not delayed loading of models')
+ if not self.modelsThread.isRunning():
+ print 'In setModel(): Starting Thread! (%d objs in queue)'%(self.modelsThread.queue.qsize())
+ self.debug('<'*80)
+ self.modelsThread.start()#self.modelsThread.IdlePriority)
+ else:
+ print 'In setModel(): Thread already started! (%d objs in queue)'%(self.modelsThread.queue.qsize())
+ self.modelsThread.next()
+ else:
+ self.info('In setModel(): models loading delayed!')
+ pass
+
+ self.debug('Out of TaurusGrid.setModel(%s)'%str(model)[:100])
+ self.updateStyle()
+ return
def getModel(self):
return self._modelNames
@@ -378,7 +422,7 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
self.updateFromList(self._modelNames)
return
- @QtCore.pyqtSignature("setTitle(QString)")
+
def setTitle(self,title):
self.title = str(title)
if hasattr(self,'title_widget'):
@@ -396,11 +440,10 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
self.warning('ERROR! Unable to parse labels property: %s'%str(e))
return []
else:
- exprs = text.split(',')
- labels = [(':' in e and tuple(e.split(':',1)) or (e,e)) for e in exprs]
+ exprs = [t.strip() for t in text.split(',')]
+ labels = [(':' in e and (e.split(':',1)[0].strip(),e.split(':',1)[-1].strip()) or (e,e)) for e in exprs]
return labels
- @QtCore.pyqtSignature("setRowLabels(QStringList)")
def setRowLabels(self,rows):
'''The model can be initialized as a list of devices or hosts or ...'''
#self.setModelCheck(model) ##It must be included
@@ -421,7 +464,6 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
self.row_labels = []
return
- @QtCore.pyqtSignature("setColumnLabels(QStringList)")
def setColumnLabels(self,columns):
'''The model can be initialized as a list of devices or hosts or ...'''
#self.setModelCheck(model) ##It must be included
@@ -441,6 +483,46 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
def resetColumnLabels(self):
self.column_labels = []
return
+
+ #FIXME: when they are called before setModel they fails because
+ # frames are not yet created, and it doesn't has memoty about this.
+ def showRowFrame(self,boolean):
+ self._show_row_frame = boolean
+ if hasattr(self,'rows_frame'):
+ if boolean:
+ self.rows_frame.show()
+ else:
+ self.rows_frame.hide()
+
+ def showColumnFrame(self,boolean):
+ self._show_column_frame = boolean
+ if hasattr(self,'columns_frame'):
+ if boolean:
+ self.columns_frame.show()
+ else:
+ self.columns_frame.hide()
+
+ def showAttributeLabels(self,boolean):
+ self.info('In showAttributeLabels(%s)'%boolean)
+ self._show_attr_labels = boolean
+ for tv in self._widgets_list:
+ try:
+ if tv and tv.labelWidget:
+ if boolean: tv.labelWidget().show()
+ else: tv.labelWidget().hide()
+ except: pass
+ return self._show_attr_labels
+
+ def showAttributeUnits(self,boolean):
+ self.info('In showAttributeUnits(%s)'%boolean)
+ self._show_attr_units = boolean
+ for tv in self._widgets_list:
+ try:
+ if tv and tv.unitsWidget:
+ if boolean: tv.unitsWidget().show()
+ else: tv.unitsWidget().hide()
+ except: pass
+ return self._show_attr_units
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# QT properties
@@ -576,10 +658,12 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
self.rows_frame = self.create_frame_with_gridlayout()
self.rows_frame.setFrameStyle(QtGui.QFrame.Box)
+ if not self._show_row_frame: self.rows_frame.hide()
self.checkboxes_frame.layout().addWidget(self.rows_frame,0,0)
self.columns_frame = self.create_frame_with_gridlayout()
self.columns_frame.setFrameStyle(QtGui.QFrame.Box)
+ if not self._show_column_frame: self.columns_frame.hide()
self.checkboxes_frame.layout().addWidget(self.columns_frame,0,1)
layout_row = 0
@@ -591,7 +675,7 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
checkbox = QtGui.QCheckBox(section)
if checkbox.text() == 'Others':
checkbox.setChecked(False)
- if not self._others: checkbox.hide()
+ if not self._show_others: checkbox.hide()
else:
checkbox.setChecked(True)
self.rows_frame.layout().addWidget(checkbox,layout_row,layout_col)
@@ -611,7 +695,7 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
checkbox = QtGui.QCheckBox(column)
if checkbox.text() == 'Others':
checkbox.setChecked(False)
- if not self._others: checkbox.hide()
+ if not self._show_others: checkbox.hide()
else:
checkbox.setChecked(True)
self.columns_frame.layout().addWidget(checkbox,layout_row,layout_col)
@@ -650,31 +734,18 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
self.table.hideColumn(table_col)
def showOthers(self,boolean):
- self._others = boolean
+ self._show_others = boolean
if hasattr(self,'rows_frame'):
for checkbox in self.rows_frame.children():
if isinstance(checkbox,QtGui.QCheckBox) and checkbox.text() == 'Others':
- if self._others: checkbox.show()
+ if self._show_others: checkbox.show()
else: checkbox.hide()
if hasattr(self,'columns_frame'):
for checkbox in self.columns_frame.children():
if isinstance(checkbox,QtGui.QCheckBox) and checkbox.text() == 'Others':
- if self._others: checkbox.show()
+ if self._show_others: checkbox.show()
else: checkbox.hide()
- #FIXME: when they are called before setModel they fails because
- # frames are not yet created, and it doesn't has memoty about this.
- def showRowFrame(self,boolean):
- if boolean == True:
- self.rows_frame.show()
- else:
- self.rows_frame.hide()
- def showColumnFrame(self,boolean):
- if boolean == True:
- self.columns_frame.show()
- else:
- self.columns_frame.hide()
-
def build_table(self,values):
"""
This is a builder. For all the elements in widgets matrix,
@@ -683,10 +754,14 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
self.debug('In TaurusGrid.build_table(%s)'%values)
widgets_matrix = self.build_widgets(values,self.showLabels)
rows = len(widgets_matrix)
- cols = len(widgets_matrix[0])
+ cols = rows and len(widgets_matrix[0]) or 0
table = QtGui.QTableWidget()
table.setItemDelegate(Delegate(table))
+ # This example replaces the blue background of selected cells in tables
+ palette = Qt.QPalette()
+ palette.setBrush(palette.Active,palette.Highlight,Qt.QBrush(Qt.Qt.white))
+ table.setPalette(palette)
table.setRowCount(rows)
table.setColumnCount(cols)
@@ -710,14 +785,14 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
for cell in row:
cell_frame = self.create_frame_with_gridlayout()
count = 0
- for synoptic in cell:
+ for synoptic in sorted(cell):
self.debug("processing synoptic %s"%synoptic)
name = model = synoptic
self.debug('Creating TaurusValue with model = %s'%model)
synoptic_value = TaurusValue(cell_frame)
- #synoptic_value.setModel(model)
self.modelsQueue.put((synoptic_value,model))
+ QtCore.QObject.connect(synoptic_value, QtCore.SIGNAL("itemClicked(QString)"), self.itemClicked)
if self.hideLabels:
synoptic_value.setLabelWidgetClass(None)
@@ -726,15 +801,51 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
cell_frame.layout().addWidget(synoptic_value,count,0)
self._widgets_list.append(synoptic_value)
count += 1
+
+ #Done in this way as TauValue.mousePressEvent are never called
+ def mousePressEvent(event,obj):
+ print 'In cell clicked'
+ targets = set(str(child.getModelName()) for child in obj.children()
+ if hasattr(child,'underMouse') and child.underMouse() and hasattr(child,'getModelName'))
+ [obj.emit(Qt.SIGNAL("itemClicked(QString)"),t) for t in targets]
+
+ cell_frame.mousePressEvent = partial(mousePressEvent,obj=cell_frame)
+ QtCore.QObject.connect(cell_frame, QtCore.SIGNAL("itemClicked(QString)"), self.itemClicked)
+
widgets_row.append(cell_frame)
widgets_matrix.append(widgets_row)
return widgets_matrix
+
+ def itemClicked(self,item_name):
+ self.info('In TaurusGrid.itemClicked(%s)'%item_name)
+ self.setItemSelected(item_name)
+ self.emit(QtCore.SIGNAL("itemClicked(QString)"),str(item_name))
+
+ def setItemSelected(self,item_name='',selected=True):
+ """ it adds a blue frame around a clicked item. """
+ if isinstance(item_name,TaurusValue):
+ self.info('In TaurusGrid.setItemSelected(%s,%s)'%(str(item_name.getModel()),selected))
+ item = item_name
+ else:
+ self.info('In TaurusGrid.setItemSelected(%s,%s)'%(str(item_name),selected))
+ if item_name: item = self.getItemByModel(item_name)
+ else: item = self._last_selected
+ if item:
+ if selected:
+ item._labelWidget.setStyleSheet('border-style: solid ; border-width: 1px; border-color: blue; color: blue; border-radius:4px;')
+ if self._last_selected and self._last_selected!=item:
+ self.setItemSelected(self._last_selected,False)
+ self._last_selected = item
+ else:
+ item._labelWidget.setStyleSheet('border-style: solid; border-width: 1px; border-color: transparent; color: black; border-radius:4px;')
+ self._last_selected = None
+ else: return None
def getItemByModel(self, model, index=0):
#a particular model can be many times, index means which one of them
+ model = str(model).lower()
for widget in self._widgets_list:
- #@todo taurus model comparision?
- if widget.getModel().lower() == model.lower():
+ if str(widget.getModel()).lower() == model:
if index <= 0:
return widget
else:
@@ -748,78 +859,6 @@ class TaurusGrid(QtGui.QFrame, TaurusBaseWidget):
ret['icon'] = ":/designer/grid.png"
return ret
-import Queue,traceback
-class TaurusEmitterThread(QtCore.QThread):
- """
- This object get items from a python Queue and performs a thread safe operation on them.
- It is useful to delay signals in a background thread.
- :param parent: a Qt/Taurus object
- :param queue: if None parent.getQueue() is used, if not then the queue passed as argument is used
- :param target: the method to be executed using each queue item as argument
- """
- def __init__(self, parent=None,queue=None,method=None):
- """Parent most not be None and must be a TaurusGraphicsScene!"""
- #if not isinstance(parent, TaurusGraphicsScene):
- #raise RuntimeError("Illegal parent for TaurusGraphicsUpdateThread")
- QtCore.QThread.__init__(self, parent)
- self.log = Logger('TaurusEmitterThread')
- self.queue = queue
- self.todo = Queue.Queue()
- self.method = method
- self.emitter = QtCore.QObject(QtGui.QApplication.instance())
- self._done = 0
- QtCore.QObject.connect(self.emitter, QtCore.SIGNAL("doSomething"), self._doSomething)
- QtCore.QObject.connect(self.emitter, QtCore.SIGNAL("somethingDone"), self._next)
- self.emitter.moveToThread(QtGui.QApplication.instance().thread())
-
- def getQueue(self):
- if self.queue: return self.queue
- elif hasattr(self.parent(),'getQueue'): self.parent().getQueue()
- else: return None
-
- def getDone(self):
- """ Returns % of done tasks in 0-1 range """
- return self._done/(self._done+self.getQueue().qsize()) if self._done else 0.
-
- def _doSomething(self,args):
- print '#'*80
- self.log.debug('At TaurusEmitterThread._doSomething(%s)'%str(args))
- if self.method:
- try:
- self.method(args)
- except:
- self.log.error('At TaurusEmitterThread._doSomething(): %s' % traceback.format_exc())
- self.emitter.emit(QtCore.SIGNAL("somethingDone"))
- self._done += 1
- return
-
- def _next(self):
- queue = self.getQueue()
- self.log.debug('At TaurusEmitterThread._next(), %d items remaining.' % queue.qsize())
- if not queue.empty():
- try:
- item = queue.get(False) #A blocking get here would hang the GUIs!!!
- self.todo.put(item)
- except Queue.Empty,e:
- pass
- return
-
- def run(self):
- print '#'*80
- self.log.debug('At TaurusEmitterThread.run()')
- self._next()
- while True:
- item = self.todo.get(True)
- if type(item) in taurus.core.util.types.StringTypes:
- if item == "exit":
- break
- else:
- continue
- self.emitter.emit(QtCore.SIGNAL("doSomething"), item)
- #End of while
- self.log.info('Out of TaurusEmitterThread.run()')
- #End of Thread
-
class Delegate(QtGui.QItemDelegate):
def __init__(self, parent=None):
@@ -839,7 +878,7 @@ def sysargs_to_dict(defaults=[]):
bar = a.split('=')
if bar[1] in ['True','False']:#convert string to boolean
bar[1] = eval(bar[1])
- result[bar[0]] = bar[1]
+ result[bar[0].replace('--','')] = bar[1]
else:
result[defaults[i]] = a
i+=1
diff --git a/lib/taurus/qt/qtgui/table/taurustable.py b/lib/taurus/qt/qtgui/table/taurustable.py
index 2d57f14..ed85951 100644
--- a/lib/taurus/qt/qtgui/table/taurustable.py
+++ b/lib/taurus/qt/qtgui/table/taurustable.py
@@ -3,27 +3,27 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
#############################################################################
-"""This module provides a base widget that can be used to display a taurus
+"""This module provides a base widget that can be used to display a taurus
model in a table widget"""
__all__ = ["TaurusBaseTableWidget"]
@@ -37,15 +37,13 @@ from qtable import QBaseTableWidget
class TaurusBaseTableWidget(QBaseTableWidget, TaurusBaseModelWidget):
"""A class:`taurus.qt.qtgui.tree.QBaseTableWidget` that connects to a
taurus model.
-
+
Filters can be inserted into this widget to restrict the items that are
seen."""
-
+
def __init__(self, parent=None, designMode=False, with_filter_widget=True,
perspective=None, proxy=None):
self.call__init__(QBaseTableWidget, parent, designMode=designMode,
- with_filter_widget=with_filter_widget)
- self.call__init__(TaurusBaseModelWidget, designMode=designMode,
+ with_filter_widget=with_filter_widget,
perspective=perspective, proxy=proxy)
-
-
+ self.call__init__(TaurusBaseModelWidget, designMode=designMode)
diff --git a/lib/taurus/qt/qtgui/table/taurusvaluestable.py b/lib/taurus/qt/qtgui/table/taurusvaluestable.py
index ece1d24..5fd37ac 100644
--- a/lib/taurus/qt/qtgui/table/taurusvaluestable.py
+++ b/lib/taurus/qt/qtgui/table/taurusvaluestable.py
@@ -95,7 +95,8 @@ class TaurusValuesIOTableModel(Qt.QAbstractTableModel):
elif role == Qt.Qt.DecorationRole:
status = self.getStatus(index)
if (self._modifiedDict.has_key((index.row(), index.column()))) and (self._writeMode):
- if self.inAlarmRange(self._modifiedDict[(index.row(), index.column())].toDouble()[0]):
+ 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:
@@ -118,7 +119,8 @@ class TaurusValuesIOTableModel(Qt.QAbstractTableModel):
return Qt.QVariant(Qt.QColor('white'))
elif role == Qt.Qt.ForegroundRole:
if self._modifiedDict.has_key((index.row(), index.column())) and (self._writeMode):
- if self.inAlarmRange(self._modifiedDict[(index.row(), index.column())].toDouble()[0]):
+ 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'))
@@ -128,8 +130,9 @@ class TaurusValuesIOTableModel(Qt.QAbstractTableModel):
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()],self._modifiedDict[index.row(), index.column()].toDouble()[0]))
+ %(tabledata[index.row(), index.column()], float_data))
return Qt.QVariant()
def setAttr(self, attr):
@@ -228,16 +231,16 @@ class TaurusValuesIOTableModel(Qt.QAbstractTableModel):
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] = str(v.toString())
+ 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],ok = v.toDouble()
+ table[k] = Qt.from_qvariant(v, float)
elif kind in 'iu':
- table[k],ok = v.toInt()
+ table[k] = Qt.from_qvariant(v, int)
elif kind == 'b':
- table[k] = v.toBool()
+ table[k] = Qt.from_qvariant(v, bool)
else:
raise TypeError('Unknown data type "%s"'%kind)
#reshape if needed
@@ -410,13 +413,13 @@ class TaurusValuesIOTableDelegate(Qt.QStyledItemDelegate):
self._initialText = None
if index.model().getType() == bool:
editor.addItems(['true', 'false'])
- a = str(index.data().toBool()).lower()
+ 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 = data.toString()
- editor.setText(data.toString())
+ self._initialText = Qt.from_qvariant(data, str)
+ editor.setText(self._initialText)
def setModelData(self, editor, model,index):
'''
@@ -461,7 +464,10 @@ class TableInlineEdit(Qt.QLineEdit):
see :meth:`Qt.QLineEdit.textEdited`
'''
color, weight = 'gray', 'normal' #default case: the value is in normal range with no pending changes
- v, b = self.displayText().toDouble()
+ 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'
@@ -764,4 +770,4 @@ if __name__ == '__main__':
taurusTableMain()
-
\ No newline at end of file
+
diff --git a/lib/taurus/qt/qtgui/taurusgui/PermanentCustomPanelsDlg.py b/lib/taurus/qt/qtgui/taurusgui/PermanentCustomPanelsDlg.py
index 006b802..bb8829e 100644
--- a/lib/taurus/qt/qtgui/taurusgui/PermanentCustomPanelsDlg.py
+++ b/lib/taurus/qt/qtgui/taurusgui/PermanentCustomPanelsDlg.py
@@ -29,65 +29,13 @@ PermanentCustomPanelDlg.py:
"""
from taurus.qt import Qt
-from ui.ui_PermanentCustomPanelsDlg import Ui_PermanentCustomPanelsDlg
-class PermanentCustomPanelsDlg(Qt.QDialog):
- '''Dialog to define which Custom panels should be permanently stored in the configuration
+class PermanentCustomPanelsDlg(object):
'''
- #@todo: drag&drop is disabled because Qt<4.6 does not have QList.setDefaultDragAndDropMode()
- def __init__(self, parent=None, designMode=False, temporaryList=None, permanentList=None):
- super(PermanentCustomPanelsDlg,self).__init__(parent)
-
- self.ui = Ui_PermanentCustomPanelsDlg()
- self.ui.setupUi(self)
-
- self.setPermanentPanels(permanentList)
- self.setTemporaryPanels(temporaryList)
-
- self.connect(self.ui.toTemporaryBT, Qt.SIGNAL('clicked(bool)'), self.onToTemp)
- self.connect(self.ui.toPermanentBT, Qt.SIGNAL('clicked(bool)'), self.onToPerm)
-
-
- def setPermanentPanels(self, permanentList):
- self.ui.permanentPanelsList.clear()
- self.ui.permanentPanelsList.addItems(permanentList)
-
- def setTemporaryPanels(self, tempList):
- self.ui.temporaryPanelsList.clear()
- self.ui.temporaryPanelsList.addItems(tempList)
-
- def _moveItem(self, fromlist, tolist):
- selected = fromlist.selectedItems()
- for item in selected:
- fromlist.takeItem(fromlist.row(item))
- tolist.addItem(item)
-
- def onToTemp(self, *args):
- self._moveItem(self.ui.permanentPanelsList, self.ui.temporaryPanelsList)
-
- def onToPerm(self, *args):
- self._moveItem(self.ui.temporaryPanelsList, self.ui.permanentPanelsList)
+ .. warning:: This class is deprecated. Use class:`taurus.qt.qtgui.panel.QDoubleList` instead
+ '''
+ def __init__(self, *args, **kwargs):
+ raise DeprecationWarning('PermanentCustomPanelsDlg is deprecated. Use taurus.qt.qtgui.panel.QDoubleList instead')
- def getTemporaryPanels(self):
- return [unicode(self.ui.temporaryPanelsList.item(row).text()) for row in xrange(self.ui.temporaryPanelsList.count())]
-
- def getPermanentPanels(self):
- return [unicode(self.ui.permanentPanelsList.item(row).text()) for row in xrange(self.ui.permanentPanelsList.count())]
-
-
-#------------------------------------------------------------------------------
-
-def main():
- app = Qt.QApplication(sys.argv)
-
-
- form = PermanentCustomPanelsDlg(temporaryList=['11','22'], permanentList=['123','33'])
- form.show()
- sys.exit(app.exec_())
-
-
-if __name__ == "__main__":
- import sys
- main()
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/taurusgui/conf/tgconf_example01/config.py b/lib/taurus/qt/qtgui/taurusgui/conf/tgconf_example01/config.py
index 72256ae..88f8944 100644
--- a/lib/taurus/qt/qtgui/taurusgui/conf/tgconf_example01/config.py
+++ b/lib/taurus/qt/qtgui/taurusgui/conf/tgconf_example01/config.py
@@ -102,6 +102,11 @@ trend = PanelDescription('Trend',
classname = 'TaurusTrend',
model = ['sys/tg_test/1/double_scalar'])
+connectionDemo = PanelDescription('Selected Instrument',
+ classname = 'PyQt4.Qt.QLineEdit',
+ sharedDataRead={'SelectedInstrument':'setText'},
+ sharedDataWrite={'SelectedInstrument':'textEdited(QString)'})
+
#===============================================================================
# Define custom toolbars to be shown. To define a toolbar, instantiate a
# ToolbarDescription object (see documentation for the gblgui_utils module)
@@ -149,3 +154,15 @@ pymca = ExternalApp(['pymca'])
# custom applet with classname='TaurusMonitorTiny')
#===============================================================================
# MONITOR = ['sys/tg_test/1/double_scalar_rww']
+
+#===============================================================================
+# Adding other widgets to the catalog of the "new panel" dialog.
+# pass a tuple of (classname,screenshot)
+# -classname may contain the module name.
+# -screenshot can either be a file name relative to the application dir or
+# a resource URL or None
+#===============================================================================
+EXTRA_CATALOG_WIDGETS = [('PyQt4.Qt.QLineEdit',':/taurus.png'),
+ ('PyQt4.Qt.QSpinBox','images/syn2.jpg'),
+ ('PyQt4.Qt.QTextEdit','/tmp/kk.png'),
+ ('PyQt4.Qt.QLabel',None)]
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/taurusgui/conf/tgconf_macrogui/config.py b/lib/taurus/qt/qtgui/taurusgui/conf/tgconf_macrogui/config.py
index 0b155bf..d1f25e1 100644
--- a/lib/taurus/qt/qtgui/taurusgui/conf/tgconf_macrogui/config.py
+++ b/lib/taurus/qt/qtgui/taurusgui/conf/tgconf_macrogui/config.py
@@ -74,7 +74,7 @@ ORGANIZATION = 'Taurus'
# To define an external application, instantiate an ExternalApp object
# See TaurusMainWindow.addExternalAppLauncher for valid values of ExternalApp
#===============================================================================
-xterm = ExternalApp(cmdargs=['xterm','spock'], text="Spock", icon='utilities-terminal')
+#xterm = ExternalApp(cmdargs=['xterm','spock'], text="Spock", icon='utilities-terminal')
#hdfview = ExternalApp(["hdfview"])
pymca = ExternalApp(['pymca'])
@@ -89,6 +89,12 @@ DOOR_NAME = ''
MACROEDITORS_PATH = ''
#===============================================================================
+# Define one or more embedded consoles in the GUI.
+# Possible items for console are 'ipython', 'tango', 'spock'
+#===============================================================================
+CONSOLE = ['tango']
+
+#===============================================================================
# Monitor widget
#===============================================================================
#MONITOR =
diff --git a/lib/taurus/qt/qtgui/taurusgui/macrolistener.py b/lib/taurus/qt/qtgui/taurusgui/macrolistener.py
index f3faf5d..03dff88 100644
--- a/lib/taurus/qt/qtgui/taurusgui/macrolistener.py
+++ b/lib/taurus/qt/qtgui/taurusgui/macrolistener.py
@@ -35,14 +35,14 @@ __all__=['MacroBroker']
__docformat__ = 'restructuredtext'
-import os, sys
+#import os, sys
import datetime
import taurus
from taurus.qt import Qt
from taurus.qt.qtgui.base import TaurusBaseComponent
from taurus.qt.qtgui.resource import getThemeIcon, getIcon
-from taurus.qt.qtgui.taurusgui.utils import PanelDescription
-from taurus.core.tango.sardana import PlotType, Normalization
+#from taurus.qt.qtgui.taurusgui.utils import PanelDescription
+from taurus.core.tango.sardana import PlotType
from taurus.core.tango.sardana.pool import getChannelConfigs
from taurus.core.util import CaselessList
@@ -120,7 +120,6 @@ class MacroBroker(Qt.QObject, TaurusBaseComponent):
TaurusMacroDescriptionViewer, DoorOutput, DoorDebug, DoorResult
from taurus.qt.qtgui.extra_sardana import ExpDescriptionEditor, SardanaEditor
- from taurus.qt.qtgui.button import TaurusCommandButton
mainwindow = self.parent()
@@ -189,8 +188,8 @@ class MacroBroker(Qt.QObject, TaurusBaseComponent):
Qt.qApp.SDM.connectReader("doorResultChanged", self.__doorResult.onDoorResultChanged)
mainwindow.createPanel(self.__doorResult, 'DoorResult', registerconfig=False, permanent=True)
- #puts sardanaEditor
- self.__sardanaEditor = SardanaEditor()
+ #puts sardanaEditor
+ self.__sardanaEditor = SardanaEditor()
Qt.qApp.SDM.connectReader("macroserverName", self.__sardanaEditor.setModel)
mainwindow.createPanel(self.__sardanaEditor, 'SardanaEditor', registerconfig=False, permanent=True)
@@ -235,8 +234,10 @@ class MacroBroker(Qt.QObject, TaurusBaseComponent):
for more details
'''
#print "@@@@@@@@", expconf
+ if expconf['ActiveMntGrp'] is None:
+ return
mgconfig = expconf['MntGrpConfigs'][expconf['ActiveMntGrp']]
- channels = dict(getChannelConfigs(mgconfig, sort=False))
+ channels = dict(getChannelConfigs(mgconfig, sort=False))
#classify by type of plot:
trends1d = {}
trends2d = {}
@@ -281,7 +282,7 @@ class MacroBroker(Qt.QObject, TaurusBaseComponent):
def _updateTemporaryTrends1D(self, trends1d):
from taurus.qt.qtgui.plot import TaurusTrend
mainwindow = self.parent()
- for i,(axes,plotables) in enumerate(trends1d.items()):
+ for axes,plotables in trends1d.items():
if axes not in self._trends1d:
w = TaurusTrend()
w.setXIsTime(False)
@@ -305,7 +306,7 @@ class MacroBroker(Qt.QObject, TaurusBaseComponent):
raise
return
mainwindow = self.parent()
- for i,(axes,plotables) in enumerate(trends2d.items()):
+ for axes,plotables in trends2d.items():
for chname in plotables:
pname = u'Trend2D - %s'%chname
if pname in self._trends2d:
diff --git a/lib/taurus/qt/qtgui/taurusgui/paneldescriptionwizard.py b/lib/taurus/qt/qtgui/taurusgui/paneldescriptionwizard.py
index df7ed4a..1f8529e 100644
--- a/lib/taurus/qt/qtgui/taurusgui/paneldescriptionwizard.py
+++ b/lib/taurus/qt/qtgui/taurusgui/paneldescriptionwizard.py
@@ -35,7 +35,7 @@ from taurus.qt.qtgui.taurusgui.utils import PanelDescription
from taurus.qt.qtgui.resource import getPixmap, getThemeIcon, getIcon
from taurus.qt.qtgui.input import GraphicalChoiceWidget
from taurus.qt.qtgui.panel import TaurusModelChooser
-from taurus.qt.qtgui.base import TaurusBaseComponent
+from taurus.qt.qtgui.base import TaurusBaseComponent, TaurusBaseWidget
from taurus.qt.qtcore.communication import SharedDataManager
from taurus.qt.qtcore.mimetypes import TAURUS_MODEL_LIST_MIME_TYPE
from taurus.qt.qtgui.util import TaurusWidgetFactory
@@ -137,6 +137,10 @@ class BlackListValidator(Qt.QValidator):
if blackList is None: blackList = []
self.blackList = blackList
self._previousState = None
+ #check the signature of the validate method (it changed from old to new versions)
+ #see :http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/python_v3.html#qvalidator
+ dummyValidator = Qt.QDoubleValidator(None)
+ self._oldMode = len(dummyValidator.validate('',0)) < 3
def validate(self, input, pos):
if str(input) in self.blackList:
@@ -146,8 +150,11 @@ class BlackListValidator(Qt.QValidator):
if state != self._previousState:
self.emit(Qt.SIGNAL('stateChanged'), state, self._previousState)
self._previousState = state
- return state, pos
-
+ if self._oldMode: #for backwards compatibility with older versions of PyQt
+ return state, pos
+ else:
+ return state, input, pos
+
#class NamePage(Qt.QWizardPage):
#
# def __init__(self, parent = None):
@@ -183,12 +190,34 @@ class BlackListValidator(Qt.QValidator):
#
-class WidgetPage(Qt.QWizardPage):
+class WidgetPage(Qt.QWizardPage, TaurusBaseWidget):
OTHER_TXT ='Other...'
-
- def __init__(self, parent = None):
+ defaultCandidates = ['TaurusForm','TaurusTrend', 'TaurusPlot',
+ 'TaurusImageDialog', 'TaurusTrend2DDialog', 'TaurusNeXusBrowser',
+ 'TaurusDbTreeWidget', 'TaurusArrayEditor',
+ 'TaurusShell', 'SardanaEditor','TaurusJDrawSynopticsView',
+ 'TaurusDevicePanel']
+ def __init__(self, parent = None, designMode=False, extraWidgets=None):
Qt.QWizardPage.__init__(self, parent)
-
+ TaurusBaseWidget.__init__(self, 'WidgetPage')
+ if extraWidgets:
+ customWidgets,customWidgetScreenshots = zip(*extraWidgets)
+ pixmaps = {}
+ for k,s in extraWidgets:
+ if s is None:
+ pixmaps[k] = None
+ else:
+ try:
+ pixmaps[k] = getPixmap(s)
+ if pixmaps[k].isNull():
+ raise Exception('Invalid Pixmap')
+ except:
+ self.warning('Could not create pixmap from %s'%s)
+ pixmaps[k] = None
+
+ else:
+ customWidgets,customWidgetScreenshots = [],[]
+ pixmaps = {}
self.setFinalPage(True)
self.setTitle('Panel type')
self.setSubTitle('Choose a name and type for the new panel')
@@ -207,21 +236,16 @@ class WidgetPage(Qt.QWizardPage):
#contents
available = TaurusWidgetFactory().getWidgetClassNames()
- candidates=['TaurusForm','TaurusTrend', 'TaurusPlot',
- 'TaurusImageDialog', 'TaurusTrend2DDialog', 'TaurusNeXusBrowser',
- 'TaurusDbTreeWidget', 'TaurusArrayEditor',
- 'TaurusShell', 'SardanaEditor']
-
choices = []
row=[]
- pixmaps={}
- for cname in candidates:
- if cname in available:
+ for cname in self.defaultCandidates+list(customWidgets):
+ if cname in available or '.' in cname:
row.append(cname)
- pixmaps[cname] = getPixmap(':/snapshot/%s.png'%cname)
+ if cname not in pixmaps:
+ pixmaps[cname] = getPixmap(':/snapshot/%s.png'%cname)
if len(row) == 3:
choices.append(row)
- row=[]
+ row=[]
row.append(self.OTHER_TXT)
choices.append(row)
@@ -256,7 +280,7 @@ class WidgetPage(Qt.QWizardPage):
if not isinstance(w, Qt.QWidget):
raise ValueError
#set the name now because it might have changed since the PanelDescription was created
- paneldesc.name = str(self.field('panelname').toString())
+ paneldesc.name = Qt.from_qvariant(self.field('panelname'), str)
#allow the wizard to proceed
return True
except Exception,e:
@@ -282,6 +306,7 @@ class WidgetPage(Qt.QWizardPage):
self.wizard().setPanelDescription( PanelDescription('', **self.widgetDescription)) #the name will be set in self.validatePage
paneltype = str(self.widgetDescription['widgetname'] or self.widgetDescription['classname'])
self.widgetTypeLB.setText("<b>Widget Type:</b> %s"%paneltype)
+
class AdvSettingsPage(Qt.QWizardPage):
@@ -432,7 +457,7 @@ class CommTableModel(Qt.QAbstractTableModel):
elif section == self.W: return Qt.QVariant("Writer (signal)")
return Qt.QVariant()
else:
- return Qt.QVariant(Qt.QString.number(section+1))
+ return Qt.QVariant(Qt.QString('%i'%(section+1)))
def data(self, index, role=Qt.Qt.DisplayRole):
if not index.isValid() or not (0 <= index.row() < self.rowCount()):
@@ -455,7 +480,7 @@ class CommTableModel(Qt.QAbstractTableModel):
if index.isValid() and (0 <= index.row() < self.rowCount()):
row = index.row()
column = index.column()
- value = str(value.toString())
+ value = Qt.from_qvariant(value, str)
self.__table[row][column] = value
self.emit(Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),index, index)
return True
@@ -513,21 +538,21 @@ class CommItemDelegate(Qt.QStyledItemDelegate):
def setModelData(self, editor, model, index):
model.setData(index, Qt.QVariant(editor.currentText()))
-class PanelDescriptionWizard(Qt.QWizard):
+class PanelDescriptionWizard(Qt.QWizard, TaurusBaseWidget):
'''A wizard-style dialog for configuring a new TaurusGui panel.
Use :meth:`getDialog` for launching it
'''
- def __init__(self, parent=None, gui=None):
+ def __init__(self, parent=None, designMode=False, gui=None, extraWidgets=None):
Qt.QWizard.__init__(self, parent)
-# self.setPixmap(Qt.QWizard.LogoPixmap, getPixmap(":/logo.png"))
-
+ name = "PanelDescriptionWizard"
+ TaurusBaseWidget.__init__(self, name)
self._panelDescription = None
if gui is None: gui = parent
if gui is not None:
self._gui = weakref.proxy(gui)
###self.setOption(self.HaveFinishButtonOnEarlyPages, True)
#self.namePG = NamePage()
- self.widgetPG = WidgetPage()
+ self.widgetPG = WidgetPage(extraWidgets=extraWidgets)
self.advSettingsPG = AdvSettingsPage()
###self.addPage(self.namePG)
@@ -553,7 +578,7 @@ class PanelDescriptionWizard(Qt.QWizard):
self._panelDescription = desc
@staticmethod
- def getDialog(parent):
+ def getDialog(parent, extraWidgets=None):
"""Static method for launching a new Dialog.
:param parent: parent widget for the new dialog
@@ -562,7 +587,7 @@ class PanelDescriptionWizard(Qt.QWizard):
and a state flag. The state is True if the dialog was accepted
and False otherwise
"""
- dlg = PanelDescriptionWizard(parent)
+ dlg = PanelDescriptionWizard(parent,extraWidgets=extraWidgets)
dlg.exec_()
return dlg.getPanelDescription(), (dlg.result() == dlg.Accepted)
@@ -605,7 +630,9 @@ def main():
form.show()
- paneldesc,ok = PanelDescriptionWizard.getDialog(form)
+ paneldesc,ok = PanelDescriptionWizard.getDialog(form, extraWidgets=[('PyQt4.Qt.QLineEdit',':/taurus.png'),
+ ('PyQt4.Qt.QSpinBox','/tmp/kk.png'),
+ ('PyQt4.Qt.QTextEdit',None)])
if ok:
w = paneldesc.getWidget(sdm=Qt.qApp.SDM)
form.setCentralWidget(w)
@@ -622,4 +649,4 @@ if __name__ == "__main__":
#test2()
main()
-
\ No newline at end of file
+
diff --git a/lib/taurus/qt/qtgui/taurusgui/taurusgui.py b/lib/taurus/qt/qtgui/taurusgui/taurusgui.py
index 7c0e5e7..de9f195 100644
--- a/lib/taurus/qt/qtgui/taurusgui/taurusgui.py
+++ b/lib/taurus/qt/qtgui/taurusgui/taurusgui.py
@@ -39,16 +39,8 @@ from taurus.qt.qtcore.communication import SharedDataManager
from taurus.qt.qtgui.util import TaurusWidgetFactory
from taurus.qt.qtgui.base import TaurusBaseWidget, TaurusBaseComponent
from taurus.qt.qtgui.container import TaurusMainWindow
-from taurus.qt.qtgui.input import GraphicalChoiceDlg
-
-try:
- from taurus.qt.qtgui.extra_pool import PoolMotorSlim, IORegisterTV, PoolChannelTV
- HAS_EXTRA_POOL = True
-except ImportError:
- HAS_EXTRA_POOL = False
-
from taurus.qt.qtgui.taurusgui.utils import ExternalApp, PanelDescription, ToolBarDescription, AppletDescription
-from PermanentCustomPanelsDlg import PermanentCustomPanelsDlg
+from taurus.qt.qtgui.panel import QDoubleListDlg
import taurus.qt.qtgui.resource
from taurus.core.util import etree
@@ -70,7 +62,6 @@ class AssociationDialog(Qt.QDialog):
def refresh(self):
currentinstrument = self.ui.instrumentCB.currentText()
- currentinstrumentIdx= self.ui.instrumentCB.currentIndex()
mainwindow = self.parent()
self.associations = mainwindow.getAllInstrumentAssociations()
@@ -144,10 +135,21 @@ class DockWidgetPanel(Qt.QDockWidget, TaurusBaseWidget):
def setPermanent(self, permanent):
self._permanent = permanent
- def setWidgetFromClassName(self, classname):
+ def setWidgetFromClassName(self, classname, modulename=None):
if self.getWidgetClassName() != classname:
- klass = TaurusWidgetFactory().getWidgetClass(classname)
- w = klass()
+ try:
+ klass = TaurusWidgetFactory().getWidgetClass(classname)
+ w = klass()
+ except:
+ try:
+ if classname is not None and '.' in classname:
+ mn,classname = classname.rsplit('.',1)
+ modulename = ("%s.%s"%(modulename or '',mn)).strip('. ')
+ module = __import__(modulename, fromlist=[''])
+ klass = getattr(module, classname)
+ w = klass()
+ except Exception, e:
+ raise RuntimeError('Cannot create widget from classname "%s". Reason: %s'%(classname, repr(e)))
#set customwidgetmap if necessary
if hasattr(w,'setCustomWidgetMap'):
w.setCustomWidgetMap(self._mainwindow.getCustomWidgetMap())
@@ -155,6 +157,12 @@ class DockWidgetPanel(Qt.QDockWidget, TaurusBaseWidget):
wname="%s-%s"%(str(self.objectName()), str(classname))
w.setObjectName(wname)
+ def getWidgetModuleName(self):
+ w = self.widget()
+ if w is None:
+ return ''
+ return w.__module__
+
def getWidgetClassName(self):
w = self.widget()
if w is None:
@@ -164,17 +172,18 @@ class DockWidgetPanel(Qt.QDockWidget, TaurusBaseWidget):
def applyConfig(self, configdict, depth=-1):
#create the widget
try:
- self.setWidgetFromClassName(configdict.get('widgetClassName'))
+ self.setWidgetFromClassName(configdict.get('widgetClassName'), modulename=configdict.get('widgetModuleName',None))
if isinstance(self.widget(),BaseConfigurableClass):
self.widget().applyConfig(configdict['widget'])
except Exception,e:
- self.info('Failed to set the widget for this panel. Reason: %s'%repr(e))
+ self.info('Failed to set the widget for this panel. Reason: %s'%repr(e))
return
TaurusBaseWidget.applyConfig(self, configdict, depth)
def createConfig(self, *args, **kwargs):
configdict = TaurusBaseWidget.createConfig(self, *args, **kwargs)
configdict['widgetClassName'] = self.getWidgetClassName()
+ configdict['widgetModuleName'] = self.getWidgetModuleName()
if isinstance(self.widget(),BaseConfigurableClass):
configdict['widget'] = self.widget().createConfig()
return configdict
@@ -197,14 +206,16 @@ class TaurusGui(TaurusMainWindow):
IMPLICIT_ASSOCIATION = '__[IMPLICIT]__'
- def __init__(self, parent=None, confname=None):
+ def __init__(self, parent=None, confname=None, configRecursionDepth=None):
TaurusMainWindow.__init__(self, parent, False, True)
+ if configRecursionDepth is not None:
+ self.defaultConfigRecursionDepth = configRecursionDepth
+
self.__panels = {}
self.__synoptics = []
self.__instrumentToPanelMap = {}
self.__panelToInstrumentMap = {}
-
self.setDockNestingEnabled(True)
self.registerConfigProperty(self._getPermanentCustomPanels, self._setPermanentCustomPanels, 'permanentCustomPanels')
@@ -223,11 +234,9 @@ class TaurusGui(TaurusMainWindow):
self.__initJorgBar()
self.__initSharedDataConnections()
self.__initToolsMenu()
-
+
self.loadConfiguration(confname)
-
- self.splashScreen().finish(self)
-
+
#connect the main window itself as a reader/writer of "short messages"
Qt.qApp.SDM.connectReader("shortMessage", self.onShortMessage)
Qt.qApp.SDM.connectWriter("shortMessage", self, 'newShortMessage')
@@ -236,6 +245,14 @@ class TaurusGui(TaurusMainWindow):
msg = '%s is ready'%Qt.qApp.applicationName()
self.emit(Qt.SIGNAL('newShortMessage'), msg)
+ if self.defaultConfigRecursionDepth >=0:
+ Qt.QMessageBox.information(self, "Fail-proof mode",
+ ('Running in fail-proof mode.'+
+ '\nLoading of potentially problematic settings is disabled.'+
+ '\nSome panels may not be loaded or may ignore previous user configuration'+
+ '\nThis will also apply when loading perspectives'),
+ Qt.QMessageBox.Ok, Qt.QMessageBox.NoButton)
+
def closeEvent(self, event):
try:
self.__macroBroker.removeTemporaryPanels()
@@ -273,7 +290,6 @@ class TaurusGui(TaurusMainWindow):
def __initViewMenu(self):
self.viewMenu.addSeparator() #the superclass may already have added stuff to the viewMenu
- self.viewMenu.addMenu(self.__panelsMenu)
#view locking
self.viewMenu.addSeparator()
self._lockviewAction = Qt.QAction(taurus.qt.qtgui.resource.getThemeIcon("system-lock-screen"),"Lock View", self)
@@ -305,17 +321,23 @@ class TaurusGui(TaurusMainWindow):
def __initSharedDataConnections(self):
#register the TAURUSGUI itself as a writer/reader for several shared data items
- self.splashScreen().showMessage("setting up shared data connections")
+ splashScreen = self.splashScreen()
+ if splashScreen is not None:
+ self.splashScreen().showMessage("setting up shared data connections")
Qt.qApp.SDM.connectWriter("macroserverName", self, 'macroserverNameChanged')
Qt.qApp.SDM.connectWriter("doorName", self, 'doorNameChanged')
Qt.qApp.SDM.connectReader("SelectedInstrument", self.onSelectedInstrument)
Qt.qApp.SDM.connectWriter("SelectedInstrument", self, 'SelectedInstrument')
Qt.qApp.SDM.connectReader("executionStarted", self.setFocusToPanel)
-
+ Qt.qApp.SDM.connectReader("selectedPerspective", self.loadPerspective)
+ Qt.qApp.SDM.connectWriter("perspectiveChanged", self, 'perspectiveChanged')
+
def __initToolsMenu(self):
if self.toolsMenu is None:
self.toolsMenu = Qt.QMenu("Tools")
self.toolsMenu.addAction(taurus.qt.qtgui.resource.getIcon(":/apps/preferences-system-session.svg"),"manage instrument-panel associations", self.onShowAssociationDialog)
+ self.toolsMenu.addAction(taurus.qt.qtgui.resource.getThemeIcon("document-save"),"Export current Panel configuration to XML", self.onExportCurrentPanelConfiguration)
+ self.toolsMenu.addAction(taurus.qt.qtgui.resource.getIcon(":/actions/data-transfer.svg"),"Show Shared Data Manager connections", self.showSDMInfo)
def setCustomWidgetMap(self, map):
'''
@@ -346,17 +368,22 @@ class TaurusGui(TaurusMainWindow):
def createConfig(self, *args, **kwargs):
'''reimplemented from TaurusMainWindow.createConfig'''
self.updatePermanentCustomPanels(showAlways=False)
- return TaurusMainWindow.createConfig(self, *args, **kwargs)
-
+ return TaurusMainWindow.createConfig(self, *args, **kwargs)
+
def removePanel(self, name=None):
- ''' remove the given panel from the GUI
+ ''' remove the given panel from the GUI.
+
+ .. note:: The panel; is actually removed from the current perspective.
+ If the panel is saved in other perspectives, it should be removed from
+ them as well.
:param name: (str or None) the name of the panel to be removed
If None given, the user will be prompted
'''
if name is None:
items = sorted(self.getPanelNames())
- name,ok = Qt.QInputDialog.getItem (self, "Remove Panel", "Panel to be removed", items, 0, False)
+ name,ok = Qt.QInputDialog.getItem (self, "Remove Panel",
+ "Panel to be removed.\n Important: you may want to save the perspective afterwards,\n and maybe remove the panel from other perspectives as well", items, 0, False)
if not ok:
return
name = unicode(name)
@@ -488,18 +515,20 @@ class TaurusGui(TaurusMainWindow):
perm = self._getPermanentCustomPanels()
temp = [n for n,p in self.__panels.iteritems() if (p.isCustom() and not p.isPermanent())]
if len(temp)>0 or showAlways:
- dlg = PermanentCustomPanelsDlg(temporaryList=temp, permanentList=perm)
+ dlg = QDoubleListDlg(winTitle='Stored panels',
+ mainLabel='Select which of the panels should be stored',
+ label1='Temporary (to be discarded)', label2='Permanent (to be stored)',
+ list1=temp, list2=perm )
result = dlg.exec_()
if result == Qt.QDialog.Accepted:
#update the permanent Custom Panels
registered = self.getConfigurableItemNames()
- perm = dlg.getPermanentPanels()
- for name in perm:
+ for name in dlg.getAll2():
if name not in registered:
self.__panels[name].setPermanent(True)
self.registerConfigDelegate(self.__panels[name], name)
#unregister any panel that is temporary
- for name in dlg.getTemporaryPanels():
+ for name in dlg.getAll1():
self.__panels[name].setPermanent(False)
self.unregisterConfigurableItem(name, raiseOnError=False)
@@ -514,7 +543,7 @@ class TaurusGui(TaurusMainWindow):
if paneldesc is None:
from taurus.qt.qtgui.taurusgui import PanelDescriptionWizard
- paneldesc,ok = PanelDescriptionWizard.getDialog(self)
+ paneldesc,ok = PanelDescriptionWizard.getDialog(self, extraWidgets=self._extraCatalogWidgets)
if not ok:
return
w = paneldesc.getWidget(sdm=Qt.qApp.SDM, setModel=False)
@@ -544,7 +573,7 @@ class TaurusGui(TaurusMainWindow):
synoptic.setModel(jdwFileName)
self.__synoptics.append(synoptic)
except Exception,e:
- print repr(e)
+ #print repr(e)
msg='Error loading synoptic file "%s".\nSynoptic won\'t be available'%jdwFileName
self.error(msg)
self.traceback(level=taurus.Info)
@@ -569,13 +598,26 @@ class TaurusGui(TaurusMainWindow):
icon=taurus.qt.qtgui.resource.getThemeIcon('image-x-generic'))
toggleSynopticAction = synopticpanel.toggleViewAction()
self.quickAccessToolBar.addAction(toggleSynopticAction)
-
+
+ def createConsole(self, kernels):
+ try:
+ from taurus.qt.qtgui.console import TaurusConsole
+ except ImportError:
+ self.warning('Cannot import taurus.qt.qtgui.console. The Console Panel will not be available')
+ return
+ console = TaurusConsole(kernels=kernels)
+ consolePanel = self.createPanel(console, "Console", permanent=True,
+ icon=taurus.qt.qtgui.resource.getThemeIcon('utilities-terminal'))
+ toggleConsoleAction = consolePanel.toggleViewAction()
+ self.quickAccessToolBar.addAction(toggleConsoleAction)
+
def createInstrumentsFromPool(self, macroservername):
- '''creates a list of instrument objects
+ '''
+ Creates a list of instrument panel descriptions by gathering the info
+ from the Pool. Each panel is a TaurusForm grouping together all those
+ elements that belong to the same instrument according to the Pool info
- :return: (list<object>) A list of instrument objects. Each instrument
- object has "name" (str) and "model" (list<str>) members
-
+ :return: (list<PanelDescription>)
'''
instrument_dict = {}
try:
@@ -595,28 +637,19 @@ class TaurusGui(TaurusMainWindow):
i_view = PanelDescription(i_name,classname='TaurusForm', floating=False, model=[])
instrument_dict[i_name] = i_view
-# motors = sorted(ms.getElementNamesWithInterface('Moveable'))
-# channels = sorted(ms.getElementNamesWithInterface('ExpChannel'))
-# ioregisters = sorted(ms.getElementNamesWithInterface('IORegister'))
-
-# pool_elements = motors + channels + ioregisters
-# for e_name in pool_elements:
-# e = taurus.Device(e_name)
-# instrument = e['Instrument'].value
-# if instrument != '':
-# i_name = instrument
-# e_name = e.alias()
-# instrument_dict[i_name].model.append(e_name)
-
- pool_elements = ms.getElementsWithInterfaces(('Moveable', 'ExpChannel', 'IORegister'))
- for elem_name, elem in pool_elements.items():
+ from operator import attrgetter
+ pool_elements = sorted(ms.getElementsWithInterface('Moveable').values(), key=attrgetter('name'))
+ pool_elements += sorted(ms.getElementsWithInterface('ExpChannel').values(), key=attrgetter('name'))
+ pool_elements += sorted(ms.getElementsWithInterface('IORegister').values(), key=attrgetter('name'))
+ for elem in pool_elements:
instrument = elem.instrument
if instrument:
i_name = instrument
e_name = elem.full_name
instrument_dict[i_name].model.append(e_name)
-
- return instrument_dict.values()
+ #filter out empty panels
+ ret = [instrument for instrument in instrument_dict.values() if len(instrument.model)>0]
+ return ret
def __getVarFromXML(self, root, nodename, default=None):
name = root.find(nodename)
@@ -643,8 +676,8 @@ class TaurusGui(TaurusMainWindow):
import imp
path, name = os.path.split(confname)
name, ext = os.path.splitext(name)
- file, filename, data = imp.find_module(name, [path])
- conf = imp.load_module(name, file, filename, data)
+ f, filename, data = imp.find_module(name, [path])
+ conf = imp.load_module(name, f, filename, data)
else: #if confname is not a dir name, we assume it is a module name in the python path
confsubdir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'conf') #the path to a conf subdirectory of the place where taurusgui.py is
oldpath = sys.path
@@ -662,7 +695,8 @@ class TaurusGui(TaurusMainWindow):
sys.exit()
sys.path = oldpath #restore the previous sys.path
except Exception, e:
- msg = 'Error loading configuration: %s'%repr(e)
+ import traceback
+ msg = 'Error loading configuration: %s'%traceback.format_exc() #repr(e)
self.error(msg)
Qt.QMessageBox.critical(self,'Initialization error', msg, Qt.QMessageBox.Abort)
sys.exit()
@@ -672,6 +706,10 @@ class TaurusGui(TaurusMainWindow):
#Get the xml root node from the xml configuration file
XML_CONFIG = getattr(conf,'XML_CONFIG', None)
+ if XML_CONFIG is None:
+ self._xmlConfigFileName = None
+ else:
+ self._xmlConfigFileName = os.path.join(self._confDirectory, XML_CONFIG)
xmlroot = etree.fromstring('<root></root>') #default fallback (in case of I/O or parse errors)
if XML_CONFIG is not None:
try:
@@ -699,6 +737,13 @@ class TaurusGui(TaurusMainWindow):
Qt.qApp.setApplicationName(APPNAME)
Qt.qApp.setOrganizationName(ORGNAME)
Qt.QApplication.instance().basicConfig()
+
+ ORGANIZATIONLOGO = getattr(conf, 'ORGANIZATION_LOGO', self.__getVarFromXML(xmlroot,"ORGANIZATION_LOGO", ':/logo.png'))
+ ##
+ if ORGANIZATIONLOGO.startswith(':'):
+ organizationIcon = taurus.qt.qtgui.resource.getIcon(ORGANIZATIONLOGO)
+ else:
+ organizationIcon = Qt.QIcon(os.path.join(self._confDirectory, ORGANIZATIONLOGO))
#if required, enforce that only one instance of this GUI can be run
SINGLEINSTANCE = getattr(conf,'SINGLE_INSTANCE', (self.__getVarFromXML(xmlroot,"SINGLE_INSTANCE", 'True').lower() == 'true') )
@@ -713,9 +758,18 @@ class TaurusGui(TaurusMainWindow):
self.resetQSettings()
self.setWindowTitle(APPNAME)
self.setWindowIcon(customIcon)
- self.jorgsBar.addAction(taurus.qt.qtgui.resource.getIcon(":/logo.png"),ORGNAME)
+ self.jorgsBar.addAction(organizationIcon,ORGNAME)
self.jorgsBar.addAction(customIcon,APPNAME)
+ #get custom widget catalog entries
+ EXTRA_CATALOG_WIDGETS = getattr(conf,'EXTRA_CATALOG_WIDGETS', []) #@todo: support also loading from xml
+ self._extraCatalogWidgets = []
+ for classname,pixmapname in EXTRA_CATALOG_WIDGETS:
+ if pixmapname and not pixmapname.startswith(":"): # If a relative file name is given, the conf directory will be used as base path
+ pixmapname = os.path.join(self._confDirectory,pixmapname)
+ self._extraCatalogWidgets.append((classname, pixmapname))
+
+
#manual panel
MANUAL_URI = getattr(conf,'MANUAL_URI', self.__getVarFromXML(xmlroot,"MANUAL_URI", None))
if MANUAL_URI is not None:
@@ -740,7 +794,7 @@ class TaurusGui(TaurusMainWindow):
from taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor.macroparameterseditor import ParamEditorManager
ParamEditorManager().parsePaths(MACROEDITORS_PATH)
ParamEditorManager().browsePaths()
-
+
#Synoptics
SYNOPTIC = getattr(conf, 'SYNOPTIC', None)
if isinstance(SYNOPTIC, basestring): #old config file style
@@ -751,19 +805,25 @@ class TaurusGui(TaurusMainWindow):
node = xmlroot.find("SYNOPTIC")
if (node is not None) and (node.text is not None):
for child in node:
- str = child.get("str")
- if str is not None and len(str): #we do not append empty strings
- SYNOPTIC.append(str)
+ s = child.get("str")
+ if s is not None and len(s): #we do not append empty strings
+ SYNOPTIC.append(s)
for s in SYNOPTIC:
self.createMainSynoptic(s)
- #Get panel descriptions from pool if required
+ #Get panel descriptions from pool if required
INSTRUMENTS_FROM_POOL = getattr(conf,'INSTRUMENTS_FROM_POOL', (self.__getVarFromXML(xmlroot,"INSTRUMENTS_FROM_POOL", 'False').lower() == 'true') )
if INSTRUMENTS_FROM_POOL:
+ try: self.splashScreen().showMessage("Gathering Instrument info from Pool")
+ except AttributeError: pass
POOLINSTRUMENTS = self.createInstrumentsFromPool(MACROSERVER_NAME) #auto create instruments from pool
else:
POOLINSTRUMENTS = []
-
+
+ CONSOLE = getattr(conf,'CONSOLE', self.__getVarFromXML(xmlroot,"CONSOLE", ['ipython']))
+ if CONSOLE:
+ self.createConsole(CONSOLE)
+
#get custom panel descriptions from the python config file
CUSTOM_PANELS = [obj for name,obj in inspect.getmembers(conf) if isinstance(obj, PanelDescription)]
@@ -779,16 +839,18 @@ class TaurusGui(TaurusMainWindow):
#create panels based on the panel descriptions gathered before
for p in CUSTOM_PANELS + POOLINSTRUMENTS:
try:
+ try: self.splashScreen().showMessage("Creating panel %s"%p.name)
+ except AttributeError: pass
w = p.getWidget(sdm=Qt.qApp.SDM, setModel=False)
if hasattr(w,'setCustomWidgetMap'):
w.setCustomWidgetMap(self.getCustomWidgetMap())
if p.model is not None:
w.setModel(p.model)
if p.instrumentkey is None:
- instrumentkey = self.IMPLICIT_ASSOCIATION
+ instrumentkey = self.IMPLICIT_ASSOCIATION
+ registerconfig = p not in POOLINSTRUMENTS #the pool instruments may change when the pool config changes, so we do not store their config
#create a panel
- self.createPanel(w, p.name, floating=p.floating, instrumentkey=instrumentkey, permanent=True)
-
+ self.createPanel(w, p.name, floating=p.floating, registerconfig=registerconfig, instrumentkey=instrumentkey, permanent=True)
except Exception,e:
msg='Cannot create panel %s'%getattr(p,'name','__Unknown__')
self.error(msg)
@@ -812,6 +874,8 @@ class TaurusGui(TaurusMainWindow):
#create toolbars based on the descriptions gathered before
for d in CUSTOM_TOOLBARS:
try:
+ try: self.splashScreen().showMessage("Creating Toolbar %s"%d.name)
+ except AttributeError: pass
w = d.getWidget(sdm=Qt.qApp.SDM, setModel=False)
if d.model is not None:
w.setModel(d.model)
@@ -837,7 +901,7 @@ class TaurusGui(TaurusMainWindow):
MONITOR = getattr(conf, 'MONITOR', self.__getVarFromXML(xmlroot,"MONITOR", []))
if MONITOR:
CUSTOM_APPLETS.append(AppletDescription('monitor', classname='TaurusMonitorTiny', model=MONITOR) )
-
+
#get custom applet descriptions from the python config file
CUSTOM_APPLETS += [obj for name,obj in inspect.getmembers(conf) if isinstance(obj, AppletDescription)]
@@ -853,6 +917,8 @@ class TaurusGui(TaurusMainWindow):
#create applet based on the descriptions gathered before
for d in CUSTOM_APPLETS:
try:
+ try: self.splashScreen().showMessage("Creating applet %s"%d.name)
+ except AttributeError: pass
w = d.getWidget(sdm=Qt.qApp.SDM, setModel=False)
if d.model is not None:
w.setModel(d.model)
@@ -883,11 +949,18 @@ class TaurusGui(TaurusMainWindow):
for a in EXTERNAL_APPS:
self.addExternalAppLauncher(a.getAction())
+
+
#get the "factory settings" filename. By default, it is called "default.ini" and resides in the configuration dir
INIFILE = getattr(conf, 'INIFILE', self.__getVarFromXML(xmlroot,"INIFILE", "default.ini"))
iniFileName = os.path.join(self._confDirectory, INIFILE) #if a relative name is given, the conf dir is used as the root path
#read the settings (or the factory settings if the regular file is not found)
+ msg = "Loading previous state"
+ if self.defaultConfigRecursionDepth >=0:
+ msg += " in Fail Proof mode"
+ try: self.splashScreen().showMessage(msg)
+ except AttributeError: pass
self.loadSettings(factorySettingsFileName=iniFileName)
def setLockView(self, locked):
@@ -915,54 +988,6 @@ class TaurusGui(TaurusMainWindow):
'''
self.statusBar().showMessage(msg)
-# def onDoorNameChanged(self, doorname):
-# ''' Slot to be called when the door name has changed
-#
-# :param doorname: (str) the tango name of the door device
-# '''
-# if getattr(self, '__qdoor',None) is not None: #disconnect it from *all* shared data providing
-# Qt.qApp.SDM.disconnectWriter("macroStatus", self.__qdoor, "macroStatusUpdated")
-# Qt.qApp.SDM.disconnectWriter("doorOutputChanged", self.__qdoor, "outputUpdated")
-# Qt.qApp.SDM.disconnectWriter("doorInfoChanged", self.__qdoor, "infoUpdated")
-# Qt.qApp.SDM.disconnectWriter("doorWarningChanged", self.__qdoor, "warningUpdated")
-# Qt.qApp.SDM.disconnectWriter("doorErrorChanged", self.__qdoor, "errorUpdated")
-# Qt.qApp.SDM.disconnectWriter("doorDebugChanged", self.__qdoor, "debugUpdated")
-# Qt.qApp.SDM.disconnectWriter("doorResultChanged", self.__qdoor, "resultUpdated")
-#
-# if doorname == "": return #@todo send signal with doorName to macroExecutorWidget in case of "" also send it to disconnect doorstateled
-# door = taurus.Device(doorname)
-# if not isinstance(door, Qt.QObject):
-# msg= "cannot connect to door %s"%doorname
-# Qt.QMessageBox.critical(self,'Door connection error', msg)
-# return
-# self.__qdoor = door
-# Qt.qApp.SDM.connectWriter("macroStatus", self.__qdoor, "macroStatusUpdated")
-# Qt.qApp.SDM.connectWriter("doorOutputChanged", self.__qdoor, "outputUpdated")
-# Qt.qApp.SDM.connectWriter("doorInfoChanged", self.__qdoor, "infoUpdated")
-# Qt.qApp.SDM.connectWriter("doorWarningChanged", self.__qdoor, "warningUpdated")
-# Qt.qApp.SDM.connectWriter("doorErrorChanged", self.__qdoor, "errorUpdated")
-# Qt.qApp.SDM.connectWriter("doorDebugChanged", self.__qdoor, "debugUpdated")
-# Qt.qApp.SDM.connectWriter("doorResultChanged", self.__qdoor, "resultUpdated")
-# #@todo: connect as a writer of other data as well
-
-# def onSelectedInstrumentChanged(self, instrumentname):
-# ''' Slot to be called when the selected instrument has changed (e.g. by user
-# clicking in the synoptic)
-#
-# :param instrumentname: (str) The name that identifies the instrument.
-# This name must be unique within the panels in the
-# GUI. This is also the name to be used for the
-# corresponding object in the jdraw synoptic
-# '''
-# instrumentname=unicode(instrumentname)
-# try:
-# inst = self.__panels[instrumentname]
-# inst.show()
-# inst.setFocus()
-# inst.raise_()
-# except KeyError:
-# pass
-
def hideAllPanels(self):
'''hides all current panels'''
for panel in self.__panels.itervalues():
@@ -1018,14 +1043,21 @@ class TaurusGui(TaurusMainWindow):
'''
return copy.deepcopy(self.__instrumentToPanelMap)
- def setAllInstrumentAssociations(self, associationsdict):
+ def setAllInstrumentAssociations(self, associationsdict, clearExisting=False):
'''
- Sets the dictionary of instrument-panel associations
-
- :return: (dict<str,str>) a dict whose keys are the instruments names
- and whose values are the corresponding associated panels (or None).
+ Sets the dictionary of instrument-panel associations.
+ By default, it keeps any existing association not present in the associationsdict.
+
+ :param associationsdict: (dict<str,str>) a dict whose keys are the instruments names
+ and whose values are the corresponding associated panels (or None)
+ :param clearExisting: (bool) if True, the the existing asociations are cleared.
+ If False (default) existing associations are
+ updated with those in associationsdict
'''
- self.__instrumentToPanelMap = copy.deepcopy(associationsdict)
+ if clearExisting:
+ self.__instrumentToPanelMap=copy.deepcopy(associationsdict)
+ else:
+ self.__instrumentToPanelMap.update(copy.deepcopy(associationsdict))
self.__panelToInstrumentMap = {}
for k,v in self.__instrumentToPanelMap.iteritems():
self.__panelToInstrumentMap[v]=k
@@ -1101,7 +1133,97 @@ class TaurusGui(TaurusMainWindow):
'''reimplemented from :class:`TaurusMainWindow` to show the manual in a panel (not just a dockwidget)'''
self.setFocusToPanel('Manual')
-
+ def onExportCurrentPanelConfiguration(self, fname=None):
+
+ if fname is None:
+ fname = self._xmlConfigFileName
+
+ if self._xmlConfigFileName is None:
+ xmlroot = etree.Element("taurusgui_config")
+ else:
+ try:
+ f = open(self._xmlConfigFileName,'r')
+ xmlroot = etree.fromstring(f.read())
+ f.close()
+ except Exception,e:
+ self.error('Cannot parse file "%s": %s',self._xmlConfigFileName, str(e))
+ return
+
+ #retrieve/create the PanelDescriptions node
+ panelDescriptionsNode = xmlroot.find("PanelDescriptions")
+ if panelDescriptionsNode is None:
+ panelDescriptionsNode = etree.SubElement(xmlroot, "PanelDescriptions")
+
+ #Get all custom panels
+ dlg = QDoubleListDlg(winTitle='Export Panels to XML',
+ mainLabel='Select which of the custom panels you want to export as xml configuration',
+ label1='Not Exported', label2='Exported',
+ list1=[n for n,p in self.__panels.iteritems() if p.isCustom()], list2=[] )
+ result = dlg.exec_()
+ if result != Qt.QDialog.Accepted:
+ return
+ exportlist = dlg.getAll2()
+
+ #create xml for those to be exported
+ registered = self.getConfigurableItemNames()
+ for name in exportlist:
+ panel = self.__panels[name]
+ if name not in registered:
+ panel.setPermanent(True)
+ self.registerConfigDelegate(panel, name)
+ panelxml = PanelDescription.fromPanel(panel).toXml()
+ panelDescriptionsNode.append(etree.fromstring(panelxml))
+ xml = etree.tostring(xmlroot, pretty_print=True)
+
+ #write to file
+ while True:
+ if fname is None:
+ fname = Qt.QFileDialog.getOpenFileName(self, "Open File", fname or self._confDirectory, self.tr("XML files (*.xml)"))
+ if not fname:
+ return
+ fname = str(fname)
+ #backup the file
+ if os.path.exists(fname):
+ import shutil
+ try:
+ bckname = "%s.orig"%fname
+ shutil.copy(fname,bckname)
+ except:
+ self.warning("%s will be overwritten but I could not create a backup in %s", fname,bckname)
+ #write the data
+ try:
+ f = open(fname,'w')
+ f.write(xml)
+ f.close()
+ break
+ except Exception,e:
+ msg = 'Cannot write to %s: %s'%(fname,str(e))
+ self.error(msg)
+ Qt.QMessageBox.warning(self, "I/O problem", msg+'\nChoose a different location.', Qt.QMessageBox.Ok, Qt.QMessageBox.NoButton)
+ fname = None
+
+
+ hint = "XML_CONFIG = %s"%os.path.relpath(fname, self._confDirectory)
+ msg = 'Configuration written in %s'%fname
+ self.info(msg)
+ Qt.QMessageBox.information(self, "Configuration updated",
+ msg+'\nMake sure that the .py configuration file in %s contains\n%s'%(self._confDirectory, hint),
+ Qt.QMessageBox.Ok, Qt.QMessageBox.NoButton)
+
+ return
+
+ def showSDMInfo(self):
+ '''pops up a dialog showing the current information from the Shared Data Manager'''
+ #w = Qt.QMessageBox( self)
+ text = 'Currently managing %i shared data objects:\n%s'%(len(Qt.qApp.SDM.activeDataUIDs()),', '.join(Qt.qApp.SDM.activeDataUIDs()) )
+ nfo = Qt.qApp.SDM.info()
+ w = Qt.QMessageBox ( Qt.QMessageBox.Information, 'Shared Data Manager Information', text,
+ buttons = Qt.QMessageBox.Close, parent = self )
+ w.setDetailedText(nfo)
+ w.show()
+ self.info(nfo)
+
+
#------------------------------------------------------------------------------
def main():
import sys
@@ -1118,12 +1240,14 @@ def main():
help="use the given configuration directory for initialization")
parser.add_option("", "--new-gui", action="store_true", dest="new_gui", default=None,
help="launch a wizard for creating a new TaurusGUI application")
+ parser.add_option("", "--fail-proof", action="store_true", dest="fail_proof", default=None,
+ help="launch in fail proof mode (it prevents potentially problematic configs from being loaded)")
app = TaurusApplication(cmd_line_parser=parser, app_name="taurusgui",
app_version=taurus.Release.version)
args = app.get_command_line_args()
options = app.get_command_line_options()
-
+
if options.new_gui: #launch app settings wizard instead of taurusgui
from taurus.qt.qtgui.taurusgui import AppSettingsWizard
Qt.QMessageBox.information(None, 'Alpha-quality warning',
@@ -1134,13 +1258,19 @@ def main():
sys.exit(app.exec_())
confname = options.config_dir
- if confname is None and len(args) == 1: #for backwards compat, we allow to specify the confname without the "--config-dir" parameter
- confname = args[0]
+ if confname is None:
+ if len(args) == 1: #for backwards compat, we allow to specify the confname without the "--config-dir" parameter
+ confname = args[0]
+ else:
+ parser.print_help(sys.stderr)
+ sys.exit(1)
+
+ if options.fail_proof:
+ configRecursionDepth = 0
else:
- parser.print_help(sys.stderr)
- sys.exit(1)
+ configRecursionDepth = None
- gui = TaurusGui(None, confname=confname)
+ gui = TaurusGui(None, confname=confname, configRecursionDepth=configRecursionDepth)
gui.show()
ret = app.exec_()
diff --git a/lib/taurus/qt/qtgui/taurusgui/ui/ui_PermanentCustomPanelsDlg.py b/lib/taurus/qt/qtgui/taurusgui/ui/ui_PermanentCustomPanelsDlg.py
deleted file mode 100644
index d8c0405..0000000
--- a/lib/taurus/qt/qtgui/taurusgui/ui/ui_PermanentCustomPanelsDlg.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Form implementation generated from reading ui file 'PermanentCustomPanelsDlg.ui'
-#
-# Created: Wed Sep 15 14:55:07 2010
-# by: PyQt4 UI code generator 4.4.3
-#
-# WARNING! All changes made in this file will be lost!
-
-from PyQt4 import QtCore, QtGui
-
-class Ui_PermanentCustomPanelsDlg(object):
- def setupUi(self, PermanentCustomPanelsDlg):
- PermanentCustomPanelsDlg.setObjectName("PermanentCustomPanelsDlg")
- PermanentCustomPanelsDlg.resize(533, 426)
- PermanentCustomPanelsDlg.setModal(True)
- self.verticalLayout_4 = QtGui.QVBoxLayout(PermanentCustomPanelsDlg)
- self.verticalLayout_4.setObjectName("verticalLayout_4")
- self.label = QtGui.QLabel(PermanentCustomPanelsDlg)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Maximum)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
- self.label.setSizePolicy(sizePolicy)
- self.label.setWordWrap(True)
- self.label.setMargin(6)
- self.label.setObjectName("label")
- self.verticalLayout_4.addWidget(self.label)
- self.horizontalLayout = QtGui.QHBoxLayout()
- self.horizontalLayout.setObjectName("horizontalLayout")
- self.groupBox_2 = QtGui.QGroupBox(PermanentCustomPanelsDlg)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth())
- self.groupBox_2.setSizePolicy(sizePolicy)
- self.groupBox_2.setObjectName("groupBox_2")
- self.verticalLayout_2 = QtGui.QVBoxLayout(self.groupBox_2)
- self.verticalLayout_2.setObjectName("verticalLayout_2")
- self.temporaryPanelsList = QtGui.QListWidget(self.groupBox_2)
- self.temporaryPanelsList.setDragEnabled(True)
- self.temporaryPanelsList.setDragDropOverwriteMode(True)
- self.temporaryPanelsList.setDragDropMode(QtGui.QAbstractItemView.NoDragDrop)
- self.temporaryPanelsList.setAlternatingRowColors(True)
- self.temporaryPanelsList.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
- self.temporaryPanelsList.setObjectName("temporaryPanelsList")
- self.verticalLayout_2.addWidget(self.temporaryPanelsList)
- self.horizontalLayout.addWidget(self.groupBox_2)
- self.verticalLayout_3 = QtGui.QVBoxLayout()
- self.verticalLayout_3.setObjectName("verticalLayout_3")
- spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem)
- self.toPermanentBT = QtGui.QToolButton(PermanentCustomPanelsDlg)
- self.toPermanentBT.setArrowType(QtCore.Qt.RightArrow)
- self.toPermanentBT.setObjectName("toPermanentBT")
- self.verticalLayout_3.addWidget(self.toPermanentBT)
- self.toTemporaryBT = QtGui.QToolButton(PermanentCustomPanelsDlg)
- self.toTemporaryBT.setArrowType(QtCore.Qt.LeftArrow)
- self.toTemporaryBT.setObjectName("toTemporaryBT")
- self.verticalLayout_3.addWidget(self.toTemporaryBT)
- spacerItem1 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem1)
- self.horizontalLayout.addLayout(self.verticalLayout_3)
- self.groupBox = QtGui.QGroupBox(PermanentCustomPanelsDlg)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
- self.groupBox.setSizePolicy(sizePolicy)
- self.groupBox.setObjectName("groupBox")
- self.verticalLayout = QtGui.QVBoxLayout(self.groupBox)
- self.verticalLayout.setObjectName("verticalLayout")
- self.permanentPanelsList = QtGui.QListWidget(self.groupBox)
- self.permanentPanelsList.setDragEnabled(True)
- self.permanentPanelsList.setDragDropOverwriteMode(True)
- self.permanentPanelsList.setDragDropMode(QtGui.QAbstractItemView.NoDragDrop)
- self.permanentPanelsList.setAlternatingRowColors(True)
- self.permanentPanelsList.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
- self.permanentPanelsList.setObjectName("permanentPanelsList")
- self.verticalLayout.addWidget(self.permanentPanelsList)
- self.horizontalLayout.addWidget(self.groupBox)
- self.verticalLayout_4.addLayout(self.horizontalLayout)
- self.buttonBox = QtGui.QDialogButtonBox(PermanentCustomPanelsDlg)
- self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
- self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
- self.buttonBox.setObjectName("buttonBox")
- self.verticalLayout_4.addWidget(self.buttonBox)
-
- self.retranslateUi(PermanentCustomPanelsDlg)
- QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), PermanentCustomPanelsDlg.accept)
- QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), PermanentCustomPanelsDlg.reject)
- QtCore.QMetaObject.connectSlotsByName(PermanentCustomPanelsDlg)
-
- def retranslateUi(self, PermanentCustomPanelsDlg):
- PermanentCustomPanelsDlg.setWindowTitle(QtGui.QApplication.translate("PermanentCustomPanelsDlg", "Permanent Panels Dialog", None, QtGui.QApplication.UnicodeUTF8))
- self.label.setText(QtGui.QApplication.translate("PermanentCustomPanelsDlg", "Please select which Custom panels you want to store permanently.", None, QtGui.QApplication.UnicodeUTF8))
- self.groupBox_2.setTitle(QtGui.QApplication.translate("PermanentCustomPanelsDlg", "Temporary Custom Panels", None, QtGui.QApplication.UnicodeUTF8))
- self.temporaryPanelsList.setSortingEnabled(True)
- self.toPermanentBT.setText(QtGui.QApplication.translate("PermanentCustomPanelsDlg", "...", None, QtGui.QApplication.UnicodeUTF8))
- self.toTemporaryBT.setText(QtGui.QApplication.translate("PermanentCustomPanelsDlg", "...", None, QtGui.QApplication.UnicodeUTF8))
- self.groupBox.setTitle(QtGui.QApplication.translate("PermanentCustomPanelsDlg", "Stored Custom Panels", None, QtGui.QApplication.UnicodeUTF8))
- self.permanentPanelsList.setSortingEnabled(True)
-
-
-if __name__ == "__main__":
- import sys
- app = QtGui.QApplication(sys.argv)
- PermanentCustomPanelsDlg = QtGui.QDialog()
- ui = Ui_PermanentCustomPanelsDlg()
- ui.setupUi(PermanentCustomPanelsDlg)
- PermanentCustomPanelsDlg.show()
- sys.exit(app.exec_())
-
diff --git a/lib/taurus/qt/qtgui/taurusgui/utils.py b/lib/taurus/qt/qtgui/taurusgui/utils.py
index ce81f6e..4719e00 100644
--- a/lib/taurus/qt/qtgui/taurusgui/utils.py
+++ b/lib/taurus/qt/qtgui/taurusgui/utils.py
@@ -52,11 +52,11 @@ class ExternalApp(object):
Uses the same initialization as that of :class:`ExternalAppAction`
Use :meth:`getAction` to obtain an instance of a :class:`ExternalAppAction`
'''
-# def __init__(self, cmdargs, text=None, icon=None, parent=None):
def __init__(self, *args, **kwargs):
''' see :meth:`ExternalAppAction.__init__`'''
self.args = args
self.kwargs = kwargs
+
def getAction(self):
'''
Returns a :class:`ExternalAppAction` with the values used when
@@ -114,12 +114,12 @@ class TaurusGuiComponentDescription(object):
def __init__(self,name, classname=None, modulename=None, widgetname=None,
sharedDataWrite=None, sharedDataRead=None,
model=None, floating=True, **kwargs):
- if classname is None and (modulename is None or widgetname is None) :
- raise ValueError('Either classname or both modulename and widgetname must be given')
self._name = name
- self._classname = classname
self._modulename = modulename
- self._widgetname = widgetname
+ self.setClassname(classname)
+ self.setWidgetname(widgetname)
+ if self.classname is None and (self.modulename is None or self.widgetname is None) :
+ raise ValueError('Module info must be given (except if passing a Taurus class name)')
self._floating = floating
if sharedDataWrite is None: sharedDataWrite = {}
self._sharedDataWrite = sharedDataWrite
@@ -137,6 +137,9 @@ class TaurusGuiComponentDescription(object):
return self._classname
def setClassname(self, classname):
+ if classname is not None and '.' in classname:
+ modulename,classname = classname.rsplit('.',1)
+ self.setModulename(modulename)
self._classname = classname
def getModulename(self):
@@ -149,6 +152,9 @@ class TaurusGuiComponentDescription(object):
return self._widgetname
def setWidgetname(self, widgetname):
+ if widgetname is not None and '.' in widgetname:
+ modulename,widgetname = widgetname.rsplit('.',1)
+ self.setModulename(modulename)
self._widgetname = widgetname
def getArea(self):
@@ -346,6 +352,23 @@ class PanelDescription(TaurusGuiComponentDescription):
self.instrumentkey = kwargs.pop('instrumentkey', None)
TaurusGuiComponentDescription.__init__(self, *args, **kwargs)
+ @staticmethod
+ def fromPanel(panel):
+ name = str(panel.objectName())
+ classname = panel.getWidgetClassName()
+ modulename = None
+ widgetname = None
+ floating = panel.isFloating()
+ sharedDataWrite = None
+ sharedDataRead = None
+ model = getattr(panel.widget(),'model',None)
+ if model is not None and not isinstance(model,basestring):#if it is not a string or None, we assume it is a sequence of strings,...
+ model = " ".join(model) #...and we convert it to a space-separated string
+
+ return PanelDescription(name, classname=classname, modulename=modulename, widgetname=widgetname,
+ floating=floating, sharedDataWrite=sharedDataWrite, sharedDataRead=sharedDataRead,
+ model=model)
+
class ToolBarDescription(TaurusGuiComponentDescription):
'''
diff --git a/lib/taurus/qt/qtgui/tree/qtree.py b/lib/taurus/qt/qtgui/tree/qtree.py
index e682dd4..c0e635b 100644
--- a/lib/taurus/qt/qtgui/tree/qtree.py
+++ b/lib/taurus/qt/qtgui/tree/qtree.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -37,11 +37,11 @@ from taurus.qt.qtgui.resource import getIcon, getThemeIcon
class _NavigationLabel(Qt.QWidget):
"""Internal widget providing a navigation label & icon"""
-
+
def __init__(self, pixmap, text, cont, index, parent=None):
super(_NavigationLabel, self).__init__(parent)
self._index = index
-
+
l = Qt.QHBoxLayout()
l.setContentsMargins(0,0,0,0)
self.setLayout(l)
@@ -53,17 +53,17 @@ class _NavigationLabel(Qt.QWidget):
l.addWidget(self._label)
if cont:
l.addWidget(Qt.QLabel(u" \u00bb "))
-
+
def label(self):
return self._label
-
+
def index(self):
return self._index
class _NavigationWidget(Qt.QFrame):
"""Internal widget that provides a navigation path to be placed in a toolbar"""
-
+
def __init__(self, treeWidget, toolBarWidget, parent=None):
super(_NavigationWidget, self).__init__(parent)
self._tree = treeWidget
@@ -71,7 +71,7 @@ class _NavigationWidget(Qt.QFrame):
l = Qt.QHBoxLayout()
l.setContentsMargins(4, 0, 4, 0)
self.setLayout(l)
-
+
def treeWidget(self):
return self._tree
@@ -80,7 +80,7 @@ class _NavigationWidget(Qt.QFrame):
def toolBarWidget(self):
return self._toolbar
-
+
def clean(self):
l = self.layout()
w = l.takeAt(0)
@@ -97,10 +97,9 @@ class _NavigationWidget(Qt.QFrame):
model = viewWidget.model()
src_model = treeWidget.getBaseQModel()
toolbar = self.toolBarWidget()
- size = None
rootPixmap = getThemeIcon("go-home").pixmap(toolbar.iconSize())
rootText = u"Top"
-
+
l = self.layout()
n = 0
while index.isValid():
@@ -108,7 +107,7 @@ class _NavigationWidget(Qt.QFrame):
name = src_model.pyData(src_index, Qt.Qt.DisplayRole)
font = src_model.pyData(src_index, Qt.Qt.FontRole)
tooltip = src_model.pyData(src_index, Qt.Qt.ToolTipRole)
- decoration = src_model.pyData(src_index, Qt.Qt.DecorationRole)
+ decoration = src_model.pyData(src_index, Qt.Qt.DecorationRole)
pixmap = None
if isinstance(decoration, Qt.QIcon):
pixmap = decoration.pixmap(toolbar.iconSize())
@@ -124,11 +123,8 @@ class _NavigationWidget(Qt.QFrame):
n += 1
rootButton = _NavigationLabel(rootPixmap, rootText, n>0, Qt.QPersistentModelIndex(index))
rootButton.setFont(model.DftFont)
- #Qt.QObject.connect(rootButton.label(), Qt.SIGNAL("linkActivated(const QString &)"), self.onGotoNode)
l.insertWidget(0, rootButton)
-
- lastLabel = l.itemAt(l.count()-1).widget()
-
+
def onGotoNode(self, *args):
label = self.sender()
persistent_index = label.parent().index()
@@ -139,11 +135,11 @@ class _NavigationWidget(Qt.QFrame):
class NavigationToolBar(BaseToolBar):
-
+
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus selection toolbar", view=view,
parent=parent, designMode=designMode)
-
+
af = ActionFactory()
self._goIntoAction = af.createAction(self, "Go Into",
icon=getThemeIcon("go-down"),
@@ -165,13 +161,13 @@ class NavigationToolBar(BaseToolBar):
def goIntoAction(self):
return self._goIntoAction
-
+
def goTopAction(self):
return self._goTopAction
-
+
def goUpAction(self):
return self._goUpAction
-
+
def goInto(self):
self.emit(Qt.SIGNAL("goIntoTriggered"))
@@ -183,7 +179,7 @@ class NavigationToolBar(BaseToolBar):
class ExpansionBar(BaseToolBar):
-
+
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus selection toolbar", view=view,
parent=parent, designMode=designMode)
@@ -209,40 +205,53 @@ class ExpansionBar(BaseToolBar):
self.addAction(self._collapseAllAction)
self.addAction(self._expandSelectionAction)
self.addAction(self._collapseSelectionAction)
-
+
def onExpandAll(self):
self.emit(Qt.SIGNAL("expandTriggered"))
-
+
def onCollapseAll(self):
self.emit(Qt.SIGNAL("collapseTriggered"))
-
+
def onExpandSelection(self):
self.emit(Qt.SIGNAL("expandSelectionTriggered"))
-
+
def onCollapseSelection(self):
self.emit(Qt.SIGNAL("collapseSelectionTriggered"))
class QBaseTreeWidget(QBaseModelWidget):
"""A pure Qt tree widget implementing a tree with a navigation toolbar"""
-
+
def __init__(self, parent=None, designMode=False, with_navigation_bar=True,
- with_filter_widget=True):
- self._with_navigation_bar = with_navigation_bar
- QBaseModelWidget.__init__(self, parent, with_filter_widget=with_filter_widget)
-
+ with_filter_widget=True, with_selection_widget=True,
+ with_refresh_widget=True, perspective=None, proxy=None):
+
+ if with_navigation_bar:
+ if isinstance(with_navigation_bar, (bool, int)):
+ self._with_navigation_bar = NavigationToolBar
+ else:
+ self._with_navigation_bar = with_navigation_bar
+ else:
+ self._with_navigation_bar = None
+
+ QBaseModelWidget.__init__(self, parent,
+ with_filter_widget=with_filter_widget,
+ with_selection_widget=with_selection_widget,
+ with_refresh_widget=with_refresh_widget,
+ perspective=perspective, proxy=proxy)
+
def createToolArea(self):
ta = QBaseModelWidget.createToolArea(self)
-
+
e_bar = self._expandBar = ExpansionBar(view=self, parent=self)
self.connect(e_bar, Qt.SIGNAL("expandTriggered"), self.expandAllTree)
self.connect(e_bar, Qt.SIGNAL("collapseTriggered"), self.collapseAllTree)
self.connect(e_bar, Qt.SIGNAL("expandSelectionTriggered"), self.expandSelectionTree)
self.connect(e_bar, Qt.SIGNAL("collapseSelectionTriggered"), self.collapseSelectionTree)
ta.append(e_bar)
-
+
if self._with_navigation_bar:
- n_bar = self._navigationToolBar = NavigationToolBar(view=self, parent=self)
+ n_bar = self._navigationToolBar = self._with_navigation_bar(view=self, parent=self)
self.connect(n_bar, Qt.SIGNAL("goIntoTriggered"), self.goIntoTree)
self.connect(n_bar, Qt.SIGNAL("goTopTriggered"), self.goTopTree)
self.connect(n_bar, Qt.SIGNAL("goUpTriggered"), self.goUpTree)
@@ -250,9 +259,11 @@ class QBaseTreeWidget(QBaseModelWidget):
else:
self._navigationToolBar = None
return ta
-
- def createViewWidget(self):
- tree = Qt.QTreeView()
+
+ def createViewWidget(self, klass=None):
+ if klass is None:
+ klass = Qt.QTreeView
+ tree = klass()
tree.setSortingEnabled(True)
tree.setUniformRowHeights(True)
tree.setAlternatingRowColors(True)
@@ -267,47 +278,46 @@ class QBaseTreeWidget(QBaseModelWidget):
h = tree.header()
h.setResizeMode(0, Qt.QHeaderView.Stretch)
return tree
-
+
def treeView(self):
return self.viewWidget()
def goIntoAction(self):
return self._navigationToolBar.goIntoAction()
-
+
def goTopAction(self):
return self._navigationToolBar.goTopAction()
-
+
def goUpAction(self):
return self._navigationToolBar.goUpAction()
-
+
def expandAllTree(self):
self.statusBar().showMessage("Expanding all items... (it may take a few seconds)")
- tree = self.viewWidget()
Qt.QTimer.singleShot(0, self._expandTree)
-
+
def _expandTree(self):
tree = self.viewWidget()
tree.expandAll()
self.statusBar().showMessage("All items expanded!", 3000)
-
+
def onExpanded(self):
self.resizeColumns()
-
+
def collapseAllTree(self):
self.viewWidget().collapseAll()
-
+
def expandSelectionTree(self):
tree = self.viewWidget()
index = tree.currentIndex()
if index.isValid():
tree.expand(index)
-
+
def collapseSelectionTree(self):
tree = self.viewWidget()
index = tree.currentIndex()
if index.isValid():
tree.collapse(index)
-
+
def resizeColumns(self):
tree = self.viewWidget()
model = tree.model()
@@ -315,34 +325,34 @@ class QBaseTreeWidget(QBaseModelWidget):
return
for c in range(model.columnCount()):
tree.resizeColumnToContents(c)
-
+
def goIntoTree(self):
tree = self.viewWidget()
index = tree.currentIndex()
base_index = self._mapToSource(index)
-
+
if not index.isValid():
return
-
+
# do not enter if the item doesn't have any children
if base_index.internalPointer().childCount() == 0:
return
-
+
tree.setRootIndex(index)
tree.setCurrentIndex(index.child(0, 0))
self._updateToolBar()
-
+
def goUpTree(self):
tree = self.viewWidget()
index = tree.rootIndex()
if not index.isValid():
return
index_parent = index.parent()
-
+
tree.setRootIndex(index_parent)
tree.setCurrentIndex(index)
self._updateToolBar()
-
+
def goTopTree(self):
tree = self.viewWidget()
current_root = tree.rootIndex()
@@ -352,7 +362,7 @@ class QBaseTreeWidget(QBaseModelWidget):
tree.setRootIndex(p)
tree.setCurrentIndex(p)
self._updateToolBar()
-
+
def _updateToolBar(self, current=None, previous=None):
if not self._with_navigation_bar:
return
@@ -360,7 +370,7 @@ class QBaseTreeWidget(QBaseModelWidget):
if current is None:
current = tree.currentIndex()
goInto = False
-
+
base_current = self._mapToSource(current)
if current.isValid():
ip = base_current.internalPointer()
@@ -369,11 +379,11 @@ class QBaseTreeWidget(QBaseModelWidget):
self._navigationToolBar._goIntoAction.setEnabled(goInto)
self._expandBar._expandSelectionAction.setEnabled(goInto)
self._expandBar._collapseSelectionAction.setEnabled(goInto)
-
+
goUp = tree.rootIndex().isValid()
self._navigationToolBar._goUpAction.setEnabled(goUp)
self._navigationToolBar._goTopAction.setEnabled(goUp)
-
+
index = tree.rootIndex()
self._navigationToolBar._navigationWidget.updateSelection(index)
-
\ No newline at end of file
+
diff --git a/lib/taurus/qt/qtgui/tree/taurusdevicetree.py b/lib/taurus/qt/qtgui/tree/taurusdevicetree.py
index bffac9d..28bad87 100755
--- a/lib/taurus/qt/qtgui/tree/taurusdevicetree.py
+++ b/lib/taurus/qt/qtgui/tree/taurusdevicetree.py
@@ -27,231 +27,190 @@
taurusdevicetree.py:
"""
-__all__ = ["TaurusDevTree"]
+__all__ = ["TaurusDevTree","TaurusSearchTree","TaurusDevTreeOptions"] #,"SearchEdit"] #"TaurusTreeNode"]
-import random,time,os,re,traceback
+import time,os,re,traceback
+from functools import partial
import PyTango # to change!!
-import subprocess
try:import icons_dev_tree
except:icons_dev_tree = None
-from taurus.qt import Qt, QtCore, QtGui
-from PyQt4 import Qwt5
+from taurus.qt import Qt
import taurus.core
from taurus.core.util import DEVICE_STATE_PALETTE,ATTRIBUTE_QUALITY_PALETTE
from taurus.qt.qtgui.base import TaurusBaseComponent, TaurusBaseWidget
-
-
-class TaurusDevTree(QtGui.QTreeWidget, TaurusBaseWidget):
- ''' This widget display the list of servers, devices or instances. '''
- __pyqtSignals__ = ("modelChanged(const QString &)","deviceSelected(QString)",\
- "addAttrSelected(QStringList)","removeAttrSelected(QStringList)","refreshTree",)
-
- def __init__(self, parent=None, designMode = False):
- name = "TaurusDevTree"
- self._useParentModel = True
- self._localModel = ''
- self.call__init__wo_kw(QtGui.QTreeWidget, parent)
- self.call__init__(TaurusBaseWidget, name, designMode=designMode)
- ##self.call__init__(TaurusBaseWidget, name, parent, designMode=designMode)
- #if isinstance(parent,TaurusBaseWidget): #It was needed to avoid exceptions in TaurusDesigner!
- #print 'in DbWidget->TaurusBaseWidget __init__(parent)'
- #self.call__init__(TaurusBaseWidget, name, parent, designMode=designMode)
- #else:
- #print 'in DbWidget->TaurusBaseWidget __init__()'
- ##self.call__init__(TaurusBaseWidget, name, designMode=designMode)
- #self.call__init__(TaurusBaseWidget, name, designMode=designMode)
-
- self.setObjectName(name)
- #self.setModelCheck('controls01:10000')
- self.defineStyle()
- self.__filters = ""
- self.__expand =1
- self.index = 0
- self._nameTopItem = ""
- self._filters = None
-
- self.DeviceMenu = {
- 'Show Panel':'showPanel',
- 'Show AtkPanel':'showAtkPanel',
- 'Show Properties':'showProperties',
- 'Refresh Tree':'refreshTree',
- }
-
- self.AttributeMenu = [
- ('Add to trends','addToPlot'),
- ('Remove from trends','removeFromPlot'),
- #'Refresh Tree':'refreshTree',
- ]
-
- #Signal
- QtCore.QObject.connect(self,QtCore.SIGNAL("itemClicked (QTreeWidgetItem *,int)"),self.deviceClicked)
- #QtCore.QObject.connect(self,QtCore.SIGNAL("itemSelectionChanged()"),self.deviceSelected) #Redudant
-
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- # My methods
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-
- def deviceClicked(self,item,column):
- self.info("In TaurusDevTree.deviceClicked(%s)"%item.text(column))
- self.deviceSelected(self.getNodeDeviceName())
+from taurus.qt.qtgui.container import TaurusWidget
+from taurus.qt.qtcore.util.emitter import SingletonWorker
+from taurus.core.util import CaselessDict
+from taurus.qt.qtcore.mimetypes import * #@TODO: Avoid implicit imports
+from taurus.qt.qtcore.util import properties
+from taurus.qt.qtcore.util.properties import djoin
+from taurus.core.tango.search import * #@TODO: Avoid implicit imports
+
+TREE_ITEM_MIME_TYPE = 'application/x-qabstractitemmodeldatalist'
+
+###############################################################################
+
+class TaurusTreeNodeContainer(object):
+ """
+ Interface that provides Node-focused methods to TaurusDevTree
+ This methods should be moved to TaurusDevTreeNode class when it will be able to retrieve currentItem from Tree.
+ """
+ _icon_map = {} #A dictionary like {device_regexp:pixmap_url}
+
+ def __init__(self):
+ raise Exception('This class is just an interface, do not instantiate it!')
+
+ @classmethod
+ def setIconMap(klass,filters):
+ """A dictionary like {device_regexp:pixmap_url}"""
+ klass._icon_map = filters
- def deviceSelected(self,device_name=''):
- '''QSIGNAL: this method is used to emit deviceSelected(QString) signal'''
- self.info("In TaurusDevTree.deviceSelected(%s)"%device_name)
+ @classmethod
+ def getIconMap(klass):
+ return klass._icon_map
+
+ def createItem(self,parent,value,text=None):
+ self.debug('createItem(%s,%s)'%(value,text))
+ USE_TREE_NODE = False
+ if USE_TREE_NODE: item = TaurusTreeNode(parent)
+ else: item = Qt.QTreeWidgetItem(parent)
+ if text is None: text = value
+ item.isAttribute = False
+ item.DeviceName = ''
+ item.draggable = ''
+ item.setText(0,Qt.QApplication.translate('',text, None, Qt.QApplication.UnicodeUTF8))
+ self.setNodeParent(item,parent)
+ item.parentNode = parent if isinstance(parent,Qt.QTreeWidgetItem) else None
+ if not item.parentNode or '/' in text:
+ f = item.font(0)
+ if not item.parentNode: f.setBold(True)
+ if '/' in text: f.setItalic(True)
+ item.setFont(0,f)
+ item.parentTree = self #hook used to call external methods with item as single argument
+ self.item_index[value.strip().split()[0]] = item
try:
- #item = self.currentItem()
- device_name = device_name or self.getNodeDeviceName()#item.text(0)
- if str(device_name).count('/')!=2: return
- #Signal
- self.trace('TaurusTree emit deviceSelected(%s) signal ...'%device_name)
- self.emit(QtCore.SIGNAL("deviceSelected(QString)"), QtCore.QString(device_name))
- except:
- self.error(traceback.format_exc())
- pass
-
- ########################################################################################################################3
-
- ## @name Context Menu Actions
- # @{
+ icon = self.getNodeIcon(item)
+ if icon: item.setIcon(0,icon)
+ except: pass
+ self.item_list.add(item)
+ return item
+
+ ###########################################################################
+ # Item members methods
+
+ def setNodeParent(self,node,parent):
+ """ Used to know which parent attributes must be expanded if found """
+ node.parentNode = parent if isinstance(parent,Qt.QTreeWidgetItem) else None
- def getNodeText(self,node=None):
+ def getNodeText(self,node=None,full=False):
if node is None: node = self.currentItem()
- return str(node.text(0)).strip().split(' ')[0]
+ if hasattr(node,'text'):
+ txt = str(node.text(0)).strip()
+ if not full: return txt.split()[0]
+ return txt
+ else: return ''
def getNodeDeviceName(self,node = None):
if node is None: node = self.currentItem()
return str(getattr(node,'DeviceName','')) or self.getNodeText(node)
+
+ def getNodeParentName(self,node=None):
+ if node is None: node = self.currentItem()
+ return self.getNodeText(node.parentNode)
+
+ def getNodePath(self,node=None):
+ """ Returns all parent nodes prior to current """
+ if node is None: node = self.currentItem()
+ p,path,names = node.parentNode,[],[]
+ while p is not None:
+ path.insert(0,p)
+ names.insert(0,self.getNodeDeviceName(p))
+ p = p.parentNode
+ return path
def getNodeAlias(self,node = None):
if node is None: node = self.currentItem()
alias = getattr(node,'AttributeAlias','')
return (alias or self.getNodeText(node))
- def contextMenuEvent(self,event):
- '''
- This function is called when right clicking on TaurusDevTree area.
- A pop up menu will be shown with the available options.
- Menus are managed using two tuple lists for each node: node.ContextMenu and node.ExpertMenu
- '''
- node = self.currentItem()
- obj = self.getNodeDeviceName(node)
+ def getNodeIcon(self,node=None):
+ #self.debug('TaurusDevTree.getNodeIcon(node) not implemented, overrided in subclasses')
+ #print 'In Vacca.TauDevTree.getNodeIcon(%s)'%node.text(0)
- if not hasattr(node,'ContextMenu'): node.ContextMenu=[]
+ #self,url = node.parentTree,''
+ if node is None: node = self.getNode()
+ try:
+ name,url = self.getNodeText(node),''
+ for k,v in self.getIconMap().items():
+ if re.match(k.lower(),name.lower()): url = v
+ if not url:
+ for k,v in self.getIconMap().items():
+ if k.lower() in name.lower(): url = v
+ #if name.count('/')==2:
+ #if any(a.startswith(name+'/') for a in getArchivedAttributes()):
+ #url = wdir('image/icons/clock.png')
+ #else:
+ #url = wdir('image/equips/icon-%s.gif'%name.split('/')[2].split('-')[0].lower())
+ #elif name.count('/')==3:
+ #url = filterAttributes(name) or wdir('image/icons/closetab.png')
+ #else:
+ #url = wdir('image/equips/icon-%s.gif'%name.lower())
+ except:
+ print traceback.format_exc()
+ #print 'Out of Vacca.TauDevTree.getNodeIcon(%s) = %s'%(node,url)
+ if not url or not os.path.isfile(url): return None
+ else: return Qt.QIcon(url)
+
+ def getNodeDraggable(self,node = None):
+ """ This method will return True only if the selected node belongs to a numeric Tango attribute """
+ numtypes = [PyTango.DevDouble,PyTango.DevFloat,PyTango.DevLong,PyTango.DevLong64,PyTango.DevULong,PyTango.DevShort,PyTango.DevUShort,PyTango.DevBoolean]
+ if node is None: node = self.currentItem()
+ try:
+ name = self.getNodeText(node).lower()
+ drag = name
+ if node.isAttribute and getattr(node,'DeviceName','') and '/' not in name: name = node.DeviceName+'/'+name
+ if name.count('/')==2: #A Device Name
+ drag = name#+'/state' #False
+ elif name.count('/')==3: #An Attribute Name
+ dtype = PyTango.AttributeProxy(name).get_config().data_type
+ if dtype in numtypes: self.debug('The attribute %s is a Numeric Attribute'%(name))
+ #else: drag = False
+ drag = getattr(node,'draggable','') or name
+ self.debug('Node(%s,%s,%s): drag: %s'%(name,node.isAttribute,node.DeviceName,drag))
+ return drag.split()[0]
+ except:
+ import traceback
+ self.warning(traceback.format_exc())
+ return False
- if not 'Search ...' in [k for k,a in node.ContextMenu]: ##Creating default menu
- if obj.count('/')==2:
- #Menu for devices
- node.ContextMenu.append(("Show Panel", self.showAtkPanel))
- node.ContextMenu.append(("Show Attributes",self.addAttrToNode))
- node.ContextMenu.append(("Go to %s Controller"%getattr(node,'DefaultParent',"Parent"),\
- lambda: self.findInTree(getattr(self.currentItem(),'DefaultParent',''))
- ))
-
- if not hasattr(node,'ExpertMenu'): setattr(node,'ExpertMenu',[])
- if not 'Show Properties' in [k for k,a in node.ExpertMenu]:
- node.ExpertMenu.append(("Show Properties", self.showProperties))
- def test_device():
- device = str(self.getNodeDeviceName())
- if device:
- comm = 'tg_devtest %s &'%device
- #subprocess.Popen(['atkpanelALBA',device]) #It works!!!
- #subprocess.Popen(['tg_devtest',device]) #But this fails!?!
- os.system(comm)
- else: self.debug('TaurusDevTree.TestDevice: Selected Device is None!')
- node.ExpertMenu.append(("Test Device", test_device))
- node.ContextMenu.append(('',None))
-
- elif obj.count('/')==3:
- #Menu for attributes
- for k,v in self.AttributeMenu:
- self.info('Adding action %s'%k)
- if type(v) is str and hasattr(self,v):
- node.ContextMenu.append((k, getattr(self,v)))
- else:
- node.ContextMenu.append((k, lambda s=self.getNodeAlias(node): v(s)))
- #node.ContextMenu.append(("add to Trends", self.addToPlot))
- #node.ContextMenu.append(("remove from Trends", self.removeFromPlot))
- node.ContextMenu.append(('',None))
+ ###########################################################################
+ # Context Menu Actions
+
+ @staticmethod
+ def setDefaultPanelClass(other):
+ TaurusTreeNodeContainer._defaultClass = other
+ @staticmethod
+ def defaultPanelClass():
+ if not hasattr(TaurusTreeNodeContainer,'_defaultClass'):
+ from taurus.qt.qtgui.panel import TaurusDevicePanel
+ TaurusTreeNodeContainer._defaultClass = TaurusDevicePanel
+ #print 'defaultPanelClass == %s'%TaurusTreeNodeContainer._defaultClass
+ obj = TaurusTreeNodeContainer._defaultClass
+ return obj
- node.ContextMenu.append(("Expand Node", self.expandAll))
- node.ContextMenu.append(("Collapse Node", self.collapseAll))
- node.ContextMenu.append(("Collapse All", lambda: self.collapseAll(ALL=True)))
- node.ContextMenu.append(("Search ...",\
- lambda: self.findInTree(str(QtGui.QInputDialog.getText(self,'Search ...','Write a part of the name',QtGui.QLineEdit.Normal)[0]))
- ))
- #configDialogAction = menu.addAction("Refresh Tree")
- #self.connect(configDialogAction, QtCore.SIGNAL("triggered()"), self.refreshTree)
- menu = Qt.QMenu(self)
-
- if hasattr(node,'ContextMenu'):
- last_was_separator = True
- for t in (type(node.ContextMenu) is dict and node.ContextMenu.items() or node.ContextMenu):
- try:
- k,action = t
- if k:
- configDialogAction = menu.addAction(k)
- if action: self.connect(configDialogAction, QtCore.SIGNAL("triggered()"), action)
- else: configDialogAction.setEnabled(False)
- last_was_separator = False
- elif not last_was_separator:
- menu.addSeparator()
- last_was_separator = True
- except Exception,e:
- self.warning('Unable to add Menu Action: %s:%s'%(t,e))
-
- if hasattr(node,'ExpertMenu'):
- menu.addSeparator()
- expert = menu.addMenu('Expert')
- #expert.addSeparator()
- last_was_separator = True
- for t in (type(node.ContextMenu) is dict and node.ExpertMenu.items() or node.ExpertMenu):
- try:
- k,action = t
- if k:
- configDialogAction = expert.addAction(k)
- if action: self.connect(configDialogAction, QtCore.SIGNAL("triggered()"), action)
- else: configDialogAction.setEnabled(False)
- last_was_separator = False
- elif not last_was_separator:
- expert.addSeparator()
- last_was_separator = True
- except Exception,e:
- self.warning('Unable to add Expert Action: %s:%s'%(t,e))
- #menu.addSeparator()
- menu.exec_(event.globalPos())
- del menu
-
def showPanel(self):
'''Display widget taurusDevicePanel'''
device = self.getNodeText()
- try:
- from taurusDevicePanel import taurusDevicePanel
- except:
- from taurusPanel.taurusDevicePanel import taurusDevicePanel
- nameclass = taurusDevicePanel()
+ nameclass = self.defaultPanelClass()
nameclass.setModel(device)
- nameclass.setSpectraAtkMode(True)
- #nameclass.show()
- obj = newDialog(self)
- obj.initComponents(nameclass)
- obj.setModal(False)
- obj.setVisible(True)
- obj.exec_()
-
- def showAtkPanel(self):
- '''Open AtkPanel'''
- device = self.getNodeText()
- #tg_host = os.environ.get('TANGO_HOST')
- #javalib = '/homelocal/sicilia/lib/java'
- #classpath = '%s/atkpanel.jar:%s/ATKCore.jar:%s/ATKWidget.jar:%s/TangORB.jar'%(javalib,javalib,javalib,javalib)
- #command = ['/usr/bin/java','-classpath', classpath, '-DTANGO_HOST=%s' % tg_host,'atkpanel.MainPanel',device]
- #subprocess.Popen(command)
-
- ##srubio at cells.es: Modified to use the improved and sorted version of ATKPanel
- subprocess.Popen(['atkpanelALBA',device])
+ nameclass.show()
+ ##nameclass.setSpectraAtkMode(True)
+ #Dialog is used to make new floating panels persistent
+ if isinstance(nameclass,TaurusWidget):
+ PopupDialog(self,nameclass)
def showProperties(self):
'''Display widget TaurusPropTable'''
@@ -259,268 +218,473 @@ class TaurusDevTree(QtGui.QTreeWidget, TaurusBaseWidget):
device = self.getNodeText()
nameclass = taurus.qt.qtgui.table.TaurusPropTable()
nameclass.setTable(device)
- obj = newDialog(self)
- obj.initComponents(nameclass)
- obj.setModal(False)
- obj.setVisible(True)
- obj.exec_()
+ nameclass.show()
+ #Dialog is used to make new floating panels persistent
+ PopupDialog(self,nameclass)
def addToPlot(self):
- item = self.currentItem()
- attr = self.getNodeAlias(item)
- self.info('In addToPlot(%s->%s)'%(item.text(0),attr))
- #item2 = item.parent()
- #dev = item2.text(0).split(' ')[0]
- #devattr = str(dev) + "/" + str(attr)
- self.emit(QtCore.SIGNAL("addAttrSelected(QStringList)"),QtCore.QStringList([str(attr)]))
+ """ This method will send a signal with the current selected node """
+ items = self.getSelectedNodes()
+ for item in items:
+ attr = self.getNodeAlias(item)
+ self.trace('In addToPlot(%s->%s)'%(item.text(0),attr))
+ self.addAttrToPlot(attr)
+ return
+
+ def addAttrToPlot(self,attr):
+ """ This method will send a signal with the given attr name, in a separate method to be called with a pre-filled list """
+ self.emit(Qt.SIGNAL("addAttrSelected(QStringList)"),Qt.QStringList([str(attr)]))
def removeFromPlot(self):
- item = self.currentItem()
- attr = getattr(item,'AttributeAlias','') or self.getNodeText(item)
- #item2 = item.parent()
- #dev = item2.text(0).split(' ')[0]
- #devattr = str(dev) + "/" + str(attr)
- self.emit(QtCore.SIGNAL("removeAttrSelected(QStringList)"),QtCore.QStringList([str(attr)]))
+ """ This method will send a signal with the current selected node """
+ items = self.getSelectedNodes()
+ for item in items:
+ item = self.currentItem()
+ attr = getattr(item,'AttributeAlias','') or self.getNodeText(item)
+ self.removeAttrFromPlot(attr)
+ return
+
+ def removeAttrFromPlot(self,attr):
+ """ This method will send a signal with the given attr name, in a separate method to be called with a pre-filled list """
+ self.emit(Qt.SIGNAL("removeAttrSelected(QStringList)"),Qt.QStringList([str(attr)]))
- def refreshTree(self):
- self.setTree(self._filters)
- self.emit(QtCore.SIGNAL("refreshTree"))
- ## @}
- ########################################################################################################################
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- ## @name Methods for building server/devices/attributes tree
- # @{
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+###############################################################################
+
+class TaurusDevTree(TaurusTreeNodeContainer,Qt.QTreeWidget, TaurusBaseWidget):
+ '''
+ This widget displays a list of servers, devices or instances.
+ To set a new Model use either setModel(filters), addModels(list), setFilters(...) or loadTree(filters)
+ setModel and loadTree are equivalent; adding a new branch to the tree
+ addModels merges the tree with new models
+ setFilters clears previous models and adds new one
+ '''
+ __pyqtSignals__ = (
+ "modelChanged(const QString &)",
+ "deviceSelected(QString)",
+ "addAttrSelected(QStringList)",
+ "removeAttrSelected(QStringList)",
+ "refreshTree",
+ "nodeFound"
+ )
+ __properties__ = (
+ 'ModelInConfig',
+ 'modifiableByUser',
+ #'useParentModel',
+ 'Filters',
+ 'AttrFilter',
+ 'Source',
+ 'ShowAlias',
+ 'ShowColors',
+ 'ShowNotExported',
+ 'MaxDevices',
+ )
+ __slots__ = (
+ "setTangoHost",
+ #"setModel",
+ "setFilters",
+ "addModels",
+ "setModelCheck",
+ #"loadTree",
+ "setTree",
+ "findInTree",
+ "setIcons",
+ "expandAll",
+ "expandNode",
+ "collapseNode",
+ )
+
+ TRACE_ALL = False
+
+ def __init__(self, parent=None, designMode = False):
+ name = "TaurusDevTree"
+ self._useParentModel = True
+ self._localModel = ''
+ self.call__init__wo_kw(Qt.QTreeWidget, parent)
+ self.call__init__(TaurusBaseWidget, name, designMode=designMode)
+
+ self.setObjectName(name)
+ self._filters = []
+ self.__attr_filter = None
+ self.__expand =1
+ self.collapsing_search = True
+ self.index = 0
+ self._nameTopItem = ""
+
+ self.excludeFromSearch = [] #This is a list of regular expressions to exclude objects from searches
+ self.dictionary = {}
+ self.item_index = CaselessDict()
+ self.item_list = set() #NOTE: as several nodes may share the same name this list will be different from item_index.values()!!!
+ self.setSelectionMode(self.ExtendedSelection)
+
+ self.ContextMenu=[]
+ self.ExpertMenu=[]
+
+ #The SingletonWorker Threads are used for expanding nodes and also for loading a new tree; both objects are the same thread, but read from different queues
+ self.Loader = SingletonWorker(parent=self,name='TreeLoader',cursor=True,start=True )
+ self.Expander = SingletonWorker(parent=self,name='NodeExpander',method=lambda node,expand:node.setExpanded(expand),cursor=True,start=True )
+
+ self.initConfig()
+
+ #Signal
+ Qt.QObject.connect(self,Qt.SIGNAL("itemClicked (QTreeWidgetItem *,int)"),self.deviceClicked)
+ Qt.QObject.connect(self,Qt.SIGNAL("nodeFound"),self,Qt.SLOT("expandNode"))
+
+ self.setDragDropMode(Qt.QAbstractItemView.DragDrop)
+ self.setModifiableByUser(True)
+ self.setModelInConfig(False) #We store Filters instead!
+ self.setAcceptDrops(True)
+ self.viewport().setAcceptDrops(True)
+ self.setDragEnabled(True)
+ self.setSupportedMimeTypes([
+ TAURUS_MODEL_LIST_MIME_TYPE, TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE,
+ TAURUS_MODEL_MIME_TYPE, TREE_ITEM_MIME_TYPE, 'text/plain'])
+
+ self.setTangoHost(os.environ['TANGO_HOST'])
+ self.defineStyle()
+
+ def getConfig(self,name):
+ properties.get_property(self,name)
- def buildDictFromFilters(self,filters):
- '''
- This method build a dictionary of instances and devices depending on the given servers,devices or instances in QTProperty or in another widget
- --- filters is a string with names of devices/servers such as "LT/VC/ALL,LT02/VC/IP-01" or "modbus,pyplc"
- --- filters is a list of devices such as ["LT/VC/ALL","LT02/VC/IP-01"]
- '''
- self.info('In TaurusDevTree.buildDictFromFilters(%s)'%filters)
- self._filters = filters
- self.db = self.getModelObj()
- if type(filters)==type("") or isinstance(filters,QtCore.QString):
- filters = str(filters).split(',')
- elif isinstance(filters,QtCore.QStringList):
- filters = list(filters)
- elif type(filters)!=type([]):
- self.info("'filters' has to be a string or the list type!")
- vals = {}
- if not filters: return vals
- if filters[0].count('/')==0:
- self.info('In TaurusDevTree.buildDictFromFilters(%s): Getting Servers'%filters)
- targets,addMe = self.db.get_server_name_list(),self.addInstToServ #Searching servers
- elif filters[0].count('/')==1:
- self.info('In TaurusDevTree.buildDictFromFilters(%s): Getting Instances'%filters)
- targets,addMe = self.db.get_server_list(),self.addDevToInst #Searching instances
- elif filters[0].count('/')==2:
- self.info('In TaurusDevTree.buildDictFromFilters(%s): Getting Devices'%filters)
- targets,addMe = self.db.get_device_exported("*"),lambda s: {s:{}} #self.addAttrToDev #Searching devices
- else:
- raise Exception('UnknownFilter!: %s'%filters[0])
+ def initConfig(self):
+ """
+ Initializing the attributes that will be kept persitent as Qt settings.
+ e.g.; for Filters property are created:
+ self.filters / self._filters / self.setFilters / self.getFilters / self.resetFilters
+ """
+ properties.set_property_methods(self,'Filters','QStringList',default='',
+ #setter = self.setFilters,
+ setter = self.addModels, #Not trivial!; it avoids QSettings erasing default model
+ #set_callback=lambda v,s=self:v and s.loadTree(v,clear=True),
+ #reset_callback=lambda s=self:s.setFilters(''),
+ qt=False,config=True
+ )
+ properties.set_property_methods(self,'MaxDevices','int',default=150,qt=False,config=True)
+ properties.set_property_methods(self,'ShowAlias','bool',default=False,qt=False,config=True)
+ properties.set_property_methods(self,'ShowNotExported','bool',default=True,qt=False,config=True)
+ properties.set_property_methods(self,'ShowColors','bool',default=True,qt=False,config=True)
+ properties.set_property_methods(self,'AttrFilter','QString',default=None,qt=False,config=True)
+ #properties.set_property_methods(self,'Expand','int',default=0)
- for t in targets:
- for f in filters:
- f = str(f)
- exp = f.replace('*','.*').lower() if '*' in f and '.*' not in f else f.lower()
- if re.match(exp,t.lower()):
- self.info('Adding node %s'%t)
- vals[t] = addMe(t)
- self.info('Out of TaurusDevTree.buildDictFromFilters(%s)'%filters)
- return vals
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # TaurusBaseWidget over writing methods
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- def checkifexist(self,name,list_):
- lower_list = [s.lower() for s in list_]
- name = str(name).lower()
- b = 0
- if name in lower_list:
- b = 1
- return b
+ def sizeHint(self):
+ return Qt.QTreeWidget.sizeHint(self)
- def addInstToServ(self,my_server):
- dict = {}
- list_inst = self.get_instances_for_server(my_server)
- lower_list_inst = [s.lower() for s in list_inst]
- for my_inst in lower_list_inst:
- if self.__expand:
- dict[my_inst] = self.addDevtoInst(my_inst)
- else:
- dict[my_inst] = 0
- return dict
+ def minimumSizeHint(self):
+ return Qt.QTreeWidget.minimumSizeHint(self)
- def addDevtoInst(self,my_inst,expand_attrs = False):
- dict = {}
- list_dev = self.get_devices_for_instance(my_inst)
- lower_list_dev = [s.lower() for s in list_dev]
- for my_dev in lower_list_dev:
- if self.__expand:
- dict[my_dev] = self.addAttrToDev(my_dev) if expand_attrs else {my_dev:{}}
- else:
- dict[my_dev] = 0
- return dict
+ @classmethod
+ def getQtDesignerPluginInfo(cls):
+ ret = TaurusBaseWidget.getQtDesignerPluginInfo()
+ ret['module'] = 'taurus.qt.qtgui.tree'
+ ret['group'] = 'Taurus Views'
+ ret['icon'] = ":/designer/listview.png"
+ return ret
+
+ def defineStyle(self):
+ self.setWindowTitle('TaurusDevTree')
+ self.setHeaderLabel('Device Browser')
+ self.setGeometry(Qt.QRect(90,60,256,192))
+ self.actionFindInTree = Qt.QAction(self)
+ self.actionFindInTree.setShortcut(Qt.QKeySequence.Find)
+ self.connect(self.actionFindInTree, Qt.SIGNAL("triggered()"), self.findDialog)
+ from taurus.qt.qtgui.table.qdictionary import QDictionaryEditor,QListEditor
+ self.ExpertMenu.append(
+ ('Edit Model Filters',
+ lambda:QListEditor.main(
+ self._filters,
+ modal=True,
+ title='Edit Model Filters',
+ callback=lambda d:self.setFilters(d)
+ )
+ #lambda:self.loadTree(
+ #str(Qt.QInputDialog.getText(None,'Set Tree Model','Enter a list of regexp separated by comma:',Qt.QLineEdit.Normal,','.join(str(f) for f in self._filters))[0])
+ #or None)
+ ))
+ self.ExpertMenu.append(
+ ('Edit Tree',
+ lambda:QDictionaryEditor.main(self.dictionary,modal=True,title='Edit Tree',callback=lambda d:self.setTree(d,clear=True))
+ ))
+ self.ExpertMenu.append(
+ ('Expand All',
+ lambda:self.expandAll()
+ ))
+ self.ExpertMenu.append(
+ ('Save Config',
+ lambda:self.saveConfigFile()
+ ))
+ self.DeviceMenu = {
+ 'Show Properties':'showProperties',
+ 'Refresh Tree':'refreshTree',
+ }
+ self.AttributeMenu = [
+ ('Add to trends','addToPlot'),
+ ('Remove from trends','removeFromPlot'),
+ ]
- def addFamilyToDomain(self,prev,expand_attrs):
- dict = {}
- children = self.get_devices_for_instance(my_inst)
- lower_list_dev = [s.lower() for s in list_dev]
- for my_dev in lower_list_dev:
- if self.__expand:
- dict[my_dev] = self.addAttrToDev(my_dev) if expand_attrs else {my_dev:{}}
+ def trace(self,msg):
+ if self.TRACE_ALL or self.getLogLevel() in ('DEBUG',40,):
+ print 'TaurusDevTree.%s: %s'%(self.getLogLevel(),msg) #@TODO: use the taurus logger instead! ~~cpascual 20121121
+
+ def setTangoHost(self,tango_host):
+ self.db = taurus.Database(tango_host)
+
+ #model = Qt.pyqtProperty("QString", TaurusBaseWidget.getModel,
+ #TaurusBaseWidget.setModel,
+ #TaurusBaseWidget.resetModel)
+
+ def getModel(self):
+ return self._filters
+
+ def getModelClass(self):
+ return list #taurus.core.TaurusDatabase
+
+ def setFilters(self,filters):
+ filters = split_model_list(filters)
+ self.trace('setFilters(%s)'%(filters))
+ assert isSequence(filters),"Filters have to be a string or list type!"
+ properties.set_property(self,'Filters',filters) #self._filters = filters
+ self.setWindowTitle('TaurusDevTree:%s'%str(filters))
+
+ self.setTree(self.getTangoDict(filters),clear=True)
+ #self.Loader.next([self.setTree,self.getTangoDict(filters),True])
+
+ def setModel(self,model):
+ TaurusBaseWidget.setModel(self,model)
+
+ def setModelCheck(self,model):
+ # Called from TaurusBaseWidget.setModel()
+ self.trace('setModelCheck(%s)'%str(model)[:80])
+ self.setFilters(model)
+
+ @Qt.pyqtSignature("addModels(QStringList)")
+ def addModels(self, modelNames):
+ '''Adds models to the existing ones:
+ :param modelNames: (sequence<str>) the names of the models to be added
+ .. seealso:: :meth:`removeModels`
+ '''
+ self.trace('In addModels(%s)'%str(modelNames)[:80])
+ #print 'Adding %d models to (%s)'%(len(modelNames),self.getFilters())
+ modelNames = split_model_list(modelNames)
+ #dct = self.getTangoDict(modelNames)
+ #self.setTree(djoin(dct,self.dictionary),clear=False)
+ self.setTree(self.getTangoDict(modelNames),clear=False)
+ self._filters = sorted(set(split_model_list(self._filters)+modelNames))
+ #self.setModel(self.getModel()+modelNames)
+
+ ############################################################################
+ # Loading/Cleaning the tree
+
+ #@Qt.pyqtSignature("loadTree(QString)")
+ #def loadTree(self,filters,clear=False):
+ #'''
+ #This method show a list of instances and devices depending on the given servers in QTProperty or in another widget,
+ #this method can be used to connect TauDevTree with another widget such as LineEdit.
+ #'''
+ #self.trace('In loadTree(%s)'%str(filters))
+ #if clear: self.setWindowTitle('TaurusDevTree:%s'%str(filters))
+ #self.setTree(self.getTangoDict(filters),clear=clear,alias=False)
+
+ def setTree(self,diction,clear=False):
+ """
+ Initializes the tree from a dictionary {'Node0.0':{'Node1.0':None,'Node1.1':None}}
+ """
+ K = len(str(dict(diction)))
+ self.trace('In setTree(%d) ...'%K)
+ self.debug(diction)
+ if diction is None: return
+ if clear or self.dictionary:
+ if not clear: diction = djoin(diction,self.dictionary) #Merging new and old models
+ self.clear()
+ self.dictionary = diction
+ if len(diction):
+ self.setNodeTree(self,diction,alias=(self.getShowAlias() or K<self.getMaxDevices()*20))
+ #Auto-Expand caused problems when loading filters from QSettings
+ if 0<len(self.item_list)<self.getMaxDevices(): self.expandAll(queue=False)
+
+ def setNodeTree(self,parent,diction,alias=False):
+ """
+ It has parent as argument to allow itself to be recursive
+ Initializes the node tree from a dictionary {'Node0.0':{'Node1.0':None,'Node1.1':None}}
+ """
+ self.debug('In setNodeTree(%d,alias=%s) ...'%(len(diction),alias))
+ if not hasattr(diction,'keys'): diction = dict.fromkeys(diction)
+ for node in sorted(diction.keys()):
+ assert int(self.index)<10000000000,'TooManyIterations!'
+ self.index = self.index + 1
+ dev_alias = alias and str(node).count('/')==2 and get_alias_for_device(node)
+ text = '%s (%s)'%(node,dev_alias) if dev_alias else node
+ if diction[node] and any(diction[node]):
+ item = self.createItem(parent,node,text)
+ self.setNodeTree(item,diction[node],alias)
else:
- dict[my_dev] = 0
- return dict
+ item = self.createItem(parent,node,text)
+
+ def clear(self):
+ while not self.Expander.getQueue().empty(): self.Expander.getQueue().get()
+ self.item_index.clear()
+ while self.item_list: self.item_list.pop()
+ Qt.QTreeWidget.clear(self)
+
+ def refreshTree(self):
+ self.setFilters(self._filters)
+ self.emit(Qt.SIGNAL("refreshTree"))
- def addAttrToDev(self,my_device,expert=False,allow_types=[PyTango.DevDouble,PyTango.DevFloat,PyTango.DevLong,PyTango.DevLong64,PyTango.DevULong,PyTango.DevShort,PyTango.DevUShort,PyTango.DevBoolean]):
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ ## @name Methods for building server/devices/attributes tree
+ # @{
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+
+ def getTangoDict(self,filters):
+ self.trace('In TaurusDevTree.getTangoDict(%s(%s))'%(type(filters),str(filters)[:80]))
+ if filters is None: return
+ result = {}
+ filters = split_model_list(filters)
+ targets = get_matching_devices(filters)
+ targets = [t.upper() for t in targets]
+ domains = set(t.split('/')[0] for t in targets)
+ for d in domains:
+ families = set(t.split('/')[1] for t in targets if t.startswith('%s/'%d))
+ result[d] = dict((f,dict.fromkeys(t for t in targets if t.startswith('%s/%s/'%(d,f)))) for f in families)
+ return result
+
+ def addAttrToDev(self,my_device,expert=False,allow_types=None):
""" This command returns the list of attributes of a given device applying display level and type filters.
@argin expert If False only PyTango.DispLevel.OPERATOR attributes are displayed
- @argin allow_types Only those types included in the list will be displayed (numeric types only by default)
+ @argin allow_types Only those types included in the list will be displayed (e.g. may be restricted to numeric types only)
"""
- #self.list_attr = self.db.get_device_attribute_list(my_device,'*') #BUG
- self.info('In addAttrToDev(%s)'%my_device)
+ numeric_types = [PyTango.DevDouble,PyTango.DevFloat,PyTango.DevLong,PyTango.DevLong64,PyTango.DevULong,PyTango.DevShort,PyTango.DevUShort,PyTango.DevBoolean,PyTango.DevState]
+ allow_types = allow_types or [PyTango.DevString]+numeric_types
+ dct = {}
+ self.trace('In addAttrToDev(%s)'%my_device)
try:
proxy = PyTango.DeviceProxy(my_device)
timeout = proxy.get_timeout_millis()
- try:
- proxy.set_timeout_millis(50)
- proxy.ping()
- list_attr = proxy.attribute_list_query()
- proxy.set_timeout_millis(timeout)
- except:
- self.error(traceback.format_exc())
- list_attr = []
- dict = {}
+ proxy.set_timeout_millis(50)
+ proxy.ping()
+ list_attr = proxy.attribute_list_query()
+ proxy.set_timeout_millis(timeout)
+
for aname,my_attr in sorted([(a.name,a) for a in list_attr]):
- if my_attr.data_type not in allow_types: continue
+ if allow_types and my_attr.data_type not in allow_types: continue
if not expert and my_attr.disp_level==PyTango.DispLevel.EXPERT: continue
label = aname==my_attr.label and aname.lower() or "%s (%s)"%(aname.lower(),my_attr.label)
- dict[str(my_device).lower()+'/'+label] = 0
+ dct[str(my_device).lower()+'/'+label] = 0
except PyTango.DevFailed,e:
self.warning('addAttrToDev(%s): %s'%(my_device,str(e)))
+ qmsg = Qt.QMessageBox(Qt.QMessageBox.Critical,'%s Error'%my_device,'%s not available'%my_device,Qt.QMessageBox.Ok,self)
+ qmsg.show()
except Exception,e:
self.warning('addAttrToDev(%s): %s'%(my_device,str(e)))
- return dict
+ qmsg = Qt.QMessageBox(Qt.QMessageBox.Critical,'%s Error'%my_device,str(e),Qt.QMessageBox.Ok,self)
+ qmsg.show()
+ return dct
def addAttrToNode(self):
node = self.currentItem()
dev = self.getNodeDeviceName(node)
- self.info('In addAttrToNode(%s)'%dev)
+ self.trace('In addAttrToNode(%s)'%dev)
attrs = self.addAttrToDev(dev)
children = [str(node.child(i).text(0)).lower() for i in range(node.childCount())]
for aname in sorted(attrs):
+ if self._attrfilter:
+ if isCallable(self._attrfilter) and not self._attrfilter(aname):
+ continue
if aname.lower() not in children:
- natt = self.createItem(node,aname)
- natt.IsAttribute = True
+ natt = self.createItem(node,value=aname,text=aname.rsplit('/')[-1])
+ natt.draggable = aname
+ natt.isAttribute = True
+ natt.DeviceName = dev
+ icon = self.getNodeIcon(natt)
+ if icon: natt.setIcon(0,icon)
alias = getattr(node,'AttributeAlias',{}) #it gets all aliases for this device attributes
- self.info('Got aliases for %s: %s' % (aname,alias))
+ if alias: self.trace('Got aliases for %s: %s' % (aname,alias))
[setattr(natt,'AttributeAlias',v) for k,v in alias.items() if k in aname.lower()]
node.setExpanded(True)
return
-
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- # Methods for database commands
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-
- def get_instances_for_server(self, server_name):
- #executable_name = class_name
- instances = self.db.get_instance_name_list(server_name)
- return [server_name+'/'+instance for instance in instances]
-
- def get_devices_for_instance(self, instance_name):
- devslist = self.db.get_device_class_list(instance_name)
- return [dev for dev in devslist if '/' in dev and not dev.startswith('dserver')]
-
- ##@}
- ############################################################################################################################3
-
- @QtCore.pyqtSignature("setTree(QString)")
- def setTree(self,filters):
- '''
- This method show a list of instances and devices depending on the given servers in QTProperty or in another widget,
- this method can be used to connect TaurusDevTree with another widget such as LineEdit.
- '''
- self.clear()
- self.drawTree(self,self.buildDictFromFilters(filters))
-
- def drawTree(self,item,diction):
- for node in sorted(diction.keys()):
- assert int(self.index)<10000000000,'TooManyIterations!'
- self.index = self.index + 1
- if diction[node]<>0:
- item1 = self.createItem(item,node)
- item1.DefaultParent = hasattr(item,'text') and str(item.text(0)).split()[0] or ''
- self.drawTree(item1,diction[node])
+
+ ###########################################################################
+ # Node getters
+
+ def getNode(self,target=None):
+ """ Gets currrent node or node by name or by regexp """
+ if target is None:
+ return self.currentItem()
+ else:
+ nodes = self.getMatchingNodes(target,1)
+ if not nodes:
+ return None
else:
- item2 = self.createItem(item,node)
-
- def createItem(self,obj,value):
- USE_TREE_NODE = False
- if USE_TREE_NODE: item = TaurusTreeNode(obj)
- else: item = QtGui.QTreeWidgetItem(obj)
- item.setText(0,QtGui.QApplication.translate('',value, None, QtGui.QApplication.UnicodeUTF8))
- return item
+ return nodes[0]
+ return
+
+ def getNodeByName(self,key):
+ return self.item_index[key]
+
+ def getNodeList(self):
+ return self.item_index.keys()
+
+ def getMatchingNodes(self,regexp,limit=0, all=False, exclude=None):
+ """ It returns all nodes matching the given expression. """
+ result,regexp = [],str(regexp).lower()
+ exclude = exclude or []
+ self.trace('In TauDevTree.getMatchingNodes(%s,%s,%s,%s)'%(regexp,limit,all,exclude))
+ if not all:
+ node = self.item_index.get(regexp,None)
+ if node is not None:
+ return [node]
+ regexp = re.compile(extend_regexp(regexp))
+ for k,node in self.item_index.iteritems():
+ nname = self.getNodeText(node,full=True).lower()
+ if (regexp.match(k) or regexp.match(nname)) and \
+ (not exclude or not any(re.match(x.lower(),y) for x in exclude for y in (k.lower(),nname))):
+ result.append(node)
+ if not all and len(result)==1: break
+ if limit and len(result)>=limit: break
+ return result
+
+ def getSelectedNodes(self):
+ return self.selectedItems()
def getAllNodes(self):
+ """ Returns a list with all node objects. """
def get_child_nodes(dct,node,fun=None):
if fun: fun(node)
dct.update([(str(node.text(0)),node)])
for j in range(node.childCount()):
get_child_nodes(dct,node.child(j))
return dct
- dct = {}
- for i in range(self.topLevelItemCount()):
- get_child_nodes(dct,self.topLevelItem(i))
- return dct
-
- def expandAll(self,ALL=False,filters='',fun=None):
- #for node in self.getAllNodes().values():
- #if not any((node.isDisabled(),node.isExpanded())):
- #node.setExpanded(True)
- filters = str(filters).lower()
- self.debug( 'In TaurusTree.expandAll(%s)'%filters)
- def expand_child_nodes(node):
- if fun: fun(node)
- result = not filters
- propagate = not filters
- for j in range(node.childCount()): #Searches on each branch of tree
- result = not filters
- child = node.child(j)
- if all([re.search(f,str(child.text(0)).lower()) for f in filters.split()]): #A matching node found
- #if re.search(str(filters).replace(' ','.*')):
- self.debug('In TaurusTree.expandAll(%s): %s matches!'%(filters,str(child.text(0)).lower()))
- child.setExpanded(True)
- result = child.text(0)
-
- children = expand_child_nodes(child)
- if children: self.debug( 'In TaurusTree.expandAll(%s): %s contains %s!'%(filters,child.text(0),children))
- if (children or result) and not propagate:
- node.setExpanded(True)
- propagate = children or result
-
- if propagate: self.debug( 'expand_child_nodes(%s): Passing %s to the top branch' % (node.text(0),propagate))
- return propagate #Propagate is True if there's a match in any of children nodes (result is reset for every branch, propagate for every top)
+ dct = {}
+ for i in range(self.topLevelItemCount()):
+ get_child_nodes(dct,self.topLevelItem(i))
+ return dct
+
+ def unpackChildren(self):
+ """ removes all nodes from the tree and returns them in a list, used for resorting """
+ allChildren = []
+ nodes = self.getAllNodes().values()
+
+ for node in nodes:
+ allChildren.extend(node.takeChildren())
+ while self.topLevelItemCount():
+ allChildren.append(self.takeTopLevelItem(0))
+ return allChildren
+
+ ## @}
+ ########################################################################################################################
- found = []
- if ALL:
- for i in range(self.topLevelItemCount()):
- res = expand_child_nodes(self.topLevelItem(i))
- if res: found.append(str(res))
- else:
- res = expand_child_nodes(self.currentItem())
- if res: found.append(str(res))
- return found
+ ###########################################################################
+ # Expand/Collapse/Search nodes
- def collapseAll(self,ALL=False,filters='',fun=None):
+ def collapseNode(self,ALL=False,filters='',fun=None):
""" Collapses the whole tree or from a given node.
@argin ALL tells whether to collapse from current item or the whole tree
@argin filters Allows to set a list of nodes to not be filtered
"""
- #for node in self.getAllNodes().values():
- #if any((node.isDisabled(), node.isExpanded())):
- #node.setExpanded(False)
filters = str(filters).lower()
found = ''
self.debug( 'In TaurusTree.collapseAll(%s)'%filters)
@@ -550,120 +714,118 @@ class TaurusDevTree(QtGui.QTreeWidget, TaurusBaseWidget):
node.removeChild(child)
del child
return found
+
+ ###########################################################################
+ # New expand/search methods
- def findInNode(self,node,regexp):
- regexp = regexp.lower()
- for child in [node.child(i) for i in range(node.childCount())]:
- if re.search(regexp,str(child.text(0)).lower()): return str(child.text(0))
- next = findInNode(self,child,regexp)
- if next: return next
- return ''
-
- @QtCore.pyqtSignature("findInTree(const QString &)")
- def findInTree(self,regexp,collapseAll=True):
- self.findInTreeOld(regexp,collapseAll)
-
- def findInTreeOld(self,regexp,collapseAll=True):
- self.debug( 'In TaurusTree.findInTree(%s)'%regexp)
- current = self.currentItem() or self.topLevelItem(0)
- #print 'The node %s seems already visible ... collapsing tree'%str(n.text(0))
+ #@Qt.pyqtSignature("expandNode")
+ def expandNode(self,node=None,expand=True):
+ """ Needed to do threaded expansion of the tree """
+ if node is None: node = self.getNode()
+ if isinstance(node,(basestring,Qt.QString)): name,node = str(node),self.getNode(node)
+ else: name = self.getNodeText(node)
+ node.setExpanded(expand)
+ return expand
+
+ def expandAll(self,queue=True):
+ self.findInTree('*',select=False,queue=queue)
+
+ def findDialog(self):
+ self.findInTree(str(Qt.QInputDialog.getText(self,'Search ...','Write a part of the name',Qt.QLineEdit.Normal)[0]))
+
+ @Qt.pyqtSignature("findInTree(const QString &)")
+ def findInTree(self,regexp,collapseAll=None,exclude=None,select=True,queue=True):
+ self.trace( 'In TauTree.findInTree(%s)'%regexp)
+ if collapseAll is None: collapseAll = self.collapsing_search
+ regexp = str(regexp).lower().strip()
+ exclude = (lambda x: x if hasattr(x,'__iter__') else [x])(exclude or self.excludeFromSearch or [])
if not regexp: return
- elif str(regexp).lower() == str(current.text(0)).lower():
- self.debug('Node already selected')
- return
- self.collapseAll(ALL=collapseAll)
- result = self.expandAll(ALL=True,filters=regexp)
- self.debug( 'expandAll(%s) returned %s: %s' % (regexp,type(result),result))
- if not result:
- self.warning( 'findInTree(): Node %s not found!?!?! %s'%(regexp,result))
- elif len(result) == 1:
- try:
- self.debug( 'findInTree(): Node %s selected'%result[0])
- node = self.findItems('.*'+str(result[0]).replace(' ','.*')+'.*',Qt.Qt.MatchRegExp|Qt.Qt.MatchRecursive)[0]#self.getAllNodes()[result]
- node.setSelected(True)
- self.setCurrentItem(node)
- self.deviceSelected(result[0]) #Searches must not trigger events!
- except:
- self.warning( 'findInTree(%s): Node %s not found!?'%(regexp,result[0]))
- self.error(traceback.format_exc())
- else:
- self.info('findInTree(%s): %d nodes found, displayed but not selected: %s'%(regexp,len(result),result))
-
- def findInTreeNew(self,regexp,collapseAll=True):
- """ This method has been rejected as it doesn't collapsed well the nodes """
- if '.*' not in regexp:
- if '*' in regexp:regexp = regexp.replace('*','.*')
- target = '.*'+str(regexp).replace(' ','.*')+'.*'
- #target = '(%s)|(%s)|(%s)' % (target,target.lower(),target.upper())
- self.debug( 'In TaurusTree.findInTree(%s)'%target)
try:
- self.info('TaurusDevTree.findInTree(%s): searching in level %d'%(regexp))
- items = (self.findItems(target,Qt.Qt.MatchRegExp|Qt.Qt.MatchRecursive))
- if not items:
- QtGui.QMessageBox.warning(None,'Search', 'Nothing found matching %s'%target, QtGui.QMessageBox.Ok)
+ t0 = time.time()
+ nodes = self.getMatchingNodes(regexp,all=True,exclude=exclude)
+ if len(nodes)>150:
+ v = Qt.QMessageBox.warning(None,'Device Tree Search',
+ 'Your search matches too many devices (%d) and may slow down the application.\nDo you want to continue?'%len(nodes),
+ Qt.QMessageBox.Ok|Qt.QMessageBox.Cancel)
+ if v == Qt.QMessageBox.Cancel:
+ self.debug('Search cancelled by user.')
+ return
+ if nodes:
+ #It's good to have first node matched to be selected fast
+ if select:
+ nodes[0].setSelected(True)
+ self.setCurrentItem(nodes[0])
+ self.deviceSelected(self.getNodeDeviceName(nodes[0])) #Searches must not trigger events!
+ self.debug('The selected node is %s'%self.getNodeText(nodes[0]))
+ #Then proceed to expand/close the rest of nodes
+ parents = set(parent for node in nodes for parent in self.getNodePath(node) if parent)
+ for item in self.item_list:
+ matched,expanded = item in parents,item.isExpanded()
+ if (matched and not expanded):
+ if queue: self.Expander.getQueue().put((item,True))
+ else: item.setExpanded(True)
+ elif (not matched and expanded and self.collapsing_search):
+ if queue: self.Expander.getQueue().put((item,False))
+ else: item.setExpanded(False)
+ if select:
+ self.scrollTo(self.indexFromItem(nodes[0]),Qt.QAbstractItemView.PositionAtTop)#Center)
+ self.debug('\tfindInTree(%s): %d nodes found in %f s' %(regexp,len(nodes),time.time()-t0))
else:
- if collapseAll:
- for i in range(self.topLevelItemCount()):
- self.collapseItem(self.topLevelItem(i))
- for it in items:
- self.expandItem(it)
- node = items[0]
- node.setSelected(True)
- self.setCurrentItem(node)
+ if collapseAll:
+ if queue: [self.Expander.getQueue().put((item,False)) for item in self.item_list if item.isExpanded()]
+ else: [item.setExpanded(False) for item in self.item_list if item.isExpanded()]
+ self.debug( 'findInTree(%s): Node not found'%(regexp))
+ if queue: self.Expander.next()
except:
- self.warning( 'findInTree(): Node %s not found, Exception:'%(target))
- self.traceback()
+ self.warning( 'findInTree(%s): failed'%(regexp))
+ self.error(traceback.format_exc())
def sortCustom(self,order):
+ assert order and len(order), 'sortCustom(order) must not be empty'
allChildren = {}
while self.topLevelItemCount():
it = self.takeTopLevelItem(0)
allChildren[str(it.text(0))]=it
- for rexp in order:
- for c in sorted(allChildren):
- if not re.match(rexp,c): continue
- it = allChildren.pop(c)
- self.debug( 'tree.sortCustom(%s): %s inserted at %d' % (order,it.text(0),self.topLevelItemCount()))
- self.insertTopLevelItem(self.topLevelItemCount(),it)
-
- for k,v in sorted(allChildren.items()): self.insertTopLevelItem(self.topLevelItemCount(),v)
+
+ sorter = lambda k,ks=[re.compile(c) for c in order]: str((i for i,r in enumerate(ks) if r.match(k.lower())).next())+str(k)
+ for c,it in sorted(allChildren.items(),key=lambda k:sorter(k[0])):
+ self.debug( 'tree.sortCustom(%s): %s inserted at %d' % (order,it.text(0),self.topLevelItemCount()))
+ self.insertTopLevelItem(self.topLevelItemCount(),it)
return
- #@QtCore.pyqtSignature("setIcons")
+ ###########################################################################
+ # Update node colors
+
+ #@Qt.pyqtSignature("setIcons")
def setIcons(self,dct={},root_name=None,regexps=True):
'''
This method change the icons depending of the status of the devices
Dict is a dictionary with name of device and colors such as {name_device:color,name_device2:color2}
An alternative may be an icon name!
'''
- #QtGui.QBrush(QtGui.QColor(143,165,203))
- #Qt.QBrush(Qt.Qt.yellow)
- secs = time.time()
- ID = int(100*random.random())
- #self.clear()
- #self.addIcons(root_name,dict)
- state2color = lambda state: QtGui.QColor(DEVICE_STATE_PALETTE.number(state))
- quality2color = lambda attr: QtGui.QColor(ATTRIBUTE_QUALITY_PALETTE.number(quality))
+ #secs = time.time()
+ #ID = int(100*random.random())
+ state2color = lambda state: Qt.QColor(DEVICE_STATE_PALETTE.number(state))
+ #quality2color = lambda attr: Qt.QColor(ATTRIBUTE_QUALITY_PALETTE.number(quality))
def update_node(node,key,dct):
if hasattr(node,'CustomForeground'):
- node.setForeground(0,QtGui.QBrush(QtGui.QColor(node.CustomForeground)))
+ node.setForeground(0,Qt.QBrush(Qt.QColor(node.CustomForeground)))
if hasattr(node,'CustomBackground'):
- node.setBackground(0,QtGui.QBrush(QtGui.QColor(node.CustomBackground)))
+ node.setBackground(0,Qt.QBrush(Qt.QColor(node.CustomBackground)))
elif hasattr(node,'StateBackground'):
- node.setBackground(0,QtGui.QBrush(state2color(dct[key])))
+ node.setBackground(0,Qt.QBrush(state2color(dct[key])))
if hasattr(node,'CustomIcon'):
- node.setIcon(0,QtGui.QIcon(node.CustomIcon))
+ node.setIcon(0,Qt.QIcon(node.CustomIcon))
else:
#key = str(node.text(0)).split(' ')[0]
if key.count('/')==2:
self.setStateIcon(node,dct and dct[key] or '')
return
-
- nodes = self.getAllNodes()
+
if not isinstance(dct,dict):
dct = dict.fromkeys(dct,'')
-
+ nodes = self.getAllNodes()
for name,node in nodes.iteritems():
name = str(name).split()[0]
if node.isHidden(): continue
@@ -680,246 +842,239 @@ class TaurusDevTree(QtGui.QTreeWidget, TaurusBaseWidget):
self.debug('In setStateIcon(...): Icons for states not available!')
return
if color=="#00ff00" or color in 'ON,OPEN,EXTRACT':
- icon = QtGui.QIcon(":/ICON_GREEN")
+ icon = Qt.QIcon(":/ICON_GREEN")
child.setIcon(0,icon)
elif color=="#ff0000" or color in 'OFF,FAULT':
- icon = QtGui.QIcon(":/ICON_RED")
+ icon = Qt.QIcon(":/ICON_RED")
child.setIcon(0,icon)
elif color=="#ff8c00" or color in 'ALARM':
- icon = QtGui.QIcon(":/ICON_ORANGE")
+ icon = Qt.QIcon(":/ICON_ORANGE")
child.setIcon(0,icon)
elif color=="#ffffff" or color in 'CLOSE,INSERT':
- icon = QtGui.QIcon(":/ICON_WHITE")
+ icon = Qt.QIcon(":/ICON_WHITE")
child.setIcon(0,icon)
elif color=="#80a0ff" or color in 'MOVING,RUNNING':
- icon = QtGui.QIcon(":/ICON_BLUE")
+ icon = Qt.QIcon(":/ICON_BLUE")
child.setIcon(0,icon)
elif color=="#ffff00" or color in 'STANDBY':
- icon = QtGui.QIcon(":/ICON_YELLOW")
+ icon = Qt.QIcon(":/ICON_YELLOW")
child.setIcon(0,icon)
elif color=="#cccc7a" or color in 'INIT':
- icon = QtGui.QIcon(":/ICON_BRAWN")
+ icon = Qt.QIcon(":/ICON_BRAWN")
child.setIcon(0,icon)
elif color=="#ff00ff" or color in 'DISABLE':
- icon = QtGui.QIcon(":/ICON_PINK")
+ icon = Qt.QIcon(":/ICON_PINK")
child.setIcon(0,icon)
elif color=="#808080f" or color in 'UNKNOWN':
- icon = QtGui.QIcon(":/ICON_GREY")
+ icon = Qt.QIcon(":/ICON_GREY")
child.setIcon(0,icon)
else:
- icon = QtGui.QIcon(":/ICON_WHITE")
+ icon = Qt.QIcon(":/ICON_WHITE")
child.setIcon(0,icon)
-
- #@QtCore.pyqtSignature("addIcons")
- def addIcons(self,root_name,dict):
- '''
- This method updates all the icons in the tree using a dictionary.
- But in fact it creates the tree from scratch !?!?!?!
- '''
- filters = dict.keys()
- dicttest = self.buildDictFromFilters(filters)
- topItem = QtGui.QTreeWidgetItem(self)
- self.setTopItemName(root_name)
- #if self._nameTopItem == "":
- #self.drawTree(self,dicttest)
- #else:
- self.drawTree(topItem,dicttest)
- for index in range(topItem.childCount()):
- child = topItem.child(index)
- self.setStateIcon(child)
-
- #list1 = ["LT/VC/ALL","LT02/VC/IP-01"]
- #self.setIcons2(list1)
-
- #def setIcons2(self,mylist):
- #'''
- #This method change the icons depending of the status of the devices
- #---- list is a list of names of device ["LT/VC/ALL","LT02/VC/IP-01"]
- #'''
- #print "\n\n\n SETICON2 ----------------------------------------------"
- #self.clear()
-
- #dicttest = self.buildDictFromFilters(mylist)
- #topItem = QtGui.QTreeWidgetItem(self)
- #self.setTopItemName("Testing")
-
- #self.drawTree(topItem,dicttest)
- #for index in range(topItem.childCount()):
- #child = topItem.child(index)
- #key = str(child.text(0))
-
- #model = str(mylist[index])
- #model = model+'/state'
- #print " model = ", model
- #model = "LT02/VC/IP-01/state"
- #myClass.setModel(self,model)
-
- #v = self.getModelValueObj()
- ##if not value:
- ## return self.getNoneValue()
- ##self._currText = "ALARM"
- #print "v = ",str(v)
- #bg_brush, fg_brush = QT_DEVICE_STATE_PALETTE.qbrush(self._currText)
- #print bg_brush.color().name()," ",fg_brush.color().name(),"\n\n"
- #color = bg_brush.color().name()
- #if color =="#00ff00":#ON,OPEN,EXTRACT
- #icon = QtGui.QIcon(":/ICON_GREEN")
- #child.setIcon(0,icon)
- #elif color =="#ff0000":#OFF,FAULT
- #icon = QtGui.QIcon(":/ICON_RED")
- #child.setIcon(0,icon)
- #elif color =="#ff8c00":#ALARM
- #icon = QtGui.QIcon(":/ICON_ORANGE")
- #child.setIcon(0,icon)
- #elif color =="#ffffff":#CLOSE,INSERT
- #icon = QtGui.QIcon(":/ICON_WHITE")
- #child.setIcon(0,icon)
- #elif color =="#80a0ff":#MOVING,RUNNING
- #icon = QtGui.QIcon(":/ICON_BLUE")
- #child.setIcon(0,icon)
- #elif color =="#ffff00":#STANDBY
- #icon = QtGui.QIcon(":/ICON_YELLOW")
- #child.setIcon(0,icon)
- #elif color =="#cccc7a":#INIT
- #icon = QtGui.QIcon(":/ICON_BRAWN")
- #child.setIcon(0,icon)
- #elif color =="#ff00ff":#DISABLE
- #icon = QtGui.QIcon(":/ICON_PINK")
- #child.setIcon(0,icon)
- #elif color =="#808080f":#UNKNOWN
- #icon = QtGui.QIcon(":/ICON_GREY")
- #child.setIcon(0,icon)
- #else:
- #icon = QtGui.QIcon(":/ICON_WHITE")
- #child.setIcon(0,icon)
-
-
- def setTopItemName(self,name):
- self._nameTopItem = name
- topItem = self.topLevelItem(0)
- font = QtGui.QFont()
- font.setPointSize(15)
- font.setItalic(True)
- topItem.setFont(0,font)
- topItem.setText(0,name)
- topItem.setExpanded(True)
- if icons_dev_tree is None:
- self.debug('In setTopItemName(...): Icons for states not available!')
- else:
- icon = QtGui.QIcon(":/ICON_FILENEW")
- topItem.setIcon(0,icon)
-
- def defineStyle(self):
- self.setGeometry(QtCore.QRect(90,60,256,192))
-
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- # Methods for QTProperty "filters" and "expand"
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-
- def setFilters(self,filters):
- self.__filters = str(filters)
- self.setTree(self.__filters)
-
- def getFilters(self):
- return self.__filters
-
- def resetFilters(self):
- self.__servers=""
- self.setTree(self.__filters)
-
- def setExpand(self,expand):
- self.__expand = expand
-
- def getExpand(self):
- return self.__expand
-
- def resetExpand(self):
- self.__expand = 0
-
+ ############################################################################
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- # TaurusBaseWidget over writing methods
+ # Event methods
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- def sizeHint(self):
- return QtGui.QTreeWidget.sizeHint(self)
-
- def minimumSizeHint(self):
- return QtGui.QTreeWidget.minimumSizeHint(self)
-
- def getModelClass(self):
- return taurus.core.TaurusDatabase
-
- @classmethod
- def getQtDesignerPluginInfo(cls):
- ret = TaurusBaseWidget.getQtDesignerPluginInfo()
- ret['module'] = 'taurus.qt.qtgui.tree'
- ret['group'] = 'Taurus Views'
- ret['icon'] = ":/designer/listview.png"
+ def deviceClicked(self,item,column):
+ self.trace("In TaurusDevTree.deviceClicked(%s)"%item.text(column))
+ self.deviceSelected(self.getNodeDeviceName())
+
+ def deviceSelected(self,device_name=''):
+ '''QSIGNAL: this method is used to emit deviceSelected(QString) signal'''
+ self.trace("In TaurusDevTree.deviceSelected(%s)"%device_name)
+ try:
+ #item = self.currentItem()
+ device_name = device_name or self.getNodeDeviceName()#item.text(0)
+ if str(device_name).count('/')!=2: return
+ #Signal
+ self.trace('TaurusTree emit deviceSelected(%s) signal ...'%device_name)
+ self.emit(Qt.SIGNAL("deviceSelected(QString)"), Qt.QString(device_name))
+ except:
+ self.error(traceback.format_exc())
+ pass
+
+ def getModelMimeData(self):
+ '''Returns a MimeData object containing the model data. The default implementation
+ fills the `TAURUS_MODEL_MIME_TYPE`. If the widget's Model class is
+ Attribute or Device, it also fills `TAURUS_ATTR_MIME_TYPE` or
+ `TAURUS_DEV_MIME_TYPE`, respectively
+ '''
+ mimeData = Qt.QMimeData()
+ node = self.currentItem()
+ draggable = self.getNodeDraggable(node)
+ if draggable:
+ slashes = draggable.count('/')-draggable.count(':')
+ #mimeData.setData('application/x-qabstractitemmodeldatalist',draggable)
+ if slashes==3: mimeData.setData(TAURUS_ATTR_MIME_TYPE, draggable)
+ elif slashes==2: mimeData.setData(TAURUS_DEV_MIME_TYPE, draggable)
+ else: mimeData.setData(TAURUS_MODEL_MIME_TYPE, draggable)
+ return mimeData
+
+ def mouseMoveEvent(self, event):
+ '''copied from TaurusBaseWidget to provide drag events'''
+ self.debug('In TaurusDevTree.mouseMoveEvent')
+ if not self._dragEnabled or not event.buttons() & Qt.Qt.LeftButton:
+ return self.getQtClass().mouseMoveEvent(self, event)
+ if (event.pos() - self.dragStartPosition).manhattanLength() < Qt.QApplication.startDragDistance():
+ return self.getQtClass().mouseMoveEvent(self, event)
+ #The mouseMoveEvent of QTreeWidget do not allow drag, commented
+ ret = None #self.getQtClass().mouseMoveEvent(self, event) #call the superclass
+ event.accept() #we make sure we accept after having called the superclass so that it is not propagated (many default implementations of mouseMoveEvent call event.ignore())
+ drag = Qt.QDrag(self)
+ drag.setMimeData(self.getModelMimeData())
+ drag.exec_(Qt.Qt.CopyAction, Qt.Qt.CopyAction)
return ret
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- # QT properties
- #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-
- def setModel(self, m):
- if isinstance(m, Qt.QAbstractItemModel):
- return Qt.QTreeWidget.setModel(self, m)
- return TaurusBaseWidget.setModel(self, m)
-
-
- model = QtCore.pyqtProperty("QString", TaurusBaseWidget.getModel,
- TaurusBaseWidget.setModel,
- TaurusBaseWidget.resetModel)
-
- filters = QtCore.pyqtProperty("QString", getFilters, setFilters, resetFilters)
-
- expand = QtCore.pyqtProperty("int", getExpand, setExpand, resetExpand)
-
- useParentModel = QtCore.pyqtProperty("bool",
- TaurusBaseWidget.getUseParentModel,
- TaurusBaseWidget.setUseParentModel,
- TaurusBaseWidget.resetUseParentModel)
+ def mimeTypes(self):
+ return self.getSupportedMimeTypes()
+
+ def dropEvent(self, event):
+ '''reimplemented to support dropping of modelnames in forms'''
+ self.debug('dropEvent(%s): %s,%s'%(event,event.mimeData(),split_model_list(event.mimeData().formats())))
+ if event.source() is self:
+ self.info('Internal drag/drop not allowed')
+ return
+ if any(s in event.mimeData().formats() for s in self.getSupportedMimeTypes()):
+ #mtype = self.handleMimeData(event.mimeData(),self.addModels)#lambda m:self.addModels('^%s$'%m))
+ event.acceptProposedAction()
+ else:
+ self.warning('Invalid model in dropped data')
+
+ ########################################################################################################################3
+ ## @name Context Menu Actions
+ # @{
+
+ def contextMenuEvent(self,event):
+ '''
+ This function is called when right clicking on TaurusDevTree area.
+ '''
+ node = self.currentItem()
+ self.showNodeContextMenu(node,event)
+ return
+
+ def showNodeContextMenu(self,node,event):
+ """
+ A pop up menu will be shown with the available options.
+ Menus are managed using two tuple lists for each node: node.ContextMenu and node.ExpertMenu
+ """
+ obj = self.getNodeText(node)
+ self.debug('showNodeContextMenu(%s)'%obj)
+ if node is None:
+ node = self
+ else:
+ if not hasattr(node,'ContextMenu'):
+ node.ContextMenu=[]
+ if not 'Search ...' in [k for k,a in node.ContextMenu]: ##Creating default menu
+ if obj.count('/')==2:
+ #Menu for devices
+ node.ContextMenu.append(("Open Panel", self.showPanel))
+ node.ContextMenu.append(("Show Attributes",self.addAttrToNode))
+ node.ContextMenu.append(("Go to %s Controller"%self.getNodeParentName(node),\
+ lambda p=self.getNodeParentName(node): p and self.findInTree(p)
+ ))
+
+ if not hasattr(node,'ExpertMenu'): setattr(node,'ExpertMenu',self.ExpertMenu)#[])
+ if not 'Show Properties' in [k for k,a in node.ExpertMenu]:
+ node.ExpertMenu.append(("Show Properties", self.showProperties))
+ def test_device():
+ device = str(self.getNodeDeviceName())
+ if device:
+ comm = 'tg_devtest %s &'%device
+ os.system(comm)
+ else: self.debug('TaurusDevTree.TestDevice: Selected Device is None!')
+ node.ExpertMenu.append(("Test Device", test_device))
+ node.ContextMenu.append(('',None))
+
+ elif obj.count('/')==3:
+ #Menu for attributes
+ for k,v in self.AttributeMenu:
+ self.debug('Adding action %s'%k)
+ if type(v) is str and hasattr(self,v):
+ node.ContextMenu.append((k, getattr(self,v)))
+ else:
+ node.ContextMenu.append((k, lambda s=self.getNodeAlias(node): v(s)))
+ #node.ContextMenu.append(("add to Trends", self.addToPlot))
+ #node.ContextMenu.append(("remove from Trends", self.removeFromPlot))
+ node.ContextMenu.append(('',None))
+ elif not hasattr(node,'ExpertMenu'): setattr(node,'ExpertMenu',self.ExpertMenu)#[])
+
+ node.ContextMenu.append(("Expand Node", self.expandNode))
+ node.ContextMenu.append(("Collapse Node", self.collapseNode))
+ node.ContextMenu.append(('Expand All',lambda:self.expandAll()))
+ node.ContextMenu.append(("Collapse All", lambda: self.collapseNode(ALL=True)))
+ node.ContextMenu.append(("Search ...",\
+ lambda: self.findInTree(str(Qt.QInputDialog.getText(self,'Search ...','Write a part of the name',Qt.QLineEdit.Normal)[0]))
+ ))
+ #configDialogAction = menu.addAction("Refresh Tree")
+ #self.connect(configDialogAction, Qt.SIGNAL("triggered()"), self.refreshTree)
+ menu = Qt.QMenu(self)
+
+ if hasattr(node,'ContextMenu'):
+ last_was_separator = True
+ for t in (type(node.ContextMenu) is dict and node.ContextMenu.items() or node.ContextMenu):
+ try:
+ k,action = t
+ if k:
+ configDialogAction = menu.addAction(k)
+ if action: self.connect(configDialogAction, Qt.SIGNAL("triggered()"), action)
+ else: configDialogAction.setEnabled(False)
+ last_was_separator = False
+ elif not last_was_separator:
+ menu.addSeparator()
+ last_was_separator = True
+ except Exception,e:
+ self.warning('Unable to add Menu Action: %s:%s'%(t,e))
+
+ if hasattr(node,'ExpertMenu'):
+ menu.addSeparator()
+ expert = menu.addMenu('Expert')
+ #expert.addSeparator()
+ last_was_separator = True
+ for t in (type(node.ContextMenu) is dict and node.ExpertMenu.items() or node.ExpertMenu):
+ try:
+ k,action = t
+ if k:
+ configDialogAction = expert.addAction(k)
+ if action: self.connect(configDialogAction, Qt.SIGNAL("triggered()"), action)
+ else: configDialogAction.setEnabled(False)
+ last_was_separator = False
+ elif not last_was_separator:
+ expert.addSeparator()
+ last_was_separator = True
+ except Exception,e:
+ self.warning('Unable to add Expert Action: %s:%s'%(t,e))
+ #menu.addSeparator()
+ menu.exec_(event.globalPos())
+ del menu
-class newDialog(QtGui.QDialog):
- """ This class create the dialog """
- def __init__(self, parent = None):
- QtGui.QDialog.__init__(self, parent)
+class PopupDialog(Qt.QDialog):
+ """
+ This class create the dialog
+ Dialog is used to make new floating panels persistent
+ """
+ def __init__(self, parent = None,target = None):
+ Qt.QDialog.__init__(self, parent)
+ if target: self.initComponents(target)
- def initComponents(self,newWidget):
- widgetLayout = QtGui.QVBoxLayout(self)
+ def initComponents(self,newWidget,show=True):
+ widgetLayout = Qt.QVBoxLayout(self)
widgetLayout.setContentsMargins(10,10,10,10)
widgetLayout.addWidget(newWidget)
+ self.setWindowTitle(newWidget.windowTitle())
+ self.setModal(False)
+ self.setVisible(True)
+ if show: self.exec_()
#####################################################################################
-#####################################################################################
-
-#class TaurusDevTreeItem(QtGui.QTreeWidgetItem, TaurusBaseWidget):
- #def __init__(self, parent=None, designMode = False):
- #try:
- #name = "TaurusDevTree"
-
- #self.call__init__wo_kw(QtGui.QTreeWidgetItem, parent)
- #self.call__init__(TaurusBaseWidget, name, designMode=designMode)
-
- #self.setObjectName(name)
- ##self.setModelCheck('controls01:10000')
- #self.defineStyle()
- #self.__filters = ""
- #self.__expand =1
- #self.index = 0
- #self._nameTopItem = ""
- #self._filters = None
- ##Signal
- ##QtCore.QObject.connect(self,QtCore.SIGNAL("itemSelectionChanged()"),self.deviceClicked)
- #except Exception,e:
- #self.info('Exception in TaurusDevTreeItem.__init__():' , str(e))
- #self.traceback()
- #pass
-class TaurusTreeNode(QtGui.QTreeWidgetItem, TaurusBaseComponent):
- """Base class for all Taurus Tree Node Items"""
+class TaurusTreeNode(Qt.QTreeWidgetItem, TaurusBaseComponent):
+ """
+ Unused; one day should replace TaurusTreeNodeContainer and all methods moved here.
+ Base class for all Taurus Tree Node Items
+ """
#---------------------------------------------------------------------------
# Write your own code here to define the signals generated by this widget
@@ -929,7 +1084,7 @@ class TaurusTreeNode(QtGui.QTreeWidgetItem, TaurusBaseComponent):
def __init__(self, name = None, parent = None):
name = name or self.__class__.__name__
- self.call__init__wo_kw(QtGui.QTreeWidgetItem, parent)
+ self.call__init__wo_kw(Qt.QTreeWidgetItem, parent)
self.call__init__(TaurusBaseComponent, name, parent)
#self.defineStyle()
@@ -966,13 +1121,13 @@ class TaurusTreeNode(QtGui.QTreeWidgetItem, TaurusBaseComponent):
#self.repaint()
#if self._parent: self._parent.repaint()
- state2color = lambda state: QtGui.QColor(DEVICE_STATE_PALETTE.number(state))
- quality2color = lambda attr: QtGui.QColor(ATTRIBUTE_QUALITY_PALETTE.number(attr))
+ state2color = lambda state: Qt.QColor(DEVICE_STATE_PALETTE.number(state))
+ quality2color = lambda attr: Qt.QColor(ATTRIBUTE_QUALITY_PALETTE.number(attr))
v = self.getModelValueObj()
if isinstance(v,PyTango.DevState):
- node.setBackground(0,QtGui.QBrush(state2color(v)))
+ node.setBackground(0,Qt.QBrush(state2color(v))) #@TODO: node is undefined. Check code
if hasattr(v,'quality'):
- self.setForeground(0,QtGui.QBrush(quality2color(v.quality)))
+ self.setForeground(0,Qt.QBrush(quality2color(v.quality)))
def isReadOnly(self):
return True
@@ -997,7 +1152,7 @@ class TaurusTreeNode(QtGui.QTreeWidgetItem, TaurusBaseComponent):
## widget
##
## Typical code is:
- ##self.connect(self, QtCore.SIGNAL('valueChangedDueToEvent(QString)'),
+ ##self.connect(self, Qt.SIGNAL('valueChangedDueToEvent(QString)'),
## self.setTextValue)
#ret = TaurusBaseWidget.attach(self)
## by default enable/disable widget according to attach state
@@ -1012,9 +1167,9 @@ class TaurusTreeNode(QtGui.QTreeWidgetItem, TaurusBaseComponent):
## Write your own code here after detaching the widget from the model
##
## Typical code is:
- ##self.emit(QtCore.SIGNAL('valueChangedDueToEvent(QString)'),
- ## QtCore.QString(value_str))
- ##self.disconnect(self, QtCore.SIGNAL('valueChangedDueToEvent(QString)'),
+ ##self.emit(Qt.SIGNAL('valueChangedDueToEvent(QString)'),
+ ## Qt.QString(value_str))
+ ##self.disconnect(self, Qt.SIGNAL('valueChangedDueToEvent(QString)'),
## self.setTextValue)
## by default disable widget when dettached
#self.setEnabled(False)
@@ -1024,11 +1179,11 @@ class TaurusTreeNode(QtGui.QTreeWidgetItem, TaurusBaseComponent):
# QT properties
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
- model = QtCore.pyqtProperty("QString", TaurusBaseWidget.getModel,
+ model = Qt.pyqtProperty("QString", TaurusBaseWidget.getModel,
TaurusBaseWidget.setModel,
TaurusBaseWidget.resetModel)
- useParentModel = QtCore.pyqtProperty("bool",
+ useParentModel = Qt.pyqtProperty("bool",
TaurusBaseWidget.getUseParentModel,
TaurusBaseWidget.setUseParentModel,
TaurusBaseWidget.resetUseParentModel)
@@ -1036,47 +1191,241 @@ class TaurusTreeNode(QtGui.QTreeWidgetItem, TaurusBaseComponent):
#---------------------------------------------------------------------------
# Write your own code here for your own widget properties
-class SearchEdit(Qt.QWidget):
- """ This class provides a search(QString) signal to be connected to TaurusDevTree.findInTree slot """
-
- __pyqtSignals__ = ("search(QString)",)
- def __init__(self,parent=None,icon=None):
- Qt.QWidget.__init__(self,parent)
+class TaurusDevTreeOptions(Qt.QWidget):
+ """ This class provides a search(QString) signal to be connected to TaurusDevTree.findInTree slot """
+ __pyqtSignals__ = (
+ "search(QString)",
+ "setFilters(QString)",
+ "hideUnexported",
+ "hideUnarchived",
+ )
+
+ def __init__(self,parent=None,icon=None):
+ Qt.QWidget.__init__(self,parent)
+
+ self.setLayout(Qt.QHBoxLayout())
+ try:
+ self._pixmap = Qt.QPixmap(icon or 'image/icons/search.png')
+ self._label = Qt.QLabel(self)
+ self._label.setPixmap(self._pixmap)
+ self.layout().addWidget(self._label)
+ except:
+ pass
+
+ self._edit = Qt.QLineEdit()
+ self._button = Qt.QPushButton()
+ self._button.setText('Search')
+ self.connect(self._edit,Qt.SIGNAL('returnPressed()'),self._button.animateClick)
+ self.connect(self._button,Qt.SIGNAL('clicked()'),self._emitSearch)
+ self.layout().addWidget(self._edit)
+ self.layout().addWidget(self._button)
+
+ def connectWithTree(self,tree):
+ Qt.QObject.connect(self,Qt.SIGNAL("search(QString)"),tree.findInTree)
+
+ def _emitSearch(self):
+ text = self._edit.text()
+ if text:
+ self.emit(Qt.SIGNAL("search(QString)"), text)
+ return
+
+SearchEdit = TaurusDevTreeOptions
+
+#####################################################################################
+
+class ServerBrowser(TaurusDevTree):
+ """ This class is used only when browsing by Server/Instance instead of Domain/Family/Member schema """
+
+ def getDeviceDict(self,filters):
+ '''
+ This method build a dictionary of instances and devices depending on the given servers,devices or instances in QTProperty or in another widget
+ --- filters is a string with names of devices/servers such as "LT/VC/ALL,LT02/VC/IP-01" or "modbus,pyplc"
+ --- filters is a list of devices such as ["LT/VC/ALL","LT02/VC/IP-01"]
+ '''
+ self.trace('In TaurusDevTree.buildDictFromFilters(%s)'%filters)
+ self._filters = filters
+ if type(filters)==type("") or isinstance(filters,Qt.QString):#@TODO:QString and QStringList should not be used (They disappear in API2)
+ filters = str(filters).split(',')
+ elif isinstance(filters,Qt.QStringList): #@TODO:QString and QStringList should not be used (They disappear in API2)
+ filters = list(filters)
+ elif type(filters)!=type([]):
+ self.debug("'filters' has to be a string or the list type!")
+ vals = {}
+ if not filters: return vals
+ if filters[0].count('/')==0:
+ self.debug('In TaurusDevTree.buildDictFromFilters(%s): Getting Servers'%filters)
+ targets,addMe = self.db.get_server_name_list(),self.addInstToServ #Searching servers
+ elif filters[0].count('/')==1:
+ self.debug('In TaurusDevTree.buildDictFromFilters(%s): Getting Instances'%filters)
+ targets,addMe = self.db.get_server_list(),self.addDevToInst #Searching instances
+ elif filters[0].count('/')==2:
+ self.debug('In TaurusDevTree.buildDictFromFilters(%s): Getting Devices'%filters)
+ targets,addMe = self.db.get_device_exported("*"),lambda s: {s:{}} #self.addAttrToDev #Searching devices
+ else:
+ raise Exception('UnknownFilter!: %s'%filters[0])
+
+ for t in targets:
+ for f in filters:
+ f = str(f)
+ exp = f.replace('*','.*').lower() if '*' in f and '.*' not in f else f.lower()
+ if re.match(exp,t.lower()):
+ self.debug('Adding node %s'%t)
+ vals[t] = addMe(t)
+ self.trace('Out of TaurusDevTree.getDeviceDict(%s)'%filters)
+ return vals
- self.setLayout(Qt.QHBoxLayout())
- try:
- self._pixmap = Qt.QPixmap(icon or 'image/icons/search.png')
- self._label = Qt.QLabel(self)
- self._label.setPixmap(self._pixmap)
- self.layout().addWidget(self._label)
- except:
- pass
+ def addInstToServ(self,my_server):
+ dict = {}
+ list_inst = self.get_instances_for_server(my_server)
+ lower_list_inst = [s.lower() for s in list_inst]
+ for my_inst in lower_list_inst:
+ if self._expand:
+ dict[my_inst] = self.addDevtoInst(my_inst)
+ else:
+ dict[my_inst] = 0
+ return dict
+
+ def addDevtoInst(self,my_inst,expand_attrs = False):
+ d = {}
+ list_dev = self.get_devices_for_instance(my_inst)
+ lower_list_dev = [s.lower() for s in list_dev]
+ for my_dev in lower_list_dev:
+ if self._expand:
+ d[my_dev] = self.addAttrToDev(my_dev) if expand_attrs else {my_dev:{}}
+ else:
+ d[my_dev] = 0
+ return d
+
+ def addFamilyToDomain(self,prev,expand_attrs):
+ d = {}
+ #children = self.get_devices_for_instance(my_inst)
+ lower_list_dev = [s.lower() for s in list_dev] #@TODO: list_dev is undefined. Check code
+ for my_dev in lower_list_dev:
+ if self._expand:
+ d[my_dev] = self.addAttrToDev(my_dev) if expand_attrs else {my_dev:{}}
+ else:
+ d[my_dev] = 0
+ return d
+
- self._edit = Qt.QLineEdit()
- self._button = Qt.QPushButton()
- self._button.setText('Search')
- self.connect(self._edit,Qt.SIGNAL('returnPressed()'),self._button.animateClick)
- self.connect(self._button,Qt.SIGNAL('clicked()'),self._emitSearch)
- self.layout().addWidget(self._edit)
- self.layout().addWidget(self._button)
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+ # Methods for database commands
+ #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
+
+ def get_instances_for_server(self, server_name):
+ #executable_name = class_name
+ instances = self.db.get_instance_name_list(server_name)
+ return [server_name+'/'+instance for instance in instances]
- def _emitSearch(self):
- text = self._edit.text()
- if text:
- self.emit(Qt.SIGNAL("search(QString)"), text)
- return
+ def get_devices_for_instance(self, instance_name):
+ devslist = self.db.get_device_class_list(instance_name)
+ return [dev for dev in devslist if '/' in dev and not dev.startswith('dserver')]
+
+
+ ##@}
#####################################################################################
-#####################################################################################
-if __name__ == "__main__":
+class TaurusSearchTree(TaurusWidget):
+ """
+ This Class is a mere wrapper providing a TaurusDevTree connected with an options panel and some optional checkboxes.
+ """
+ __pyqtSignals__ = list(getattr(TaurusWidget,'__pyqtSignals__',[]))+[
+ "search(QString)",
+ "modelChanged(const QString &)",
+ "deviceSelected(QString)",
+ "addAttrSelected(QStringList)",
+ "removeAttrSelected(QStringList)",
+ "refreshTree",
+ "nodeFound"
+ ]
+ __slots__ = (
+ "setTangoHost",
+ #"setModel",
+ "addModels",
+ "setModelCheck",
+ #"loadTree",
+ "setTree",
+ "findInTree",
+ "expandAll",
+ "setFilters",
+ )
+
+ @staticmethod
+ def method_forwarder(*args,**kwargs):
+ m,o = kwargs['method'],kwargs['object']
+ #print 'Calling %s.%s'%(o,m)
+ getattr(o.__class__,m)(o,*args)
+
+ def defineStyle(self):
+ #print('In TaurusSearchTree.defineStyle()')
+ self.setWindowTitle('TaurusDevTree')
+ self.setLayout(Qt.QVBoxLayout())
+ self.edit = TaurusDevTreeOptions(self)
+ self.tree = TaurusDevTree(self)
+ self.layout().addWidget(self.edit)
+ self.layout().addWidget(self.tree)
+ self.registerConfigDelegate(self.tree)
+ #Slot forwarding ...
+ for k in TaurusDevTree.__dict__.keys():
+ #if k in ['__init__','defineStyle']: continue
+ if k not in self.__slots__: continue
+ try: setattr(self,k,partial(self.method_forwarder,method=k,object=self.tree))
+ except Exception,e: self.warning('Unable to add slot %s: %s'%(k,e))
+ #Event forwarding ...
+ for signal in TaurusDevTree.__pyqtSignals__:
+ #Qt.QObject.connect(self,Qt.SIGNAL("search(QString)"),tree.findInTree)
+ #self.emit(Qt.SIGNAL("search(QString)"), text)
+ Qt.QObject.connect(self.tree,Qt.SIGNAL(signal),lambda args,f=self,s=signal:f.emit(Qt.SIGNAL(s),args))
+ self.edit.connectWithTree(self)
+ return
+
+
+#####################################################################################
+
+def taurusDevTreeMain():
import sys
- app = Qt.QApplication(sys.argv)
+ from taurus.qt.qtgui.application import TaurusApplication
+ from taurus.core.util import argparse
+
+ if False:
+ app = Qt.QApplication([])
+ args = sys.argv[1:]
+ #args = app.get_command_line_args()
+ #app = TaurusApplication(sys.argv)
+ if not args: args = ['database']
+ else:
+ taurus.setLogLevel(taurus.Debug)
+ parser = argparse.get_taurus_parser()
+ parser.set_usage("%prog [options] devname [attrs]")
+ parser.set_description("Taurus Application inspired in Jive and Atk Panel")
+ parser.add_option("--config", "--config-file", dest="config_file", default=None,
+ help="use the given config file for initialization")
+ app = TaurusApplication(cmd_line_parser=parser,app_name="TaurusDevicePanel",
+ app_version=taurus.Release.version)
+ args = app.get_command_line_args()
+ options = app.get_command_line_options()
+
+ form = TaurusSearchTree()
+ #try:
+ #if options.tango_host is None:
+ #options.tango_host = taurus.Database().getNormalName()
+ #form.setTangoHost(options.tango_host)
+ #except: pass
+
+ def trace(m): print(m)
+ [setattr(form.tree,f,trace) for f in ('info','warning','error','trace')]
- args=sys.argv[1:]
- if not args: args = ['Starter']
- form = TaurusDevTree()
- form.setModel(os.getenv('TANGO_HOST'))
- form.setTree(args)
+ form.setLogLevel(taurus.Debug)
+ form.tree.setLogLevel(taurus.Debug)
+ #set a model list from the command line or launch the chooser
+ if options.config_file is not None:
+ form.tree.loadConfigFile(options.config_file)
+ if len(args)>0:
+ models=args
+ form.setModel(models)
form.show()
sys.exit(app.exec_())
+
+if __name__ == "__main__":
+ taurusDevTreeMain()
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/tree/taurustree.py b/lib/taurus/qt/qtgui/tree/taurustree.py
index dc5e55d..1b1b112 100644
--- a/lib/taurus/qt/qtgui/tree/taurustree.py
+++ b/lib/taurus/qt/qtgui/tree/taurustree.py
@@ -40,9 +40,9 @@ class TaurusBaseTreeWidget(QBaseTreeWidget, TaurusBaseModelWidget):
with_filter_widget=True, perspective=None, proxy=None):
self.call__init__(QBaseTreeWidget, parent, designMode=designMode,
with_navigation_bar=with_navigation_bar,
- with_filter_widget=with_filter_widget)
- self.call__init__(TaurusBaseModelWidget, designMode=designMode,
+ with_filter_widget=with_filter_widget,
perspective=perspective, proxy=proxy)
+ self.call__init__(TaurusBaseModelWidget, designMode=designMode)
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# TaurusBaseWidget overwriting
@@ -51,4 +51,4 @@ class TaurusBaseTreeWidget(QBaseTreeWidget, TaurusBaseModelWidget):
def updateStyle(self):
"""overwritten from class:`taurus.qt.qtgui.base.TaurusBaseWidget`. It is called when
the taurus model changes."""
- self.resizeColumns()
\ No newline at end of file
+ self.resizeColumns()
diff --git a/lib/taurus/qt/qtgui/util/__init__.py b/lib/taurus/qt/qtgui/util/__init__.py
index 15da35e..805208e 100644
--- a/lib/taurus/qt/qtgui/util/__init__.py
+++ b/lib/taurus/qt/qtgui/util/__init__.py
@@ -32,4 +32,5 @@ from .taurusactionfactory import *
from .taurusaction import *
from .tauruscolor import *
from .tauruswidgetfactory import *
-from .taurusscreenshot import *
\ No newline at end of file
+from .taurusscreenshot import *
+from .qdraganddropdebug import *
\ No newline at end of file
diff --git a/lib/taurus/qt/qtgui/util/qdraganddropdebug.py b/lib/taurus/qt/qtgui/util/qdraganddropdebug.py
new file mode 100644
index 0000000..86c9e93
--- /dev/null
+++ b/lib/taurus/qt/qtgui/util/qdraganddropdebug.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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__ = ["DropDebugger"]
+__docformat__ = 'restructuredtext'
+
+from taurus.qt import Qt
+
+class DropDebugger(Qt.QLabel):
+ '''A simple utility for debugging drag&drop.
+ This widget will accept drops and show a pop-up with the contents
+ of the MIME data passed in the drag&drop'''
+
+ def __init__(self, parent=None):
+ Qt.QLabel.__init__(self, parent)
+ self.setAcceptDrops(True)
+ self.setText('Drop something here')
+ self.setMinimumSize(300,200)
+ self.setWindowTitle('Drag&Drop Debugger')
+
+ def dragEnterEvent(self,event):
+ event.acceptProposedAction()
+
+ def dropEvent(self, event):
+ '''reimplemented to support drag&drop of models. See :class:`QWidget`'''
+ msg = '<b>MIMETYPE</b>: DATA. <ul>'
+ mimedata = event.mimeData()
+ for format in mimedata.formats():
+ data = mimedata.data(format)
+ msg += '<li><b>{0}</b>: "{1}"</li>'.format(format, data)
+ msg+='</ul>'
+ Qt.QMessageBox.information( self, "Drop event received", msg)
+
+
+if __name__=='__main__':
+ import sys
+ from taurus.qt.qtgui.application import TaurusApplication
+
+ app = TaurusApplication()
+ w=DropDebugger()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/lib/taurus/qt/qtgui/util/taurusaction.py b/lib/taurus/qt/qtgui/util/taurusaction.py
index 90e4750..6c949ef 100644
--- a/lib/taurus/qt/qtgui/util/taurusaction.py
+++ b/lib/taurus/qt/qtgui/util/taurusaction.py
@@ -57,7 +57,8 @@ class ExternalAppAction(Qt.QAction, BaseConfigurableClass):
if isinstance(cmdargs, (basestring, Qt.QString)):
import shlex
cmdargs = shlex.split(str(cmdargs))
- if text is None: text = os.path.basename(cmdargs[0])
+ self.path = os.path.realpath(cmdargs and cmdargs[0] or '')
+ if text is None: text = os.path.basename(cmdargs and cmdargs[0] or '')
if icon is None:
icon = getThemeIcon(self.DEFAULT_ICON_NAME)
elif isinstance(icon,basestring):
@@ -66,7 +67,7 @@ class ExternalAppAction(Qt.QAction, BaseConfigurableClass):
Qt.QAction.__init__(self, Qt.QIcon(icon), text, parent)
BaseConfigurableClass.__init__(self)
-
+ self._process = []
self.setCmdArgs(cmdargs)
self.connect(self, Qt.SIGNAL("triggered()"), self.actionTriggered)
self.setToolTip("Launches %s (external application)"%text)
@@ -91,26 +92,46 @@ class ExternalAppAction(Qt.QAction, BaseConfigurableClass):
def cmdArgs(self):
return self.__cmdargs
+ #def trigger(self,args=''):
+ #if args: self.setCmdArgs(args) #self.cmdArgs.append(args)
+ #Qt.QAction.trigger(self)
@Qt.pyqtSignature("triggered()")
- def actionTriggered(self):
+ def actionTriggered(self,args=None):
'''launches the external application as a subprocess'''
import subprocess
try:
- subprocess.Popen(self.cmdArgs())
+ if args is not None:
+ if isinstance(args, (basestring, Qt.QString)):
+ import shlex
+ args = shlex.split(str(args))
+ args = self.cmdArgs()+args
+ else:
+ args = self.cmdArgs()
+ self._process.append(subprocess.Popen(args))
except OSError:
Qt.QMessageBox.warning(self.parentWidget(), "Error launching %s"%unicode(self.text()),
"Cannot launch application:\n"+
" ".join(self.__cmdargs) +
"\nHint: Check that %s is installed and in the path"%unicode(self.text())
)
+
+ def kill(self):
+ #Kills all processes opened by this application
+ [p.kill() for p in self._process]
+
def check(self):
'''Returns True if the application is available for executing
:return: (bool)
'''
- raise NotImplementedError #@todo: implement a checker (check if self.cmdargs[0] is in the execution path and is executable
-
+ #raise NotImplementedError #@todo: implement a checker (check if self.cmdargs[0] is in the execution path and is executable
+ path = os.path.realpath(self.cmdArgs()[0])
+ try:
+ os.stat(path)
+ return True
+ except:
+ return False
class TaurusMenu(Qt.QMenu):
"""Base class for Taurus Menus"""
diff --git a/lib/taurus/qt/qtgui/util/tauruswidgetfactory.py b/lib/taurus/qt/qtgui/util/tauruswidgetfactory.py
index 4f29cd6..39a5886 100644
--- a/lib/taurus/qt/qtgui/util/tauruswidgetfactory.py
+++ b/lib/taurus/qt/qtgui/util/tauruswidgetfactory.py
@@ -196,7 +196,7 @@ class TaurusWidgetFactory(taurus.core.util.Singleton, taurus.core.util.Logger):
return [ klass for mod_name, klass in self._qt_widgets.values()]
def getWidgetClass(self, name):
- return self._qt_widgets.get(name)[1]
+ return self._qt_widgets[name][1]
def getTaurusWidgetClassNames(self):
return self._taurus_widgets.keys()
diff --git a/lib/taurus/qt/taurusqtoptions.py b/lib/taurus/qt/taurusqtoptions.py
index b0a8307..2155d8d 100644
--- a/lib/taurus/qt/taurusqtoptions.py
+++ b/lib/taurus/qt/taurusqtoptions.py
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -25,7 +25,7 @@
"""The taurus.qt options submodule. It contains qt-specific part of taurus"""
-__all__ = ["QT_API", "QT_USE_API2", "QT_API_PYQT", "QT_API_PYSIDE" ]
+__all__ = ["QT_API", "QT_USE_API2", "QT_API_PYQT", "QT_API_PYSIDE"]
__docformat__ = 'restructuredtext'
@@ -34,18 +34,20 @@ import imp
QT_API_PYQT = 'pyqt'
QT_API_PYSIDE = 'pyside'
-QT_USE_API2 = False
+QT_USE_API2 = True
+
def get_logger():
import taurus.core.util
return taurus.core.util.Logger('TaurusQt')
+
def prepare_pyqt():
if not QT_USE_API2:
return
- # For PySide compatibility, use the new-style string API that automatically
- # converts QStrings to Unicode Python strings. Also, automatically unpack
- # QVariants to their underlying objects.
+ # For PySide compatibility, use the new-style string API that
+ # automatically converts QStrings to Unicode Python strings. Also,
+ # automatically unpack QVariants to their underlying objects.
import sip
if sip.SIP_VERSION >= 0x040900:
try:
@@ -60,16 +62,18 @@ def prepare_pyqt():
sip_ver = sip.SIP_VERSION_STR
get_logger().debug("Using old SIP %s (advised >= 4.9)", sip_ver)
+
def prepare_pyside():
pass
QT_APIs = {
- QT_API_PYQT : ('PyQt4',prepare_pyqt),
- QT_API_PYSIDE : ('PySide',prepare_pyside),
+ QT_API_PYQT: ('PyQt4', prepare_pyqt),
+ QT_API_PYSIDE: ('PySide', prepare_pyside),
}
QT_PREFERED_APIs = QT_API_PYQT, QT_API_PYSIDE
+
def init():
# Select Qt binding, using the QT_API environment variable if available.
ret_api = os.environ.get('QT_API')
@@ -82,12 +86,11 @@ def init():
except ImportError:
pass
if ret_api is None:
- raise ImportError('No Qt API available (known APIs : %s)'
+ raise ImportError('No Qt API available (known APIs : %s)'
% ", ".join(QT_PREFERED_APIs))
-
+
prepare = QT_APIs[ret_api][1]
prepare()
return ret_api
QT_API = init()
-
diff --git a/scripts/taurusdemo b/scripts/taurusdemo
index 52fa060..4560edf 100755
--- a/scripts/taurusdemo
+++ b/scripts/taurusdemo
@@ -3,21 +3,21 @@
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
-##
+##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## 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/>.
##
@@ -25,25 +25,26 @@
import sys
import operator
-import PyQt4.Qt as Qt
import taurus.core.util
import taurus.qt.qtgui.util
import taurus.qt.qtgui.application
+from taurus.qt import Qt
+
class TaurusDemoPanel(Qt.QWidget):
def __init__(self, parent=None):
Qt.QWidget.__init__(self, parent)
self._groups = {}
-
+
layout = Qt.QGridLayout()
self.setLayout(layout)
-
+
wf = taurus.qt.qtgui.util.TaurusWidgetFactory()
-
+
taurus_widgets = wf.getWidgets()
-
+
demos = {}
for widget_name in taurus_widgets:
widget_module_name, widget_class = taurus_widgets[widget_name]
@@ -54,17 +55,17 @@ class TaurusDemoPanel(Qt.QWidget):
if hasattr(internal_widget_module, "demo"):
if operator.isCallable(internal_widget_module.demo):
demos[internal_widget_module_name] = internal_widget_module.demo
-
+
groups = set()
-
+
for demo_name in demos.keys():
parts = demo_name.split(".")
group = parts[-2]
groups.add(group)
-
+
for group in groups:
self.addGroup(group)
-
+
for demo_name in sorted(demos.keys()):
demo_func = demos[demo_name]
parts = demo_name.split(".")
@@ -77,7 +78,7 @@ class TaurusDemoPanel(Qt.QWidget):
print 80*"-"
print "Problems adding demo", demo_name
print e
-
+
def addGroup(self, name):
g = Qt.QGroupBox(name)
layout = self.layout()
@@ -85,7 +86,7 @@ class TaurusDemoPanel(Qt.QWidget):
l = Qt.QGridLayout()
g.setLayout(l)
self._groups[name] = g
-
+
def addDemo(self, name, f, group):
g = self._groups[group]
layout = g.layout()
@@ -95,7 +96,7 @@ class TaurusDemoPanel(Qt.QWidget):
button._f = f
layout.addWidget(button, row, 0)
button.connect(button, Qt.SIGNAL("clicked()"), self.go)
-
+
def go(self):
b = self.sender()
f = b._f
@@ -118,7 +119,7 @@ class TaurusDemoPanel(Qt.QWidget):
return
d = Qt.QErrorMessage()
d.showMessage(str(e))
-
+
def main():
import taurus.core.util.argparse
diff --git a/scripts/taurusdesigner b/scripts/taurusdesigner
index 3f5f816..1395e56 100755
--- a/scripts/taurusdesigner
+++ b/scripts/taurusdesigner
@@ -26,9 +26,9 @@
import sys
import os.path
import optparse
-from PyQt4 import Qt as Qt
import taurus
+from taurus.qt import Qt
def env_index(env, env_name):
env_name = str(env_name)
@@ -90,12 +90,12 @@ if len(options.tauruspath) > 0:
#print "PYQTDESIGNERPATH=%s" % get_env(env, "PYQTDESIGNERPATH")
# Start Designer.
-designer_bin = Qt.QLibraryInfo.location(Qt.QLibraryInfo.BinariesPath)
+designer_bin = str(Qt.QLibraryInfo.location(Qt.QLibraryInfo.BinariesPath))
if sys.platform == "darwin":
- designer_bin.append("/Designer.app/Contents/MacOS/Designer")
+ designer_bin += "/Designer.app/Contents/MacOS/Designer"
else:
- designer_bin.append("/designer")
+ designer_bin += "/designer"
designer = Qt.QProcess()
designer.setProcessChannelMode(Qt.QProcess.ForwardedChannels)
@@ -103,4 +103,4 @@ designer.setEnvironment(env)
designer.start(designer_bin, args)
designer.waitForFinished(-1)
-sys.exit(designer.exitCode())
\ No newline at end of file
+sys.exit(designer.exitCode())
diff --git a/scripts/taurusremotelogmonitor b/scripts/taurusremotelogmonitor
new file mode 100755
index 0000000..ff02f46
--- /dev/null
+++ b/scripts/taurusremotelogmonitor
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus, a Tango User Interface Library
+##
+## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
+##
+## 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/>.
+##
+#############################################################################
+
+import optparse
+import logging.handlers
+import socket
+
+import taurus
+from taurus.qt.qtgui.application import TaurusApplication
+from taurus.qt.qtgui.table import QLoggingWidget
+
+def main():
+ import taurus
+ dft_port = logging.handlers.DEFAULT_TCP_LOGGING_PORT
+ host = socket.gethostname()
+
+ help_gui = "gui mode [default]"
+ help_con = "console mode"
+ help_port = "port where log server is running [default: %d]" % dft_port
+ help_name = "filter specific log object [default: None, meaning don't " \
+ "filter]"
+ help_level = "filter specific log level." \
+ "Allowed values are (case insensitive): critical, "\
+ "error, warning/warn, info, debug, trace [default: debug]."
+
+ parser = optparse.OptionParser()
+ parser.add_option("-g", "--gui", dest="gui", default=True,
+ action="store_true", help=help_gui)
+ parser.add_option("-c", "--console", dest="gui",
+ action="store_false", help=help_con)
+ parser.add_option("--log-port", dest="log_port", default=dft_port,
+ type="int", help=help_port)
+ parser.add_option("--log-name", dest="log_name", default=None,
+ type="string", help=help_name)
+ parser.add_option("--log-level", dest="log_level", default="debug",
+ type="string", help=help_level)
+
+ app = TaurusApplication(app_name="Taurus remote logger",
+ app_version="1.0",
+ org_domain="Taurus", org_name="Taurus community",
+ cmd_line_parser=parser)
+
+ options = app.get_command_line_options()
+
+ port, name = options.log_port, options.log_name
+ level_str = options.log_level.capitalize()
+ gui = options.gui
+
+ level = taurus.Trace
+ if hasattr(taurus, level_str):
+ level = getattr(taurus, level_str)
+
+ if gui:
+ w = QLoggingWidget(perspective="Remote")
+ w.setMinimumSize(1024, 600)
+
+ filterbar = w.getFilterBar()
+ filterbar.setLogLevel(level)
+ if name is not None:
+ filterbar.setFilterText(name)
+ w.getPerspectiveBar().setEnabled(False)
+ w.getQModel().connect_logging(host, port)
+ w.show()
+ app.exec_()
+ w.getQModel().disconnect_logging()
+ else:
+ import taurus.core.util.remotelogmonitor
+ taurus.core.util.remotelogmonitor.log(host=host, port=port, name=name,
+ level=level)
+if __name__ == '__main__':
+ main()
diff --git a/setup.py b/setup.py
index d64b7ee..ce99fea 100644
--- a/setup.py
+++ b/setup.py
@@ -69,6 +69,7 @@ packages = [
'taurus.core.util.argparse',
'taurus.core.util.decorator',
'taurus.core.util.report',
+ 'taurus.core.utils', # old, deprecated: maintain for compatibility
'taurus.core.resource',
'taurus.core.simulation',
'taurus.core.evaluation',
@@ -113,6 +114,7 @@ packages = [
'taurus.qt.qtgui.model',
'taurus.qt.qtgui.panel',
'taurus.qt.qtgui.panel.report',
+ 'taurus.qt.qtgui.panel.report.ui',
'taurus.qt.qtgui.panel.ui',
'taurus.qt.qtgui.plot',
'taurus.qt.qtgui.plot.ui',
@@ -170,23 +172,8 @@ requires = [
'spyder (>=2.1)', # shell, editor
]
-def get_resource_package_data():
- data = ['*.rcc']
- import PyQt4.Qt
- if not hasattr(PyQt4.Qt.QIcon, "fromTheme"):
- tango_icons_dir = abspath('lib', 'taurus', 'qt', 'qtgui', 'resource',
- 'tango-icons')
- for tango_icon_item in os.listdir(tango_icons_dir):
- if tango_icon_item.startswith("."):
- continue
- abs_item = os.path.join(tango_icons_dir, tango_icon_item)
- if not os.path.isdir(abs_item):
- continue
- data.append('tango-icons/%s/*' % tango_icon_item)
- return data
-
package_data = {
- 'taurus.qt.qtgui.resource' : get_resource_package_data(),
+ 'taurus.qt.qtgui.resource' : ['*.rcc'],
'taurus.qt.qtgui.util' : ['tauruswidget_template',
'tauruswidget_qtdesignerplugin_template'],
'taurus.qt.uic' : ['pyuic4/*'],
@@ -382,7 +369,8 @@ class build(dftbuild):
user_options = dftbuild.user_options + \
[('logo=', None, "alternative logo file (default is taurus.png)"),
('with-extra-widgets', None, "distribute extra widgets"),
- ('no-doc', None, "do not build documentation")]
+ ('no-doc', None, "do not build documentation"),
+ ('with-tango-icons', None, "add Tango icons too (not just *.rcc files)")]
boolean_options = dftbuild.boolean_options + ['with-extra-widgets', 'no-doc']
@@ -391,6 +379,7 @@ class build(dftbuild):
self.logo = None
self.doc_fmt = None
self.no_doc = None
+ self.with_tango_icons = None
self.with_extra_widgets = True
def finalize_options (self):
@@ -401,7 +390,7 @@ class build(dftbuild):
def run(self):
if self.with_extra_widgets:
self.distribution.packages.extend(extra_packages)
-
+ self.distribution.package_data['taurus.qt.qtgui.resource'].extend(self.get_extra_resource_package_data())
dftbuild.run(self)
def has_doc(self):
@@ -414,6 +403,21 @@ class build(dftbuild):
def has_resources(self):
return os.path.isdir(abspath('lib','taurus','qt','qtgui','resource'))
+
+ def get_extra_resource_package_data(self):
+ data = []
+ import PyQt4.Qt
+ if self.with_tango_icons or not hasattr(PyQt4.Qt.QIcon, "fromTheme"):
+ tango_icons_dir = abspath('lib', 'taurus', 'qt', 'qtgui', 'resource',
+ 'tango-icons')
+ for tango_icon_item in os.listdir(tango_icons_dir):
+ if tango_icon_item.startswith("."):
+ continue
+ abs_item = os.path.join(tango_icons_dir, tango_icon_item)
+ if not os.path.isdir(abs_item):
+ continue
+ data.append('tango-icons/%s/*' % tango_icon_item)
+ return data
sub_commands = [('build_resources', has_resources)] + \
dftbuild.sub_commands + \
@@ -473,12 +477,14 @@ class install(dftinstall):
user_options = list(dftinstall.user_options)
user_options.extend([
- ('install-man=', None, 'installation directory for Unix man pages'),
- ('install-html=', None, "installation directory for HTML documentation")])
+ ('install-man=', None, 'installation directory for Unix man pages'),
+ ('install-html=', None, "installation directory for HTML documentation"),
+ ('no-doc', None, "do not install HTML documentation")])
def initialize_options(self):
self.install_man = None
self.install_html = None
+ self.no_doc = None
dftinstall.initialize_options(self)
def finalize_options(self):
@@ -503,6 +509,8 @@ class install(dftinstall):
self.install_man = os.path.join(self.install_data, 'share', 'man')
if self.install_html is None:
self.install_html = os.path.join(self.install_data, 'share', 'doc', 'taurus', 'html')
+ if self.no_doc is None:
+ self.no_doc = False
self.dump_dirs("Installation directories")
def expand_dirs(self):
@@ -513,8 +521,10 @@ class install(dftinstall):
return os.name == "posix"
def has_html(self):
+ if self.no_doc:
+ return False
return sphinx is not None
-
+
sub_commands = list(dftinstall.sub_commands)
sub_commands.append(('install_man', has_man))
sub_commands.append(('install_html', has_html))
@@ -579,7 +589,6 @@ if sphinx:
def run(self):
self.resource_dir = abspath('lib', 'taurus', 'qt', 'qtgui', 'resource')
self.taurus = os.path.join(self.resource_dir, 'taurus.png')
- import PyQt4.Qt
orig_dir = os.path.abspath(os.curdir)
os.chdir(self.resource_dir)
@@ -693,13 +702,13 @@ if sphinx:
class build_doc(BuildDoc):
user_options = BuildDoc.user_options + \
- [('use-inkscape', None,
- "Use inkscape for building the icon catalog (useful if QApplication cannot be used when building, but requires inkscape)")]
- boolean_options = BuildDoc.boolean_options + ['use-inkscape']
+ [('external-img-tools', None,
+ "Use external tools for converting the icon catalog (useful if QApplication cannot be used while building, but requires inkscape and imagemagick)")]
+ boolean_options = BuildDoc.boolean_options + ['external-img-tools']
def initialize_options (self):
BuildDoc.initialize_options(self)
- self.use_inkscape = False
+ self.external_img_tools = False
def has_doc_api(self):
return True
@@ -751,20 +760,20 @@ if sphinx:
# copy the tango icons to the build directory of documentation
target = os.path.join(build_dir, 'devel')
- if not self.use_inkscape:
+ if not self.external_img_tools:
import PyQt4.Qt
if PyQt4.Qt.qApp.instance() is None:
self.app = PyQt4.Qt.QApplication([])
print("\tBuilding PNGs for icon catalog")
- os.path.walk(resource, svg_to_png, (resource, target, self.use_inkscape))
+ os.path.walk(resource, svg_to_png, (resource, target, self.external_img_tools))
return
cmdclass['build_doc'] = build_doc
def svg_to_png(arg, dirname, fnames):
- resource, target, use_inkscape = arg
- if not use_inkscape:
+ resource, target, external_img_tools = arg
+ if not external_img_tools:
import PyQt4.Qt
relpath = os.path.relpath(dirname, start=resource)
path = os.path.join(target, relpath)
@@ -777,9 +786,12 @@ def svg_to_png(arg, dirname, fnames):
target_fname = fbase + ".png"
full_target_fname = os.path.join(path, target_fname)
if not os.path.isfile(full_target_fname):
- if use_inkscape:
- cmd = "inkscape -z -e '%s' -w 24 '%s' &>/dev/null"%(full_target_fname, full_source_fname)
+ if external_img_tools:
+ cmd = "inkscape -z '%s' -e '%s' -w 24 >/dev/null 2>/dev/null"%(full_source_fname, full_target_fname)
ok = not(os.system(cmd))
+ if not ok:
+ cmd = "convert -resize 24 '%s' '%s' >/dev/null 2>/dev/null"%(full_source_fname, full_target_fname)
+ ok = not(os.system(cmd))
else:
pixmap = PyQt4.Qt.QPixmap(full_source_fname)
pix = pixmap.scaledToWidth(24, PyQt4.Qt.Qt.SmoothTransformation)
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/taurus.git
More information about the debian-science-commits
mailing list