From 1f57f81fd62ced3c432a366eb5ccc5e5ac00ae04 Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Thu, 25 Jan 2018 16:39:04 +0100 Subject: [PATCH 001/830] Add expconf macros module --- src/sardana/macroserver/macros/expconf.py | 187 ++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 src/sardana/macroserver/macros/expconf.py diff --git a/src/sardana/macroserver/macros/expconf.py b/src/sardana/macroserver/macros/expconf.py new file mode 100644 index 0000000000..4dc62347d8 --- /dev/null +++ b/src/sardana/macroserver/macros/expconf.py @@ -0,0 +1,187 @@ +import time + +from sardana.macroserver.macro import Macro, Type, ParamRepeat + + +class MGManager(object): + """ + Class to manages the measurement group + """ + + def __init__(self, macro_obj, mnt_grp, channels=None): + self.macro = macro_obj + self.mnt_grp = mnt_grp + self.__filterMntChannels(channels) + + def __filterMntChannels(self, channels): + # Check if the channels exit in the mntGrp + self.all_channels_names = self.mnt_grp.getChannelNames() + if channels is None: + return + channels_names = [] + for channel in channels: + if isinstance(channel, str): + channel_name = channel + else: + channel_name = channel.name + + if channel_name in self.all_channels_names: + channels_names.append(channel_name) + else: + msg = 'The channel {0} is not in {1}'.format(channel_name, + self.mnt_grp) + self.macro.warning(msg) + self.channels_names = channels_names + + def enable_channels(self): + self.mnt_grp.enableChannels(self.channels_names) + self.macro.output('Channels enabled') + + def enable_only_channels(self): + dis_ch = list(set(self.all_channels_names) - set(self.channels_names)) + self.disable_only_channels(dis_ch) + self.macro.output('Enabled only the selected channels') + + def disable_channels(self): + self.mnt_grp.disableChannels(self.channels_names) + self.macro.output('Channels disabled') + + def disable_only_channels(self, dis_ch=None): + self.mnt_grp.enableChannels(self.all_channels_names) + time.sleep(0.2) + if dis_ch is None: + dis_ch = self.channels_names + self.mnt_grp.disableChannels(dis_ch) + self.macro.output('Disable only the selected channels') + + def enable_all(self): + self.mnt_grp.enableChannels(self.all_channels_names) + self.macro.output('Enable all the channels') + + def status(self): + out_line = '{0:<15} {1:^10} {2:^10} {3:^10} {4:^10}' + self.macro.output(out_line.format('Channel', 'Enabled', 'Plot_type', + 'Plot axes', 'Output')) + for channel in self.mnt_grp.getChannels(): + name = channel['name'] + enabled = ['False', 'True'][channel['enabled']] + plot_type = ['No', 'Spectrum', 'Image'][channel['plot_type']] + plot_axis = channel['plot_axes'] + output = ['False', 'True'][channel['output']] + self.macro.output(out_line.format(name, enabled, plot_type, + plot_axis, output)) + + +class meas_enable_ch(Macro): + """ + Enable the Counter Timers selected + + """ + + param_def = [ + ['MeasurementGroup', Type.MeasurementGroup, None, + "Measurement Group to work"], + ['ChannelState', + ParamRepeat(['channel', Type.ExpChannel, None, 'Channel to change ' + 'state'], min=1), + None, 'List of channels to Enable'], + ] + + def run(self, mntGrp, channels): + mg_manager = MGManager(self, mntGrp, channels) + mg_manager.enable_channels() + + +class meas_enable_ch_only(Macro): + """ + Enable the Counter Timers selected + + """ + + param_def = [ + ['MeasurementGroup', Type.MeasurementGroup, None, + "Measurement Group to work"], + ['ChannelState', + ParamRepeat(['channel', Type.ExpChannel, None, 'Channel to change ' + 'state'], min=1), + None, 'List of channels to Enable'], + ] + + def run(self, mntGrp, channels): + mg_manager = MGManager(self, mntGrp, channels) + mg_manager.enable_only_channels() + + +class meas_disable_ch(Macro): + """ + Enable the Counter Timers selected + + """ + + param_def = [ + ['MeasurementGroup', Type.MeasurementGroup, None, + "Measurement Group to work"], + ['ChannelState', + ParamRepeat(['channel', Type.ExpChannel, None, 'Channel to change ' + 'state'], min=1), + None, 'List of channels to Enable'], + ] + + def run(self, mntGrp, channels): + mg_manager = MGManager(self, mntGrp, channels) + mg_manager.disable_channels() + + +class meas_disable_ch_only(Macro): + """ + Enable the Counter Timers selected + + """ + + param_def = [ + ['MeasurementGroup', Type.MeasurementGroup, None, + "Measurement Group to work"], + ['ChannelState', + ParamRepeat(['channel', Type.ExpChannel, None, 'Channel to change ' + 'state'], min=1), + None, 'List of channels to Enable'], + ] + + def run(self, mntGrp, channels): + mg_manager = MGManager(self, mntGrp, channels) + mg_manager.disable_only_channels() + + +class meas_enable_all(Macro): + """ + Enable all counter channels of the measurement group + """ + + param_def = [ + ['MeasurementGroup', Type.MeasurementGroup, None, "Measurement"], ] + + def run(self, mntGrp): + mg_manager = MGManager(self, mntGrp) + mg_manager.enable_all() + + +class meas_status(Macro): + """ + Shows the current configuration of the measurementGroup, + if the parameter is empty it shows the state of the ActiveMeasurementGroup + """ + param_def = [ + ['MeasurementGroup', Type.MeasurementGroup, None, "Measurement"], ] + + def run(self, mntGrp): + mg_manager = MGManager(self, mntGrp) + mg_manager.status() + + +class select_mntGrp(Macro): + param_def = [ + ['mntGrp', Type.MeasurementGroup, None, 'mntGroup name']] + + def run(self, mntGrp): + self.setEnv('ActiveMntGrp', str(mntGrp)) + self.info("Active Measurement Group : %s" % str(mntGrp)) From aa0ec93c7e868409a1a362c28fbadbe2a68e8b8f Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 3 Apr 2018 13:22:41 +0200 Subject: [PATCH 002/830] Add SEP16 Add SEP16 - Migration of third party repositories (controllers and macros). It is totally opened for ideas/changes. --- doc/source/sep/SEP16.md | 137 ++++++++++++++++++++++++++++++++++++++++ doc/source/sep/index.md | 5 +- 2 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 doc/source/sep/SEP16.md diff --git a/doc/source/sep/SEP16.md b/doc/source/sep/SEP16.md new file mode 100644 index 0000000000..4d63731b05 --- /dev/null +++ b/doc/source/sep/SEP16.md @@ -0,0 +1,137 @@ + Title: Migration of third-party repositories (controllers and macros) + SEP: 16 + State: DRAFT + Reason: + SEP15 migrated the Sardana repository from SourceForge to GitHub. + The third-party repositories are still at SourceForge and a decision + is pending on what to do with them. + Date: 2017-04-03 + Drivers: Zbigniew Reszela + URL: + License: http://www.jclark.com/xml/copying.txt + Abstract: + SEP15 migrated the Sardana project from SourceForge to GitHub but + left the third-party repositories, controllers and macros, at their + original location. This SEP takes care about the third-party + repositories reorganization so the plugins maintenance will become + more developer friendly and plugins more accessible to the user. + This is achieved by simply transferring the project ownerships + from the Sardana administrators to the plugins developers at the same + time giving some advices on how to organize the projects based + on the current experience. + + +Current Situation +----------------- + +Controllers and macros repositories are two separate repositories which +host all the plugins, both the ones that are generic and the ones which +are specific to the systems like ALBA’s or DESY’s beamlines. + +The controllers repository has two top level directories which divide +the plugins into the ones written in Python and the ones written in C++ +(obsolete). Underneath, one directory per controller type exists e.g. motor, +pseudomotor, countertimer, etc. Finally, controller modules are either +directly placed in that directories e.g. AdlinkAICoTiCtrl.py, or +a dedicated directory is created for a given controller IcePAPCtrl or +a family of them PmacCtrl. + +In the macros repository, the macro modules are either grouped in +directories, usually per system, e.g. ALBA_BL22_CLAESS, DESY_P01 or placed +directly in the repository e.g. albaemmacros.py. + +No third party repository exists neither for recorders, nor for GUIs/widgets. + +Objectives +---------- + +1. Enable natural way of working using git, like for example, use of the +feature branches, pull-requests, tags, etc. +2. Let developers organize plugins in projects so they could have their own +issue trackers, wiki pages, etc. +3. Do not force the hosting platform. +4. Give visibility to the well maintained plugins. + +Design +------ + +* Sardana organization will no more manage the plugins repositories. + These will be managed directly by their developers. +* Sardana organization will advice on how to organize the plugin projects. +* Sardana organization will maintain a register of the third party plugins. + +### Register + +A new repository, called sardana-plugins, will be added to the sardana-org +GitHub organization. This repository will not contain any of the plugins +itself but will serve as a register of all the plugins. The only role of this +register will be to list all the plugins together with the information +on where to find more information about them e.g. links to the project pages +or artifacts. Github searches over this repository will be useful when looking +for a plugin. + +**Option 1** + +This is a conservative option. It simply reflects the organization of +the current repositories and adds two more categories: Recorders and GUIs. + +Register will be divided in the following categories: +* Motor controllers +* Pseudomotor controllers +* Counter/timer controllers +* Pseudocounter controllers +* I/O register controllers +* 0D controllers +* 1D controllers +* 2D controllers +* Trigger/gate controllers +* Macros +* Recorders +* GUIs + +**Option 2** + +This option is more revolutionary. Register will be divided into the following +categories: +* Hardware - lists plugins for specific hardware like, motor controllers, + counting cards, etc. Example: IcePAP, Pmac, NI6602. +* Instrument - lists plugins for a specific instrument like, tables, + monochromators, attenuators. Example: three-legged table, DCM. +* System - lists plugins for the complete systems e.g. beamlines, laboratories. +* Software - lists plugins for interacting with other control systems, + frameworks e.g. Lima, Tango, Taurus. +* Other - lists plugins that does not meet any other criteria. + +**Option 3** + +Mix of options 1 and 2. The register will have all the categories from option 1 +and the System category from option 2. This is because maintaining an +up-to-date register of plugins from a system like a beamline is not realistic. + +This sardana-plugins repository will be managed exactly the same as the sardana +repository (administrators, push permissions, etc.). In order to add a new +plugin to the register, one would need to open a PR. + +Implementation +-------------- + +1. Implement sardana-plugins register: use the markdown format with one file + per category. +2. Start accepting PR to the sardana-plugins register whenever this SEP gets + into the CANDIDATE state. +3. Remove write permissions to the current third-party repositories + in SourceForge with the Jul18 release. + +Advices on how to manage plugins projects +--------------------------------------------------- + +(Since I anticipate this section to evolve over time it will not be part of +this SEP but will be added to the documentation) + +1. Description of the plugins e.g. the purpose, dependencies, installation + instructions must be documented e.g. README file, project’s wiki pages + or documentation. +2. Related controllers, macros, recorders and GUIs should coexist in the same + repository e.g. IcePAP controller (motor and trigger/gate) and IcePAP + macros. In this case a top level directories in the repository + e.g. controllers and macros could be useful to group them. diff --git a/doc/source/sep/index.md b/doc/source/sep/index.md index a293b472c5..6e46d19b2a 100644 --- a/doc/source/sep/index.md +++ b/doc/source/sep/index.md @@ -25,7 +25,8 @@ Proposals list [SEP12][] | CANDIDATE | Use python Enum instead of taurus Enumeration [SEP13][] | REJECTED (moved to [TEP13][]) | Unified plugins support in Taurus & Sardana [SEP14][] | DRAFT | MSENV taurus schema - [SEP15][] | ACCEPTED | Moving Sardana to Github + [SEP15][] | ACCEPTED | Moving Sardana to Github + [SEP16][] | DRAFT | Migration of third-party repositories (controllers and macros) @@ -46,7 +47,7 @@ Proposals list [SEP13]: http://www.sardana-controls.org/sep/?SEP13.md [SEP14]: http://www.sardana-controls.org/sep/?SEP14.md [SEP15]: http://www.sardana-controls.org/sep/?SEP15.md - +[SEP16]: [TEP3]: http://www.taurus-scada.org/tep/?TEP3.md [TEP13]: http://www.taurus-scada.org/tep/?TEP13.md From 4663381e4f9875eab15a079dca41f1e29deaf355 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 3 Apr 2018 13:35:01 +0200 Subject: [PATCH 003/830] Add temporary URL to the SEP16 document --- doc/source/sep/SEP16.md | 2 +- doc/source/sep/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/sep/SEP16.md b/doc/source/sep/SEP16.md index 4d63731b05..526d37a153 100644 --- a/doc/source/sep/SEP16.md +++ b/doc/source/sep/SEP16.md @@ -7,7 +7,7 @@ is pending on what to do with them. Date: 2017-04-03 Drivers: Zbigniew Reszela - URL: + URL: http://github.com/reszelaz/sardana/blob/aa0ec93c7e868409a1a362c28fbadbe2a68e8b8f/doc/source/sep/SEP16.md License: http://www.jclark.com/xml/copying.txt Abstract: SEP15 migrated the Sardana project from SourceForge to GitHub but diff --git a/doc/source/sep/index.md b/doc/source/sep/index.md index 6e46d19b2a..8fcce33da9 100644 --- a/doc/source/sep/index.md +++ b/doc/source/sep/index.md @@ -47,7 +47,7 @@ Proposals list [SEP13]: http://www.sardana-controls.org/sep/?SEP13.md [SEP14]: http://www.sardana-controls.org/sep/?SEP14.md [SEP15]: http://www.sardana-controls.org/sep/?SEP15.md -[SEP16]: +[SEP16]: http://github.com/reszelaz/sardana/blob/aa0ec93c7e868409a1a362c28fbadbe2a68e8b8f/doc/source/sep/SEP16.md [TEP3]: http://www.taurus-scada.org/tep/?TEP3.md [TEP13]: http://www.taurus-scada.org/tep/?TEP13.md From 266a438403deacbdc66228da07ea56a26ab7a116 Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Tue, 3 Apr 2018 15:23:32 +0200 Subject: [PATCH 004/830] Make temporary URL dynamic The URL listed in the SEP is static (it won't reflect changes). Use sep16 branch instead of a specific blob url --- doc/source/sep/SEP16.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/sep/SEP16.md b/doc/source/sep/SEP16.md index 526d37a153..8adfdfede5 100644 --- a/doc/source/sep/SEP16.md +++ b/doc/source/sep/SEP16.md @@ -7,7 +7,7 @@ is pending on what to do with them. Date: 2017-04-03 Drivers: Zbigniew Reszela - URL: http://github.com/reszelaz/sardana/blob/aa0ec93c7e868409a1a362c28fbadbe2a68e8b8f/doc/source/sep/SEP16.md + URL: https://github.com/reszelaz/sardana/blob/sep16/doc/source/sep/SEP16.md License: http://www.jclark.com/xml/copying.txt Abstract: SEP15 migrated the Sardana project from SourceForge to GitHub but From c7723325ffaabed3ad8c2885a266e38aca6c0288 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 5 Apr 2018 18:34:20 +0200 Subject: [PATCH 005/830] Add objective about repositories organization --- doc/source/sep/SEP16.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/source/sep/SEP16.md b/doc/source/sep/SEP16.md index 8adfdfede5..518fb3d099 100644 --- a/doc/source/sep/SEP16.md +++ b/doc/source/sep/SEP16.md @@ -46,11 +46,12 @@ Objectives ---------- 1. Enable natural way of working using git, like for example, use of the -feature branches, pull-requests, tags, etc. -2. Let developers organize plugins in projects so they could have their own +feature branches, pull-requests, tags, independent git history etc. +2. Let developers organize their repositories according to their preferences. +3. Let developers organize plugins in projects so they could have their own issue trackers, wiki pages, etc. -3. Do not force the hosting platform. -4. Give visibility to the well maintained plugins. +4. Do not force the hosting platform. +5. Give visibility to the well maintained plugins. Design ------ From 3afe739a69e845869a1529d2d92496ff64dd120d Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Mon, 9 Jul 2018 10:53:38 +0200 Subject: [PATCH 006/830] plotselect macro --- src/sardana/macroserver/macros/standard.py | 34 +++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index a95013714f..42775e25e2 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -25,7 +25,7 @@ __all__ = ["ct", "mstate", "mv", "mvr", "pwa", "pwm", "repeat", "set_lim", "set_lm", "set_pos", "settimer", "uct", "umv", "umvr", "wa", "wm", - "tw", "logmacro"] + "tw", "logmacro", "plotselect"] __docformat__ = 'restructuredtext' @@ -854,3 +854,35 @@ def run(self, nr): self.__loop() progress = ((i + 1) / float(nr)) * 100 yield progress + + +class plotselect(Macro): + """ + plotselect counter1 counter2 ... (change plot display of active measurement + group) + + """ + param_def = [ + ['plotChs', ParamRepeat( + ['plotChs', Type.String, 'None', ""], min=0), None, ""] + ] + + def run(self, plotChs): + mntGrp = self.getEnv('ActiveMntGrp') + self.mntGrp = self.getObj(mntGrp, type_class=Type.MeasurementGroup) + cfg = self.mntGrp.getConfiguration() + + # Enable Plot only in the channels passed. + for channel in self.mntGrp.getChannels(): + if channel['name'] in plotChs[0]: + # Enable Plot + self.info("Plot channel %s" % channel['name']) + channel['plot_type'] = 1 + channel['plot_axes'] = [''] + else: + # Disable Plot + channel['plot_type'] = 0 + channel['plot_axes'] = [] + + # Force set Configuration. + self.mntGrp.setConfiguration(cfg.raw_data) From 44a2328e1540398556c559ff373b2c0101590c61 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Tue, 10 Jul 2018 18:41:00 +0200 Subject: [PATCH 007/830] remove index from plotChs --- src/sardana/macroserver/macros/standard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 42775e25e2..9601c3711c 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -874,7 +874,7 @@ def run(self, plotChs): # Enable Plot only in the channels passed. for channel in self.mntGrp.getChannels(): - if channel['name'] in plotChs[0]: + if channel['name'] in plotChs: # Enable Plot self.info("Plot channel %s" % channel['name']) channel['plot_type'] = 1 From 742b9bff7e9847384a0903acf3921c4ed3a2f8ea Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Thu, 12 Jul 2018 12:09:16 +0200 Subject: [PATCH 008/830] Flake8 fix --- src/sardana/taurus/core/tango/sardana/pool.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index f81d7044df..6175ecbaaa 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -707,22 +707,27 @@ class CTExpChannel(ExpChannel): """ Class encapsulating CTExpChannel functionality.""" pass + class ZeroDExpChannel(ExpChannel): """ Class encapsulating ZeroDExpChannel functionality.""" pass + class OneDExpChannel(ExpChannel): """ Class encapsulating OneDExpChannel functionality.""" pass + class TwoDExpChannel(ExpChannel): """ Class encapsulating TwoDExpChannel functionality.""" pass + class PseudoCounter(ExpChannel): """ Class encapsulating PseudoCounter functionality.""" pass + class TriggerGate(PoolElement): """ Class encapsulating TriggerGate functionality.""" pass From 2c8e997d4f10c1aada4c93d7336492c28d22939b Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Mon, 16 Jul 2018 15:35:12 +0200 Subject: [PATCH 009/830] raw_data privatisation --- src/sardana/taurus/core/tango/sardana/pool.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 6175ecbaaa..137ba81dee 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1220,9 +1220,11 @@ def getChannelConfigs(mgconfig, ctrls=None, sort=True): class MGConfiguration(object): def __init__(self, mg, data): self._mg = weakref.ref(mg) + self._raw_data = None if isinstance(data, (str, unicode)): data = CodecFactory().decode(('json', data), ensure_ascii=True) self.raw_data = data + self._raw_data = data self.__dict__.update(data) # dict From aaab4559c23eefecce976da52f7f18bb55a931da Mon Sep 17 00:00:00 2001 From: Roberto Homs Date: Mon, 16 Jul 2018 16:25:14 +0200 Subject: [PATCH 010/830] Add set_data method Implement method to update the raw_data instead of creating a new MGConfiguration objcet per each change event. --- src/sardana/taurus/core/tango/sardana/pool.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 137ba81dee..6b9ad970c3 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1221,9 +1221,13 @@ class MGConfiguration(object): def __init__(self, mg, data): self._mg = weakref.ref(mg) self._raw_data = None + self.set_data(data) + + def set_data(self, data, force=False): + # object each time if isinstance(data, (str, unicode)): data = CodecFactory().decode(('json', data), ensure_ascii=True) - self.raw_data = data + if not force: self._raw_data = data self.__dict__.update(data) @@ -1631,6 +1635,11 @@ def getCountersInfo(self): def getValues(self, parallel=True): return self.getConfiguration().read(parallel=parallel) + def _setConfiguration(self, data): + if self._configuration is None: + self._configuration = MGConfiguration(self, data) + else: + self._configuration.set_data(data) def getValueBuffers(self): value_buffers = [] From 7bd10780d363ae9d90581978135e15b29cd46e8f Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Mon, 16 Jul 2018 16:36:19 +0200 Subject: [PATCH 011/830] Add base setter/getter methods Add base setter/getter method to read/write the configuration. --- src/sardana/taurus/core/tango/sardana/pool.py | 152 +++++++++++++++++- 1 file changed, 151 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 6b9ad970c3..e1b99c0562 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1235,10 +1235,24 @@ def set_data(self, data, force=False): # where key is the channel name and value is the channel data in form # of a dict as receveid by the MG configuration attribute self.channels = channels = CaselessDict() + self.channels_names = channels_names = CaselessDict() + self.channels_labels = channels_labels = CaselessDict() + self.controllers_names = controllers_names = CaselessDict() - for _, ctrl_data in self.controllers.items(): + # TODO private controllers attr + for ctrl_name, ctrl_data in self.controllers.items(): + try: + if ctrl_name != '__tango__': + proxy = DeviceProxy(ctrl_name) + controllers_names[proxy.alias()] = ctrl_data + except Exception: + pass for channel_name, channel_data in ctrl_data['channels'].items(): channels[channel_name] = channel_data + name = channel_data['name'] + channels_names[name] = channel_data + name = channel_data['label'] + channels_labels[name] = channel_data ##################### # @todo: the for-loops above could be replaced by something like: @@ -1520,6 +1534,142 @@ def _read(self): ret[channel_data['full_name']] = None return ret + def _get_proxy(self, element): + try: + proxy = DeviceProxy(element) + except Exception: + try: + proxy = AttributeProxy(element) + except Exception: + raise KeyError(element) + return proxy + + def _get_channel_data(self, channel_name): + if channel_name in self.channels_names: + return self.channels_names[channel_name] + elif channel_name in self.channels_labels: + return self.channels_labels[channel_name] + elif channel_name in self.channels: + return self.channels[channel_name] + else: + # TODO: Improve this way + proxy = self._get_proxy(channel_name) + try: + alias = proxy.alias() + except Exception: + # The attribute proxy does not have alias. + alias = proxy.name() + names = self.channels_names.keys() + self.channels_labels.keys() + if alias not in names: + raise KeyError('Channel "{0}" is not on the ' + 'MntGrp "{1}"'.format(alias, self.label)) + return self._get_channel_data(alias) + + def _get_ctrl_data(self, ctrl_name): + if ctrl_name in self.controllers_names: + return self.controllers_names[ctrl_name] + elif ctrl_name in self.controllers: + return self.controllers[ctrl_name] + else: + # TODO: Improve this way + proxy = self._get_proxy(ctrl_name) + alias = proxy.alias() + if alias not in self.controllers_names: + raise KeyError('Controller "{}" is not on the ' + 'MntGrp "{1}"'.format(alias, self.label)) + return self._get_ctrl_data(alias) + + def _set_channels_key(self, key, value, channels_names=None, + apply_cfg=True): + + self._local_changes = True + if channels_names is None: + channels_names = self.channels.keys() + # Protections: + if key in ['enabled', 'output']: + if type(value) != bool: + raise ValueError('The value must be a boolean') + + for channel_name in channels_names: + channel = self._get_channel_data(channel_name) + channel[key] = value + if apply_cfg: + self.applyConfiguration() + + def _get_channels_key(self, key, channels_names=None, use_fullname=False): + result = OrderedDict({}) + + if channels_names is None: + channels_names = self.channels.keys() + + for channel_name in channels_names: + channel = self._get_channel_data(channel_name) + + value = channel[key] + if use_fullname: + label = channel + else: + label = channel['label'] + if key == 'plot_axes': + res = [] + for v in value: + if v not in ['', '']: + v = self.channels[v]['label'] + res.append(v) + value = res + result[label] = value + return result + + def _set_ctrls_key(self, key, value, ctrls_names=None, apply_cfg=True): + self._local_changes = True + if ctrls_names is None: + ctrls_names = self.controllers.keys() + + for ctrl_name in ctrls_names: + if ctrl_name == '__tango__': + continue + ctrl = self._get_ctrl_data(ctrl_name) + ctrl[key] = value + if apply_cfg: + self.applyConfiguration() + + def _get_ctrls_key(self, key, ctrls_names=None, use_fullname=False): + result = OrderedDict({}) + if ctrls_names is None: + ctrls_names = self.controllers.keys() + + for ctrl_name in ctrls_names: + if ctrl_name == '__tango__': + continue + ctrl = self._get_ctrl_data(ctrl_name) + label = ctrl_name + value = ctrl[key] + if key == 'synchronization': + value = AcqSynchType.get(value) + if not use_fullname: + label = DeviceProxy(ctrl_name).alias() + if key in ['timer', 'monitor']: + value = self.channels[value]['label'] + elif key == 'synchronizer' and value != 'software': + value = DeviceProxy(value).alias() + result[label] = value + return result + + def _get_ctrl_from_channels(self, channels_names, unique=False): + result = OrderedDict({}) + + if channels_names is None: + channels_names = self.channels.keys() + + for channel_name in channels_names: + channel = self._get_channel_data(channel_name) + ctrl = channel['_controller_name'] + if unique and ctrl in result.values(): + raise KeyError('There are more than one channel of the same ' + 'controller') + result[channel['full_name']] = ctrl + + return result class MeasurementGroup(PoolElement): """ Class encapsulating MeasurementGroup functionality.""" From f7acb6965fbc28fb2ec32fffb00d2eddd81c826d Mon Sep 17 00:00:00 2001 From: Roberto Homs Date: Mon, 16 Jul 2018 16:44:27 +0200 Subject: [PATCH 012/830] Implement applyConfiguration method Add method to update the configuration on the Pool and implement protections in case of multi-client collisions. --- src/sardana/taurus/core/tango/sardana/pool.py | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index e1b99c0562..296993c177 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1219,8 +1219,10 @@ def getChannelConfigs(mgconfig, ctrls=None, sort=True): class MGConfiguration(object): def __init__(self, mg, data): - self._mg = weakref.ref(mg) + self._mg = weakref.ref(mg)() self._raw_data = None + self._pending_event_data = None + self._local_changes = False self.set_data(data) def set_data(self, data, force=False): @@ -1228,6 +1230,16 @@ def set_data(self, data, force=False): if isinstance(data, (str, unicode)): data = CodecFactory().decode(('json', data), ensure_ascii=True) if not force: + if self._raw_data == data: + # The new data received on the on_change_event was generated by + # this object. + return + elif self._local_changes: + self._pending_event_data = data + return + self._pending_event_data = None + self._local_changes = False + self._raw_data = data self.__dict__.update(data) @@ -1671,6 +1683,22 @@ def _get_ctrl_from_channels(self, channels_names, unique=False): return result + def applyConfiguration(self, timeout=3): + if not self._local_changes: + return + if self._pending_event_data is not None: + self.set_data(self._pending_event_data, force=True) + raise RuntimeError('The configuration changed on the server ' + 'during your changes.') + self._mg.setConfiguration(self._raw_data) + self._local_changes = False + self._pending_event_data = None + t1 = time.time() + while self._mg._flg_event: + time.sleep(0.01) + if (time.time() - t1) >= timeout: + raise RuntimeError('Timeout on applying configuration') + class MeasurementGroup(PoolElement): """ Class encapsulating MeasurementGroup functionality.""" @@ -1782,6 +1810,12 @@ def getChannelsEnabledInfo(self): def getCountersInfo(self): return self.getConfiguration().getCountersInfoList() + self._flg_event = False + def setConfiguration(self, configuration): + self._flg_event = True + codec = CodecFactory().getCodec('json') + f, data = codec.encode(('', configuration)) + self.write_attribute('configuration', data) def getValues(self, parallel=True): return self.getConfiguration().read(parallel=parallel) @@ -1791,6 +1825,12 @@ def _setConfiguration(self, data): else: self._configuration.set_data(data) + 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) + self._flg_event = False def getValueBuffers(self): value_buffers = [] for channel_info in self.getChannels(): From 17c4f42970b2315eb12b32b759ef9859cf634900 Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Mon, 16 Jul 2018 16:56:24 +0200 Subject: [PATCH 013/830] Move configuration methods to MGConfiguration --- src/sardana/taurus/core/tango/sardana/pool.py | 107 +++++++----------- 1 file changed, 41 insertions(+), 66 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 296993c177..074b238b18 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1746,8 +1746,7 @@ def getTimerName(self): return self.getTimer()['name'] def getTimer(self): - cfg = self.getConfiguration() - return cfg.channels[cfg.timer] + return self.channels[self.timer] def getTimerValue(self): return self.getTimerName() @@ -1756,26 +1755,27 @@ def getMonitorName(self): return self.getMonitor()['name'] def getMonitor(self): - cfg = self.getConfiguration() - return cfg.channels[cfg.monitor] + return self.channels[self.monitor] - def setTimer(self, timer_name): - try: - self.getChannel(timer_name) - except KeyError: - raise Exception("%s does not contain a channel named '%s'" - % (str(self), timer_name)) - cfg = self.getConfiguration().raw_data - cfg['timer'] = timer_name - import json - self.write_attribute("configuration", json.dumps(cfg)) + def getValues(self, parallel=True): + return self.read(parallel=parallel) - def getChannels(self): - return self.getConfiguration().getChannels() + def setTimer(self, timer, apply_cfg=True): + """ + Set the Global Timer to the measurement group, also it changes the + timer in the controllers with the previous timer. + + :param timer: timer name + """ + result = self._get_ctrl_from_channels([timer], unique=True) + + for timer, ctrl in result.items(): + self._local_changes = True + self._raw_data['timer'] = timer + self._set_ctrls_key('timer', timer, [ctrl], apply_cfg) def getCounters(self): - cfg = self.getConfiguration() - return [c for c in self.getChannels() if c['full_name'] != cfg.timer] + return [c for c in self.getChannels() if c['full_name'] != self.timer] def getChannelNames(self): return [ch['name'] for ch in self.getChannels()] @@ -1790,26 +1790,39 @@ 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() + return self.channels[name] def getChannelsEnabledInfo(self): - """Returns information about **only enabled** channels present in the + """ + Returns information about **only enabled** channels present in the measurement group in a form of ordered, based on the channel index, list. :return: list with channels info :rtype: list """ - return self.getConfiguration().getChannelsInfoList(only_enabled=True) + return self.getChannelsInfoList(only_enabled=True) def getCountersInfo(self): - return self.getConfiguration().getCountersInfoList() + return self.getCountersInfoList() + + def enableChannels(self, channels, apply_cfg=True): + """ + Enable acquisition of the indicated channels. + + :param channels: (seq) a sequence of strings indicating + channel names + """ + self.setEnabledChannels(True, channels, apply_cfg) + + def disableChannels(self, channels, apply_cfg=True): + """ + Disable acquisition of the indicated channels. + + :param channels: (seq) a sequence of strings indicating + channel names + """ + self.setEnabledChannels(False, channels, apply_cfg) self._flg_event = False def setConfiguration(self, configuration): self._flg_event = True @@ -1817,8 +1830,6 @@ def setConfiguration(self, configuration): f, data = codec.encode(('', configuration)) self.write_attribute('configuration', data) - def getValues(self, parallel=True): - return self.getConfiguration().read(parallel=parallel) def _setConfiguration(self, data): if self._configuration is None: self._configuration = MGConfiguration(self, data) @@ -1951,42 +1962,6 @@ def unsubscribeValueBuffer(self, cb=None): else: value_buffer_obj.unsubscribeEvent(channel.valueBufferChanged) - def enableChannels(self, channels): - '''Enable acquisition of the indicated channels. - - :param channels: (seq) a sequence of strings indicating - channel names - ''' - self._enableChannels(channels, True) - - def disableChannels(self, channels): - '''Disable acquisition of the indicated channels. - - :param channels: (seq) a sequence of strings indicating - channel names - ''' - self._enableChannels(channels, False) - - def _enableChannels(self, channels, state): - found = {} - for channel in channels: - found[channel] = False - cfg = self.getConfiguration() - for channel in cfg.getChannels(): - name = channel['name'] - if name in channels: - channel['enabled'] = state - found[name] = True - wrong_channels = [] - for ch, f in found.items(): - if f is False: - wrong_channels.append(ch) - if len(wrong_channels) > 0: - msg = 'channels: %s are not present in measurement group' % \ - wrong_channels - raise Exception(msg) - self.setConfiguration(cfg.raw_data) - def _start(self, *args, **kwargs): self.Start() From a3ea5d5e086557ba737c819c928580accf72e9a2 Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Tue, 17 Jul 2018 10:11:42 +0200 Subject: [PATCH 014/830] Implement __repr__ method Add the __repr__ method to do a pretty print of the configuration. --- src/sardana/taurus/core/tango/sardana/pool.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 074b238b18..d52a7c226f 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -44,6 +44,7 @@ import time import traceback import weakref +import json import numpy import PyTango @@ -1823,6 +1824,9 @@ def disableChannels(self, channels, apply_cfg=True): channel names """ self.setEnabledChannels(False, channels, apply_cfg) + + def __repr__(self): + return json.dumps(self._raw_data, indent=4, sort_keys=True) self._flg_event = False def setConfiguration(self, configuration): self._flg_event = True From 538932a22a672592bac2963083778e07b97efd18 Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Tue, 17 Jul 2018 10:15:09 +0200 Subject: [PATCH 015/830] Add setter/getter for configuration parameters Add new setter/getter to change the configuration. --- src/sardana/taurus/core/tango/sardana/pool.py | 316 ++++++++++++++++-- 1 file changed, 280 insertions(+), 36 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index d52a7c226f..068aab7260 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +from __future__ import absolute_import ############################################################################## ## @@ -46,12 +47,11 @@ import weakref import json import numpy - import PyTango from PyTango import DevState, AttrDataFormat, AttrQuality, DevFailed, \ - DeviceProxy -from taurus import Factory, Device, Attribute + DeviceProxy, AttributeProxy +from taurus import Factory, Device from taurus.core.taurusbasetypes import TaurusEventType try: @@ -70,6 +70,16 @@ from .sardana import BaseSardanaElementContainer, BaseSardanaElement from .motion import Moveable, MoveableSource +try: + from collections import OrderedDict +except ImportError: + # For Python < 2.7 + from ordereddict import OrderedDict + + +from sardana.pool import AcqSynchType +from sardana.taurus.core.tango.sardana import PlotType + Ready = Standby = DevState.ON Counting = Acquiring = Moving = DevState.MOVING Alarm = DevState.ALARM @@ -1700,48 +1710,282 @@ def applyConfiguration(self, timeout=3): if (time.time() - t1) >= timeout: raise RuntimeError('Timeout on applying configuration') -class MeasurementGroup(PoolElement): - """ Class encapsulating MeasurementGroup functionality.""" + def getEnabledChannels(self, channels=None, use_fullname=False): + """get acquisition Enabled channels. - def __init__(self, name, **kw): - """PoolElement initialization.""" - self._configuration = None - self._channels = None - self._last_integ_time = None - self.call__init__(PoolElement, name, **kw) + :param channels: (seq) a list of channels names to get the + Enabled info + :param use_fullname: (bool) returns a full name instead sardana + element name - self.__cfg_attr = self.getAttribute('configuration') - self.__cfg_attr.addListener(self.on_configuration_changed) + :return a OrderedDict where the key are the channels and value the + Enabled state + """ - self._value_buffer_cb = None - self._codec = CodecFactory().getCodec("json") + return self._get_channels_key('enabled', channels, use_fullname) - def _create_str_tuple(self): - channel_names = ", ".join(self.getChannelNames()) - return self.getName(), self.getTimerName(), channel_names + def setEnabledChannels(self, state, channels=None, apply_cfg=True): + """Enable acquisition of the indicated channels. - def getConfigurationAttrEG(self): - return self._getAttrEG('Configuration') + :param state: The state of the channels to be set. + :param channels: (seq) a sequence of strings indicating + channel names + """ - def setConfiguration(self, configuration): - codec = CodecFactory().getCodec('json') - f, data = codec.encode(('', configuration)) - self.write_attribute('configuration', data) + self._set_channels_key('enabled', state, channels, apply_cfg) - def _setConfiguration(self, data): - self._configuration = MGConfiguration(self, data) + def getOutputChannels(self, channels=None, use_fullname=False): + """get the output State of the channels. - def getConfiguration(self, force=False): - if force or self._configuration is None: - data = self.getConfigurationAttrEG().readValue(force=True) - self._setConfiguration(data) - return self._configuration + :param channels: (list) a string indicating the channel name, + in case of None, it will return all the Outputs Info + :param use_fullname: (bool) returns a full name instead sardana + element name - 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) + :return a OrderedDict where keys are channel names and + value the Outputs configuration + """ + + return self._get_channels_key('output', channels, use_fullname) + + def setOutputChannels(self, state, channels=None, apply_cfg=True): + """Set the Output state of the indicated channels. + + :param state: (bool) Indicate the state of the output. + :param channels: (seq) a sequence of strings indicating + channel names + """ + + self._set_channels_key('output', state, channels, apply_cfg) + + def getPlotTypeChannels(self, channels=None, use_fullname=False): + """get the Plot Type for the channel indicated. In case of empty + channel value it will return all the Plot Type Info + + :param channels: (list) Indicate the channel to return the + Plot Type Info + :param use_fullname: (bool) returns a full name instead sardana + element name + + :return a OrderedDict where keys are channel names and + value the plot axes info + """ + + return self._get_channels_key('plot_type', channels, use_fullname) + + def setPlotTypeChannels(self, ptype, channels=None, apply_cfg=True): + """Set the Plot Type for the indicated channels. + + :param ptype: string indicating the type name + :param channels: (seq) a list of strings indicating the channels + to apply the PlotType + """ + + msg_error = 'Wrong value! PlotType allowed: ' \ + '{0}'.format(PlotType.keys()) + if type(ptype) == str: + if ptype.lower() not in map(str.lower, PlotType.keys()): + raise ValueError(msg_error) + for value in PlotType.keys(): + if value.lower() == ptype.lower(): + ptype = PlotType[value] + break + elif type(ptype) == int: + try: + PlotType[ptype] + except Exception: + raise ValueError(msg_error) + else: + raise ValueError() + self._set_channels_key('plot_type', ptype, channels, apply_cfg) + + def getPlotAxesChannels(self, channels=None, use_fullname=False): + """get the PlotAxes for the channel indicated. In case of empty channel + value it will return all the PlotAxes Info + + :param channels: (list) Indicate the channel to return the + PlotAxes Info + :param use_fullname: (bool) returns a full name instead sardana + element name + + :return a OrderedDict where keys are channel names and + value the plot axes info + """ + + return self._get_channels_key('plot_axes', channels, use_fullname) + + def setPlotAxesChannels(self, axes, channels_names=None, apply_cfg=True): + """Set the PlotAxes for the indicated channels. + + :param axes: string indicating the axis name + :param channels_names: (seq) a list of strings indicating the + channels to apply the PlotAxes + """ + if channels_names is None: + channels_names = self.channels.keys() + + for channel_name in channels_names: + channel_data = self._get_channel_data(channel_name) + + # Check the current channel plot type + plot_type = PlotType[PlotType[channel_data['plot_type']]] + if plot_type == PlotType.No: + raise RuntimeError('You must set firs the PlotType') + elif plot_type == PlotType.Spectrum: + if len(axes) != 1: + raise ValueError('The Spectrum Type only allows one axis') + elif plot_type == PlotType.Image: + if len(axes) != 2: + raise ValueError('The Image Type only allows two axis') + + # Validate axes values + for value in axes: + if value in ['', '']: + continue + else: + self._get_channel_data(value) + channel_name = channel_data['name'] + self._set_channels_key('plot_axes', axes, [channel_name], + apply_cfg) + + def getCtrlsTimer(self, ctrls=None, use_fullname=False): + """get the acquisition Timer. + + :param ctrls: list of Controllers names to get the timer + info + :param use_fullname: returns a full name instead sardana + element name + + :return a OrderedDict where keys are controller names and + value the Timer Info + """ + + return self._get_ctrls_key('timer', ctrls, use_fullname) + + def setCtrlsTimer(self, timers, apply_cfg=True): + """Set the acquisition Timer to the controllers compatibles, + it finds the controller comptible with this timer and set it + . + :param timer_name: strings indicating the timer name + """ + result = self._get_ctrl_from_channels(timers, unique=True) + meas_ctrl = self.channels[self.timer]['_controller_name'] + + for timer, ctrl in result.items(): + if ctrl == meas_ctrl: + self._local_changes = True + self._raw_data['timer'] = timer + self._set_ctrls_key('timer', timer, [ctrl], apply_cfg) + + def getCtrlsMonitor(self, ctrls=None, use_fullname=False): + """get the Monitor for the channel indicated. In case of empty channel + value it will return all the Monitor Info + + :param ctrls: Indicate the controllers to return the Monitor Info + :param use_fullname: returns a full name instead sardana + element name + + :return a OrderedDict where keys are channel names and + value the Monitor Info + """ + + return self._get_ctrls_key('monitor', ctrls, use_fullname) + + def setCtrlsMonitor(self, monitors, apply_cfg=True): + """Set the Monitor for to the controllers compatibles, + it finds the controller comptible with this timer and set it + + :param monitors: (seq) a list of strings indicating the channels + to apply the monitor + :param monitor: string indicating the monitor name + """ + + result = self._get_ctrl_from_channels(monitors, unique=True) + meas_ctrl = self.channels[self.monitor]['_controller_name'] + + for monitor, ctrl in result.items(): + if ctrl == meas_ctrl: + self._local_changes = True + self._raw_data['monitor'] = monitor + self._set_ctrls_key('monitor', monitor, [ctrl], apply_cfg) + + def getCtrlsSynchronization(self, ctrls=None, use_fullname=False): + """get the Synchronization for the channel indicated. In case of empty + ctrl value it will return all the Synchronization Info + + :param ctrl: Indicate the controllers to return the + Synchronization Info + :param use_fullname: returns a full name instead sardana + element name + + :return a OrderedDict where keys are controllers names and + value the Synchronization Info + """ + + return self._get_ctrls_key('synchronization', ctrls, use_fullname) + + def setCtrlsSynchronization(self, synchronization, ctrls=None, + apply_cfg=True): + """Set the Synchronization to the indicated controllers. + + :param synchronization: string indicating the synchronization + :param ctrls: (seq) a list of strings indicating the channels + to apply the Synchronization + name + """ + msg_error = 'Wrong value! Synchronization allowed: ' \ + '{0}'.format(AcqSynchType.keys()) + if type(synchronization) == str: + if synchronization.lower() not in map(str.lower, + AcqSynchType.keys()): + raise ValueError(msg_error) + for value in AcqSynchType.keys(): + if value.lower() == synchronization.lower(): + synchronization = AcqSynchType[value] + break + elif type(synchronization) == int: + try: + AcqSynchType[synchronization] + except Exception: + raise ValueError(msg_error) + else: + raise ValueError() + self._set_ctrls_key('synchronization', synchronization, ctrls, + apply_cfg) + + def getCtrlsSynchronizer(self, ctrls=None, use_fullname=False): + """get the synchronizer for the channel indicated. In case of empty + channel value it will return all the Synchronizers Info + + :param ctrls: Indicate the controllers to return the + Synchronizer Info + :param use_fullname: returns a full name instead sardana + element name + :return a OrderedDict where keys are controllers names and + value the synchronizer info + """ + + return self._get_ctrls_key('synchronizer', ctrls, use_fullname) + + def setCtrlsSynchronizer(self, synchronizer, ctrls=None, apply_cfg=True): + """Set the synchronizer for the indicated controollers. In case of + empty ctrls value it will be applied to all the controllers + + :param syncronizer: string indicating the synchronizer name + :param ctrls: (seq) a list of strings indicating the + controllers to apply the synchronizer + """ + if synchronizer == 'software': + pass + else: + # TODO: Improve how to check if the element is a trigger_gate + sync = Device(synchronizer) + if 'triggergate' not in sync.name(): + raise ValueError('The "{0}" is not a ' + 'triggergate'.format(synchronizer)) + synchronizer = sync.getFullName() + + self._set_ctrls_key('synchronizer', synchronizer, ctrls, apply_cfg) def getTimerName(self): return self.getTimer()['name'] From 54a2cb60ef402663cb02a2e9c181776ee5569568 Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Tue, 17 Jul 2018 10:19:00 +0200 Subject: [PATCH 016/830] Update get configuration methods Change the MeasurementGroup methods to get the configuration. --- src/sardana/taurus/core/tango/sardana/pool.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 068aab7260..80afe82cc8 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2071,7 +2071,30 @@ def disableChannels(self, channels, apply_cfg=True): def __repr__(self): return json.dumps(self._raw_data, indent=4, sort_keys=True) + + +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) + + self.__cfg_attr = self.getAttribute('configuration') + self.__cfg_attr.addListener(self.on_configuration_changed) self._flg_event = False + self._value_buffer_cb = None + self._codec = CodecFactory().getCodec("json") + + def _create_str_tuple(self): + channel_names = ", ".join(self.getChannelNames()) + return self.getName(), self.getTimerName(), channel_names + + def getConfigurationAttrEG(self): + return self._getAttrEG('Configuration') def setConfiguration(self, configuration): self._flg_event = True codec = CodecFactory().getCodec('json') @@ -2084,6 +2107,11 @@ def _setConfiguration(self, data): else: self._configuration.set_data(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 From b560183f47eb818f61cccf669a76a724e88d03ef Mon Sep 17 00:00:00 2001 From: Roberto Homs Date: Tue, 17 Jul 2018 10:20:19 +0200 Subject: [PATCH 017/830] Export MGConfiguration API Export the MGConfiguration API to the MeasurementGroup class --- src/sardana/taurus/core/tango/sardana/pool.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 80afe82cc8..08761ff394 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2089,6 +2089,16 @@ def __init__(self, name, **kw): self._value_buffer_cb = None self._codec = CodecFactory().getCodec("json") + def __getattr__(self, item): + try: + return PoolElement.__getattr__(self, item) + except Exception: + try: + return self._configuration.__getattribute__(item) + except Exception: + raise AttributeError("'{0}' object has not attribute " + "'{1}'".format('MeasurementGroup', item)) + def _create_str_tuple(self): channel_names = ", ".join(self.getChannelNames()) return self.getName(), self.getTimerName(), channel_names From 23716e44ae7253bd07e37d45aceb664b312fa615 Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Tue, 17 Jul 2018 11:23:06 +0200 Subject: [PATCH 018/830] Add deprecation warning The method MeasurementGroup.getChannelsInfo has conflicts with the same method of the MGConfiguration class. --- src/sardana/taurus/core/tango/sardana/pool.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 08761ff394..de37c15483 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2105,6 +2105,7 @@ def _create_str_tuple(self): def getConfigurationAttrEG(self): return self._getAttrEG('Configuration') + def setConfiguration(self, configuration): self._flg_event = True codec = CodecFactory().getCodec('json') @@ -2122,12 +2123,20 @@ def getConfiguration(self, force=False): 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) self._flg_event = False + + # TODO, should be removed + def getChannelsInfo(self): + self.warning('Deprecation warning: you should use "getChannelsInfoList" ' + 'instead of "getChannelsInfo"') + return self.getConfiguration().getChannelsInfoList() + def getValueBuffers(self): value_buffers = [] for channel_info in self.getChannels(): From 07d81ea8cabbd222acffabefb4b68dcdbcd91436 Mon Sep 17 00:00:00 2001 From: Daniel Roldan Date: Tue, 17 Jul 2018 11:35:37 +0200 Subject: [PATCH 019/830] Fix flake8 error --- src/sardana/taurus/core/tango/sardana/pool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index de37c15483..f6d018bab1 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2133,8 +2133,8 @@ def on_configuration_changed(self, evt_src, evt_type, evt_value): # TODO, should be removed def getChannelsInfo(self): - self.warning('Deprecation warning: you should use "getChannelsInfoList" ' - 'instead of "getChannelsInfo"') + self.warning('Deprecation warning: you should use ' + '"getChannelsInfoList" instead of "getChannelsInfo"') return self.getConfiguration().getChannelsInfoList() def getValueBuffers(self): From c515fa29bd94ab7f4dce7b39c8fabcd134c30c56 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Fri, 20 Jul 2018 14:17:04 +0200 Subject: [PATCH 020/830] check if channel exists in plotselect add warning message to plotselect if a channel does not exists in the current measurement group --- src/sardana/macroserver/macros/standard.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 9601c3711c..7787bb0825 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -871,9 +871,10 @@ def run(self, plotChs): mntGrp = self.getEnv('ActiveMntGrp') self.mntGrp = self.getObj(mntGrp, type_class=Type.MeasurementGroup) cfg = self.mntGrp.getConfiguration() - + channels = self.mntGrp.getChannels() + # Enable Plot only in the channels passed. - for channel in self.mntGrp.getChannels(): + for channel in channels: if channel['name'] in plotChs: # Enable Plot self.info("Plot channel %s" % channel['name']) @@ -884,5 +885,10 @@ def run(self, plotChs): channel['plot_type'] = 0 channel['plot_axes'] = [] + # check if plotChs exists + for plotCh in plotChs: + if plotCh not in channles: + self.warning('channel %s does not exist in the current measurement group' % plotCh) + # Force set Configuration. self.mntGrp.setConfiguration(cfg.raw_data) From 49cc165221e03f26058fa61431a03b7005ca57e6 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 25 Jul 2018 23:59:56 +0200 Subject: [PATCH 021/830] implement scanstats macro --- src/sardana/macroserver/macros/scan.py | 62 +++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 78850a877d..f02b363eba 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -33,7 +33,8 @@ "d2scanc", "d3scanc", "d4scanc", "dscanc", "meshc", "a2scanct", "a3scanct", "a4scanct", "ascanct", "meshct", - "scanhist", "getCallable", "UNCONSTRAINED"] + "scanhist", "getCallable", "UNCONSTRAINED", + "scanstats"] __docformat__ = 'restructuredtext' @@ -1848,3 +1849,62 @@ def getTimeEstimation(self): def getIntervalEstimation(self): return self.nr_interv + + +class scanstats(Macro): + """Calculate basic statistics of the first enabled and plotted counter in + the active measurement group for the last scan. Print it and publish it + in the env. The macro must be hooked in the post-scan hook place. + """ + + def run(self, *args): + parent = self.getParentMacro() + if parent: + mntGrp = self.getEnv('ActiveMntGrp') + self.mntGrp = self.getObj(mntGrp, type_class=Type.MeasurementGroup) + channels = self.mntGrp.getChannels() + select_channel = '' + + for channel in channels: + if channel['enabled'] & channel['plot_type'] == 1: + select_channel = channel['name'] + break + + # in case no channel is enabled and plotted just take the first + if select_channel == '': + select_channel = channels[0]['name'] + + select_motor = str(parent.motors[0]) + + self.info('Statistics on channel: %s' % select_channel) + self.info('Statistics for movable: %s' % select_motor) + data = parent.data + + counter_data = [] + motor_data = [] + + for idx, rc in data.items(): + counter_data.append(rc[select_channel]) + motor_data.append(rc[select_motor]) + + counter_data = numpy.array(counter_data) + motor_data = numpy.array(motor_data) + + CEN = numpy.sum(counter_data*motor_data)/numpy.sum(counter_data) + PEAK = motor_data[numpy.argmax(counter_data)] + # print statistics + self.info('Min:\t %g' % numpy.min(counter_data)) + self.info('Max:\t %g' % numpy.max(counter_data)) + self.info('Min at:\t %g' % motor_data[numpy.argmin(counter_data)]) + self.info('Max at:\t %g' % PEAK) + self.info('Mean:\t %g' % numpy.mean(counter_data)) + self.info('Integral:\t %g' % numpy.sum(counter_data)) + self.info('CEN:\t %g' % CEN) + # set CEN and PEAK as env variables + # set the motor only in case it is hard to access it from another + # macro liek pic or cen + self.setEnv('ScanStats', {'PEAK':PEAK, 'CEN':CEN, + 'motor':select_motor}) + else: + self.warning('for now the scanstats macro can only be executed as' + ' a post-scan hook') From 68fa6d13f71daa5ffe993a9d2344ff3261780c5c Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Thu, 26 Jul 2018 00:13:14 +0200 Subject: [PATCH 022/830] fix flake8 --- src/sardana/macroserver/macros/scan.py | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index f02b363eba..f5ba31a4d8 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1856,7 +1856,7 @@ class scanstats(Macro): the active measurement group for the last scan. Print it and publish it in the env. The macro must be hooked in the post-scan hook place. """ - + def run(self, *args): parent = self.getParentMacro() if parent: @@ -1864,32 +1864,32 @@ def run(self, *args): self.mntGrp = self.getObj(mntGrp, type_class=Type.MeasurementGroup) channels = self.mntGrp.getChannels() select_channel = '' - + for channel in channels: if channel['enabled'] & channel['plot_type'] == 1: - select_channel = channel['name'] + select_channel = channel['name'] break - + # in case no channel is enabled and plotted just take the first if select_channel == '': select_channel = channels[0]['name'] - + select_motor = str(parent.motors[0]) - - self.info('Statistics on channel: %s' % select_channel) + + self.info('Statistics on channel: %s' % select_channel) self.info('Statistics for movable: %s' % select_motor) data = parent.data - + counter_data = [] motor_data = [] - + for idx, rc in data.items(): counter_data.append(rc[select_channel]) motor_data.append(rc[select_motor]) - + counter_data = numpy.array(counter_data) motor_data = numpy.array(motor_data) - + CEN = numpy.sum(counter_data*motor_data)/numpy.sum(counter_data) PEAK = motor_data[numpy.argmax(counter_data)] # print statistics @@ -1900,11 +1900,11 @@ def run(self, *args): self.info('Mean:\t %g' % numpy.mean(counter_data)) self.info('Integral:\t %g' % numpy.sum(counter_data)) self.info('CEN:\t %g' % CEN) - # set CEN and PEAK as env variables + # set CEN and PEAK as env variables # set the motor only in case it is hard to access it from another # macro liek pic or cen - self.setEnv('ScanStats', {'PEAK':PEAK, 'CEN':CEN, - 'motor':select_motor}) + self.setEnv('ScanStats', {'PEAK': PEAK, 'CEN': CEN, + 'motor': select_motor}) else: self.warning('for now the scanstats macro can only be executed as' ' a post-scan hook') From 6457f67f3219da2af2cff54ffe1e6f35aaadc26e Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 1 Aug 2018 13:16:28 +0200 Subject: [PATCH 023/830] correct check if channel exists or is enabled --- src/sardana/macroserver/macros/standard.py | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 7787bb0825..804de7631c 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -872,23 +872,27 @@ def run(self, plotChs): self.mntGrp = self.getObj(mntGrp, type_class=Type.MeasurementGroup) cfg = self.mntGrp.getConfiguration() channels = self.mntGrp.getChannels() - + channelNames = [] + # Enable Plot only in the channels passed. for channel in channels: - if channel['name'] in plotChs: - # Enable Plot - self.info("Plot channel %s" % channel['name']) - channel['plot_type'] = 1 - channel['plot_axes'] = [''] - else: - # Disable Plot - channel['plot_type'] = 0 - channel['plot_axes'] = [] + if channel['enabled']: + channelNames.append(channel['name']) + if channel['name'] in plotChs: + # Enable Plot + self.info("Plot channel %s" % channel['name']) + channel['plot_type'] = 1 + channel['plot_axes'] = [''] + else: + # Disable Plot + channel['plot_type'] = 0 + channel['plot_axes'] = [] # check if plotChs exists for plotCh in plotChs: - if plotCh not in channles: - self.warning('channel %s does not exist in the current measurement group' % plotCh) + if plotCh not in channelNames: + self.warning('channel %s is not enabled or does not exist in' + ' the current measurement group' % plotCh) # Force set Configuration. self.mntGrp.setConfiguration(cfg.raw_data) From 9ce5c270b0740b67a583a9ca335520160461d62e Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 1 Aug 2018 15:39:20 +0200 Subject: [PATCH 024/830] calculate stats for all enabled channels --- src/sardana/macroserver/macros/scan.py | 64 +++++++++++++++----------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index f5ba31a4d8..1e377f3077 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1867,43 +1867,55 @@ def run(self, *args): for channel in channels: if channel['enabled'] & channel['plot_type'] == 1: - select_channel = channel['name'] + select_channel = channel['label'] break # in case no channel is enabled and plotted just take the first if select_channel == '': - select_channel = channels[0]['name'] + select_channel = channels[0]['label'] select_motor = str(parent.motors[0]) - - self.info('Statistics on channel: %s' % select_channel) - self.info('Statistics for movable: %s' % select_motor) + + # calculate stats for all enabled channels data = parent.data + stats = {} + + for channel in channels: + if channel['enabled']: + channel_name = channel['label'] + counter_data = [] + motor_data = [] + + for idx, rc in data.items(): + counter_data.append(rc[channel_name]) + motor_data.append(rc[channel_name]) + + counter_data = numpy.array(counter_data) + motor_data = numpy.array(motor_data) + + stats[channel_name] = {'min': numpy.min(counter_data), + 'max': numpy.max(counter_data), + 'minpos': motor_data[numpy.argmin(counter_data)], + 'maxpos': motor_data[numpy.argmax(counter_data)], + 'mean': numpy.mean(counter_data), + 'int': numpy.sum(counter_data), + 'cen': numpy.sum(counter_data*motor_data)/numpy.sum(counter_data)} + + self.info('Statistics on channel: %s' % select_channel) + self.info('Statistics for movable: %s' % select_motor) - counter_data = [] - motor_data = [] - - for idx, rc in data.items(): - counter_data.append(rc[select_channel]) - motor_data.append(rc[select_motor]) - - counter_data = numpy.array(counter_data) - motor_data = numpy.array(motor_data) - - CEN = numpy.sum(counter_data*motor_data)/numpy.sum(counter_data) - PEAK = motor_data[numpy.argmax(counter_data)] # print statistics - self.info('Min:\t %g' % numpy.min(counter_data)) - self.info('Max:\t %g' % numpy.max(counter_data)) - self.info('Min at:\t %g' % motor_data[numpy.argmin(counter_data)]) - self.info('Max at:\t %g' % PEAK) - self.info('Mean:\t %g' % numpy.mean(counter_data)) - self.info('Integral:\t %g' % numpy.sum(counter_data)) - self.info('CEN:\t %g' % CEN) + self.info('Min: %g' % stats[select_channel]['min']) + self.info('Max: %g' % stats[select_channel]['max']) + self.info('Min at: %g' % stats[select_channel]['minpos']) + self.info('Max at: %g' % stats[select_channel]['maxpos']) + self.info('Mean: %g' % stats[select_channel]['mean']) + self.info('Integral: %g' % stats[select_channel]['int']) + self.info('CEN: %g' % stats[select_channel]['cen']) # set CEN and PEAK as env variables # set the motor only in case it is hard to access it from another - # macro liek pic or cen - self.setEnv('ScanStats', {'PEAK': PEAK, 'CEN': CEN, + # macro like pic or cen + self.setEnv('ScanStats', {'stats': stats, 'counter': select_channel, 'motor': select_motor}) else: self.warning('for now the scanstats macro can only be executed as' From a78f3cedbffb4873fb2916884dc47182cd2b4c0a Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 1 Aug 2018 15:47:56 +0200 Subject: [PATCH 025/830] fix flake8 some overlengths are nasty to break --- src/sardana/macroserver/macros/scan.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 1e377f3077..a56e773738 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1875,31 +1875,31 @@ def run(self, *args): select_channel = channels[0]['label'] select_motor = str(parent.motors[0]) - + # calculate stats for all enabled channels data = parent.data stats = {} - + for channel in channels: if channel['enabled']: channel_name = channel['label'] counter_data = [] motor_data = [] - + for idx, rc in data.items(): counter_data.append(rc[channel_name]) motor_data.append(rc[channel_name]) counter_data = numpy.array(counter_data) motor_data = numpy.array(motor_data) - + stats[channel_name] = {'min': numpy.min(counter_data), - 'max': numpy.max(counter_data), - 'minpos': motor_data[numpy.argmin(counter_data)], - 'maxpos': motor_data[numpy.argmax(counter_data)], - 'mean': numpy.mean(counter_data), - 'int': numpy.sum(counter_data), - 'cen': numpy.sum(counter_data*motor_data)/numpy.sum(counter_data)} + 'max': numpy.max(counter_data), + 'minpos': motor_data[numpy.argmin(counter_data)], + 'maxpos': motor_data[numpy.argmax(counter_data)], + 'mean': numpy.mean(counter_data), + 'int': numpy.sum(counter_data), + 'cen': numpy.sum(counter_data*motor_data)/numpy.sum(counter_data)} self.info('Statistics on channel: %s' % select_channel) self.info('Statistics for movable: %s' % select_motor) @@ -1915,7 +1915,8 @@ def run(self, *args): # set CEN and PEAK as env variables # set the motor only in case it is hard to access it from another # macro like pic or cen - self.setEnv('ScanStats', {'stats': stats, 'counter': select_channel, + self.setEnv('ScanStats', {'stats': stats, + 'counter': select_channel, 'motor': select_motor}) else: self.warning('for now the scanstats macro can only be executed as' From 38ccee42b020e9b6ac4d185dee26f960ed503473 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 1 Aug 2018 15:56:49 +0200 Subject: [PATCH 026/830] init genv macro get env variable --- src/sardana/macroserver/macros/env.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index abcaeefdbd..3aaba12570 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -23,7 +23,7 @@ """Environment related macros""" -__all__ = ["dumpenv", "load_env", "lsenv", "senv", "usenv", +__all__ = ["dumpenv", "load_env", "lsenv", "senv", "usenv", "genv" "lsvo", "setvo", "usetvo", "lsgh", "defgh", "udefgh"] @@ -193,6 +193,23 @@ def run(self, env, value): self.output(line) +class genv(Macro): + """Sets the given environment variable to the given value""" + + param_def = [['name', Type.Env, None, + 'Environment variable name. Can be one of the following:\n' + ' - - global variable\n' + ' - . - variable value for a specific door\n' + ' - . - variable value for a specific macro\n' + ' - .. - variable value for a specific macro running on a specific door'], + ] + + def run(self, env): + v = self.getEnv(env) + line = '%s = %s' % (env, str(v)) + self.output(line) + + class usenv(Macro): """Unsets the given environment variable""" param_def = [ From 065160906f240b52a93423834776cea71dbdf20a Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 1 Aug 2018 16:12:45 +0200 Subject: [PATCH 027/830] init pic cen --- src/sardana/macroserver/macros/standard.py | 61 +++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index a95013714f..7551919fb3 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -25,7 +25,7 @@ __all__ = ["ct", "mstate", "mv", "mvr", "pwa", "pwm", "repeat", "set_lim", "set_lm", "set_pos", "settimer", "uct", "umv", "umvr", "wa", "wm", - "tw", "logmacro"] + "tw", "logmacro", "pic", "cen"] __docformat__ = 'restructuredtext' @@ -38,7 +38,7 @@ from PyTango import DevState from sardana.macroserver.macro import Macro, macro, Type, ParamRepeat, \ - ViewOption, iMacro, Hookable + ViewOption, iMacro, Hookable, OptionalParam from sardana.macroserver.msexception import StopException from sardana.macroserver.scan.scandata import Record ########################################################################## @@ -854,3 +854,60 @@ def run(self, nr): self.__loop() progress = ((i + 1) / float(nr)) * 100 yield progress + + + +class pic(Macro): + """This macro moves the motor of the last scan to the peak position for a + given counter. If no counter is given, it selects the first plotted counter + from the expconf. + """ + + param_def = [ + ['counter', Type.ExpChannel, OptionalParam, 'name of counter'] + ] + + def prepare(self, counter): + self.stats = self.getEnv('ScanStats') + if counter == -1: + counter = self.stats['counter'] + self.info('use counter: %s' % counter) + + def run(self, counter): + motor_name = self.stats['motor'] + motor = self.getMotion([motor_name]) + self.info(motor.getPositon()) + + + +class cen(Hookable, Macro): + """This macro executes as many repetitions of it's body hook macros as + specified by nr parameter. If nr parameter has negative value, + repetitions will be executed until you stop repeat macro.""" + + # hints = { 'allowsHooks': ('body', 'break', 'continue') } + hints = {'allowsHooks': ('body',)} + + param_def = [ + ['nr', Type.Integer, None, 'Nr of iterations'] + ] + + def prepare(self, nr): + # self.breakHooks = self.getHooks("break") + # self.continueHooks = self.getHooks("continue") + self.bodyHooks = self.getHooks("body") + + def __loop(self): + self.checkPoint() + for bodyHook in self.bodyHooks: + bodyHook() + + def run(self, nr): + if nr < 0: + while True: + self.__loop() + else: + for i in range(nr): + self.__loop() + progress = ((i + 1) / float(nr)) * 100 + yield progress \ No newline at end of file From 1eb52fed2f73d793cbc50440f4748a7abcd48dab Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 1 Aug 2018 22:03:51 +0200 Subject: [PATCH 028/830] fix flake8 --- src/sardana/macroserver/macros/env.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 3aaba12570..09882c29e6 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -23,7 +23,7 @@ """Environment related macros""" -__all__ = ["dumpenv", "load_env", "lsenv", "senv", "usenv", "genv" +__all__ = ["dumpenv", "load_env", "lsenv", "senv", "usenv", "genv", "lsvo", "setvo", "usetvo", "lsgh", "defgh", "udefgh"] @@ -199,9 +199,12 @@ class genv(Macro): param_def = [['name', Type.Env, None, 'Environment variable name. Can be one of the following:\n' ' - - global variable\n' - ' - . - variable value for a specific door\n' - ' - . - variable value for a specific macro\n' - ' - .. - variable value for a specific macro running on a specific door'], + ' - . - variable value for a specific ' + 'door\n' + ' - . - variable value for a specific' + ' macro\n' + ' - .. - variable value' + ' for a specific macro running on a specific door'], ] def run(self, env): From ec6eed388238a9b3617ae68fc42762abe45126b2 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 1 Aug 2018 22:05:57 +0200 Subject: [PATCH 029/830] fix flake8 --- src/sardana/macroserver/macros/standard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 804de7631c..43122273fa 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -892,7 +892,7 @@ def run(self, plotChs): for plotCh in plotChs: if plotCh not in channelNames: self.warning('channel %s is not enabled or does not exist in' - ' the current measurement group' % plotCh) + ' the current measurement group' % plotCh) # Force set Configuration. self.mntGrp.setConfiguration(cfg.raw_data) From 9d562aac9a3c57b927a690793c10c256f5128821 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Thu, 2 Aug 2018 09:53:12 +0200 Subject: [PATCH 030/830] add edge logic and correct bug for motor values --- src/sardana/macroserver/macros/scan.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index a56e773738..fefc663806 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1888,9 +1888,10 @@ def run(self, *args): for idx, rc in data.items(): counter_data.append(rc[channel_name]) - motor_data.append(rc[channel_name]) + motor_data.append(rc[select_motor]) counter_data = numpy.array(counter_data) + counter_data_grad = numpy.gradient(counter_data) motor_data = numpy.array(motor_data) stats[channel_name] = {'min': numpy.min(counter_data), @@ -1899,7 +1900,8 @@ def run(self, *args): 'maxpos': motor_data[numpy.argmax(counter_data)], 'mean': numpy.mean(counter_data), 'int': numpy.sum(counter_data), - 'cen': numpy.sum(counter_data*motor_data)/numpy.sum(counter_data)} + 'cen': numpy.sum(counter_data*motor_data)/numpy.sum(counter_data), + 'edge': numpy.sum(counter_data_grad*motor_data)/numpy.sum(counter_data_grad)} self.info('Statistics on channel: %s' % select_channel) self.info('Statistics for movable: %s' % select_motor) @@ -1912,6 +1914,7 @@ def run(self, *args): self.info('Mean: %g' % stats[select_channel]['mean']) self.info('Integral: %g' % stats[select_channel]['int']) self.info('CEN: %g' % stats[select_channel]['cen']) + self.info('EDGE: %g' % stats[select_channel]['edge']) # set CEN and PEAK as env variables # set the motor only in case it is hard to access it from another # macro like pic or cen From 66e90d9a1578da69308da3e2455f4032be7b73a4 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Thu, 2 Aug 2018 09:56:41 +0200 Subject: [PATCH 031/830] improve pic, cen and add edge and wher macros fix flake8 --- src/sardana/macroserver/macros/standard.py | 110 ++++++++++++++------- 1 file changed, 77 insertions(+), 33 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 7551919fb3..edb4ff1c46 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -25,7 +25,7 @@ __all__ = ["ct", "mstate", "mv", "mvr", "pwa", "pwm", "repeat", "set_lim", "set_lm", "set_pos", "settimer", "uct", "umv", "umvr", "wa", "wm", - "tw", "logmacro", "pic", "cen"] + "tw", "logmacro", "pic", "cen", "edge", "wher"] __docformat__ = 'restructuredtext' @@ -856,7 +856,6 @@ def run(self, nr): yield progress - class pic(Macro): """This macro moves the motor of the last scan to the peak position for a given counter. If no counter is given, it selects the first plotted counter @@ -869,45 +868,90 @@ class pic(Macro): def prepare(self, counter): self.stats = self.getEnv('ScanStats') - if counter == -1: - counter = self.stats['counter'] - self.info('use counter: %s' % counter) + if counter is OptionalParam: + self.counter = self.stats['counter'] + else: + self.counter = counter.getName() - def run(self, counter): - motor_name = self.stats['motor'] - motor = self.getMotion([motor_name]) - self.info(motor.getPositon()) - + self.motor_name = self.stats['motor'] + self.motor = self.getMotion([self.motor_name]) + def run(self, counter): + current_pos = self.motor.readPosition()[0] + peak = self.stats['stats'][self.counter]['maxpos'] + self.info('move motor %s from current position\nat %f\nto peak of' + ' counter %s\nat %f' % (self.motor_name, current_pos, + self.counter, peak)) + self.motor.move(peak) -class cen(Hookable, Macro): - """This macro executes as many repetitions of it's body hook macros as - specified by nr parameter. If nr parameter has negative value, - repetitions will be executed until you stop repeat macro.""" - # hints = { 'allowsHooks': ('body', 'break', 'continue') } - hints = {'allowsHooks': ('body',)} +class cen(Macro): + """This macro moves the motor of the last scan to the cen position for a + given counter. If no counter is given, it selects the first plotted counter + from the expconf. + """ param_def = [ - ['nr', Type.Integer, None, 'Nr of iterations'] + ['counter', Type.ExpChannel, OptionalParam, 'name of counter'] ] - def prepare(self, nr): - # self.breakHooks = self.getHooks("break") - # self.continueHooks = self.getHooks("continue") - self.bodyHooks = self.getHooks("body") + def prepare(self, counter): + self.stats = self.getEnv('ScanStats') + if counter is OptionalParam: + self.counter = self.stats['counter'] + else: + self.counter = counter.getName() - def __loop(self): - self.checkPoint() - for bodyHook in self.bodyHooks: - bodyHook() + self.motor_name = self.stats['motor'] + self.motor = self.getMotion([self.motor_name]) - def run(self, nr): - if nr < 0: - while True: - self.__loop() + def run(self, counter): + current_pos = self.motor.readPosition()[0] + cen = self.stats['stats'][self.counter]['cen'] + self.info('move motor %s from current position\nat %f\nto cen of' + ' counter %s\nat %f' % (self.motor_name, current_pos, + self.counter, cen)) + self.motor.move(cen) + + +class edge(Macro): + """This macro moves the motor of the last scan to the edge position for a + given counter. If no counter is given, it selects the first plotted counter + from the expconf. + """ + + param_def = [ + ['counter', Type.ExpChannel, OptionalParam, 'name of counter'] + ] + + def prepare(self, counter): + self.stats = self.getEnv('ScanStats') + if counter is OptionalParam: + self.counter = self.stats['counter'] else: - for i in range(nr): - self.__loop() - progress = ((i + 1) / float(nr)) * 100 - yield progress \ No newline at end of file + self.counter = counter.getName() + + self.motor_name = self.stats['motor'] + self.motor = self.getMotion([self.motor_name]) + + def run(self, counter): + current_pos = self.motor.readPosition()[0] + edge = self.stats['stats'][self.counter]['edge'] + self.info('move motor %s from current position\nat %f\nto edge of' + ' counter %s\nat %f' % (self.motor_name, current_pos, + self.counter, edge)) + self.motor.move(edge) + + +class wher(Macro): + """This macro shows the current position of the last scanned motor. + """ + + def prepare(self): + self.stats = self.getEnv('ScanStats') + self.motor_name = self.stats['motor'] + self.motor = self.getMotion([self.motor_name]) + + def run(self): + current_pos = self.motor.readPosition()[0] + self.info('motor %s is\nat %f' % (self.motor_name, current_pos)) From 35e85ad5c0a2c4acac39267949fa2f81a303bcee Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Thu, 2 Aug 2018 13:42:39 +0200 Subject: [PATCH 032/830] fix getting the environment and doc string --- src/sardana/macroserver/macros/env.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 09882c29e6..47409d4111 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -194,7 +194,7 @@ def run(self, env, value): class genv(Macro): - """Sets the given environment variable to the given value""" + """Gets the given environment variable""" param_def = [['name', Type.Env, None, 'Environment variable name. Can be one of the following:\n' @@ -207,9 +207,9 @@ class genv(Macro): ' for a specific macro running on a specific door'], ] - def run(self, env): - v = self.getEnv(env) - line = '%s = %s' % (env, str(v)) + def run(self, var): + env = self.getAllDoorEnv() + line = '%s = %s' % (str(var), env[var]) self.output(line) From 6380bcd24ea52a4d405c6401a27770ce4e1ce1eb Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Fri, 3 Aug 2018 10:27:10 +0200 Subject: [PATCH 033/830] compatibility for door env --- src/sardana/macroserver/macros/env.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 47409d4111..355285ebc2 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -208,8 +208,15 @@ class genv(Macro): ] def run(self, var): - env = self.getAllDoorEnv() - line = '%s = %s' % (str(var), env[var]) + pars = var.split(".") + if len(pars) == 1: + env = self.getAllDoorEnv() + line = '%s = %s' % (str(var), env[var]) + else: + env = self.getEnv(key=None, macro_name=pars[0]) + names_list = list(env.keys()) + names_list.sort(key=str.lower) + line = '%s = %s' % (str(var), str(env[pars[1]])) self.output(line) From 36ec6ae8b1b112ca64cb23c35b4cf8c03884ae59 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Wed, 26 Sep 2018 11:57:33 +0200 Subject: [PATCH 034/830] Allow measurement groups renaming --- src/sardana/pool/pool.py | 6 +++++- src/sardana/pool/poolmeasurementgroup.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py index 026504f2ff..1803a98441 100644 --- a/src/sardana/pool/pool.py +++ b/src/sardana/pool/pool.py @@ -54,6 +54,7 @@ from sardana.pool.poolmonitor import PoolMonitor from sardana.pool.poolmetacontroller import TYPE_MAP_OBJ from sardana.pool.poolcontrollermanager import ControllerManager +from sardana.pool.poolmeasurementgroup import PoolMeasurementGroup class Graph(dict): @@ -543,7 +544,10 @@ def create_measurement_group(self, **kwargs): def rename_element(self, old_name, new_name): elem = self.get_element_by_name(old_name) - elem.controller.rename_element(old_name, new_name) + if type(elem) == PoolMeasurementGroup: + elem.rename_element(old_name, new_name) + else: + elem.controller.rename_element(old_name, new_name) PoolContainer.rename_element(self, old_name, new_name) elem = self.get_element_by_name(new_name) self.fire_event(EventType("ElementChanged"), elem) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 0ad982b431..6074d77972 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -203,6 +203,22 @@ def add_user_element(self, element, index=None): if element.get_type() is ElementType.TriggerGate: return return PoolGroupElement.add_user_element(self, element, index) + + def rename_element(self, old_name, new_name, propagate=1): + """Rename element in the controller. + + :param old_name: old name of the element + :type old_name: :obj:`str` + :param new_name: new name of the element + :type new_name: :obj:`str` + :param propagate: 0 for not propagating, 1 to propagate, + 2 propagate with priority + :type propagate: :obj:`int` + """ + self._config['label'] = new_name + self.fire_event(EventType("configuration", priority=propagate), + self._config) + # ------------------------------------------------------------------------- # configuration # ------------------------------------------------------------------------- From e253f7f36b1285c2926c65c686d9374d525bc342 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 27 Sep 2018 08:10:10 +0200 Subject: [PATCH 035/830] Read dial position from the motor API Use the motor API to read the dial position instead of the Tango API. --- src/sardana/macroserver/macros/standard.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index d9463e8570..e8f2746dd3 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -97,6 +97,8 @@ def run(self, motor_list): value = attr.value if value is None: value = float('NaN') + if attr.name == 'dialposition': + value = motor.getDialPosition() data[name].append(value) req2delete.append(name) except PyTango.AsynReplyNotArrived: @@ -378,7 +380,7 @@ def run(self, motor_list): try: val1 = fmt % motor.getDialPosition(force=True) val1 = str_fmt % val1 - except: + except Exception as e: val1 = str_fmt % motor.getDialPosition(force=True) dPosObj = motor.getDialPositionObj() From 4d11caf0e3b0149e3f494e1a228fac137be1a6b9 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 27 Sep 2018 11:39:22 +0200 Subject: [PATCH 036/830] Fix flake8 error --- src/sardana/macroserver/macros/standard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index e8f2746dd3..4b14ab8e43 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -380,7 +380,7 @@ def run(self, motor_list): try: val1 = fmt % motor.getDialPosition(force=True) val1 = str_fmt % val1 - except Exception as e: + except Exception: val1 = str_fmt % motor.getDialPosition(force=True) dPosObj = motor.getDialPositionObj() From 76e709580122f481aa17bb0457ea42f541fd5a68 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 9 Jan 2019 15:29:54 +0100 Subject: [PATCH 037/830] Change title to "Plugins register" --- doc/source/sep/SEP16.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/sep/SEP16.md b/doc/source/sep/SEP16.md index 518fb3d099..ff6047ef1d 100644 --- a/doc/source/sep/SEP16.md +++ b/doc/source/sep/SEP16.md @@ -1,4 +1,4 @@ - Title: Migration of third-party repositories (controllers and macros) + Title: Plugins (controllers, macros, etc.) register SEP: 16 State: DRAFT Reason: From 43bbdc8d6c734cfc8802e9f2a99e9e12693eae5b Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Thu, 21 Mar 2019 15:40:49 +0100 Subject: [PATCH 038/830] Use PY_OBJECT for signal signatures Some signals use `object` in their signatures, but this may be problematic when object is imported from builtins (futurize module). To avoid it use taurus.external.qt.compat.PY_OBJECT --- src/sardana/spock/inputhandler.py | 4 ++-- .../qt/qtcore/tango/sardana/macroserver.py | 22 +++++++++---------- .../favouriteseditor/favouriteseditor.py | 4 ++-- .../favouriteseditor/historyviewer.py | 4 ++-- .../qtgui/extra_macroexecutor/macrobutton.py | 8 +++---- .../extra_macroexecutor/macroexecutor.py | 4 ++-- .../sequenceeditor/sequenceeditor.py | 6 ++--- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 6 ++--- .../qt/qtgui/extra_sardana/expdescription.py | 4 ++-- 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/sardana/spock/inputhandler.py b/src/sardana/spock/inputhandler.py index 1a5f528bbf..eb88af3e75 100644 --- a/src/sardana/spock/inputhandler.py +++ b/src/sardana/spock/inputhandler.py @@ -34,7 +34,7 @@ from taurus.core import TaurusManager from taurus.core.util.singleton import Singleton -from taurus.external.qt import Qt +from taurus.external.qt import Qt, compat from taurus.qt.qtgui.dialog import TaurusMessageBox, TaurusInputDialog from sardana.taurus.core.tango.sardana.macroserver import BaseInputHandler @@ -71,7 +71,7 @@ def input_timeout(self, input_data): class MessageHandler(Qt.QObject): - messageArrived = Qt.pyqtSignal(object) + messageArrived = Qt.pyqtSignal(compat.PY_OBJECT) def __init__(self, conn, parent=None): Qt.QObject.__init__(self, parent) diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py index af882eddc1..c1ff26b73d 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py @@ -30,7 +30,7 @@ import copy from taurus.core.taurusbasetypes import TaurusEventType -from taurus.external.qt import Qt +from taurus.external.qt import Qt, compat from sardana.taurus.core.tango.sardana.macroserver import BaseMacroServer, \ BaseDoor @@ -49,15 +49,15 @@ class QDoor(BaseDoor, Qt.QObject): # sometimes we emit None hence the type is object # (but most of the data are passed with type list) - resultUpdated = Qt.pyqtSignal(object) - recordDataUpdated = Qt.pyqtSignal(object) - macroStatusUpdated = Qt.pyqtSignal(object) - errorUpdated = Qt.pyqtSignal(object) - warningUpdated = Qt.pyqtSignal(object) - infoUpdated = Qt.pyqtSignal(object) - outputUpdated = Qt.pyqtSignal(object) - debugUpdated = Qt.pyqtSignal(object) - experimentConfigurationChanged = Qt.pyqtSignal(object) + resultUpdated = Qt.pyqtSignal(compat.PY_OBJECT) + recordDataUpdated = Qt.pyqtSignal(compat.PY_OBJECT) + macroStatusUpdated = Qt.pyqtSignal(compat.PY_OBJECT) + errorUpdated = Qt.pyqtSignal(compat.PY_OBJECT) + warningUpdated = Qt.pyqtSignal(compat.PY_OBJECT) + infoUpdated = Qt.pyqtSignal(compat.PY_OBJECT) + outputUpdated = Qt.pyqtSignal(compat.PY_OBJECT) + debugUpdated = Qt.pyqtSignal(compat.PY_OBJECT) + experimentConfigurationChanged = Qt.pyqtSignal(compat.PY_OBJECT) elementsChanged = Qt.pyqtSignal() environmentChanged = Qt.pyqtSignal() @@ -172,7 +172,7 @@ class QMacroServer(BaseMacroServer, Qt.QObject): elementsUpdated = Qt.pyqtSignal() elementsChanged = Qt.pyqtSignal() macrosUpdated = Qt.pyqtSignal() - environmentChanged = Qt.pyqtSignal(object) + environmentChanged = Qt.pyqtSignal(compat.PY_OBJECT) def __init__(self, name, qt_parent=None, **kw): self.call__init__wo_kw(Qt.QObject, qt_parent) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py index d1d763811c..c0200922d5 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py @@ -28,7 +28,7 @@ """ import copy -from taurus.external.qt import Qt +from taurus.external.qt import Qt, compat from taurus.qt.qtgui.resource import getIcon from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtcore.configuration import BaseConfigurableClass @@ -103,7 +103,7 @@ def getQtDesignerPluginInfo(cls): class FavouritesMacrosList(Qt.QListView, BaseConfigurableClass): - favouriteSelected = Qt.pyqtSignal(object) + favouriteSelected = Qt.pyqtSignal(compat.PY_OBJECT) def __init__(self, parent=None): Qt.QListView.__init__(self, parent) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py index 4f2a3406ec..9cb2c53b09 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py @@ -28,7 +28,7 @@ """ import copy -from taurus.external.qt import Qt +from taurus.external.qt import Qt, compat from taurus.qt.qtgui.resource import getIcon from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtcore.configuration import BaseConfigurableClass @@ -114,7 +114,7 @@ def getQtDesignerPluginInfo(cls): class HistoryMacrosList(Qt.QListView, BaseConfigurableClass): - historySelected = Qt.pyqtSignal(object) + historySelected = Qt.pyqtSignal(compat.PY_OBJECT) def __init__(self, parent=None): Qt.QListView.__init__(self, parent) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index b10c89b1ec..95f1023880 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -37,7 +37,7 @@ import taurus from taurus.core import TaurusEventType, TaurusDevice -from taurus.external.qt import Qt +from taurus.external.qt import Qt, compat from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtgui.base import TaurusBaseWidget from taurus.qt.qtgui.button import TaurusCommandButton @@ -53,7 +53,7 @@ class DoorStateListener(Qt.QObject): __pyqtSignals__ = ["doorStateChanged"] - doorStateChanged = Qt.pyqtSignal(object) + doorStateChanged = Qt.pyqtSignal(compat.PY_OBJECT) def eventReceived(self, evt_src, evt_type, evt_value): if evt_type not in (TaurusEventType.Change, TaurusEventType.Periodic): @@ -74,8 +74,8 @@ class MacroButton(TaurusWidget): __pyqtSignals__ = ['statusUpdated', 'resultUpdated'] - statusUpdated = Qt.pyqtSignal(object) - resultUpdated = Qt.pyqtSignal(object) + statusUpdated = Qt.pyqtSignal(compat.PY_OBJECT) + resultUpdated = Qt.pyqtSignal(compat.PY_OBJECT) def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 7bfeee426d..d4c6d0c910 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -32,7 +32,7 @@ import PyTango -from taurus.external.qt import Qt +from taurus.external.qt import Qt, compat from taurus import Device from taurus.qt.qtgui.container import TaurusWidget, TaurusMainWindow, TaurusBaseContainer from taurus.qt.qtgui.display import TaurusLed @@ -626,7 +626,7 @@ class TaurusMacroExecutorWidget(TaurusWidget): doorChanged = Qt.pyqtSignal('QString') macroNameChanged = Qt.pyqtSignal('QString') macroStarted = Qt.pyqtSignal('QString') - plotablesFilterChanged = Qt.pyqtSignal(object) + plotablesFilterChanged = Qt.pyqtSignal(compat.PY_OBJECT) shortMessageEmitted = Qt.pyqtSignal('QString') def __init__(self, parent=None, designMode=False): diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 04b9deeea1..23c2d5c597 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -85,7 +85,7 @@ def onToggle(self, trueFalse): class MacroSequenceTree(Qt.QTreeView, BaseConfigurableClass): macroNameChanged = Qt.pyqtSignal('QString') - macroChanged = Qt.pyqtSignal(object) + macroChanged = Qt.pyqtSignal(compat.PY_OBJECT) def __init__(self, parent=None): Qt.QTreeView.__init__(self, parent) @@ -325,8 +325,8 @@ def dropEvent(self, event): class TaurusSequencerWidget(TaurusWidget): macroStarted = Qt.pyqtSignal('QString') - plotablesFilterChanged = Qt.pyqtSignal(object) - currentMacroChanged = Qt.pyqtSignal(object) + plotablesFilterChanged = Qt.pyqtSignal(compat.PY_OBJECT) + currentMacroChanged = Qt.pyqtSignal(compat.PY_OBJECT) macroNameChanged = Qt.pyqtSignal('QString') shortMessageEmitted = Qt.pyqtSignal('QString') sequenceEmpty = Qt.pyqtSignal() diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index df4d4d36c8..647a3df6b4 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -28,7 +28,7 @@ import PyTango import numpy -from taurus.external.qt import Qt +from taurus.external.qt import Qt, compat import taurus from taurus.core.util.colors import DEVICE_STATE_PALETTE @@ -64,7 +64,7 @@ class LimitsListener(Qt.QObject): can do whatever with it. """ - updateLimits = Qt.pyqtSignal(object) + updateLimits = Qt.pyqtSignal(compat.PY_OBJECT) def __init__(self): Qt.QObject.__init__(self) @@ -812,7 +812,7 @@ class TaurusAttributeListener(Qt.QObject): If that is the case it emits a signal with the event's value. """ - eventReceivedSignal = Qt.pyqtSignal(object) + eventReceivedSignal = Qt.pyqtSignal(compat.PY_OBJECT) def __init__(self): Qt.QObject.__init__(self) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index 293332471c..57929a2f2e 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -29,7 +29,7 @@ import json -from taurus.external.qt import Qt, QtCore, QtGui +from taurus.external.qt import Qt, QtCore, QtGui, compat import copy import taurus import taurus.core @@ -173,7 +173,7 @@ class ExpDescriptionEditor(Qt.QWidget, TaurusBaseWidget): ''' createExpConfChangedDialog = Qt.pyqtSignal() - experimentConfigurationChanged = Qt.pyqtSignal(object) + experimentConfigurationChanged = Qt.pyqtSignal(compat.PY_OBJECT) def __init__(self, parent=None, door=None, plotsButton=True, autoUpdate=False): From ad8ca697d67910acb22974b95edeaaf64d17e7a1 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Wed, 19 Jun 2019 17:19:18 +0200 Subject: [PATCH 039/830] Fix issue #1027 ParamEditorModel data method returns the `node.value()`, but since 26dc568 the macros does not initialize the default parameter value this method returns None. Return the `node.defValue()` if the `node.value()` is None. Fix #1027 --- .../qtgui/extra_macroexecutor/macroparameterseditor/model.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py index 6cf78837f8..8d9c2b5ce5 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py @@ -181,7 +181,10 @@ def data(self, index, role): if index.column() == 0: return node.name() elif index.column() == 1: - return str(node.value()) + value = node.value() + if value is None: + value = node.defValue() + return str(value) return None def setData(self, index, value, role=Qt.Qt.EditRole): From 25eaeb73d26e2e252318c5145cf4c1d92b0e8d9d Mon Sep 17 00:00:00 2001 From: cfalcon Date: Thu, 20 Jun 2019 17:41:14 +0200 Subject: [PATCH 040/830] Remove optional parameters checking validateAllExpresion skips the checking for optional parameters. It causes an exception with param repeats macros. Remove it, since it seems not necessary --- .../qtgui/extra_macroexecutor/macroexecutor.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 7bfeee426d..fa908c30a5 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -287,17 +287,14 @@ def validateAllExpresion(self, secValidation=False): message = "" + txt + " " + e[0] problems.append(message) except IndexError: - param_info = macro_params_info[counter-1] - # Skip validation in case of optional parameters - if param_info['default_value'] == Optional: - self.model().setData(self.currentIndex, None) - else: - txt = str(ix.sibling(ix.row(), 0).data()) - problems.append("" + txt + " is missing!") + txt = str(ix.sibling(ix.row(), 0).data()) + problems.append("" + txt + " is missing!") - data = str(ix.data()) - if data != 'None': - self.model().setData(self.currentIndex, 'None') + data = str(ix.data()) + if data != 'None': + self.model().setData(self.currentIndex, 'None') + else: + self.model().setData(self.currentIndex, None) counter += 1 ix = self.getIndex() self.currentIndex = ix From 8ebc0290b4cacd9b0aa2e9948c91ba2e031be8e5 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Thu, 20 Jun 2019 17:49:30 +0200 Subject: [PATCH 041/830] Revert changes in defValue method SingleParamNode.defValue method returns a str since 93ee0dc9. Revert it. --- src/sardana/taurus/core/tango/sardana/macro.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index ef89e3acde..7a8dd5a65a 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -511,9 +511,7 @@ def setValue(self, value): self._value = value def defValue(self): - if self._defValue is None: - return None - return str(self._defValue) + return self._defValue def setDefValue(self, defValue): if defValue == "None": @@ -532,7 +530,7 @@ def toXml(self): # set value attribute only if it is different than the default value # the server will assign the default value anyway. if value is not None or str(value).lower() != 'none': - if value != self.defValue(): + if value != str(self.defValue()): paramElement.set("value", value) return paramElement From 7eb9e78c4ba2c13892f7643f0ac0aebf5d20f91e Mon Sep 17 00:00:00 2001 From: cfalcon Date: Thu, 20 Jun 2019 17:53:25 +0200 Subject: [PATCH 042/830] Treat optional parameters as None ParamEditorModel.data method returns None instead of Optional dictionary for optional parameters --- .../qtgui/extra_macroexecutor/macroparameterseditor/model.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py index 8d9c2b5ce5..24abaa608a 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py @@ -34,6 +34,7 @@ from sardana.taurus.core.tango.sardana import macro from sardana.taurus.qt.qtgui.extra_macroexecutor import globals +from sardana.macroserver.msparameter import Optional class ParamEditorModel(Qt.QAbstractItemModel): @@ -184,6 +185,9 @@ def data(self, index, role): value = node.value() if value is None: value = node.defValue() + if value == Optional: + # TODO: treat optional parameters + value = None return str(value) return None From c3fb9b16fc7eda0632af7b0d7790a2aa6db6f877 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Fri, 21 Jun 2019 11:25:45 +0200 Subject: [PATCH 043/830] Fix bug in fromList SingleParamNode.fromList expects list or str. After reverting the changes in defValue, it returns obj instead of str representation. Fix issue, casting the defValue to str. --- src/sardana/taurus/core/tango/sardana/macro.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 7a8dd5a65a..775cfded84 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -570,7 +570,7 @@ def fromList(self, v): Empty list indicates default value.""" if isinstance(v, list): if len(v) == 0: - v = self.defValue() + v = str(self.defValue()) elif not isinstance(self.parent(), RepeatNode): msg = "Only members of repeat parameter allow list values" raise ValueError(msg) From c08cf0e54add965df204c3604c3eca70e1f44b5d Mon Sep 17 00:00:00 2001 From: teresanunez Date: Fri, 21 Jun 2019 11:44:27 +0200 Subject: [PATCH 044/830] Update how_to_release.md --- doc/how_to_release.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/how_to_release.md b/doc/how_to_release.md index 3879e82a8f..9491488560 100644 --- a/doc/how_to_release.md +++ b/doc/how_to_release.md @@ -43,8 +43,10 @@ tested. participating institute. The master branch is protected, so the reviews need to be cleared (or dismissed with an explanation) before the release can be merged. -6. Perform manual tests (see checklist below). You may use the CI artifacts - (e.g., from appveyor) and post the results in the comments of the PR. +6. Create issues for the manual tests on the different operating systems (Debian, CentOS, Windows, + and openSuse) with the check list [below](https://github.com/sardana-org/sardana/blob/develop/doc/how_to_release.md). + You may use the CI artifacts (e.g., from appveyor) and post the results in the comments + of the corresponding issue. 7. Once all reviews are cleared, update the date of the release in the CHANGELOG.md, merge the PR and tag in master. 8. Check that travis-ci correctly uploaded to PyPI (triggered by a tag push) From 20647bd5ef718adf31b79c9624169814a03ea665 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Fri, 21 Jun 2019 15:51:12 +0200 Subject: [PATCH 045/830] Remove unnecessary code The check seems to be unnecessary and causes troubles with ParamRepeats. Avoid to get macor_params_info since it is not used. --- .../qt/qtgui/extra_macroexecutor/macroexecutor.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index fa908c30a5..0c7a0921f6 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -265,17 +265,7 @@ def validateAllExpresion(self, secValidation=False): self.currentIndex = ix counter = 1 - # Get the parameters information to check if there are optional - # paramters - ms_obj = self.getModelObj() - macro_obj = ms_obj.getElementInfo(mlist[0]) - macro_params_info = None - if macro_obj is not None: - macro_params_info = macro_obj.parameters - while not ix == Qt.QModelIndex(): - if macro_params_info is None: - break try: propValue = mlist[counter] try: From 4b24003ebbb7ecedaaefb2b857b81330bfcd755c Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 26 Jun 2019 11:28:07 +0200 Subject: [PATCH 046/830] Delete how_to_release.md Moved to WIKI --- doc/how_to_release.md | 175 ------------------------------------------ 1 file changed, 175 deletions(-) delete mode 100644 doc/how_to_release.md diff --git a/doc/how_to_release.md b/doc/how_to_release.md deleted file mode 100644 index 9491488560..0000000000 --- a/doc/how_to_release.md +++ /dev/null @@ -1,175 +0,0 @@ -# How to release - -This is a guide for sardana release managers: it details the steps for making -an official release, including a checklist of stuff that should be manually -tested. - -## The release process - -1. During all the development, use the Milestones to keep track of the intended - release for each issue. -2. Previous to the release deadline, re-check the open issues/PR and update - the assignation issues/PR to the release milestone. Request feedback from - the devel community. -3. Work to close all the PR/issues remaining open in the milestone. This can - be either done in develop or in a release branch called `release-XXX` - (where `XXX` is the milestone name, e.g. `Jul17`). If a release branch is - used, `develop` is freed to continue with integrations that may not be - suitable for this release. On the other hand, it adds a bit more work - because the release-related PRs (which are done against the `release-XXX` - branch), may need to be also merged to develop. - Note: the `release-XXX` branch *can* live in the sardana-org repo or on a - personal fork (in which case you should do step 4.v **now** to allow other - integrators to push directly to it). -4. Create the release branch if it was not done already in the previous step - and: - 1. Review and update the CHANGELOG.md if necessary. See [this](http://keepachangelog.com). - 2. Bump version using `bumpversion && bumpversion release` - (use [semver](http://semver.org/) criteria to choose amongst `major`, - `minor` or `patch`. OPTIONAL: Sardana depends on Taurus, and the - required version of Taurus may need to be bumped as well. Taurus and - other dependencies are specified in: `setup.py` (requires list of - strings) and `src/sardana/requirements.py` (`__requires__` dictionary - and taurus.core value). - 3. The version numbers used in the man pages of the Sardana scripts are - bumped (you may use `taurus/doc/makeman` script executing it from the - doc directory e.g. `sardana/doc`) and committing the changes. - There is a known [problem with the spock version number](https://github.com/sardana-org/sardana/issues/518). - 4. In the code use version number instead of milestone in deprecation - warnings (if any) e.g. replace *Jul18* with *2.5.0*. - 5. Create a PR to merge the `release-XXX` against the **`master`** branch - of the sardana-org repo -5. Request reviews in the PR from at least one integrator from each - participating institute. The master branch is protected, so the reviews need - to be cleared (or dismissed with an explanation) before the release can be - merged. -6. Create issues for the manual tests on the different operating systems (Debian, CentOS, Windows, - and openSuse) with the check list [below](https://github.com/sardana-org/sardana/blob/develop/doc/how_to_release.md). - You may use the CI artifacts (e.g., from appveyor) and post the results in the comments - of the corresponding issue. -7. Once all reviews are cleared, update the date of the release in the - CHANGELOG.md, merge the PR and tag in master. -8. Check that travis-ci correctly uploaded to PyPI (triggered by a tag push) - - Previously: - ~~Release to PyPI **from a clean checkout** and using [twine](https://github.com/pypa/twine):~~ - >``` - >cd /tmp - >git clone https://github.com/sardana-org/sardana.git -b - >cd sardana - >python setup.py sdist bdist_wheel - >twine upload dist/* - >``` -9. Merge also the `release-XXX` branch into develop, and bump the version of - develop with `bumpversion patch` -10. Complete GitHub release (upload artifacts, edit text) -11. Create news in www.tango-controls.org - 1. On the News page click on Submit a news and fill up the form (if it doesn't work, try opening in new tab): - * Title: New Release Of Sardana X.X.X (Jan|JulXX) - * Ilustration: sardana or official logo (use png) - * Summary: short summary of the news (do not include the whole changelog here..) - * Categories: Release - 2. After submitting click on Modify this content text of the area \<\\> and provide detailes of the release e.g. changelog. -12. Notify mailing lists (sardana-users@lists.sourceforge.net, sardana-devel@lists.sourceforge.net, info@tango-controls.org) -13. Close the milestone for current release, and open a new one (there should be milestones for next 2 releases open). - -## Manual test checklist - -This is a check-list of manual tests. It is just orientative. Expand it -at will. This list assumes a clean environment with all Sardana dependencies -already installed and access to a Tango system with the TangoTest DS running. - -Hint: this list can be used as a template to be copy-pasted on a release manual test issues - -### Installation -- [ ] Install Sardana (on Linux from the tar.gz : `pip install ` - and on Windows from MSI) - **Note:** On openSuse 11.1 there are problems with pip, try `python setup - .py install` - -### Create testing environment and run testsuite -- [ ] Start Pool demo2. In a console do `Pool demo2`. -- [ ] Start MacroServer demo2 and connect to the Pool demo2. - In another console do: `MacroServer demo2` -- [ ] Set MacroServer's MacroPath to point to the macro examples. - In another IPython console do: - `PyTango.DeviceProxy('macroserver/demo2/1').put_property({'MacroPath':'/macroserver/macros/examples'})` - **Note:** Remember to use OS path separator e.g. '/' on Linux and '\' on - Windows -- [ ] Restart MacroServer e.g. Ctrl+C in the MacroServer's console and - start it again. -- [ ] Create spock profile demo2. In another console do `spock --profile=demo2` -- [ ] In spock run `sar_demo` macro. -- [ ] Edit `/sardanacustomsettings.py` - to point to the demo2 door e.g. `UNITTEST_DOOR_NAME = "door/demo2/1"` -- [ ] Run testsuite. In another console do `sardanatestsuite` - **Note:** On openSuse 11.1 and Windows there are known problems with - testsuite. Check previous release comments. - -### Test Sardana using Spock and expconf -- [ ] Test interactive macros from spock e.g. `ask_for_moveable`, `ask_peak` - **Note**: On Windows there are known bugs. -- [ ] Execute `umvr` macro and verify that the position updates arrives. -- [ ] In expconf configure scan files by setting ScanDir to: `/tmp/` on Linux - `C:\Users\\tmp` on Windows and ScanFile to: `demo1.h5, demo1.dat`. -- [ ] Configure online plot to show counters: On expconf GUI select for all - the counter channels, Plot Type 'Spectrum' and Plot Axes '\' -- [ ] Configure snapshot group: with a motor and the `sys/tg_test/1/ampli` - attribute. -- [ ] Add the `sys/tg_test/1/double_scalar` attribute to the measurement - group. -- [ ] Open online plot (This should ask to enable JsonRecorder, set it to true. Otherwise enable it in spock: `senv JsonRecorder True`). -- [ ] Run step scan -- [ ] Verify that records appear in spock output. -- [ ] Verify that records were stored in scan files. -- [ ] Verify that records were plotted on the online plot -- [ ] Run `showscan` and access to the last scan data. -- [ ] With `edmac` modify existing macro: `ask_peak` and run it to verify that the change - was applied. -- [ ] With `edmac` create a new macro in a new macro library: - `edmac my_macro /macroserver/macros/examples/my_macros.py` - and run it. - **Note:** Remember to use OS path separator e.g. '/' on Linux and '\' on - Windows - -### Test Sardana with TaurusGUI - -- [ ] Create the GUI using this [guide](https://sourceforge.net/p/sardana/wiki/Howto-GUI_creation) - -#### PMTV (PoolMotorTaurusValue) -- [ ] Move motors from the slit panel in absolute and relative modes. -- [ ] Show expert view. -- [ ] Show compact mode. - -#### macroexecutor -- [ ] Execute `ascan` macro -- [ ] Pause it in the middel and resume -- [ ] Abort it -- [ ] Add it to favorites -- [ ] Run `lsm` macro -- [ ] Execute `ascan` from favorites -- [ ] Run `lsmac` macro -- [ ] Execute `ascan` from history -- [ ] Edit `dscan` macro in spock yellow line and run it -- [ ] Restart macroexecutor application -- [ ] Run `lsm` from history -- [ ] Run `ascan` from favorites - -#### sequencer -- [ ] Add `ascan` macro to the sequence -- [ ] Add `lsct` macro as a `post-acq` hook of `ascan` -- [ ] Add `dscan` macro to the sequence -- [ ] Run the sequence -- [ ] Save sequence to a file -- [ ] Start new sequence -- [ ] Load sequence from a files -- [ ] Run the loaded sequence - -#### sardanaeditor -**Note:** There are known bugs on CentOS and Windows -- [ ] Open sardanaeditor with macroserver name as argument. -- [ ] Browse macro libraries and open an existing macro. -- [ ] Edit existing macro and save & apply chaneges. -- [ ] Execute macro to see if changes were aplied. -- [ ] Create a new macro using template. -- [ ] Execute the newly created macro. From 645bba625d057a576afb9de2e55a5a167ebedf21 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 27 Jun 2019 10:25:53 +0200 Subject: [PATCH 047/830] Remove use of ParamRepeat --- src/sardana/macroserver/macros/env.py | 39 ++++++++-------- .../macroserver/macros/examples/parameters.py | 8 ++-- .../macroserver/macros/examples/scans.py | 17 ++++--- .../macroserver/macros/examples/submacros.py | 5 +-- src/sardana/macroserver/macros/expert.py | 31 ++++++------- src/sardana/macroserver/macros/lists.py | 7 ++- src/sardana/macroserver/macros/scan.py | 19 ++++---- src/sardana/macroserver/macros/standard.py | 45 ++++++++----------- 8 files changed, 80 insertions(+), 91 deletions(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index ad7e5a7dfe..0a3b5760e4 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -30,7 +30,7 @@ __docformat__ = 'restructuredtext' from taurus.console.list import List -from sardana.macroserver.macro import Macro, Type, ParamRepeat +from sardana.macroserver.macro import Macro, Type from sardana.macroserver.msexception import UnknownEnv ########################################################################## @@ -128,8 +128,8 @@ class lsenv(Macro): """Lists the environment in alphabetical order""" param_def = [ - ['macro_list', - ParamRepeat(['macro', Type.MacroClass, None, 'macro name'], min=0), + ['macro_list', [['macro', Type.MacroClass, None, 'macro name'], + {'min': 0}], None, 'List of macros to show environment'], ] @@ -174,17 +174,19 @@ def reprValue(self, v, max=54): class senv(Macro): """Sets the given environment variable to the given value""" - param_def = [['name', Type.Env, None, - 'Environment variable name. Can be one of the following:\n' - ' - - global variable\n' - ' - . - variable value for a specific door\n' - ' - . - variable value for a specific macro\n' - ' - .. - variable value for a specific macro running on a specific door'], - ['value_list', - ParamRepeat(['value', Type.String, None, - 'environment value item'], min=1), - None, 'value(s). one item will eval to a single element. More than one item will eval to a tuple of elements'], - ] + param_def = [ + ['name', Type.Env, None, + 'Environment variable name. Can be one of the following:\n' + ' - - global variable\n' + ' - . - variable value for a specific door\n' + ' - . - variable value for a specific macro\n' + ' - .. - variable value for a ' + 'specific macro running on a specific door'], + ['value_list', [['value', Type.String, None, + 'environment value item'], {'min': 1}], + None, 'value(s). one item will eval to a single element. More than ' + 'one item will eval to a tuple of elements'], + ] def run(self, env, value): if len(value) == 1: @@ -199,9 +201,8 @@ def run(self, env, value): class usenv(Macro): """Unsets the given environment variable""" param_def = [ - ['environment_list', - ParamRepeat( - ['env', Type.Env, None, 'Environment variable name'], min=1), + ['environment_list', [['env', Type.Env, None, + 'Environment variable name'], {'min': 1}], None, 'List of environment items to be removed'], ] @@ -379,8 +380,8 @@ class defgh(Macro): param_def = [ ['macro_name', Type.String, None, ('Macro name with parameters. ' 'Ex.: "mv exp_dmy01 10"')], - ['hookpos_list', - ParamRepeat(['position', Type.String, None, 'macro name'], min=1), + ['hookpos_list', [['position', Type.String, None, 'macro name'], + {'min': 1}], None, 'List of positions where the hook has to be executed'], ] diff --git a/src/sardana/macroserver/macros/examples/parameters.py b/src/sardana/macroserver/macros/examples/parameters.py index 681aed83b8..47326235a9 100644 --- a/src/sardana/macroserver/macros/examples/parameters.py +++ b/src/sardana/macroserver/macros/examples/parameters.py @@ -23,7 +23,7 @@ """This module contains macros that demonstrate the usage of macro parameters""" -from sardana.macroserver.macro import Macro, Type, ParamRepeat +from sardana.macroserver.macro import Macro, Type __all__ = ["pt0", "pt1", "pt2", "pt3", "pt3d", "pt4", "pt5", "pt6", "pt7", "pt7d1", "pt7d2", "pt8", "pt9", "pt10", "pt11", "pt12", "pt13", @@ -261,9 +261,9 @@ class pt9(Macro): """ param_def = [ - ['m_p_pair', - ParamRepeat(['motor', Type.Motor, None, 'Motor to move'], - ['pos', Type.Float, None, 'Position to move to'], min=1, max=2), + ['m_p_pair', [['motor', Type.Motor, None, 'Motor to move'], + ['pos', Type.Float, None, 'Position to move to'], + {'min': 1, 'max': 2}], None, 'List of motor/position pairs'], ] diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index a263240ad5..c629beeae8 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -38,7 +38,7 @@ import numpy -from sardana.macroserver.macro import Macro, Hookable, Type, ParamRepeat +from sardana.macroserver.macro import Macro, Hookable, Type from sardana.macroserver.scan import * @@ -294,8 +294,9 @@ class regscan(Macro): ['integ_time', Type.Float, None, 'Integration time'], ['start_pos', Type.Float, None, 'Start position'], ['step_region', - ParamRepeat(['next_pos', Type.Float, None, 'next position'], - ['region_nr_intervals', Type.Float, None, 'Region number of intervals']), + [['next_pos', Type.Float, None, 'next position'], + ['region_nr_intervals', Type.Float, None, + 'Region number of intervals']], None, 'List of tuples: (next_pos, region_nr_intervals'] ] @@ -355,8 +356,9 @@ class reg2scan(Macro): ['integ_time', Type.Float, None, 'Integration time'], ['start_pos', Type.Float, None, 'Start position'], ['step_region', - ParamRepeat(['next_pos', Type.Float, None, 'next position'], - ['region_nr_intervals', Type.Float, None, 'Region number of intervals']), + [['next_pos', Type.Float, None, 'next position'], + ['region_nr_intervals', Type.Float, None, + 'Region number of intervals']], None, 'List of tuples: (next_pos, region_nr_intervals'] ] @@ -420,8 +422,9 @@ class reg3scan(Macro): ['integ_time', Type.Float, None, 'Integration time'], ['start_pos', Type.Float, None, 'Start position'], ['step_region', - ParamRepeat(['next_pos', Type.Float, None, 'next position'], - ['region_nr_intervals', Type.Float, None, 'Region number of intervals']), + [['next_pos', Type.Float, None, 'next position'], + ['region_nr_intervals', Type.Float, None, + 'Region number of intervals']], None, 'List of tuples: (next_pos, region_nr_intervals'] ] diff --git a/src/sardana/macroserver/macros/examples/submacros.py b/src/sardana/macroserver/macros/examples/submacros.py index d447c7070c..c887c2d422 100644 --- a/src/sardana/macroserver/macros/examples/submacros.py +++ b/src/sardana/macroserver/macros/examples/submacros.py @@ -29,7 +29,7 @@ __docformat__ = 'restructuredtext' -from sardana.macroserver.macro import Macro, Type, ParamRepeat +from sardana.macroserver.macro import Macro, Type #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # First example: @@ -47,8 +47,7 @@ def run(self): class call_wm(Macro): param_def = [ - ['motor_list', - ParamRepeat(['motor', Type.Motor, None, 'Motor to move']), + ['motor_list', [['motor', Type.Motor, None, 'Motor to move']], None, 'List of motor to show'], ] diff --git a/src/sardana/macroserver/macros/expert.py b/src/sardana/macroserver/macros/expert.py index 102be48ba7..5b38f46f5e 100644 --- a/src/sardana/macroserver/macros/expert.py +++ b/src/sardana/macroserver/macros/expert.py @@ -38,7 +38,7 @@ from sardana.macroserver.msexception import UnknownMacroLibrary from sardana.macroserver.msparameter import WrongParam -from sardana.macroserver.macro import Macro, Type, ParamRepeat, Table, LibraryError +from sardana.macroserver.macro import Macro, Type, Table, LibraryError ########################################################################## # @@ -70,9 +70,8 @@ class defmeas(Macro): param_def = [ ['name', Type.String, None, 'Measurement group name'], - ['channel_list', - ParamRepeat(['channel', Type.String, None, - 'Measurement Channel'],), + ['channel_list', [['channel', Type.String, None, + 'Measurement Channel']], None, 'List of measurement channels'], ] @@ -95,10 +94,9 @@ class udefmeas(Macro): """Deletes existing measurement groups""" param_def = [ - ['mntgrps', - ParamRepeat(['mntgrp', Type.MeasurementGroup, None, + ['mntgrps', [['mntgrp', Type.MeasurementGroup, None, 'Measurement group name'], - min=1), + {'min': 1}], None, 'List of measurement group names'], ] def run(self, mntgrps): @@ -143,8 +141,8 @@ class udefelem(Macro): """Deletes existing elements""" param_def = [ - ['elements', - ParamRepeat(['element', Type.Element, None, 'element name'], min=1), + ['elements', [['element', Type.Element, None, 'element name'], + {'min': 1}], None, 'List of element(s) name'], ] @@ -174,9 +172,8 @@ class defctrl(Macro): param_def = [['class', Type.ControllerClass, None, 'controller class'], ['name', Type.String, None, 'new controller name'], - ['roles_props', - ParamRepeat(['role_prop', Type.String, None, - 'a role or property item'], min=0), + ['roles_props', [['role_prop', Type.String, None, + 'a role or property item'], {'min': 0}], None, 'roles and/or properties']] def run(self, ctrl_class, name, props): @@ -189,9 +186,8 @@ class udefctrl(Macro): """Deletes existing controllers""" param_def = [ - ['controllers', - ParamRepeat(['controller', Type.Controller, None, 'controller name'], - min=1), + ['controllers', [['controller', Type.Controller, None, + 'controller name'], {'min': 1}], None, 'List of controller(s) name(s)'], ] def run(self, controllers): @@ -218,9 +214,8 @@ class send2ctrl(Macro): """Sends the given data directly to the controller""" param_def = [['controller', Type.Controller, None, 'Controller name'], - ['data', - ParamRepeat(['string item', Type.String, - None, 'a string item'],), + ['data', [['string item', Type.String, None, + 'a string item']], None, 'data to be sent']] def run(self, controller, data): diff --git a/src/sardana/macroserver/macros/lists.py b/src/sardana/macroserver/macros/lists.py index 9ce53ac90a..5fcfb2b618 100644 --- a/src/sardana/macroserver/macros/lists.py +++ b/src/sardana/macroserver/macros/lists.py @@ -34,7 +34,7 @@ from taurus.console import Alignment from taurus.console.list import List -from sardana.macroserver.macro import Macro, Type, ParamRepeat, ViewOption +from sardana.macroserver.macro import Macro, Type, ViewOption Left, Right, HCenter = Alignment.Left, Alignment.Right, Alignment.HCenter @@ -47,9 +47,8 @@ class _ls(Macro): # TODO: duplication of the default value definition is a workaround # for #427. See commit message cc3331a for more details. param_def = [ - ['filter', - ParamRepeat(['filter', Type.String, ".*", - 'a regular expression filter'], min=1), + ['filter', [['filter', Type.String, ".*", + 'a regular expression filter'], {'min': 1}], [".*"], 'a regular expression filter'], ] diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 10d04c8b9c..eea033cc40 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -46,8 +46,7 @@ from taurus.core.util import SafeEvaluator from sardana.macroserver.msexception import UnknownEnv -from sardana.macroserver.macro import Hookable, Macro, Type, ParamRepeat, \ - Table, List +from sardana.macroserver.macro import Hookable, Macro, Type, Table, List from sardana.macroserver.scan.gscan import SScan, CTScan, HScan, \ MoveableDesc, CSScan, TScan from sardana.util.motion import MotionPath @@ -491,9 +490,9 @@ class amultiscan(aNscan, Macro): param_def = [ ['motor_start_end_list', - ParamRepeat(['motor', Type.Moveable, None, 'Moveable to move'], - ['start', Type.Float, None, 'Starting position'], - ['end', Type.Float, None, 'Final position']), + [['motor', Type.Moveable, None, 'Moveable to move'], + ['start', Type.Float, None, 'Starting position'], + ['end', Type.Float, None, 'Final position']], None, 'List of motor, start and end positions'], ['nr_interv', Type.Integer, None, 'Number of scan intervals'], ['integ_time', Type.Float, None, 'Integration time'] @@ -524,9 +523,9 @@ class dmultiscan(dNscan, Macro): param_def = [ ['motor_start_end_list', - ParamRepeat(['motor', Type.Moveable, None, 'Moveable to move'], - ['start', Type.Float, None, 'Starting position'], - ['end', Type.Float, None, 'Final position']), + [['motor', Type.Moveable, None, 'Moveable to move'], + ['start', Type.Float, None, 'Starting position'], + ['end', Type.Float, None, 'Final position']], None, 'List of motor, start and end positions'], ['nr_interv', Type.Integer, None, 'Number of scan intervals'], ['integ_time', Type.Float, None, 'Integration time'] @@ -861,8 +860,8 @@ class fscan(Macro, Hookable): ['indepvars', Type.String, None, 'Independent Variables'], ['integ_time', Type.String, None, 'Integration time'], ['motor_funcs', - ParamRepeat(['motor', Type.Moveable, None, 'motor'], - ['func', Type.String, None, 'curve defining path']), + [['motor', Type.Moveable, None, 'motor'], + ['func', Type.String, None, 'curve defining path']], None, 'List of motor and path curves'] ] diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 98c1bdb4f0..f0730b7e29 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -38,8 +38,8 @@ import PyTango from PyTango import DevState -from sardana.macroserver.macro import Macro, macro, Type, ParamRepeat, \ - ViewOption, iMacro, Hookable +from sardana.macroserver.macro import Macro, macro, Type, ViewOption, \ + iMacro, Hookable from sardana.macroserver.msexception import StopException, UnknownEnv from sardana.macroserver.scan.scandata import Record from sardana.macroserver.macro import Optional @@ -55,8 +55,7 @@ class _wm(Macro): """Show motor positions""" param_def = [ - ['motor_list', - ParamRepeat(['motor', Type.Moveable, None, 'Motor to move']), + ['motor_list', [['motor', Type.Moveable, None, 'Motor to move']], None, 'List of motor to show'], ] @@ -146,8 +145,7 @@ class _wum(Macro): """Show user motor positions""" param_def = [ - ['motor_list', - ParamRepeat(['motor', Type.Moveable, None, 'Motor to move']), + ['motor_list', [['motor', Type.Moveable, None, 'Motor to move']], None, 'List of motor to show'], ] @@ -206,9 +204,8 @@ class wa(Macro): # TODO: duplication of the default value definition is a workaround # for #427. See commit message cc3331a for more details. param_def = [ - ['filter', - ParamRepeat(['filter', Type.String, '.*', - 'a regular expression filter'], min=1), + ['filter', [['filter', Type.String, '.*', + 'a regular expression filter'], {'min': 1}], ['.*'], 'a regular expression filter'], ] @@ -239,9 +236,8 @@ class pwa(Macro): # TODO: duplication of the default value definition is a workaround # for #427. See commit message cc3331a for more details. param_def = [ - ['filter', - ParamRepeat(['filter', Type.String, '.*', - 'a regular expression filter'], min=1), + ['filter', [['filter', Type.String, '.*', + 'a regular expression filter'], {'min': 1}], ['.*'], 'a regular expression filter'], ] @@ -321,9 +317,8 @@ class wm(Macro): """Show the position of the specified motors.""" param_def = [ - ['motor_list', - ParamRepeat(['motor', Type.Moveable, None, - 'Motor to see where it is']), + ['motor_list', [['motor', Type.Moveable, None, + 'Motor to see where it is']], None, 'List of motor to show'], ] @@ -412,9 +407,8 @@ class wum(Macro): """Show the user position of the specified motors.""" param_def = [ - ['motor_list', - ParamRepeat(['motor', Type.Moveable, None, - 'Motor to see where it is']), + ['motor_list', [['motor', Type.Moveable, None, + 'Motor to see where it is']], None, 'List of motor to show'], ] @@ -449,8 +443,7 @@ class pwm(Macro): """Show the position of the specified motors in a pretty table""" param_def = [ - ['motor_list', - ParamRepeat(['motor', Type.Moveable, None, 'Motor to move']), + ['motor_list', [['motor', Type.Moveable, None, 'Motor to move']], None, 'List of motor to show'], ] @@ -463,8 +456,8 @@ class mv(Macro): param_def = [ ['motor_pos_list', - ParamRepeat(['motor', Type.Moveable, None, 'Motor to move'], - ['pos', Type.Float, None, 'Position to move to']), + [['motor', Type.Moveable, None, 'Motor to move'], + ['pos', Type.Float, None, 'Position to move to']], None, 'List of motor/position pairs'], ] @@ -547,8 +540,8 @@ class mvr(Macro): param_def = [ ['motor_disp_list', - ParamRepeat(['motor', Type.Moveable, None, 'Motor to move'], - ['disp', Type.Float, None, 'Relative displacement']), + [['motor', Type.Moveable, None, 'Motor to move'], + ['disp', Type.Float, None, 'Relative displacement']], None, 'List of motor/displacement pairs'], ] @@ -839,8 +832,8 @@ def run(self, timer): % timer) -@macro([['message', ParamRepeat(['message_item', Type.String, None, - 'message item to be reported']), None, +@macro([['message',[['message_item', Type.String, None, + 'message item to be reported']], None, 'message to be reported']]) def report(self, message): """Logs a new record into the message report system (if active)""" From 55c2ffea2e1d6f42d99cce067ba42b05e18281ce Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 27 Jun 2019 11:12:50 +0200 Subject: [PATCH 048/830] Fix flake8 errors --- src/sardana/macroserver/macros/env.py | 2 +- src/sardana/macroserver/macros/standard.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 0a3b5760e4..64d6f1ad92 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -183,7 +183,7 @@ class senv(Macro): ' - .. - variable value for a ' 'specific macro running on a specific door'], ['value_list', [['value', Type.String, None, - 'environment value item'], {'min': 1}], + 'environment value item'], {'min': 1}], None, 'value(s). one item will eval to a single element. More than ' 'one item will eval to a tuple of elements'], ] diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index f0730b7e29..b552c2b7cd 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -832,8 +832,8 @@ def run(self, timer): % timer) -@macro([['message',[['message_item', Type.String, None, - 'message item to be reported']], None, +@macro([['message', [['message_item', Type.String, None, + 'message item to be reported']], None, 'message to be reported']]) def report(self, message): """Logs a new record into the message report system (if active)""" From fbd7bbc6ac1779030796d1b69c4828137d10c16f Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 3 Jul 2019 11:15:25 +0200 Subject: [PATCH 049/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7eff671c4c..e02edb9224 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,8 @@ This file follows the formats and conventions from [keepachangelog.com] remotelly (#1099) * Pop-up message when expconf configuration changed externally (#1094) * Remove circlular references between the macro object and the FIO recorder (#1121) +* Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` + to avoid problems when using `builtins` from `future` (#1082) ### Deprecated From c97ecaf835407f4f77bd26d613276c3e40a58c3f Mon Sep 17 00:00:00 2001 From: Antonio Milan Otero Date: Wed, 3 Jul 2019 14:11:43 +0200 Subject: [PATCH 050/830] Bump version 2.8.0 to 2.8.1-alpha --- .bumpversion.cfg | 2 +- src/sardana/release.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 919226ed95..4631c0ffd9 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,7 +3,7 @@ commit = True message = Bump version {current_version} to {new_version} tag = False tag_name = {new_version} -current_version = 2.8.0 +current_version = 2.8.1-alpha parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? serialize = {major}.{minor}.{patch}-{release} diff --git a/src/sardana/release.py b/src/sardana/release.py index d1a24465da..1f2354cbfc 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = '2.8.0' +version = '2.8.1-alpha' # generate version_info and revision (**deprecated** since v 2.1.2--alpha). if '-' in version: From 1f05860d241a42cc4bd549140c91fe316fe678e9 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 3 Jul 2019 17:42:46 +0200 Subject: [PATCH 051/830] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc4b08fef9..1bd7cb2e89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ This file follows the formats and conventions from [keepachangelog.com] ## [Unreleased] +### Fixed + +* Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` + to avoid problems when using `builtins` from `future` (#1082) + ## [2.8.0] 2019-07-01 ### Added @@ -79,8 +84,6 @@ This file follows the formats and conventions from [keepachangelog.com] remotelly (#1099) * Pop-up message when expconf configuration changed externally (#1094) * Remove circlular references between the macro object and the FIO recorder (#1121) -* Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` - to avoid problems when using `builtins` from `future` (#1082) ### Deprecated From bdbf156c3b59926f07a3d0d9514f3fe2b4f95ba0 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 4 Jul 2019 16:43:32 +0200 Subject: [PATCH 052/830] Add todo on SingleParamNode.setValue usage --- src/sardana/taurus/core/tango/sardana/macro.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 775cfded84..72c633570c 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -483,7 +483,12 @@ def setMax(self, max): class SingleParamNode(ParamNode): - """Single parameter class.""" + """Single parameter class. + + .. todo: All the usages of setValue are converting the + values to str before calling the setter. + This most probably should not be the case - to be fixed + """ def __init__(self, parent=None, param=None): ParamNode.__init__(self, parent, param) From 9ea3b041491e776cd674e1b62700ede76037b536 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 4 Jul 2019 16:44:57 +0200 Subject: [PATCH 053/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bd7cb2e89..5c18f34408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This file follows the formats and conventions from [keepachangelog.com] ### Fixed +* Fix default macro parameter values in macroexecutor (#1153) * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) From e1a967ee557a764dc14328be8ea810da8b70a924 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 12:53:47 +0200 Subject: [PATCH 054/830] 2to3: asserts --- src/sardana/taurus/core/tango/sardana/test/test_macro.py | 2 +- src/sardana/test/test_sardanavalue.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_macro.py b/src/sardana/taurus/core/tango/sardana/test/test_macro.py index 0f6090537f..69c1a3df42 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_macro.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_macro.py @@ -105,7 +105,7 @@ def _validateXML(self, macronode_xml, expected_xml): msg = "XML encodings are not equal" # TODO: check why macronode_str has an extra whitespace charactger # at the end. strips should not be necessary - self.assertEquals(expected_str.strip(), macronode_str.strip(), msg) + self.assertEqual(expected_str.strip(), macronode_str.strip(), msg) def verifyXML(self, macro_name, param_def, param_str, expected_xml_rep): """ diff --git a/src/sardana/test/test_sardanavalue.py b/src/sardana/test/test_sardanavalue.py index 08a287bb5c..9ffc3a009d 100644 --- a/src/sardana/test/test_sardanavalue.py +++ b/src/sardana/test/test_sardanavalue.py @@ -72,7 +72,7 @@ def testSardanaValueWithExceptionInfo(self): self.assertEqual(sar_val.error, True, 'The error attribute should be True.') - self.assertRegexpMatches(representation, ".*.*", + self.assertRegex(representation, ".*.*", 'The SardanaValue representation does not contain .') def testSardanaValueWithNoExceptionInfo(self): @@ -84,7 +84,7 @@ def testSardanaValueWithNoExceptionInfo(self): sar_val = SardanaValue(value=value) returned_string = sar_val.__repr__() - self.assertRegexpMatches(returned_string, repr(value), + self.assertRegex(returned_string, repr(value), 'The SardanaValue representation does not contain its value') self.assertEqual(sar_val.error, False, From 3c9886420f895ff6bca66da6a55712cf535e9949 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 12:56:54 +0200 Subject: [PATCH 055/830] 2to3: basestring --- src/sardana/macroserver/macros/standard.py | 2 +- src/sardana/macroserver/msmacromanager.py | 2 +- src/sardana/taurus/qt/qtgui/extra_sardana/startup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 98c1bdb4f0..b42c82f731 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -957,7 +957,7 @@ def run(self, ScanFilePath_list, ScanID): ScanDir = self.getEnv('ScanDir') except UnknownEnv: ScanDir = '' - if not (isinstance(ScanDir, basestring) and len(ScanDir) > 0): + if not (isinstance(ScanDir, str) and len(ScanDir) > 0): msg = ('Data is not stored until ScanDir is correctly ' 'set! Provide ScanDir with newfile macro: ' '`newfile [/] ` ' diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 5ae6f2c6b7..692729f8b5 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -952,7 +952,7 @@ def getFilterClass(self): except AttributeError: pass else: - if isinstance(log_macro_filter, basestring): + if isinstance(log_macro_filter, str): try: module_name, filter_name = log_macro_filter.rsplit('.', 1) __import__(module_name) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py index 59fe225040..3a03d76fe6 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py @@ -123,7 +123,7 @@ def runfile(filename, args=None, wdir=None): else: verbose = os.environ.get("UMD_VERBOSE", "").lower() == "true" __umd__.run(verbose=verbose) - if args is not None and not isinstance(args, basestring): + if args is not None and not isinstance(args, str): raise TypeError("expected a character buffer object") glbs = globals() if '__ipythonshell__' in glbs: From bd276303420885cebc27b93c7951609c77d736c8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 13:57:22 +0200 Subject: [PATCH 056/830] 2to3: dict --- src/sardana/macroserver/basetypes.py | 2 +- src/sardana/macroserver/macro.py | 2 +- src/sardana/macroserver/macros/discrete.py | 6 +- src/sardana/macroserver/macros/env.py | 8 +-- .../macros/examples/specific_experiments.py | 2 +- src/sardana/macroserver/macros/expert.py | 2 +- src/sardana/macroserver/macros/scan.py | 2 +- src/sardana/macroserver/macros/standard.py | 4 +- .../macroserver/macros/test/test_scanct.py | 8 +-- src/sardana/macroserver/macroserver.py | 24 ++++---- src/sardana/macroserver/msdoor.py | 2 +- src/sardana/macroserver/msenvmanager.py | 10 ++-- src/sardana/macroserver/msmacromanager.py | 22 ++++---- src/sardana/macroserver/msmetamacro.py | 4 +- src/sardana/macroserver/msparameter.py | 18 +++--- src/sardana/macroserver/msrecordermanager.py | 12 ++-- src/sardana/macroserver/mstypemanager.py | 10 ++-- .../macroserver/recorders/examples/dummy.py | 2 +- .../macroserver/recorders/h5storage.py | 6 +- src/sardana/macroserver/recorders/output.py | 2 +- .../macroserver/recorders/sharedmemory.py | 6 +- src/sardana/macroserver/recorders/storage.py | 10 ++-- src/sardana/macroserver/scan/gscan.py | 4 +- .../macroserver/scan/recorder/storage.py | 6 +- src/sardana/macroserver/scan/scandata.py | 6 +- .../macroserver/scan/test/test_recorddata.py | 10 ++-- .../test/test_msrecordermanager.py | 2 +- src/sardana/pool/controller.py | 6 +- src/sardana/pool/pool.py | 12 ++-- src/sardana/pool/poolacquisition.py | 34 +++++------ src/sardana/pool/poolaction.py | 10 ++-- src/sardana/pool/poolbasegroup.py | 16 +++--- src/sardana/pool/poolcontroller.py | 16 +++--- src/sardana/pool/poolcontrollermanager.py | 10 ++-- .../DiscretePseudoMotorController.py | 4 +- .../DummyCounterTimerController.py | 6 +- .../poolcontrollers/DummyMotorController.py | 4 +- .../poolcontrollers/DummyOneDController.py | 4 +- .../poolcontrollers/DummyTwoDController.py | 4 +- .../poolcontrollers/DummyZeroDController.py | 2 +- .../HklPseudoMotorController.py | 10 ++-- src/sardana/pool/poolcontrollers/test/base.py | 4 +- src/sardana/pool/poolextension.py | 2 +- src/sardana/pool/poolgroupelement.py | 4 +- src/sardana/pool/poolinstrument.py | 4 +- src/sardana/pool/poolmeasurementgroup.py | 6 +- src/sardana/pool/poolmetacontroller.py | 14 ++--- src/sardana/pool/poolmonitor.py | 8 +-- src/sardana/pool/poolmotion.py | 16 +++--- src/sardana/pool/poolmotorgroup.py | 4 +- src/sardana/pool/poolpseudocounter.py | 10 ++-- src/sardana/pool/poolpseudomotor.py | 14 ++--- src/sardana/pool/poolsynchronization.py | 2 +- src/sardana/pool/test/base.py | 12 ++-- src/sardana/pool/test/helper.py | 2 +- .../pool/test/test_measurementgroup.py | 2 +- src/sardana/pool/test/util.py | 6 +- src/sardana/sardanacontainer.py | 2 +- src/sardana/sardanadefs.py | 2 +- src/sardana/sardanameta.py | 8 +-- src/sardana/sardanamodulemanager.py | 4 +- src/sardana/spock/inputhandler.py | 2 +- src/sardana/tango/core/SardanaDevice.py | 2 +- src/sardana/tango/core/util.py | 14 ++--- src/sardana/tango/macroserver/Door.py | 6 +- src/sardana/tango/macroserver/MacroServer.py | 4 +- src/sardana/tango/macroserver/test/base.py | 2 +- src/sardana/tango/pool/Controller.py | 8 +-- src/sardana/tango/pool/MeasurementGroup.py | 6 +- src/sardana/tango/pool/Pool.py | 16 +++--- src/sardana/tango/pool/PoolDevice.py | 12 ++-- src/sardana/tango/pool/test/base.py | 8 +-- src/sardana/tango/pool/test/base_sartest.py | 2 +- .../tango/pool/test/test_measurementgroup.py | 16 +++--- .../taurus/core/tango/sardana/macroserver.py | 16 +++--- .../taurus/core/tango/sardana/motion.py | 4 +- src/sardana/taurus/core/tango/sardana/pool.py | 56 +++++++++---------- .../taurus/core/tango/sardana/sardana.py | 16 +++--- .../core/tango/sardana/test/test_pool.py | 6 +- .../qt/qtcore/tango/sardana/macroserver.py | 2 +- .../taurus/qt/qtcore/tango/sardana/model.py | 2 +- .../qt/qtgui/extra_macroexecutor/common.py | 2 +- .../extra_macroexecutor/macroexecutor.py | 2 +- .../sequenceeditor/model.py | 2 +- .../sequenceeditor/sequenceeditor.py | 2 +- .../qt/qtgui/extra_pool/poolioregister.py | 2 +- .../qt/qtgui/extra_sardana/expdescription.py | 10 ++-- .../qt/qtgui/extra_sardana/macrotree.py | 2 +- .../qtgui/extra_sardana/measurementgroup.py | 16 +++--- .../taurus/qt/qtgui/extra_sardana/startup.py | 4 +- .../qt/qtgui/macrolistener/macrolistener.py | 8 +-- src/sardana/tools/config/get_pool_config.py | 20 +++---- src/sardana/tools/config/pexpect23.py | 4 +- src/sardana/tools/config/sardana.py | 32 +++++------ 94 files changed, 381 insertions(+), 381 deletions(-) diff --git a/src/sardana/macroserver/basetypes.py b/src/sardana/macroserver/basetypes.py index 73e2a262a9..b19e583f23 100644 --- a/src/sardana/macroserver/basetypes.py +++ b/src/sardana/macroserver/basetypes.py @@ -141,7 +141,7 @@ def getNonAttrItemList(self): def __build_base_types(): - for sardana_type, info in INTERFACES.items(): + for sardana_type, info in list(INTERFACES.items()): _, doc = info class _I(ElementParamInterface): diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 5f7b8b225c..a29fedf5b8 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -185,7 +185,7 @@ def getAllowedHookHints(self): return self.__class__.hints.get('allowsHooks') def getHints(self): - return self._getHookHintsDict().keys() + return list(self._getHookHintsDict().keys()) def getHooks(self, hint=None): """This will return a list of hooks that have the given hint. Two reserved diff --git a/src/sardana/macroserver/macros/discrete.py b/src/sardana/macroserver/macros/discrete.py index f95467990d..77c34f3124 100644 --- a/src/sardana/macroserver/macros/discrete.py +++ b/src/sardana/macroserver/macros/discrete.py @@ -51,7 +51,7 @@ def get_configuration(self): return data def has_calibration(self): - return all(['set' in self[x].keys() for x in self.keys()]) + return all(['set' in list(self[x].keys()) for x in list(self.keys())]) def add_point(self, label, pos, setpos, dmin, dmax): point = dict() @@ -65,7 +65,7 @@ def add_point(self, label, pos, setpos, dmin, dmax): else: point['set'] = float(setpos) # If point exists, we use current min, max values - if label in self.keys() and math.isinf(dmin) and math.isinf(dmax): + if label in list(self.keys()) and math.isinf(dmin) and math.isinf(dmax): p = self[label] min_pos = point['set'] + p['set'] - p['min'] max_pos = point['set'] + p['set'] - p['max'] @@ -185,7 +185,7 @@ def run(self, pseudo): row_head_str = [] value_list = [] - for k, v in conf.items(): + for k, v in list(conf.items()): row_head_str.append(k) _row_values = [k] for i in col_head_str: diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index ad7e5a7dfe..8905859998 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -56,7 +56,7 @@ class dumpenv(Macro): def run(self): env = self.getGlobalEnv() out = List(['Name', 'Value', 'Type']) - for k, v in env.iteritems(): + for k, v in env.items(): str_v = reprValue(v) type_v = type(v).__name__ out.appendRow([str(k), str_v, type_v]) @@ -71,7 +71,7 @@ class lsvo(Macro): def run(self): vo = self.getViewOptions() out = List(['View option', 'Value']) - for key, value in vo.items(): + for key, value in list(vo.items()): out.appendRow([key, str(value)]) for line in out.genOutput(): @@ -344,10 +344,10 @@ def run(self): name = hook[0] places = hook[1] for place in places: - if place not in default_dict.keys(): + if place not in list(default_dict.keys()): default_dict[place] = [] default_dict[place].append(name) - for pos in default_dict.keys(): + for pos in list(default_dict.keys()): pos_set = 0 for hook in default_dict[pos]: if pos_set: diff --git a/src/sardana/macroserver/macros/examples/specific_experiments.py b/src/sardana/macroserver/macros/examples/specific_experiments.py index 7d0650c9b3..2f209a1938 100644 --- a/src/sardana/macroserver/macros/examples/specific_experiments.py +++ b/src/sardana/macroserver/macros/examples/specific_experiments.py @@ -69,7 +69,7 @@ def prepare(self, start, final, nr_interv, integ_time, **opts): # print "!!!!!", type(self.getInstrument('/instrument/monochromator')), self.getEnv('MonochromatorEnergy', macro_name=self.name) # ElementWithInterface('Instrument','monochromator') - for n, e in self.getElementsWithInterface('Instrument').iteritems(): + for n, e in self.getElementsWithInterface('Instrument').items(): inst = e.getObj() # ,inst.getElements() print n, e.name, inst.getFullName(), type(e), type(inst), type(inst.getPoolObj()) diff --git a/src/sardana/macroserver/macros/expert.py b/src/sardana/macroserver/macros/expert.py index 102be48ba7..880bd248f7 100644 --- a/src/sardana/macroserver/macros/expert.py +++ b/src/sardana/macroserver/macros/expert.py @@ -517,7 +517,7 @@ def run(self, obj): def dump_properties(self, obj): data = obj.serialize() - table = Table([data.values()], row_head_str=data.keys(), + table = Table([list(data.values())], row_head_str=list(data.keys()), row_head_fmt='%*s', col_sep=' = ') self.output("Properties:") self.output("-----------") diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 10d04c8b9c..a18ba9de4b 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -877,7 +877,7 @@ def prepare(self, *args, **opts): self.funcstrings = [item[1] for item in args[2]] globals_lst = [dict(zip(indepvars, values)) - for values in zip(*indepvars.values())] + for values in zip(*list(indepvars.values()))] self.paths = [[SafeEvaluator(globals).eval( func) for globals in globals_lst] for func in self.funcstrings] diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index b42c82f731..df600ec74e 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -82,7 +82,7 @@ def run(self, motor_list): data[name] = [] # get additional motor information (ctrl name & axis) if show_ctrlaxis: - for name, motor in motors.iteritems(): + for name, motor in motors.items(): ctrl_name = self.getController(motor.controller).name axis_nb = str(getattr(motor, "axis")) data[name].extend((ctrl_name, axis_nb)) @@ -90,7 +90,7 @@ def run(self, motor_list): # collect asynchronous replies while len(requests) > 0: req2delete = [] - for name, _id in requests.iteritems(): + for name, _id in requests.items(): motor = motors[name] try: attrs = motor.read_attributes_reply(_id) diff --git a/src/sardana/macroserver/macros/test/test_scanct.py b/src/sardana/macroserver/macros/test/test_scanct.py index b58a8478e2..c200937755 100644 --- a/src/sardana/macroserver/macros/test/test_scanct.py +++ b/src/sardana/macroserver/macros/test/test_scanct.py @@ -66,10 +66,10 @@ def parsingOutputPoints(self, log_output): def orderPointsData(self, data): """A helper method to know if points are ordered based on getData. """ - obtained_nb_points_data = len(data.keys()) + obtained_nb_points_data = len(list(data.keys())) ordered_points_data = 0 for i in range(obtained_nb_points_data - 1): - if int(data.keys()[i + 1]) >= int(data.keys()[i]): + if int(list(data.keys())[i + 1]) >= int(list(data.keys())[i]): ordered_points_data = 1 else: ordered_points_data = 0 @@ -133,11 +133,11 @@ def check_using_data(self, expected_nb_points): # Test data from macro (macro_executor.getData()) data = self.macro_executor.getData() order_points_data = self.utils.orderPointsData(data) - obtained_nb_points_data = len(data.keys()) + obtained_nb_points_data = len(list(data.keys())) msg = ("The ascanct execution did not return any scan point.\n" "Checked using macro data.") - self.assertTrue(len(data.keys()) > 0, msg) + self.assertTrue(len(list(data.keys())) > 0, msg) msg = ("The ascanct execution did not return the expected number of " "points.\nExpected " + str(expected_nb_points) + " points." diff --git a/src/sardana/macroserver/macroserver.py b/src/sardana/macroserver/macroserver.py index ef90eb32f1..30b1be4a04 100644 --- a/src/sardana/macroserver/macroserver.py +++ b/src/sardana/macroserver/macroserver.py @@ -79,7 +79,7 @@ def __init__(self, **kwargs): #: dictionary #: dict<:data:`~sardana.ElementType`, :class:`~sardana.macroserver.macroserver.TypeData`> TYPE_MAP_OBJ = {} -for t, d in TYPE_MAP.items(): +for t, d in list(TYPE_MAP.items()): o = TypeData(type=t, name=d[0], family=d[1], klass=d[2], auto_full_name=d[3]) TYPE_MAP_OBJ[t] = o @@ -293,7 +293,7 @@ def set_pool_names(self, pool_names): :param pool_names: sequence of pool names :type pool_names: seq""" - for pool in self._pools.values(): + for pool in list(self._pools.values()): elements_attr = pool.getAttribute("Elements") elements_attr.removeListener(self.on_pool_elements_changed) @@ -315,7 +315,7 @@ def get_pool_names(self): the list of names of the pools this macro server is connected to :rtype: seq""" - return self._pools.keys() + return list(self._pools.keys()) def get_pool(self, pool_name): """Returns the device pool object corresponding to the given device name @@ -331,7 +331,7 @@ def get_pools(self): :return: the list of pools this macro server is connected to :rtype: seq""" - return self._pools.values() + return list(self._pools.values()) def on_pool_elements_changed(self, evt_src, evt_type, evt_value): if evt_type not in CHANGE_EVT_TYPES: @@ -385,13 +385,13 @@ def get_remote_elements_info(self): def get_local_elements_info(self): # fill macro library info ret = [macrolib.serialize() - for macrolib in self.get_macro_libs().values()] + for macrolib in list(self.get_macro_libs().values())] # fill macro info ret += [macro.serialize() - for macro in self.get_macros().values()] + for macro in list(self.get_macros().values())] # fill parameter type info ret += [paramtype.serialize() - for paramtype in self.get_data_types().values()] + for paramtype in list(self.get_data_types().values())] return ret @@ -526,7 +526,7 @@ def get_macro_functions(self): def get_macro_libs_summary_info(self): libs = self.get_macro_libs() ret = [] - for module_name, macro_lib_info in libs.items(): + for module_name, macro_lib_info in list(libs.items()): elem = "%s (%s)" % (macro_lib_info.name, macro_lib_info.file_path) ret.append(elem) return ret @@ -662,7 +662,7 @@ def set_env_obj(self, data): env_man = self.environment_manager new, change = {}, {} - for key, value in data.items(): + for key, value in list(data.items()): d = new if env_man.hasEnv(key): d = change @@ -681,7 +681,7 @@ def change_env(self, data): del_env = data.get('del', []) new, change = {}, {} - for key, value in new_change_env.items(): + for key, value in list(new_change_env.items()): d = new if env_man.hasEnv(key): d = change @@ -750,14 +750,14 @@ def find_objects(self, param, type_class=All, subtype=All, pool=All): if not type_inst.hasCapability(ParamType.ItemList): continue if self.is_macroserver_interface(type_class_name): - for name, obj in type_inst.getObjDict(pool=pool).items(): + for name, obj in list(type_inst.getObjDict(pool=pool).items()): for re_obj in re_objs: if re_obj.match(name) is not None: obj_type = ElementType[obj.get_type()] if subtype is MacroServer.All or re_subtype.match(obj_type): obj_set.add(obj) else: - for name, obj in type_inst.getObjDict(pool=pool).items(): + for name, obj in list(type_inst.getObjDict(pool=pool).items()): for re_obj in re_objs: if re_obj.match(name) is not None: obj_type = obj.getType() diff --git a/src/sardana/macroserver/msdoor.py b/src/sardana/macroserver/msdoor.py index 0e0529a57f..26a3cd0e85 100644 --- a/src/sardana/macroserver/msdoor.py +++ b/src/sardana/macroserver/msdoor.py @@ -82,7 +82,7 @@ def rebuild(self): self.clear() door = self.door macros = self.door.get_macros() - for macro_name, macro_meta in macros.items(): + for macro_name, macro_meta in list(macros.items()): self[macro_name] = MacroProxy(door, macro_meta) diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index 7292b6a89d..3a9afcc500 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -136,7 +136,7 @@ def setEnvironmentDb(self, f_name): def _fillEnvironmentCaches(self, env): # fill the three environment caches env_dict = self._global_env - for k, v in env.items(): + for k, v in list(env.items()): k_parts = k.split('.', 1) key = k_parts[0] @@ -315,7 +315,7 @@ def getAllDoorMacroEnv(self, door_name, macro_name): m_env = self._macro_env.get(macro_name, {}) # put the doors global environment - for k, v in d_env.iteritems(): + for k, v in d_env.items(): if k.count('.') == 0: ret[k] = v @@ -323,7 +323,7 @@ def getAllDoorMacroEnv(self, door_name, macro_name): ret.update(m_env) # put the door and macro specific environment - for k, v in d_env.iteritems(): + for k, v in d_env.items(): if k.count('.') > 0: m_name, key = k.split('.', 1) if m_name is macro_name: @@ -382,7 +382,7 @@ def _dictFromSequence(self, seq): def _encode(self, d): ret = {} - for k, v in d.iteritems(): + for k, v in d.items(): if isinstance(v, (str, unicode)): try: v = eval(v) @@ -460,7 +460,7 @@ def setEnvObj(self, obj): raise TypeError("obj parameter must be a sequence or a map") obj = self._encode(obj) - for k, v in obj.iteritems(): + for k, v in obj.items(): self._setOneEnv(k, v) return obj diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 692729f8b5..3c30ddb0dc 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -241,7 +241,7 @@ class A != class A).""" self._macro_path = p macro_file_names = self._findMacroLibNames() - for mod_name, file_name in macro_file_names.iteritems(): + for mod_name, file_name in macro_file_names.items(): dir_name = os.path.dirname(file_name) path = [dir_name] try: @@ -539,7 +539,7 @@ def reloadMacroLib(self, module_name, path=None): macro_name = macro.__name__ if macro_name in self._overwritten_macros: isoverwritten = True - elif (macro_name in self._macro_dict.keys() + elif (macro_name in list(self._macro_dict.keys()) and self._macro_dict[macro_name].lib != macro_lib): isoverwritten = True msg = ('Macro "{}" defined in "{}" macro library' @@ -563,7 +563,7 @@ def reloadMacroLib(self, module_name, path=None): finally: if macro_errors: msg = "" - for key, value in macro_errors.iteritems(): + for key, value in macro_errors.items(): msg_part = ("\n" + "Error adding macro(s): " + key + "\n" + "It presents an error: \n" + str(value)) msg += str(msg_part) + "\n" @@ -617,7 +617,7 @@ def getMacroLibs(self, filter=None): return self._modules expr = re.compile(filter, re.IGNORECASE) ret = {} - for name, macro_lib in self._modules.iteritems(): + for name, macro_lib in self._modules.items(): if expr.match(name) is None: continue ret[name] = macro_lib @@ -638,7 +638,7 @@ def getMacros(self, filter=None): expr = re.compile(filter, re.IGNORECASE) ret = {} - for name, macro in self._macro_dict.iteritems(): + for name, macro in self._macro_dict.items(): if expr.match(name) is None: continue ret[name] = macro @@ -656,7 +656,7 @@ def getMacroClasses(self, filter=None): :obj:`dict`\<:obj:`str`\, :class:`~sardana.macroserver.msmetamacro.MacroClass`\>""" macros = self.getMacros(filter=filter) macro_classes = {} - for name, macro in macros.items(): + for name, macro in list(macros.items()): if macro.get_type() == ElementType.MacroClass: macro_classes[name] = macro return macro_classes @@ -673,7 +673,7 @@ def getMacroFunctions(self, filter=None): :obj:`dict`\<:obj:`str`\, :class:`~sardana.macroserver.msmetamacro.MacroFunction`\>""" macros = self.getMacros(filter=filter) macro_classes = {} - for name, macro in macros.items(): + for name, macro in list(macros.items()): if macro.get_type() == ElementType.MacroFunction: macro_classes[name] = macro return macro_classes @@ -699,12 +699,12 @@ def removeMacro(self, macro_name): def getMacroLib(self, name): if os.path.isabs(name): abs_file_name = name - for lib in self._modules.values(): + for lib in list(self._modules.values()): if lib.file_path == abs_file_name: return lib elif name.count(os.path.extsep): file_name = name - for lib in self._modules.values(): + for lib in list(self._modules.values()): if lib.file_name == file_name: return lib module_name = name @@ -1389,7 +1389,7 @@ def clearRunningMacro(self): def __stopObjects(self): """Stops all the reserved objects in the executor""" - for _, objs in self._reserved_macro_objs.items(): + for _, objs in list(self._reserved_macro_objs.items()): for obj in objs: try: obj.stop() @@ -1401,7 +1401,7 @@ def __stopObjects(self): def __abortObjects(self): """Aborts all the reserved objects in the executor""" - for _, objs in self._reserved_macro_objs.items(): + for _, objs in list(self._reserved_macro_objs.items()): for obj in objs: try: obj.abort() diff --git a/src/sardana/macroserver/msmetamacro.py b/src/sardana/macroserver/msmetamacro.py index 6986f1ce0c..8e869304de 100644 --- a/src/sardana/macroserver/msmetamacro.py +++ b/src/sardana/macroserver/msmetamacro.py @@ -188,7 +188,7 @@ def build_parameter_info(self, param_def=None): if repeat: rep = type_class opts = sep = '' - for opt, val in rep.items(): + for opt, val in list(rep.items()): opts += '%s%s=%s' % (sep, opt, val) sep = ', ' info.append(opts) @@ -210,7 +210,7 @@ def build_result_info(self, result_def=None): if repeat: rep = type_class opts = sep = '' - for opt, val in rep.items(): + for opt, val in list(rep.items()): opts += '%s%s=%s' % (sep, opt, val) sep = ', ' info.append(opts) diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py index f7bd8ade2c..8c75105ecd 100644 --- a/src/sardana/macroserver/msparameter.py +++ b/src/sardana/macroserver/msparameter.py @@ -140,7 +140,7 @@ def removeType(self, name): pass def __str__(self): - return str(self._type_names.keys()) + return str(list(self._type_names.keys())) # def __getattr__(self, name): # if name not in self._pending_type_names: @@ -197,7 +197,7 @@ def __init__(self, *param_def, **opts): self._obj.append(self.opts) def items(self): - return self.opts.items() + return list(self.opts.items()) def __getattr__(self, name): return self.opts[name] @@ -252,10 +252,10 @@ def getObjDict(self, pool=ParamType.All, cache=False): for elem_info in pool.getElements(): if self.accepts(elem_info): objs[elem_info.name] = elem_info - for macro_lib_name, macro_lib in macro_server.get_macros().items(): + for macro_lib_name, macro_lib in list(macro_server.get_macros().items()): if self.accepts(macro_lib): objs[macro_lib_name] = macro_lib - for macro_name, macro in macro_server.get_macros().items(): + for macro_name, macro in list(macro_server.get_macros().items()): if self.accepts(macro): objs[macro_name] = macro @@ -263,11 +263,11 @@ def getObjDict(self, pool=ParamType.All, cache=False): def getObjListStr(self, pool=ParamType.All, cache=False): obj_dict = self.getObjDict(pool=pool, cache=cache) - return obj_dict.keys() + return list(obj_dict.keys()) def getObjList(self, pool=ParamType.All, cache=False): obj_dict = self.getObjDict(pool=pool, cache=cache) - return obj_dict.values() + return list(obj_dict.values()) def serialize(self, *args, **kwargs): kwargs = ParamType.serialize(self, *args, **kwargs) @@ -325,18 +325,18 @@ def getObjDict(self, pool=ParamType.All, cache=False): else: pools = macro_server.get_pool(pool), for pool in pools: - for elem_info in pool.getElementsWithInterface(self._name).values(): + for elem_info in list(pool.getElementsWithInterface(self._name).values()): if self.accepts(elem_info): objs[elem_info.name] = elem_info return objs def getObjListStr(self, pool=ParamType.All, cache=False): obj_dict = self.getObjDict(pool=pool, cache=cache) - return obj_dict.keys() + return list(obj_dict.keys()) def getObjList(self, pool=ParamType.All, cache=False): obj_dict = self.getObjDict(pool=pool, cache=cache) - return obj_dict.values() + return list(obj_dict.values()) class AttrParamType(ParamType): diff --git a/src/sardana/macroserver/msrecordermanager.py b/src/sardana/macroserver/msrecordermanager.py index f4fc1b7b42..d00f6a3672 100644 --- a/src/sardana/macroserver/msrecordermanager.py +++ b/src/sardana/macroserver/msrecordermanager.py @@ -120,7 +120,7 @@ def setRecorderPath(self, recorder_path): recorder_file_names = self._findRecorderLibNames( _recorder_path) - for mod_name, file_name in recorder_file_names.iteritems(): + for mod_name, file_name in recorder_file_names.items(): dir_name = os.path.dirname(file_name) path = [dir_name] try: @@ -164,7 +164,7 @@ def getRecorderMetaClasses(self, filter=None, extension=None): if filter is None: filter = DataRecorder ret = {} - for name, klass in self._recorder_dict.items(): + for name, klass in list(self._recorder_dict.items()): if not issubclass(klass.recorder_class, filter): continue if extension is not None: @@ -176,7 +176,7 @@ def getRecorderMetaClasses(self, filter=None, extension=None): # second look into the standard map else: _map = self._scan_recorder_map - if extension not in _map.keys(): + if extension not in list(_map.keys()): continue elif klass not in _map[extension]: continue @@ -198,7 +198,7 @@ def getRecorderClasses(self, filter=None, extension=None): meta_klasses = self.getRecorderMetaClasses(filter=filter, extension=extension) return dict((key, value.klass) - for (key, value) in meta_klasses.items()) + for (key, value) in list(meta_klasses.items())) def getRecorderClass(self, klass_name): """ Return the Recorder class for the given class name. @@ -262,7 +262,7 @@ def reloadRecorderLib(self, module_name, path=None): for recorder in old_recorder_lib.get_recorders(): self._recorder_dict.pop(recorder.name) # remove recorders from the map - for _, recorders in self._scan_recorder_map.iteritems(): + for _, recorders in self._scan_recorder_map.items(): try: recorders.remove(recorder) except: @@ -342,7 +342,7 @@ def addRecorder(self, recorder_lib, klass): def _addRecorderToMap(self, recorder_class): klass = recorder_class.klass - for ext in klass.formats.values(): + for ext in list(klass.formats.values()): recorders = self._scan_recorder_map.get(ext, []) if len(recorders) == 0: recorders.append(recorder_class) diff --git a/src/sardana/macroserver/mstypemanager.py b/src/sardana/macroserver/mstypemanager.py index 7aee3c1a11..b0de143ba2 100644 --- a/src/sardana/macroserver/mstypemanager.py +++ b/src/sardana/macroserver/mstypemanager.py @@ -72,7 +72,7 @@ def cleanUp(self): return if self._modules: - for _, types_dict in self._modules.items(): + for _, types_dict in list(self._modules.items()): for type_name in types_dict: Type.removeType(type_name) @@ -138,8 +138,8 @@ def addType(self, type_obj): def getTypeListStr(self): type_list_basic, type_list_obj = [], [] - for _, type_class_dict in self._modules.items(): - for tname, tklass in type_class_dict.items(): + for _, type_class_dict in list(self._modules.items()): + for tname, tklass in list(type_class_dict.items()): if tklass.hasCapability(ParamType.ItemList): type_list_obj.append("%s*" % tname) else: @@ -149,7 +149,7 @@ def getTypeListStr(self): return type_list def getTypeClass(self, type_name): - for _, type_class_dict in self._modules.items(): + for _, type_class_dict in list(self._modules.items()): tklass = type_class_dict.get(type_name) if tklass is None: continue @@ -163,4 +163,4 @@ def getTypes(self): return self._inst_dict def getTypeNames(self): - return self._inst_dict.keys() + return list(self._inst_dict.keys()) diff --git a/src/sardana/macroserver/recorders/examples/dummy.py b/src/sardana/macroserver/recorders/examples/dummy.py index 847a29785a..420d8b1d68 100644 --- a/src/sardana/macroserver/recorders/examples/dummy.py +++ b/src/sardana/macroserver/recorders/examples/dummy.py @@ -47,7 +47,7 @@ def _startRecordList(self, recordlist): self.fd.write("Starting new recording\n") self.fd.write("# Title : %s\n" % recordlist.getEnvironValue('title')) env = recordlist.getEnviron() - for envky in env.keys(): + for envky in list(env.keys()): if envky != 'title' and envky != 'labels': self.fd.write("# %8s : %s \n" % (envky, str(env[envky]))) self.fd.write("# Started: %s\n" % env['starttime']) diff --git a/src/sardana/macroserver/recorders/h5storage.py b/src/sardana/macroserver/recorders/h5storage.py index 92f9825dd2..7b1f66786f 100644 --- a/src/sardana/macroserver/recorders/h5storage.py +++ b/src/sardana/macroserver/recorders/h5storage.py @@ -155,7 +155,7 @@ def _startRecordList(self, recordlist): nxentry = self.fd.create_group(self.entryname) except ValueError: # Warn and abort - if self.entryname in self.fd.keys(): + if self.entryname in list(self.fd.keys()): msg = ('{ename:s} already exists in {fname:s}. ' 'Aborting macro to prevent data corruption.\n' 'This is likely caused by a wrong ScanID\n' @@ -282,7 +282,7 @@ def _createPreScanSnapshot(self, env): _snap = _meas.create_group('pre_scan_snapshot') _snap.attrs['NX_class'] = 'NXcollection' - meas_keys = _meas.keys() + meas_keys = list(_meas.keys()) for dd in self.preScanSnapShot: label = self.sanitizeName(dd.label) @@ -498,7 +498,7 @@ def _createNXData(self): _meas = nxentry['measurement'] # write the 1D NXdata group - for axes, v in plots1d.items(): + for axes, v in list(plots1d.items()): _nxdata = nxentry.create_group(plots1d_names[axes]) _nxdata.attrs['NX_class'] = 'NXdata' diff --git a/src/sardana/macroserver/recorders/output.py b/src/sardana/macroserver/recorders/output.py index b41bba4b57..527e671649 100644 --- a/src/sardana/macroserver/recorders/output.py +++ b/src/sardana/macroserver/recorders/output.py @@ -158,7 +158,7 @@ def _startRecordList(self, recordlist): for fr in [r for r in dh.recorders if isinstance(r, BaseFileRecorder)]: if not hasattr(self._stream, "getAllEnv") or \ - "ScanRecorder" in self._stream.getAllEnv().keys(): + "ScanRecorder" in list(self._stream.getAllEnv().keys()): message = "%s from %s" % ( fr.getFormat(), fr.__class__.__name__) else: diff --git a/src/sardana/macroserver/recorders/sharedmemory.py b/src/sardana/macroserver/recorders/sharedmemory.py index 8ba8ed7868..39df47b85f 100644 --- a/src/sardana/macroserver/recorders/sharedmemory.py +++ b/src/sardana/macroserver/recorders/sharedmemory.py @@ -91,7 +91,7 @@ def putAllEnv(self, d): if not self.isInitialized(): return p, a = self.program, self.array_ENV - for k, v in d.iteritems(): + for k, v in d.items(): self.sps.putenv(p, a, k, str(v)) def _startRecordList(self, recordlist): @@ -238,7 +238,7 @@ def _startRecordList(self, recordlist): self.putenv('title', recordlist.getEnvironValue('title')) - for env, val in recordlist.getEnviron().items(): + for env, val in list(recordlist.getEnviron().items()): if env != 'title' and env != 'labels': self.putenv(env, val) @@ -286,7 +286,7 @@ def _writeRecord(self, record): myj = 0 - for val2 in record.data.values(): + for val2 in list(record.data.values()): valsmca = [] if type(val2) in [list]: if dim_list[myj] == 1: diff --git a/src/sardana/macroserver/recorders/storage.py b/src/sardana/macroserver/recorders/storage.py index 772fede0e6..53e6e30d53 100644 --- a/src/sardana/macroserver/recorders/storage.py +++ b/src/sardana/macroserver/recorders/storage.py @@ -89,7 +89,7 @@ def setFileName(self, filename): self.filename = "%s_%s.%s" % (tpl[0], "[ScanId]", tpl[2]) def getFormat(self): - return self.formats.keys()[0] + return list(self.formats.keys())[0] def _startRecordList(self, recordlist): @@ -304,7 +304,7 @@ def setFileName(self, filename): self.currentlist = None def getFormat(self): - return self.formats.keys()[0] + return list(self.formats.keys())[0] def _startRecordList(self, recordlist): '''Prepares and writes the scan header.''' @@ -560,7 +560,7 @@ def _startRecordList(self, recordlist): try: self.fd.makegroup(self.entryname, "NXentry") except self.nxs.NeXusError: - entrynames = self.fd.getentries().keys() + entrynames = list(self.fd.getentries().keys()) #================================================================== ##Warn and abort @@ -687,7 +687,7 @@ def _createPreScanSnapshot(self, env): self.fd.closegroup() # we are back at the measurement group measurement_entries = self.fd.getentries() - for label, nid in links.items(): + for label, nid in list(links.items()): if label not in measurement_entries: self.fd.makelink(nid) @@ -832,7 +832,7 @@ def _createNXData(self): continue # @todo: implement support for images and other # write the 1D NXdata group - for axes, v in plots1d.items(): + for axes, v in list(plots1d.items()): self.fd.openpath("/%s:NXentry" % (self.entryname)) groupname = plots1d_names[axes] self.fd.makegroup(groupname, 'NXdata') diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index e5f5278028..5085f3c352 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1580,7 +1580,7 @@ def configure_motor(self, motor, attributes): :param attributes: (OrderedDict) dictionary with attribute names (keys) and attribute values (values) """ - for param, value in attributes.items(): + for param, value in list(attributes.items()): try: motor._getAttrEG(param).write(value) except Exception: @@ -2428,7 +2428,7 @@ def _go_through_waypoints(self): theoretical_positions = generate_positions(motors, starts, finals, nr_points) theoretical_timestamps = generate_timestamps(synch, dt_timestamp) - for index, data in theoretical_positions.items(): + for index, data in list(theoretical_positions.items()): data.update(theoretical_timestamps[index]) initial_data[index + self._index_offset] = data # TODO: this changes the initial data on-the-fly - seems like not diff --git a/src/sardana/macroserver/scan/recorder/storage.py b/src/sardana/macroserver/scan/recorder/storage.py index 526a2da681..a6208e90e2 100644 --- a/src/sardana/macroserver/scan/recorder/storage.py +++ b/src/sardana/macroserver/scan/recorder/storage.py @@ -105,7 +105,7 @@ def setFileName(self, filename): # extension (defaults to hdf5) extension = os.path.splitext(filename)[1] inv_formats = dict(itertools.izip( - self.formats.itervalues(), self.formats.iterkeys())) + iter(self.formats.values()), iter(self.formats.keys()))) self.nxfilemode = inv_formats.get(extension.lower(), 'w5') self.currentlist = None @@ -223,7 +223,7 @@ def _writeData(self, name, data, dtype, shape=None, chunks=None, attrs=None): self.fd.opendata(name) self.fd.putdata(data) if attrs is not None: - for k, v in attrs.items(): + for k, v in list(attrs.items()): self.fd.putattr(k, v) nid = self.fd.getdataID() self.fd.closedata() @@ -317,7 +317,7 @@ def FileRecorder(filename, macro, **pars): if len_klasses == 0: klass = rec_manager.getRecorderClass('SPEC_FileRecorder') elif len_klasses == 1: - klass = klasses.values()[0] + klass = list(klasses.values())[0] else: raise AmbiguousRecorderError('Choice of recorder for %s ' 'extension is ambiguous' % ext) diff --git a/src/sardana/macroserver/scan/scandata.py b/src/sardana/macroserver/scan/scandata.py index 14897fd6e1..ea0d722037 100644 --- a/src/sardana/macroserver/scan/scandata.py +++ b/src/sardana/macroserver/scan/scandata.py @@ -269,7 +269,7 @@ def isValid(self): return 1 for ky in self.needed + self.__needed: - if ky not in self.keys(): + if ky not in list(self.keys()): return 0 else: return 1 @@ -399,7 +399,7 @@ def applyZeroOrderInterpolation(self, record): if self.currentIndex > 0: data = record.data prev_data = self.records[self.currentIndex - 1].data - for k, v in data.items(): + for k, v in list(data.items()): if v is None: continue # numpy arrays (1D or 2D) are valid values and does not require @@ -414,7 +414,7 @@ def applyZeroOrderInterpolation(self, record): def applyExtrapolation(self, record): """Apply extrapolation to the given record""" data = record.data - for k, v in data.items(): + for k, v in list(data.items()): if v is None: continue # numpy arrays (1D or 2D) are valid values and does not require diff --git a/src/sardana/macroserver/scan/test/test_recorddata.py b/src/sardana/macroserver/scan/test/test_recorddata.py index 714c498508..7961c503dd 100644 --- a/src/sardana/macroserver/scan/test/test_recorddata.py +++ b/src/sardana/macroserver/scan/test/test_recorddata.py @@ -85,14 +85,14 @@ def setUp(self): def prepareScandData(self, data, apply_interpolation=False): scan_dir, scan_file = os.path.split(self.file_name) - env = createScanDataEnvironment(data.keys(), scan_dir, scan_file) + env = createScanDataEnvironment(list(data.keys()), scan_dir, scan_file) self.scan_data = ScanData(environment=env, data_handler=self.data_handler, apply_interpolation=apply_interpolation) self.srcs = [] self.inputs = {} max_len = -1 - for name, dat in data.items(): + for name, dat in list(data.items()): des = DummyEventSource(name, self.scan_data, dat, [0] * len(dat)) self.srcs.append(des) input_list = [] @@ -106,7 +106,7 @@ def prepareScandData(self, data, apply_interpolation=False): if max_len < len_il: max_len = len_il # Pading the list to fill it with float('Nan') - for name, dat in self.inputs.items(): + for name, dat in list(self.inputs.items()): diff = max_len - len(dat) self.inputs[name] = dat + [float('Nan')] * diff @@ -125,7 +125,7 @@ def recorddata(self, data, apply_interpolation): # Test the generated nxs file f = self.nxs.load(self.file_name) m = f['entry1']['measurement'] - for chn in data.keys(): + for chn in list(data.keys()): chn_data = m[chn].nxdata # check the data element by element for i in range(len(chn_data)): @@ -152,7 +152,7 @@ def zeroOrderInterpolation(self, data, apply_interpolation): # Test the generated nxs file f = self.nxs.load(self.file_name) m = f['entry1']['measurement'] - for chn in data.keys(): + for chn in list(data.keys()): chn_data = m[chn].nxdata # check the interpolations for i in range(len(chn_data)): diff --git a/src/sardana/macroserver/test/test_msrecordermanager.py b/src/sardana/macroserver/test/test_msrecordermanager.py index 4d2152098c..329fabcc7d 100644 --- a/src/sardana/macroserver/test/test_msrecordermanager.py +++ b/src/sardana/macroserver/test/test_msrecordermanager.py @@ -149,7 +149,7 @@ def test_SameFormats(self): self._updateRecorderManager(recorder_path) klasses = self.manager.getRecorderMetaClasses(filter=BaseFileRecorder, extension='.spec') - klass = klasses.values()[0] + klass = list(klasses.values())[0] # retrieve path to the recorder library path = os.sep.join(klass.lib.full_name.split(os.sep)[:-1]) msg = 'Ordered path precedence is not maintained by RecorderManager' diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index d90e9fe881..e13694c3df 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -308,7 +308,7 @@ def __init__(self, inst, props, *args, **kwargs): self._args = args self._kwargs = kwargs self._api_version = self._findAPIVersion() - for prop_name, prop_value in props.items(): + for prop_name, prop_value in list(props.items()): setattr(self, prop_name, prop_value) def _findAPIVersion(self): @@ -1427,9 +1427,9 @@ def GetPseudoMotor(self, index_or_role): dict_ids = self._getPoolController().get_element_ids() dict_axis = self._getPoolController().get_element_axis() pseudo_motor_ids = [] - for akey, aname in dict_axis.items(): + for akey, aname in list(dict_axis.items()): pseudo_motor_ids.append( - dict_ids.keys()[dict_ids.values().index(aname)]) + list(dict_ids.keys())[list(dict_ids.values()).index(aname)]) return self._getElem(index_or_role, self.pseudo_motor_roles, self.__pseudo_motor_role_elements, pseudo_motor_ids) diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py index 026504f2ff..e6f1c432df 100644 --- a/src/sardana/pool/pool.py +++ b/src/sardana/pool/pool.py @@ -326,7 +326,7 @@ def get_controller_classes_summary_info(self): def get_elements_str_info(self, obj_type=None): if obj_type is None: - objs = self.get_element_id_map().values() + objs = list(self.get_element_id_map().values()) objs.extend(self.get_controller_classes()) objs.extend(self.get_controller_libs()) elif obj_type == ElementType.ControllerClass: @@ -340,7 +340,7 @@ def get_elements_str_info(self, obj_type=None): def get_elements_info(self, obj_type=None): if obj_type is None: - objs = self.get_element_id_map().values() + objs = list(self.get_element_id_map().values()) objs.extend(self.get_controller_classes()) objs.extend(self.get_controller_libs()) objs.append(self) @@ -355,14 +355,14 @@ def get_elements_info(self, obj_type=None): def get_acquisition_elements_info(self): ret = [] - for _, element in self.get_element_name_map().items(): + for _, element in list(self.get_element_name_map().items()): if element.get_type() not in TYPE_ACQUIRABLE_ELEMENTS: continue acq_channel = element.get_default_acquisition_channel() full_name = "{0}/{1}".format(element.full_name, acq_channel) info = dict(name=element.name, full_name=full_name, origin='local') ret.append(info) - ret.extend(self._extra_acquisition_element_names.values()) + ret.extend(list(self._extra_acquisition_element_names.values())) return ret def get_acquisition_elements_str_info(self): @@ -410,7 +410,7 @@ def create_controller(self, **kwargs): ctrl_prop_info = {} else: ctrl_prop_info = ctrl_class_info.ctrl_properties - for k, v in kwargs['properties'].items(): + for k, v in list(kwargs['properties'].items()): info = ctrl_prop_info.get(k) if info is None: props[k] = v @@ -755,6 +755,6 @@ def get_moveable_graph(self): for elem_type in TYPE_MOVEABLE_ELEMENTS: moveable_elems_map.update(elem_type_map[elem_type]) graph = Graph() - for moveable in moveable_elems_map.values(): + for moveable in list(moveable_elems_map.values()): self._build_element_dependencies(moveable, graph) return graph diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index eaadc93b49..2a1ab50ebf 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -1010,7 +1010,7 @@ def action_loop(self): # read value every n times if not i % nb_states_per_value: self.read_value(ret=values) - for acquirable, value in values.items(): + for acquirable, value in list(values.items()): if is_value_error(value): self.error("Loop read value error for %s" % acquirable.name) @@ -1029,7 +1029,7 @@ def action_loop(self): self.raw_read_value(ret=values) self.raw_read_value_ref(ret=value_refs) - for acquirable, state_info in states.items(): + for acquirable, state_info in list(states.items()): # first update the element state so that value calculation # that is done after takes the updated state into account acquirable.set_state_info(state_info, propagate=0) @@ -1120,7 +1120,7 @@ def action_loop(self): # read value every n times if not i % nb_states_per_value: self.read_value_loop(ret=values) - for acquirable, value in values.items(): + for acquirable, value in list(values.items()): acquirable.put_value(value) time.sleep(nap) @@ -1139,7 +1139,7 @@ def action_loop(self): self.raw_read_value(ret=values) self.raw_read_value_ref(ret=value_refs) - for acquirable, state_info in states.items(): + for acquirable, state_info in list(states.items()): # first update the element state so that value calculation # that is done after takes the updated state into account acquirable.set_state_info(state_info, propagate=0) @@ -1216,7 +1216,7 @@ def action_loop(self): # read value every n times if not i % nb_states_per_value: self.read_value(ret=values) - for acquirable, value in values.items(): + for acquirable, value in list(values.items()): if is_value_error(value): self.error("Loop read value error for %s" % acquirable.name) @@ -1227,7 +1227,7 @@ def action_loop(self): else: acquirable.extend_value_buffer(value) self.read_value_ref(ret=value_refs) - for acquirable, value_ref in value_refs.items(): + for acquirable, value_ref in list(value_refs.items()): if is_value_error(value_ref): self.error("Loop read value ref error for %s" % acquirable.name) @@ -1245,7 +1245,7 @@ def action_loop(self): self.raw_read_value(ret=values) self.raw_read_value_ref(ret=value_refs) - for acquirable, state_info in states.items(): + for acquirable, state_info in list(states.items()): # first update the element state so that value calculation # that is done after takes the updated state into account acquirable.set_state_info(state_info, propagate=0) @@ -1322,7 +1322,7 @@ def action_loop(self): # read values to send a first event when starting to acquire with ActionContext(self): self.raw_read_value_loop(ret=values) - for acquirable, value in values.items(): + for acquirable, value in list(values.items()): acquirable.put_value(value, propagate=2) while True: @@ -1333,7 +1333,7 @@ def action_loop(self): # read value every n times if not i % nb_states_per_value: self.read_value_loop(ret=values) - for acquirable, value in values.items(): + for acquirable, value in list(values.items()): acquirable.put_value(value) time.sleep(nap) @@ -1351,7 +1351,7 @@ def action_loop(self): self.raw_read_state_info(ret=states) self.raw_read_value_loop(ret=values) - for acquirable, state_info in states.items(): + for acquirable, state_info in list(states.items()): # first update the element state so that value calculation # that is done after takes the updated state into account acquirable.set_state_info(state_info, propagate=0) @@ -1430,7 +1430,7 @@ def action_loop(self): nap = self._acq_sleep_time while True: self.read_value(ret=values) - for acquirable, value in values.items(): + for acquirable, value in list(values.items()): acquirable.put_current_value(value, propagate=0) if self._stopped or self._aborted: break @@ -1443,7 +1443,7 @@ def action_loop(self): with ActionContext(self): self.raw_read_state_info(ret=states) - for acquirable, state_info in states.items(): + for acquirable, state_info in list(states.items()): # first update the element state so that value calculation # that is done after takes the updated state into account state_info = acquirable._from_ctrl_state_info(state_info) @@ -1485,7 +1485,7 @@ def action_loop(self): # read values to send a first event when starting to acquire self.read_value(ret=values) - for acquirable, value in values.items(): + for acquirable, value in list(values.items()): acquirable.put_value(value, propagate=2) while True: @@ -1497,7 +1497,7 @@ def action_loop(self): # read value every n times if not i % 5: self.read_value(ret=values) - for acquirable, value in values.items(): + for acquirable, value in list(values.items()): acquirable.put_value(value) i += 1 @@ -1507,7 +1507,7 @@ def action_loop(self): # first update the element state so that value calculation # that is done after takes the updated state into account - for acquirable, state_info in states.items(): + for acquirable, state_info in list(states.items()): acquirable.set_state_info(state_info, propagate=0) # Do NOT send events before we exit the OperationContext, otherwise @@ -1518,11 +1518,11 @@ def action_loop(self): def finish_hook(*args, **kwargs): # read values and propagate the change to all listeners self.read_value(ret=values) - for acquirable, value in values.items(): + for acquirable, value in list(values.items()): acquirable.put_value(value, propagate=2) # finally set the state and propagate to all listeners - for acquirable, state_info in states.items(): + for acquirable, state_info in list(states.items()): acquirable.set_state_info(state_info, propagate=2) self.set_finish_hook(finish_hook) diff --git a/src/sardana/pool/poolaction.py b/src/sardana/pool/poolaction.py index 2a20c406e3..5786903e9c 100644 --- a/src/sardana/pool/poolaction.py +++ b/src/sardana/pool/poolaction.py @@ -397,7 +397,7 @@ def finish_action(self): """Finishes the action execution. If a finish hook is defined it safely executes it. Otherwise nothing happens""" hooks = self._finish_hooks - for hook, permanent in hooks.items(): + for hook, permanent in list(hooks.items()): try: hook() except: @@ -410,19 +410,19 @@ def finish_action(self): def stop_action(self, *args, **kwargs): """Stop procedure for this action.""" self._stopped = True - for pool_ctrl, elements in self._pool_ctrl_dict.items(): + for pool_ctrl, elements in list(self._pool_ctrl_dict.items()): pool_ctrl.stop_elements(elements) def abort_action(self, *args, **kwargs): """Aborts procedure for this action""" self._aborted = True - for pool_ctrl, elements in self._pool_ctrl_dict.items(): + for pool_ctrl, elements in list(self._pool_ctrl_dict.items()): pool_ctrl.abort_elements(elements) def emergency_break(self): """Tries to execute a stop. If it fails try an abort""" self._stopped = True - for pool_ctrl, elements in self._pool_ctrl_dict.items(): + for pool_ctrl, elements in list(self._pool_ctrl_dict.items()): pool_ctrl.emergency_break(elements) def was_stopped(self): @@ -540,7 +540,7 @@ def _raw_read_ctrl_state_info(self, ret, pool_ctrl): state_infos, error = pool_ctrl.raw_read_axis_states(axes) if error: pool_ctrl.warning("Read state error") - for elem, (state_info, exc_info) in state_infos.items(): + for elem, (state_info, exc_info) in list(state_infos.items()): if exc_info is not None: pool_ctrl.debug("Axis %s error details:", elem.axis, exc_info=exc_info) diff --git a/src/sardana/pool/poolbasegroup.py b/src/sardana/pool/poolbasegroup.py index 07586d81c5..f878bea301 100644 --- a/src/sardana/pool/poolbasegroup.py +++ b/src/sardana/pool/poolbasegroup.py @@ -75,7 +75,7 @@ def _get_action_cache(self): def _set_action_cache(self, action_cache): physical_elements = self.get_physical_elements() if self._action_cache is not None: - for ctrl_physical_elements in physical_elements.values(): + for ctrl_physical_elements in list(physical_elements.values()): for physical_element in ctrl_physical_elements: action_cache.remove_element(physical_element) @@ -86,7 +86,7 @@ def _fill_action_cache(self, action_cache=None, physical_elements=None): action_cache = self._create_action_cache() if physical_elements is None: physical_elements = self.get_physical_elements() - for _, ctrl_physical_elements in physical_elements.items(): + for _, ctrl_physical_elements in list(physical_elements.items()): for physical_element in ctrl_physical_elements: action_cache.add_element(physical_element) return action_cache @@ -113,7 +113,7 @@ def _calculate_states(self, state_info=None): # lock! si = elem.inspect_state(), elem.inspect_status() state_info[elem] = si - for elem, elem_state_info in state_info.items(): + for elem, elem_state_info in list(state_info.items()): elem_type = elem.get_type() if elem_type == ElementType.External: continue @@ -257,7 +257,7 @@ def get_physical_elements_iterator(self): :return: an iterator over the physical elements. :rtype: iter<:class:`~sardana.pool.poolelement.PoolElement` >""" - for _, elements in self.get_physical_elements().items(): + for _, elements in list(self.get_physical_elements().items()): for element in elements: yield element @@ -317,7 +317,7 @@ def _find_physical_elements(self, element, physical_elements=None, own_elements.add(element) physical_elements_set.add(element) else: - for ctrl, elements in element.get_physical_elements().items(): + for ctrl, elements in list(element.get_physical_elements().items()): own_elements = physical_elements.get(ctrl) if own_elements is None: physical_elements[ctrl] = own_elements = set() @@ -357,7 +357,7 @@ def clear_user_elements(self): def stop(self): msg = "" - for ctrl, elements in self.get_physical_elements().items(): + for ctrl, elements in list(self.get_physical_elements().items()): self.debug("Stopping %s %s", ctrl.name, [e.name for e in elements]) try: @@ -383,7 +383,7 @@ def stop(self): def abort(self): msg = "" - for ctrl, elements in self.get_physical_elements().items(): + for ctrl, elements in list(self.get_physical_elements().items()): self.debug("Aborting %s %s", ctrl.name, [e.name for e in elements]) try: @@ -410,7 +410,7 @@ def abort(self): # -------------------------------------------------------------------------- def get_operation(self): - for _, elements in self.get_physical_elements().items(): + for _, elements in list(self.get_physical_elements().items()): for element in elements: op = element.get_operation() if op is not None: diff --git a/src/sardana/pool/poolcontroller.py b/src/sardana/pool/poolcontroller.py index 1371713833..f800908c44 100644 --- a/src/sardana/pool/poolcontroller.py +++ b/src/sardana/pool/poolcontroller.py @@ -380,7 +380,7 @@ def re_init(self): self._ctrl_info = self._lib_info.get_controller(class_name) self._init() - for elem in elem_axis.values(): + for elem in list(elem_axis.values()): self.add_element(elem, propagate=0) state, status = State.Fault, "" @@ -567,7 +567,7 @@ def raw_read_axis_states(self, axes=None, ctrl_states=None): information for each axis and a boolean telling if an error occured :rtype: dict, bool""" if axes is None: - axes = self._element_axis.keys() + axes = list(self._element_axis.keys()) if ctrl_states is None: ctrl_states = {} @@ -660,7 +660,7 @@ def raw_read_axis_values(self, axes=None, ctrl_values=None): :return: a map containing the controller value information for each axis :rtype: dict""" if axes is None: - axes = self._element_axis.keys() + axes = list(self._element_axis.keys()) if ctrl_values is None: ctrl_values = {} @@ -744,7 +744,7 @@ def raw_read_axis_value_refs(self, axes=None, ctrl_values=None): :rtype: dict """ if axes is None: - axes = self._element_axis.keys() + axes = list(self._element_axis.keys()) if ctrl_values is None: ctrl_values = {} @@ -830,7 +830,7 @@ def stop_elements(self, elements=None): """ if elements is None: - axes = self.get_element_axis().keys() + axes = list(self.get_element_axis().keys()) else: axes = [e.axis for e in elements] error_axes = self.stop_axes(axes) @@ -916,7 +916,7 @@ def abort_elements(self, elements=None): """ if elements is None: - axes = self.get_element_axis().keys() + axes = list(self.get_element_axis().keys()) else: axes = [e.axis for e in elements] error_axes = self.abort_axes(axes) @@ -972,13 +972,13 @@ def send_to_controller(self, stream): def raw_move(self, axis_pos): ctrl = self.ctrl ctrl.PreStartAll() - for axis, dial_position in axis_pos.items(): + for axis, dial_position in list(axis_pos.items()): ret = ctrl.PreStartOne(axis, dial_position) if not ret: raise Exception("%s.PreStartOne(%d, %f) returns False" % (self.name, axis, dial_position)) - for axis, dial_position in axis_pos.items(): + for axis, dial_position in list(axis_pos.items()): ctrl.StartOne(axis, dial_position) ctrl.StartAll() diff --git a/src/sardana/pool/poolcontrollermanager.py b/src/sardana/pool/poolcontrollermanager.py index aeb7d17ad9..c1012ebecd 100644 --- a/src/sardana/pool/poolcontrollermanager.py +++ b/src/sardana/pool/poolcontrollermanager.py @@ -164,7 +164,7 @@ def setControllerPath(self, controller_path, reload=True): controller_file_names = self._findControllerLibNames() - for mod_name, file_name in controller_file_names.iteritems(): + for mod_name, file_name in controller_file_names.items(): dir_name = os.path.dirname(file_name) path = [dir_name] try: @@ -488,7 +488,7 @@ def getControllerLibs(self, filter=None): ret, expr = [], None if filter is not None: expr = re.compile(filter, re.IGNORECASE) - for name, lib in self._modules.iteritems(): + for name, lib in self._modules.items(): if lib.has_errors() or (expr is not None and expr.match(name) is None): continue ret.append(lib) @@ -500,7 +500,7 @@ def getControllers(self, filter=None): return sorted(self._controller_dict.values()) expr = re.compile(filter, re.IGNORECASE) - ret = sorted([kls for n, kls in self._controller_dict.iteritems() + ret = sorted([kls for n, kls in self._controller_dict.items() if not expr.match(n) is None]) return ret @@ -519,12 +519,12 @@ def getControllerMetaClasses(self, controller_names): def getControllerLib(self, name): if os.path.isabs(name): abs_file_name = name - for lib in self._modules.values(): + for lib in list(self._modules.values()): if lib.file_path == abs_file_name: return lib elif name.count(os.path.extsep): file_name = name - for lib in self._modules.values(): + for lib in list(self._modules.values()): if lib.file_name == file_name: return lib module_name = name diff --git a/src/sardana/pool/poolcontrollers/DiscretePseudoMotorController.py b/src/sardana/pool/poolcontrollers/DiscretePseudoMotorController.py index 6e3613b96c..c3ac6ef5ac 100644 --- a/src/sardana/pool/poolcontrollers/DiscretePseudoMotorController.py +++ b/src/sardana/pool/poolcontrollers/DiscretePseudoMotorController.py @@ -270,14 +270,14 @@ def setConfiguration(self, axis, value): labels = [] positions = [] calibration = [] - for k, v in mapping.items(): + for k, v in list(mapping.items()): labels.append(k) pos = int(v['pos']) if pos in positions: msg = 'position {0} is already used'.format(pos) raise ValueError(msg) positions.append(pos) - if all([x in v.keys() for x in ['min', 'set', 'max']]): + if all([x in list(v.keys()) for x in ['min', 'set', 'max']]): calibration.append([v['min'], v['set'], v['max']]) self._labels_cfg = labels self._positions_cfg = positions diff --git a/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py b/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py index e49043cbbd..81e7560f8d 100644 --- a/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py +++ b/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py @@ -185,7 +185,7 @@ def ReadAll(self): if self.counting_channels: now = time.time() elapsed_time = now - self.start_time - for axis, channel in self.read_channels.items(): + for axis, channel in list(self.read_channels.items()): self._updateChannelState(axis, elapsed_time) if channel.is_counting: self._updateChannelValue(axis, elapsed_time) @@ -250,7 +250,7 @@ def _updateChannelValue(self, axis, elapsed_time): def _finish(self, elapsed_time, axis=None): if axis is None: - for axis, channel in self.counting_channels.items(): + for axis, channel in list(self.counting_channels.items()): channel.is_counting = False self._updateChannelValue(axis, elapsed_time) elif axis in self.counting_channels: @@ -341,6 +341,6 @@ def event_received(self, src, type_, value): # for the moment only react on first trigger if type_.name.lower() == "active" and value == 0: self._armed = False - for axis, channel in self.counting_channels.iteritems(): + for axis, channel in self.counting_channels.items(): channel.is_counting = True self.start_time = time.time() diff --git a/src/sardana/pool/poolcontrollers/DummyMotorController.py b/src/sardana/pool/poolcontrollers/DummyMotorController.py index 2910c00752..64eb53b2e3 100644 --- a/src/sardana/pool/poolcontrollers/DummyMotorController.py +++ b/src/sardana/pool/poolcontrollers/DummyMotorController.py @@ -585,7 +585,7 @@ def StartOne(self, axis, pos): def StartAll(self): #raise Exception("Cannot move on StartAll") t = time.time() - for motion, pos in self.motions.items(): + for motion, pos in list(self.motions.items()): motion.startMotion(motion.getCurrentUserPosition(t), pos, t) def AbortOne(self, axis): @@ -657,7 +657,7 @@ def StartOne(self, axis, pos): self.motions[self.m[idx]] = pos def StartAll(self): - for motion, pos in self.motions.items(): + for motion, pos in list(self.motions.items()): motion.curr_pos = pos def AbortOne(self, axis): diff --git a/src/sardana/pool/poolcontrollers/DummyOneDController.py b/src/sardana/pool/poolcontrollers/DummyOneDController.py index 35ad53d039..8d32a6e62e 100644 --- a/src/sardana/pool/poolcontrollers/DummyOneDController.py +++ b/src/sardana/pool/poolcontrollers/DummyOneDController.py @@ -187,7 +187,7 @@ def _updateChannelValue(self, axis, elapsed_time): def _finish(self, elapsed_time, axis=None): if axis is None: - for axis, channel in self.counting_channels.items(): + for axis, channel in list(self.counting_channels.items()): channel.is_counting = False self._updateChannelValue(axis, elapsed_time) else: @@ -212,7 +212,7 @@ def ReadAll(self): if self.counting_channels: now = time.time() elapsed_time = now - self.start_time - for axis, channel in self.read_channels.items(): + for axis, channel in list(self.read_channels.items()): self._updateChannelState(axis, elapsed_time) if channel.is_counting: self._updateChannelValue(axis, elapsed_time) diff --git a/src/sardana/pool/poolcontrollers/DummyTwoDController.py b/src/sardana/pool/poolcontrollers/DummyTwoDController.py index 73b6403d27..0c78168cd7 100644 --- a/src/sardana/pool/poolcontrollers/DummyTwoDController.py +++ b/src/sardana/pool/poolcontrollers/DummyTwoDController.py @@ -329,7 +329,7 @@ def ReadOne(self, axis): def _finish(self, elapsed_time, axis=None): if axis is None: - for axis, channel in self.counting_channels.items(): + for axis, channel in list(self.counting_channels.items()): channel.is_counting = False self._updateChannelValue(axis, elapsed_time) elif axis in self.counting_channels: @@ -434,7 +434,7 @@ def event_received(self, src, type_, value): # for the moment only react on first trigger if type_.name.lower() == "active" and value == 0: self._armed = False - for axis, channel in self.counting_channels.iteritems(): + for axis, channel in self.counting_channels.items(): channel.is_counting = True self.start_time = time.time() diff --git a/src/sardana/pool/poolcontrollers/DummyZeroDController.py b/src/sardana/pool/poolcontrollers/DummyZeroDController.py index b65092c33d..656e588e16 100644 --- a/src/sardana/pool/poolcontrollers/DummyZeroDController.py +++ b/src/sardana/pool/poolcontrollers/DummyZeroDController.py @@ -70,7 +70,7 @@ def PreReadOne(self, ind): self.read_channels[ind] = channel def ReadAll(self): - for channel in self.read_channels.values(): + for channel in list(self.read_channels.values()): self._setChannelValue(channel) def ReadOne(self, ind): diff --git a/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py index 9de372e631..f104065e8f 100644 --- a/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py +++ b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py @@ -352,7 +352,7 @@ def __init__(self, inst, props, *args, **kwargs): self.detector = Hkl.Detector.factory_new(Hkl.DetectorType(0)) - for key, factory in Hkl.factories().iteritems(): + for key, factory in Hkl.factories().items(): if key == self.DiffractometerType: self.geometry = factory.create_new_geometry() self.engines = factory.create_new_engine_list() @@ -491,8 +491,8 @@ def CalcAllPhysical(self, pseudo_pos, curr_physical_pos): self.getWavelength() solutions = self._solutions(values, curr_physical_pos) - if self.selected_trajectory > len(solutions.items()): - self.selected_trajectory = len(solutions.items()) - 1 + if self.selected_trajectory > len(list(solutions.items())): + self.selected_trajectory = len(list(solutions.items())) - 1 for i, item in enumerate(solutions.items()): if i == self.selected_trajectory: angles = item.geometry_get().axis_values_get(USER) @@ -586,7 +586,7 @@ def getHKLModeList(self): # something special with the hkl one ???  neverthless I would # be possible to create a self.engines instead of recomputing # it all the time. - for key, factory in Hkl.factories().iteritems(): + for key, factory in Hkl.factories().items(): if key == self.DiffractometerType: new_engines = factory.create_new_engine_list() new_engines.init(self.geometry, self.detector, self.sample) @@ -788,7 +788,7 @@ def setComputeTrajectoriesSim(self, values): curr_physical_pos = self.geometry.axis_values_get(USER) solutions = self._solutions(values, curr_physical_pos) self.trajectorylist = [item.geometry_get().axis_values_get(USER) - for item in solutions.items()] + for item in list(solutions.items())] self.lastpseudos = tuple(values) def getTrajectoryList(self): diff --git a/src/sardana/pool/poolcontrollers/test/base.py b/src/sardana/pool/poolcontrollers/test/base.py index 6fe43e1422..081a42e0f4 100644 --- a/src/sardana/pool/poolcontrollers/test/base.py +++ b/src/sardana/pool/poolcontrollers/test/base.py @@ -90,7 +90,7 @@ def stateOne(self, expected_state=State.On): def start_action(self, configuration): """ This method set the axis parameters and pre start the axis. """ - for key, value in configuration.items(): + for key, value in list(configuration.items()): self.axisPar(key, value) self.ctrl.SynchOne(configuration) @@ -240,7 +240,7 @@ def __init__(self): self.passive_events = {} def getCount(self): - count = len(self.passive_events.keys()) + count = len(list(self.passive_events.keys())) return count count = property(getCount) diff --git a/src/sardana/pool/poolextension.py b/src/sardana/pool/poolextension.py index a9e78ed9eb..739962c8b2 100644 --- a/src/sardana/pool/poolextension.py +++ b/src/sardana/pool/poolextension.py @@ -80,7 +80,7 @@ def translate_ctrl_value(value): if isinstance(value, SardanaValue): return value - for _, translator in __CTRL_VALUE_TRANSLATORS.items(): + for _, translator in list(__CTRL_VALUE_TRANSLATORS.items()): try: return translator(value) except CannotTranslateException: diff --git a/src/sardana/pool/poolgroupelement.py b/src/sardana/pool/poolgroupelement.py index cb76b84533..173e389822 100644 --- a/src/sardana/pool/poolgroupelement.py +++ b/src/sardana/pool/poolgroupelement.py @@ -46,7 +46,7 @@ def serialize(self, *args, **kwargs): kwargs = PoolBaseElement.serialize(self, *args, **kwargs) elements = [elem.name for elem in self.get_user_elements()] physical_elements = [] - for elem_list in self.get_physical_elements().values(): + for elem_list in list(self.get_physical_elements().values()): for elem in elem_list: physical_elements.append(elem.name) kwargs['elements'] = elements @@ -66,7 +66,7 @@ def set_action_cache(self, action_cache): def read_state_info(self): state_info = {} ctrl_state_info = self.get_action_cache().read_state_info(serial=True) - for elem, ctrl_elem_state_info in ctrl_state_info.items(): + for elem, ctrl_elem_state_info in list(ctrl_state_info.items()): elem_state_info = elem._from_ctrl_state_info(ctrl_elem_state_info) elem.put_state_info(elem_state_info) state = elem.get_state(cache=True, propagate=0) diff --git a/src/sardana/pool/poolinstrument.py b/src/sardana/pool/poolinstrument.py index 6741e59fe6..56a0d1944b 100644 --- a/src/sardana/pool/poolinstrument.py +++ b/src/sardana/pool/poolinstrument.py @@ -71,7 +71,7 @@ def remove_instrument(self, instrument): del self._child_instruments[instrument.id] def get_instruments(self): - return self._child_instruments.values() + return list(self._child_instruments.values()) def set_parent_instrument(self, instrument): if instrument: @@ -93,7 +93,7 @@ def remove_element(self, element): del self._elements[element.id] def get_elements(self): - return [e() for e in self._elements.values()] + return [e() for e in list(self._elements.values())] def has_instruments(self): return len(self._child_instruments) > 0 diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index b13e91da23..2d3bba6b6e 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -518,7 +518,7 @@ def get_timerable_ctrls(self, acq_synch=None, enabled=None): """ timerable_ctrls = [] if acq_synch is None: - for ctrls in self._timerable_ctrls.values(): + for ctrls in list(self._timerable_ctrls.values()): timerable_ctrls += ctrls elif isinstance(acq_synch, list): acq_synch_list = acq_synch @@ -635,7 +635,7 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): user_config['label'] = label user_config['description'] = description - for ctrl_name, ctrl_data in cfg['controllers'].items(): + for ctrl_name, ctrl_data in list(cfg['controllers'].items()): # backwards compatibility for measurement groups created before # implementing feature-372: # https://sourceforge.net/p/sardana/tickets/372/ @@ -729,7 +729,7 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): ctrl_enabled = False if 'channels' in ctrl_data: user_config_ctrl['channels'] = user_config_channel = {} - for ch_name, ch_data in ctrl_data['channels'].items(): + for ch_name, ch_data in list(ctrl_data['channels'].items()): if external: validator = TangoAttributeNameValidator() full_name = ch_data.get('full_name', ch_name) diff --git a/src/sardana/pool/poolmetacontroller.py b/src/sardana/pool/poolmetacontroller.py index 242c737306..ac34119bd2 100644 --- a/src/sardana/pool/poolmetacontroller.py +++ b/src/sardana/pool/poolmetacontroller.py @@ -119,7 +119,7 @@ def __init__(self, **kwargs): #: dictionary #: dict<:data:`~sardana.ElementType`, :class:`~sardana.pool.poolmetacontroller.TypeData`> TYPE_MAP_OBJ = {} -for t, d in TYPE_MAP.items(): +for t, d in list(TYPE_MAP.items()): o = TypeData(type=t, name=d[0], family=d[1], klass=d[2], auto_full_name=d[3], ctrl_klass=d[4]) TYPE_MAP_OBJ[t] = o @@ -272,7 +272,7 @@ def __init__(self, **kwargs): dep_msg = ("Defining the controller property description using a " + "string is deprecated, use " + "sardana.pool.controller.Description constant instead.") - for k, v in klass.class_prop.items(): # old member + for k, v in list(klass.class_prop.items()): # old member props[k] = DataInfo.toDataInfo(k, v) if Description in v: self.ctrl_properties_descriptions.append(v[Description]) @@ -280,7 +280,7 @@ def __init__(self, **kwargs): self.warning(dep_msg) self.ctrl_properties_descriptions.append(v['Description']) - for k, v in klass.ctrl_properties.items(): + for k, v in list(klass.ctrl_properties.items()): props[k] = DataInfo.toDataInfo(k, v) if Description in v: self.ctrl_properties_descriptions.append(v[Description]) @@ -292,13 +292,13 @@ def __init__(self, **kwargs): self.dict_extra['properties_desc'] = self.ctrl_properties_descriptions self.ctrl_attributes = ctrl_attrs = CaselessDict() - for k, v in klass.ctrl_attributes.items(): + for k, v in list(klass.ctrl_attributes.items()): ctrl_attrs[k] = DataInfo.toDataInfo(k, v) self.axis_attributes = axis_attrs = CaselessDict() - for k, v in klass.ctrl_extra_attributes.items(): # old member + for k, v in list(klass.ctrl_extra_attributes.items()): # old member axis_attrs[k] = DataInfo.toDataInfo(k, v) - for k, v in klass.axis_attributes.items(): + for k, v in list(klass.axis_attributes.items()): axis_attrs[k] = DataInfo.toDataInfo(k, v) self.types = types = self.__build_types() @@ -330,7 +330,7 @@ def __init__(self, **kwargs): def __build_types(self): types = [] klass = self.klass - for _type, type_data in TYPE_MAP_OBJ.items(): + for _type, type_data in list(TYPE_MAP_OBJ.items()): if _type not in TYPE_ELEMENTS: continue if issubclass(klass, type_data.ctrl_klass): diff --git a/src/sardana/pool/poolmonitor.py b/src/sardana/pool/poolmonitor.py index 6549fa03ea..ecc8d0e31c 100644 --- a/src/sardana/pool/poolmonitor.py +++ b/src/sardana/pool/poolmonitor.py @@ -76,7 +76,7 @@ def on_pool_changed(self, evt_src, evt_type, evt_value): types = set(pool_ctrl.get_ctrl_types()) if types.isdisjoint(TYPE_PSEUDO_ELEMENTS): ctrl_ids.append(pool_ctrl.id) - elem_ids.extend(pool_ctrl.get_element_ids().keys()) + elem_ids.extend(list(pool_ctrl.get_element_ids().keys())) elem_ids.sort() self._elem_ids = elem_ids self._ctrl_ids = ctrl_ids @@ -106,7 +106,7 @@ def update_state_info(self): else: blocked_ctrls.add(ctrl) - for ctrl, ctrl_elems in ctrl_items.items(): + for ctrl, ctrl_elems in list(ctrl_items.items()): ret = ctrl.lock(blocking=False) if ret: ctrls.append(ctrl) @@ -123,7 +123,7 @@ def update_state_info(self): elem.unlock() def _update_state_info_serial(self, pool_ctrls): - for pool_ctrl, elems in pool_ctrls.items(): + for pool_ctrl, elems in list(pool_ctrls.items()): self._update_ctrl_state_info(pool_ctrl, elems) def _update_ctrl_state_info(self, pool_ctrl, elems): @@ -131,7 +131,7 @@ def _update_ctrl_state_info(self, pool_ctrl, elems): state_infos, exc_info = pool_ctrl.raw_read_axis_states(axes) if len(exc_info): self.info("STATE ERROR %s", exc_info) - for elem, state_info in state_infos.items(): + for elem, state_info in list(state_infos.items()): state_info = elem._from_ctrl_state_info(state_info) elem.set_state_info(state_info) diff --git a/src/sardana/pool/poolmotion.py b/src/sardana/pool/poolmotion.py index 02adc3c5fc..65b4e53f78 100644 --- a/src/sardana/pool/poolmotion.py +++ b/src/sardana/pool/poolmotion.py @@ -184,7 +184,7 @@ def _recover_start_error(self, ctrl, meth_name, read_state=False): if read_state: states = {} self.read_state_info(ret=states) - for moveable, state_info in states.items(): + for moveable, state_info in list(states.items()): state_info = moveable._from_ctrl_state_info(state_info) moveable._set_state_info(state_info) @@ -262,7 +262,7 @@ def start_action(self, *args, **kwargs): pool.motion_loop_states_per_position) self._motion_info = motion_info = {} - for moveable, motion_data in items.items(): + for moveable, motion_data in list(items.items()): it = moveable.instability_time motion_info[moveable] = PoolMotionItem(moveable, *motion_data, instability_time=it) @@ -336,7 +336,7 @@ def action_loop(self): state_error_occured = self._state_error_occured(states) timestamp = time.time() in_motion = False - for moveable, state_info in states.items(): + for moveable, state_info in list(states.items()): motion_item = motion_info[moveable] state_info = moveable._from_ctrl_state_info(state_info) @@ -420,7 +420,7 @@ def action_loop(self): self.error("Loop final read position error 2. " "Cannot send final position event!!!") - for moveable, position_info in positions.items(): + for moveable, position_info in list(positions.items()): moveable.put_dial_position(position_info, propagate=2) # send state @@ -438,7 +438,7 @@ def action_loop(self): if not i % nb_states_per_pos: self.read_dial_position(ret=positions) # send position - for moveable, position_value in positions.items(): + for moveable, position_value in list(positions.items()): if position_value.error: self.error("Loop read position error for %s" % moveable.name) @@ -447,14 +447,14 @@ def action_loop(self): time.sleep(nap) def _state_error_occured(self, d): - for _, (state_info, exc_info) in d.items(): + for _, (state_info, exc_info) in list(d.items()): state = state_info[0] if exc_info is not None or state not in _NON_ERROR_STATES: return True return False def _position_error_occured(self, positions): - for _, value in positions.items(): + for _, value in list(positions.items()): if value.error: return True @@ -487,7 +487,7 @@ def _recover_state_moving_error(self, location, emergency_stop, states): # send positions positions = {} self.read_dial_position(ret=positions) - for moveable, position_info in positions.items(): + for moveable, position_info in list(positions.items()): moveable.put_dial_position(position_info, propagate=2) motion_info = self._motion_info diff --git a/src/sardana/pool/poolmotorgroup.py b/src/sardana/pool/poolmotorgroup.py index af082aff7b..297cba123f 100644 --- a/src/sardana/pool/poolmotorgroup.py +++ b/src/sardana/pool/poolmotorgroup.py @@ -112,7 +112,7 @@ def update(self, cache=True, propagate=1): if not cache: dial_position_values = self.obj.motion.read_dial_position( serial=True) - for motion_obj, position_value in dial_position_values.items(): + for motion_obj, position_value in list(dial_position_values.items()): motion_obj.put_dial_position( position_value, propagate=propagate) @@ -292,7 +292,7 @@ def _start_move(self, new_positions): self._aborted = False items = self.calculate_motion(new_positions) timestamp = time.time() - for item, position_info in items.items(): + for item, position_info in list(items.items()): item.set_write_position(position_info[0], timestamp=timestamp, propagate=0) if not self._simulation_mode: diff --git a/src/sardana/pool/poolpseudocounter.py b/src/sardana/pool/poolpseudocounter.py index efe7c45318..39f7f1aad9 100644 --- a/src/sardana/pool/poolpseudocounter.py +++ b/src/sardana/pool/poolpseudocounter.py @@ -53,7 +53,7 @@ def __init__(self, *args, **kwargs): value_buf.add_listener(self.on_change) def on_change(self, evt_src, evt_type, evt_value): - for idx in evt_value.iterkeys(): + for idx in evt_value.keys(): physical_values = [] for value_buf in self.obj.get_physical_value_buffer_iterator(): try: @@ -209,7 +209,7 @@ def update(self, cache=True, propagate=1): values = self.obj.acquisition.read_value(serial=True) if not len(values): self._local_timestamp = time.time() - for acq_obj, value in values.items(): + for acq_obj, value in list(values.items()): acq_obj.put_value(value, propagate=propagate) @@ -233,7 +233,7 @@ def serialize(self, *args, **kwargs): kwargs = PoolBaseChannel.serialize(self, *args, **kwargs) elements = [elem.name for elem in self.get_user_elements()] physical_elements = [] - for elem_list in self.get_physical_elements().values(): + for elem_list in list(self.get_physical_elements().values()): for elem in elem_list: physical_elements.append(elem.name) cl_name = self.__class__.__name__ @@ -274,7 +274,7 @@ def set_action_cache(self, action_cache): def get_siblings(self): if self._siblings is None: self._siblings = siblings = set() - for axis, sibling in self.controller.get_element_axis().items(): + for axis, sibling in list(self.controller.get_element_axis().items()): if axis == self.axis: continue siblings.add(sibling) @@ -399,7 +399,7 @@ def read_state_info(self, state_info=None): state_info = {} action_cache = self.get_action_cache() ctrl_state_infos = action_cache.read_state_info(serial=True) - for obj, ctrl_state_info in ctrl_state_infos.items(): + for obj, ctrl_state_info in list(ctrl_state_infos.items()): state_info = obj._from_ctrl_state_info(ctrl_state_info) obj.put_state_info(state_info) for user_element in self.get_user_elements(): diff --git a/src/sardana/pool/poolpseudomotor.py b/src/sardana/pool/poolpseudomotor.py index 33344ca223..a3282db844 100644 --- a/src/sardana/pool/poolpseudomotor.py +++ b/src/sardana/pool/poolpseudomotor.py @@ -202,7 +202,7 @@ def calc_physical(self, new_position): positions = obj.get_siblings_positions() positions[obj] = new_position new_positions = len(positions) * [None] - for pseudo, position in positions.items(): + for pseudo, position in list(positions.items()): new_positions[pseudo.axis - 1] = position result = obj.controller.calc_all_physical(new_positions, @@ -227,7 +227,7 @@ def update(self, cache=True, propagate=1): serial=True) if not len(dial_position_values): self._local_timestamp = time.time() - for motion_obj, position_value in dial_position_values.items(): + for motion_obj, position_value in list(dial_position_values.items()): motion_obj.put_dial_position( position_value, propagate=propagate) @@ -258,7 +258,7 @@ def serialize(self, *args, **kwargs): kwargs = PoolElement.serialize(self, *args, **kwargs) elements = [elem.name for elem in self.get_user_elements()] physical_elements = [] - for elem_list in self.get_physical_elements().values(): + for elem_list in list(self.get_physical_elements().values()): for elem in elem_list: physical_elements.append(elem.name) cl_name = self.__class__.__name__ @@ -293,7 +293,7 @@ def set_action_cache(self, action_cache): def get_siblings(self): if self._siblings is None: self._siblings = siblings = set() - for axis, sibling in self.controller.get_element_axis().items(): + for axis, sibling in list(self.controller.get_element_axis().items()): if axis == self.axis: continue siblings.add(sibling) @@ -486,7 +486,7 @@ def read_state_info(self, state_info=None): state_info = {} action_cache = self.get_action_cache() ctrl_state_infos = action_cache.read_state_info(serial=True) - for motion_obj, ctrl_state_info in ctrl_state_infos.items(): + for motion_obj, ctrl_state_info in list(ctrl_state_infos.items()): state_info[motion_obj] = motion_state_info = \ motion_obj._from_ctrl_state_info(ctrl_state_info) motion_obj.put_state_info(motion_state_info) @@ -534,7 +534,7 @@ def calculate_motion(self, new_position, items=None, calculated=None): write_pos=self.drift_correction) positions[self] = new_position pseudo_positions = len(positions) * [None] - for pseudo, position in positions.items(): + for pseudo, position in list(positions.items()): pseudo_positions[pseudo.axis - 1] = position curr_physical_positions = self._position.get_physical_positions() physical_positions = self.controller.calc_all_physical(pseudo_positions, @@ -592,7 +592,7 @@ def _start_move(self, new_position): self._stopped = False items = self.calculate_motion(new_position) timestamp = time.time() - for item, position_info in items.items(): + for item, position_info in list(items.items()): item.set_write_position(position_info[0], timestamp=timestamp, propagate=1) if not self._simulation_mode: diff --git a/src/sardana/pool/poolsynchronization.py b/src/sardana/pool/poolsynchronization.py index aaf81832bc..feb1934a67 100644 --- a/src/sardana/pool/poolsynchronization.py +++ b/src/sardana/pool/poolsynchronization.py @@ -261,7 +261,7 @@ def action_loop(self): time.sleep(nap) # Set element states after ending the triggering - for element, state_info in states.items(): + for element, state_info in list(states.items()): with element: element.clear_operation() state_info = element._from_ctrl_state_info(state_info) diff --git a/src/sardana/pool/test/base.py b/src/sardana/pool/test/base.py index b65e3147dd..fe8b3f3ef5 100644 --- a/src/sardana/pool/test/base.py +++ b/src/sardana/pool/test/base.py @@ -193,26 +193,26 @@ def setUp(self): self.createMotorElement(ctrl_obj, name, axis) # Check the elements creation - cts = len(self.cts.keys()) - tgs = len(self.tgs.keys()) - mots = len(self.mots.keys()) + cts = len(list(self.cts.keys())) + tgs = len(list(self.tgs.keys())) + mots = len(list(self.mots.keys())) expected_cts = self.nctelems * self.nctctrls msg = 'Something happened during the creation of CT elements.\n' + \ 'Expected %s and there are %s, %s' % \ - (expected_cts, cts, self.cts.keys()) + (expected_cts, cts, list(self.cts.keys())) if cts != expected_cts: raise Exception(msg) expected_tgs = self.ntgelems * self.ntgctrls msg = 'Something happened during the creation of TG elements.\n' + \ 'Expected %s and there are %s, %s' % \ - (expected_tgs, tgs, self.tgs.keys()) + (expected_tgs, tgs, list(self.tgs.keys())) if tgs != expected_tgs: raise Exception(msg) expected_mots = self.nmotelems * self.nmotctrls msg = 'Something happened during the creation of MOT elements.\n' + \ 'Expected %s and there are %s, %s' % \ - (self.nmotelems, mots, self.mots.keys()) + (self.nmotelems, mots, list(self.mots.keys())) if mots != expected_mots: raise Exception(msg) diff --git a/src/sardana/pool/test/helper.py b/src/sardana/pool/test/helper.py index ce4649f0f3..93505598f1 100644 --- a/src/sardana/pool/test/helper.py +++ b/src/sardana/pool/test/helper.py @@ -67,7 +67,7 @@ def createPoolController(pool, conf): ctrl_properties = ctrl_class_info.ctrl_properties else: ctrl_properties = {} - for prop_info in ctrl_properties.values(): + for prop_info in list(ctrl_properties.values()): prop_name = prop_info.name prop_value = properties.get(prop_name) if prop_value is None: diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index a2ea0944fa..b317333b4e 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -215,7 +215,7 @@ def meas_cont_stop_acquisition(self, config, synchronization, msg = "The number of busy workers is not zero; numBW = %s" % (numBW) self.assertEqual(numBW, 0, msg) # print the acquisition records - for i, record in enumerate(zip(*self.attr_listener.data.values())): + for i, record in enumerate(zip(*list(self.attr_listener.data.values()))): print i, record def meas_contpos_acquisition(self, config, synchronization, moveable, diff --git a/src/sardana/pool/test/util.py b/src/sardana/pool/test/util.py index 0a5ab0a3c2..5cb2b77592 100644 --- a/src/sardana/pool/test/util.py +++ b/src/sardana/pool/test/util.py @@ -51,8 +51,8 @@ def event_received(self, *args, **kwargs): # of buffered attributes) or from the value in case of normal # attributes chunk = v - idx = chunk.keys() - value = [sardana_value.value for sardana_value in chunk.values()] + idx = list(chunk.keys()) + value = [sardana_value.value for sardana_value in list(chunk.values())] # filling the measurement records with self.data_lock: channel_data = self.data.get(obj_name, []) @@ -65,7 +65,7 @@ def get_table(self): '''Construct a table-like array with padded channel data as columns. Return the ''' with self.data_lock: - max_len = max([len(d) for d in self.data.values()]) + max_len = max([len(d) for d in list(self.data.values())]) dtype_spec = [] table = [] for k in sorted(self.data.keys()): diff --git a/src/sardana/sardanacontainer.py b/src/sardana/sardanacontainer.py index 7e64de7cfb..74efc71da2 100644 --- a/src/sardana/sardanacontainer.py +++ b/src/sardana/sardanacontainer.py @@ -204,7 +204,7 @@ def get_elements_by_type(self, t): elem_types_dict = self._element_types.get(t) if elem_types_dict is None: return [] - return elem_types_dict.values() + return list(elem_types_dict.values()) def get_element_names_by_type(self, t): """Returns a list of all pool object names of the given type diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index 4b0cad17f2..d6cb7094c5 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -495,7 +495,7 @@ def __root_expand_sardana_interface_data(): #: An enumeration describing the all possible sardana interfaces Interface = Enumeration("Interface", - __root_expand_sardana_interface_data().items()) + list(__root_expand_sardana_interface_data().items())) def __create_sardana_interfaces(): diff --git a/src/sardana/sardanameta.py b/src/sardana/sardanameta.py index 1ccda440ed..a841c59dfb 100644 --- a/src/sardana/sardanameta.py +++ b/src/sardana/sardanameta.py @@ -180,7 +180,7 @@ def get_meta_classes(self): :return: a sequence of meta classes that belong to this library :rtype: seq<:class:~`sardana.sardanameta.SardanaClass`>""" - return self.meta_classes.values() + return list(self.meta_classes.values()) def has_meta_class(self, meta_class_name): """Returns True if the given meta class name belongs to this library @@ -218,7 +218,7 @@ def get_meta_functions(self): :return: a sequence of meta functions that belong to this library :rtype: seq<:class:~`sardana.sardanameta.SardanaFunction`>""" - return self.meta_functions.values() + return list(self.meta_functions.values()) def has_meta_function(self, meta_function_name): """Returns True if the given meta function name belongs to this library @@ -380,8 +380,8 @@ def serialize(self, *args, **kwargs): kwargs['file_name'] = self.file_name kwargs['path'] = self.path kwargs['description'] = self.description - kwargs['elements'] = self.meta_classes.keys() + \ - self.meta_functions.keys() + kwargs['elements'] = list(self.meta_classes.keys()) + \ + list(self.meta_functions.keys()) if self.exc_info is None: kwargs['exc_summary'] = None kwargs['exc_info'] = None diff --git a/src/sardana/sardanamodulemanager.py b/src/sardana/sardanamodulemanager.py index d45623fe9a..8c59feae56 100644 --- a/src/sardana/sardanamodulemanager.py +++ b/src/sardana/sardanamodulemanager.py @@ -102,7 +102,7 @@ def add_python_path(self, path): with self._path_lock: path_id = self.get_new_id() - for _, p_info in pif.items(): + for _, p_info in list(pif.items()): p_info[0] += path_len pif[path_id] = [0, path] @@ -293,7 +293,7 @@ def unloadModule(self, module_name): def unloadModules(self, module_list=None): """Unloads the given module name""" - modules = module_list or self._modules.keys() + modules = module_list or list(self._modules.keys()) for module in modules: self.unloadModule(module) diff --git a/src/sardana/spock/inputhandler.py b/src/sardana/spock/inputhandler.py index eb88af3e75..f2b099f9c0 100644 --- a/src/sardana/spock/inputhandler.py +++ b/src/sardana/spock/inputhandler.py @@ -125,7 +125,7 @@ def input(self, input_data=None): interfaces = ms.getInterfaces() if data_type in interfaces: input_data['data_type'] = [ - elem.name for elem in interfaces[data_type].values()] + elem.name for elem in list(interfaces[data_type].values())] self._conn.send(input_data) ret = self._conn.recv() return ret diff --git a/src/sardana/tango/core/SardanaDevice.py b/src/sardana/tango/core/SardanaDevice.py index bbfd962b08..93cd40e7b2 100644 --- a/src/sardana/tango/core/SardanaDevice.py +++ b/src/sardana/tango/core/SardanaDevice.py @@ -184,7 +184,7 @@ def init_device_nodb(self): """Internal method. Initialize the device when tango database is not being used (example: in demos)""" _, _, props = self._get_nodb_device_info() - for prop_name, prop_value in props.items(): + for prop_name, prop_value in list(props.items()): setattr(self, prop_name, prop_value) def delete_device(self): diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 04a2a654fd..e7ee850df8 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -389,7 +389,7 @@ def from_tango_state_to_state(state): DataType.String: DevString, DataType.Boolean: DevBoolean, } -R_TTYPE_MAP = dict((v, k) for k, v in TTYPE_MAP.items()) +R_TTYPE_MAP = dict((v, k) for k, v in list(TTYPE_MAP.items())) #: dictionary dict<:class:`sardana.DataFormat`, :class:`PyTango.AttrFormat`> TFORMAT_MAP = { @@ -397,7 +397,7 @@ def from_tango_state_to_state(state): DataFormat.OneD: SPECTRUM, DataFormat.TwoD: IMAGE, } -R_TFORMAT_MAP = dict((v, k) for k, v in TFORMAT_MAP.items()) +R_TFORMAT_MAP = dict((v, k) for k, v in list(TFORMAT_MAP.items())) #: dictionary dict<:class:`sardana.DataAccess`, :class:`PyTango.AttrWriteType`> TACCESS_MAP = { @@ -405,7 +405,7 @@ def from_tango_state_to_state(state): DataAccess.ReadWrite: READ_WRITE, } -R_TACCESS_MAP = dict((v, k) for k, v in TACCESS_MAP.items()) +R_TACCESS_MAP = dict((v, k) for k, v in list(TACCESS_MAP.items())) def exception_str(etype=None, value=None, sep='\n'): @@ -528,7 +528,7 @@ def ask_yes_no(prompt, default=None): elif d_l in ('n', 'no'): prompt += " (N/y) ?" - while ans not in answers.keys(): + while ans not in list(answers.keys()): try: ans = raw_input(prompt + ' ').lower() if not ans: # response was an empty string @@ -536,7 +536,7 @@ def ask_yes_no(prompt, default=None): except KeyboardInterrupt: print except EOFError: - if default in answers.keys(): + if default in list(answers.keys()): ans = default print else: @@ -738,8 +738,8 @@ def prepare_server(args, tango_args): # to pool_names = [] pools = get_dev_from_class(db, "Pool") - all_pools = pools.keys() - for pool in pools.values(): + all_pools = list(pools.keys()) + for pool in list(pools.values()): pool_alias = pool[2] if pool_alias is not None: all_pools.append(pool_alias) diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py index 8def9d4c7d..c6cc888802 100644 --- a/src/sardana/tango/macroserver/Door.py +++ b/src/sardana/tango/macroserver/Door.py @@ -175,7 +175,7 @@ def delete_device(self): self.macro_executor.abort() self.macro_executor.clearRunningMacro() - for handler, filter, format in self._handler_dict.values(): + for handler, filter, format in list(self._handler_dict.values()): handler.finish() door = self.door @@ -400,7 +400,7 @@ def is_ResumeMacro_allowed(self): def RunMacro(self, par_str_list): # first empty all the buffers - for handler, filter, fmt in self._handler_dict.values(): + for handler, filter, fmt in list(self._handler_dict.values()): handler.clearBuffer() if len(par_str_list) == 0: @@ -424,7 +424,7 @@ def GetMacroEnv(self, argin): macro_env = self.door.get_macro_class_info(macro_name).env env = self.door.get_env(macro_env, macro_name=macro_name) ret = [] - for k, v in env.iteritems(): + for k, v in env.items(): ret.extend((k, v)) return ret diff --git a/src/sardana/tango/macroserver/MacroServer.py b/src/sardana/tango/macroserver/MacroServer.py index dd80efb168..b8e1ff6adb 100644 --- a/src/sardana/tango/macroserver/MacroServer.py +++ b/src/sardana/tango/macroserver/MacroServer.py @@ -68,7 +68,7 @@ def delete_device(self): self._macro_server.clear_log_report() # Workaround for bug #494. factory = taurus.Factory("tango") - for attr in factory.tango_attrs.values(): + for attr in list(factory.tango_attrs.values()): attr.cleanUp() def init_device(self): @@ -261,7 +261,7 @@ def GetMacroInfo(self, macro_names): codec = CodecFactory().getCodec('json') ret = [] - for _, macro in macro_server.get_macros().items(): + for _, macro in list(macro_server.get_macros().items()): if macro.name in macro_names: ret.append(codec.encode(('', macro.serialize()))[1]) diff --git a/src/sardana/tango/macroserver/test/base.py b/src/sardana/tango/macroserver/test/base.py index ee8b7cd40c..f6fccfefc7 100644 --- a/src/sardana/tango/macroserver/test/base.py +++ b/src/sardana/tango/macroserver/test/base.py @@ -78,7 +78,7 @@ def setUp(self, properties=None): self._msstarter.addNewDevice(self.door_name, klass='Door') # Add properties if properties: - for key, values in properties.items(): + for key, values in list(properties.items()): db.put_device_property(self.ms_name, {key: values}) # start MS server diff --git a/src/sardana/tango/pool/Controller.py b/src/sardana/tango/pool/Controller.py index 9b590505c6..dccbc3e13e 100644 --- a/src/sardana/tango/pool/Controller.py +++ b/src/sardana/tango/pool/Controller.py @@ -127,14 +127,14 @@ def _get_ctrl_properties(self): props = {} if prop_infos: props.update(db.get_device_property( - self.get_name(), prop_infos.keys())) - for p in props.keys(): + self.get_name(), list(prop_infos.keys()))) + for p in list(props.keys()): if len(props[p]) == 0: props[p] = None ret = {} missing_props = [] - for prop_name, prop_value in props.items(): + for prop_name, prop_value in list(props.items()): if prop_value is None: dv = prop_infos[prop_name].default_value if dv is None: @@ -235,7 +235,7 @@ def get_dynamic_attributes(self): return PoolDevice.get_dynamic_attributes(self) self._dynamic_attributes_cache = dyn_attrs = CaselessDict() self._standard_attributes_cache = std_attrs = CaselessDict() - for attr_name, attr_data in info.ctrl_attributes.items(): + for attr_name, attr_data in list(info.ctrl_attributes.items()): name, tg_info = to_tango_attr_info(attr_name, attr_data) dyn_attrs[attr_name] = attr_name, tg_info, attr_data return std_attrs, dyn_attrs diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 7daadbe3be..28f90a9cd7 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -159,14 +159,14 @@ def _synchronization_str2enum(self, synchronization): enums as keys instead of strings. ''' for group in synchronization: - for param, conf in group.iteritems(): + for param, conf in group.items(): group.pop(param) param = SynchParam.fromStr(param) group[param] = conf # skip repeats cause its value is just a long number if param == SynchParam.Repeats: continue - for domain, value in conf.iteritems(): + for domain, value in conf.items(): conf.pop(domain) domain = SynchDomain.fromStr(domain) conf[domain] = value @@ -208,7 +208,7 @@ def write_AcquisitionMode(self, attr): acq_mode = AcqMode.lookup[acq_mode_str] except KeyError: raise Exception("Invalid acquisition mode. Must be one of " + - ", ".join(AcqMode.keys())) + ", ".join(list(AcqMode.keys()))) self.measurement_group.acquisition_mode = acq_mode def read_Configuration(self, attr): diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index 3fae33c04f..a0511281fa 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -339,7 +339,7 @@ def CreateController(self, argin): "interface" % (class_name, type_str)) # check that necessary property values are set - for prop_name, prop_info in ctrl_class.ctrl_properties.items(): + for prop_name, prop_info in list(ctrl_class.ctrl_properties.items()): prop_value = properties.get(prop_name) if prop_value is None: if prop_info.default_value is None: @@ -450,7 +450,7 @@ def create_controller_cb(device_name): # Determine which controller writtable attributes have default value # and apply them to the newly created controller attrs = [] - for attr_name, attr_info in ctrl_class.ctrl_attributes.items(): + for attr_name, attr_info in list(ctrl_class.ctrl_attributes.items()): default_value = attr_info.default_value if default_value is None: continue @@ -466,10 +466,10 @@ def create_controller_cb(device_name): # for pseudo motor/counter controller also create pseudo # motor(s)/counter(s) automatically if elem_type == ElementType.PseudoMotor: - for pseudo_motor_info in pseudo_motor_infos.values(): + for pseudo_motor_info in list(pseudo_motor_infos.values()): self._create_single_element(pseudo_motor_info) elif elem_type == ElementType.PseudoCounter: - for pseudo_counter_info in pseudo_counter_infos.values(): + for pseudo_counter_info in list(pseudo_counter_infos.values()): self._create_single_element(pseudo_counter_info) #@DebugIt() @@ -621,7 +621,7 @@ def create_element_cb(device_name): # them to the newly created element ctrl_class_info = ctrl.get_ctrl_info() attrs = [] - for attr_name, attr_info in ctrl_class_info.getAxisAttributes().items(): + for attr_name, attr_info in list(ctrl_class_info.getAxisAttributes().items()): default_value = attr_info.default_value if default_value is None: continue @@ -790,7 +790,7 @@ def _format_create_json_arguments(self, argin): elems = [elems] for elem in elems: d = {} - for k, v in elem.items(): + for k, v in list(elem.items()): d[str(k)] = str(v) ret.append(d) return ret @@ -865,7 +865,7 @@ def _format_CreateMotorGroup_arguments(self, argin): elems = [elems] for elem in elems: d = {} - for k, v in elem.items(): + for k, v in list(elem.items()): d[str(k)] = str(v) ret.append(d) return ret @@ -890,7 +890,7 @@ def _format_CreateMeasurementGroup_arguments(self, argin): elems = [elems] for elem in elems: d = {} - for k, v in elem.items(): + for k, v in list(elem.items()): d[str(k)] = str(v) ret.append(d) return ret diff --git a/src/sardana/tango/pool/PoolDevice.py b/src/sardana/tango/pool/PoolDevice.py index 2946605ec2..946a4076e8 100644 --- a/src/sardana/tango/pool/PoolDevice.py +++ b/src/sardana/tango/pool/PoolDevice.py @@ -194,7 +194,7 @@ def initialize_dynamic_attributes(self): read = self.__class__._read_DynamicAttribute write = self.__class__._write_DynamicAttribute is_allowed = self.__class__._is_DynamicAttribute_allowed - for attr_name, data_info in std_attrs.items(): + for attr_name, data_info in list(std_attrs.items()): attr_name, data_info, attr_info = data_info attr = self.add_standard_attribute(attr_name, data_info, attr_info, read, @@ -205,7 +205,7 @@ def initialize_dynamic_attributes(self): read = self.__class__._read_DynamicAttribute write = self.__class__._write_DynamicAttribute is_allowed = self.__class__._is_DynamicAttribute_allowed - for attr_name, data_info in dyn_attrs.items(): + for attr_name, data_info in list(dyn_attrs.items()): attr_name, data_info, attr_info = data_info attr = self.add_dynamic_attribute(attr_name, data_info, attr_info, read, @@ -219,7 +219,7 @@ def remove_unwanted_dynamic_attributes(self, new_std_attrs, new_dyn_attrs): dev_class = self.get_device_class() multi_attr = self.get_device_attr() multi_class_attr = dev_class.get_class_attr() - static_attr_names = map(str.lower, dev_class.attr_list.keys()) + static_attr_names = map(str.lower, list(dev_class.attr_list.keys())) static_attr_names.extend(('state', 'status')) new_attrs = CaselessDict(new_std_attrs) @@ -670,7 +670,7 @@ def _get_dynamic_attributes(self): std_attrs_lower = [attr.lower() for attr in dev_class.standard_attr_list] - for attr_name, attr_info in axis_attrs.items(): + for attr_name, attr_info in list(axis_attrs.items()): attr_name_lower = attr_name.lower() if attr_name_lower in std_attrs_lower: data_info = DataInfo.toDataInfo(attr_name, attr_info) @@ -855,7 +855,7 @@ def _encode_value_chunk(self, value_chunk): :rtype: str""" index = [] value = [] - for idx, sdn_value in value_chunk.iteritems(): + for idx, sdn_value in value_chunk.items(): index.append(idx) value.append(sdn_value.value) data = dict(index=index, value=value) @@ -873,7 +873,7 @@ def _encode_value_ref_chunk(self, value_ref_chunk): """ index = [] value_ref = [] - for idx, sdn_value in value_ref_chunk.iteritems(): + for idx, sdn_value in value_ref_chunk.items(): index.append(idx) value_ref.append(sdn_value.value) data = dict(index=index, value_ref=value_ref) diff --git a/src/sardana/tango/pool/test/base.py b/src/sardana/tango/pool/test/base.py index c043ac70ed..fb87a954cc 100644 --- a/src/sardana/tango/pool/test/base.py +++ b/src/sardana/tango/pool/test/base.py @@ -62,7 +62,7 @@ def setUp(self, properties=None): self._starter.addNewDevice(self.pool_name, klass='Pool') # Add properties if properties is not None: - for key, values in properties.items(): + for key, values in list(properties.items()): db.put_device_property(self.pool_name, {key: values}) # start Pool server @@ -89,12 +89,12 @@ class ControllerLoadsTestCase(BasePoolTestCase): def test_controller_loads(self): """Test that the controller library and class can be loaded. """ - libraries = self.pool.getElementsOfType('ControllerLibrary').values() + libraries = list(self.pool.getElementsOfType('ControllerLibrary').values()) libraries_names = [lib.getName() for lib in libraries] - classes = self.pool.getElementsOfType('ControllerClass').values() + classes = list(self.pool.getElementsOfType('ControllerClass').values()) classes_names = [cls.getName() for cls in classes] - for test_lib, test_classes in self.controller_classes.items(): + for test_lib, test_classes in list(self.controller_classes.items()): msg = 'ControllerLibrary %s was not correctly loaded.' % test_lib self.assertIn(test_lib, libraries_names, msg) msg = 'ControllerClass %s was not correctly loaded.' diff --git a/src/sardana/tango/pool/test/base_sartest.py b/src/sardana/tango/pool/test/base_sartest.py index d4ee463536..3ce5a8ac90 100644 --- a/src/sardana/tango/pool/test/base_sartest.py +++ b/src/sardana/tango/pool/test/base_sartest.py @@ -37,7 +37,7 @@ def _cleanup_device(dev_name): device = taurus.Device(dev_name) # tango_alias_devs contains any names in which we have referred # to the device, could be alias, short name, etc. pop all of them - for k, v in factory.tango_alias_devs.items(): + for k, v in list(factory.tango_alias_devs.items()): if v is device: factory.tango_alias_devs.pop(k) full_name = device.getFullName() diff --git a/src/sardana/tango/pool/test/test_measurementgroup.py b/src/sardana/tango/pool/test/test_measurementgroup.py index 7dca88966c..a007d7eac2 100644 --- a/src/sardana/tango/pool/test/test_measurementgroup.py +++ b/src/sardana/tango/pool/test/test_measurementgroup.py @@ -121,18 +121,18 @@ def create_meas(self, config): self.expchan_names = [] self.tg_names = [] ordered_chns = [None] * 10 - for ctrl_name, ctrl_config in config.items(): + for ctrl_name, ctrl_config in list(config.items()): channels = ctrl_config["channels"] - for chn, chn_config in channels.items(): + for chn, chn_config in list(channels.items()): index = chn_config["index"] ordered_chns[index] = chn self.expchan_names = [chn for chn in ordered_chns if chn is not None] self.pool.CreateMeasurementGroup([self.mg_name] + self.expchan_names) - for ctrl_name in config.keys(): + for ctrl_name in list(config.keys()): ctrl_config = config.pop(ctrl_name) channels = ctrl_config["channels"] - for chn_name in channels.keys(): + for chn_name in list(channels.keys()): chn_config = channels.pop(chn_name) chn_full_name = _get_full_name(DeviceProxy(chn_name)) channels[chn_full_name] = chn_config @@ -160,7 +160,7 @@ def create_meas(self, config): ctrl_test_config = config[ctrl] channels = ctrl_config['channels'] channels_test = ctrl_test_config.pop("channels") - for chn, chn_config in channels.items(): + for chn, chn_config in list(channels.items()): chn_test_config = channels_test[chn] chn_config.update(chn_test_config) ctrl_config.update(ctrl_test_config) @@ -179,8 +179,8 @@ def prepare_meas(self, params): def _add_attribute_listener(self, config): self.attr_listener = TangoAttributeListener() chn_names = [] - for ctrl_config in config.values(): - for chn, chn_config in ctrl_config["channels"].items(): + for ctrl_config in list(config.values()): + for chn, chn_config in list(ctrl_config["channels"].items()): if chn_config.get("value_ref_enabled", False): buffer_attr = "ValueRefBuffer" else: @@ -257,7 +257,7 @@ def stopMeas(self): self.meas.stop() def tearDown(self): - for channel, event_id in self.event_ids.items(): + for channel, event_id in list(self.event_ids.items()): channel.unsubscribe_event(event_id) try: # Delete the meas diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 75e2248d2a..6e81ec7ea0 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -202,8 +202,8 @@ def get(self, cache=False): scan_file = [scan_file] ret['ScanFile'] = scan_file mnt_grps = macro_server.getElementsOfType("MeasurementGroup") - mnt_grps_names = [mnt_grp.name for mnt_grp in mnt_grps.values()] - mnt_grps_full_names = mnt_grps.keys() + mnt_grps_names = [mnt_grp.name for mnt_grp in list(mnt_grps.values())] + mnt_grps_full_names = list(mnt_grps.keys()) active_mnt_grp = env.get('ActiveMntGrp') if active_mnt_grp is None and len(mnt_grps): @@ -236,7 +236,7 @@ def get(self, cache=False): def set(self, conf, mnt_grps=None): """Sets the ExperimentConfiguration dictionary.""" if mnt_grps is None: - mnt_grps = conf['MntGrpConfigs'].keys() + mnt_grps = list(conf['MntGrpConfigs'].keys()) codec = CodecFactory().getCodec('json') msg_error = '' @@ -513,7 +513,7 @@ def stop(self, synch=True): def _clearRunMacro(self): # Clear the log buffer - map(LogAttr.clearLogBuffer, self._log_attr.values()) + map(LogAttr.clearLogBuffer, list(self._log_attr.values())) self._running_macros = None self._running_macro = None self._user_xml = None @@ -816,7 +816,7 @@ def __delattr__(self, key): ms.removeEnvironment(key) def __dir__(self): - return [key for key in self.keys() if not key.startswith("_")] + return [key for key in list(self.keys()) if not key.startswith("_")] class BaseMacroServer(MacroServerDevice): @@ -865,13 +865,13 @@ def _on_environment_changed(self, evt_src, evt_type, evt_value): env = CodecFactory().decode(evt_value.value) - for key, value in env.get('new', {}).items(): + for key, value in list(env.get('new', {}).items()): self._addEnvironment(key, value) added.add(key) for key in env.get('del', []): self._removeEnvironment(key) removed.add(key) - for key, value in env.get('change', {}).items(): + for key, value in list(env.get('change', {}).items()): self._removeEnvironment(key) self._addEnvironment(key, value) changed.add(key) @@ -1125,7 +1125,7 @@ def validateSingleParam(self, singleParamNode): "%s parameter value: %s is above maximum allowed value." % (name, value)) else: - allowedInterfaces = self.getInterfaces().keys() + allowedInterfaces = list(self.getInterfaces().keys()) if type not in allowedInterfaces: raise Exception( "No element with %s interface exist in this sardana " diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py index 099c83fcdb..87f71a5473 100644 --- a/src/sardana/taurus/core/tango/sardana/motion.py +++ b/src/sardana/taurus/core/tango/sardana/motion.py @@ -271,12 +271,12 @@ def init_by_names(self, names, moveable_srcs, allow_repeat, allow_unknown): # map ms_moveables = {} - for moveable_source, ms_names in ms_elem_names.items(): + for moveable_source, ms_names in list(ms_elem_names.items()): moveable = moveable_source.getMoveable(ms_names) ms_moveables[moveable_source] = moveable # list - moveable_list = ms_moveables.values() + moveable_list = list(ms_moveables.values()) # list pos_to_moveable = len(names) * [None, ] diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index af7421ddd8..537afb8d9d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -336,7 +336,7 @@ def cleanUp(self): f = self.factory() attr_map = self._attrEG - for attr_name in attr_map.keys(): + for attr_name in list(attr_map.keys()): attrEG = attr_map.pop(attr_name) attr = attrEG.getAttribute() attrEG = None @@ -615,7 +615,7 @@ def removeElement(self, elem): def getElementByAxis(self, axis): pool = self.getPoolObj() - for _, elem in pool.getElementsOfType(self.getMainType()).items(): + for _, elem in list(pool.getElementsOfType(self.getMainType()).items()): if (elem.controller != self.getFullName() or elem.getAxis() != axis): continue @@ -623,7 +623,7 @@ def getElementByAxis(self, axis): def getElementByName(self, name): pool = self.getPoolObj() - for _, elem in pool.getElementsOfType(self.getMainType()).items(): + for _, elem in list(pool.getElementsOfType(self.getMainType()).items()): if (elem.controller != self.getFullName() or elem.getName() != name): continue @@ -638,7 +638,7 @@ def getUsedAxes(self): pool = self.getPoolObj() axes = [] - for _, elem in pool.getElementsOfType(self.getMainType()).items(): + for _, elem in list(pool.getElementsOfType(self.getMainType()).items()): if elem.controller != self.getFullName(): continue axes.append(elem.getAxis()) @@ -1344,9 +1344,9 @@ def getChannelConfigs(mgconfig, ctrls=None, sort=True): chconfigs = [] if not mgconfig: return [] - for ctrl_name, ctrl_data in mgconfig['controllers'].items(): + for ctrl_name, ctrl_data in list(mgconfig['controllers'].items()): if ctrls is None or ctrl_name in ctrls: - for ch_name, ch_data in ctrl_data['channels'].items(): + for ch_name, ch_data in list(ctrl_data['channels'].items()): ch_data.update({'_controller_name': ctrl_name}) chconfigs.append((ch_name, ch_data)) if sort: @@ -1372,8 +1372,8 @@ def __init__(self, mg, data): # of a dict as receveid by the MG configuration attribute self.channels = channels = CaselessDict() - for _, ctrl_data in self.controllers.items(): - for channel_name, channel_data in ctrl_data['channels'].items(): + for _, ctrl_data in list(self.controllers.items()): + for channel_name, channel_data in list(ctrl_data['channels'].items()): channels[channel_name] = channel_data ##################### @@ -1387,7 +1387,7 @@ def __init__(self, mg, data): # ordered by channel index in the MG. self.channel_list = len(channels) * [None] - for channel in channels.values(): + for channel in list(channels.values()): self.channel_list[channel['index']] = channel # dict]> @@ -1431,7 +1431,7 @@ def _build(self): self.cache = cache = {} tg_attr_validator = TangoAttributeNameValidator() - for channel_name, channel_data in self.channels.items(): + for channel_name, channel_data in list(self.channels.items()): cache[channel_name] = None data_source = channel_data['source'] params = tg_attr_validator.getParams(data_source) @@ -1495,7 +1495,7 @@ def prepare(self): # prepare missing tango devices if self.tango_dev_channels_in_error > 0: - for dev_name, dev_data in self.tango_dev_channels.items(): + for dev_name, dev_data in list(self.tango_dev_channels.items()): if dev_data[0] is None: try: dev_data[0] = DeviceProxy(dev_name) @@ -1505,7 +1505,7 @@ def prepare(self): # prepare missing tango attribute configuration if self.tango_channels_info_in_error > 0: - for _, attr_data in self.tango_channels_info.items(): + for _, attr_data in list(self.tango_channels_info.items()): dev_name, attr_name, attr_info = attr_data if attr_info.has_info(): continue @@ -1527,7 +1527,7 @@ def getChannelInfo(self, channel_name): 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(): + for d_name, a_name, ch_info in list(self.tango_channels_info.values()): if ch_info.name.lower() == channel_name: return d_name, a_name, ch_info @@ -1548,7 +1548,7 @@ def getChannelsInfo(self, only_enabled=False): self.prepare() ret = CaselessDict(self.tango_channels_info) ret.update(self.non_tango_channels) - for ch_name, (_, _, ch_info) in ret.items(): + for ch_name, (_, _, ch_info) in list(ret.items()): if only_enabled and not ch_info.enabled: ret.pop(ch_name) return ret @@ -1564,7 +1564,7 @@ def getChannelsInfoList(self, only_enabled=False): """ channels_info = self.getChannelsInfo(only_enabled=only_enabled) ret = [] - for _, (_, _, ch_info) in channels_info.items(): + for _, (_, _, ch_info) in list(channels_info.items()): ret.append(ch_info) ret = sorted(ret, lambda x, y: cmp(x.index, y.index)) return ret @@ -1596,9 +1596,9 @@ def getTangoDevChannels(self, only_enabled=False): if not only_enabled: return self.tango_dev_channels tango_dev_channels = {} - for dev_name, dev_data in self.tango_dev_channels.items(): + for dev_name, dev_data in list(self.tango_dev_channels.items()): dev_proxy, attrs = dev_data[0], copy.deepcopy(dev_data[1]) - for attr_name, channel_data in attrs.items(): + for attr_name, channel_data in list(attrs.items()): if not channel_data["enabled"]: attrs.pop(attr_name) tango_dev_channels[dev_name] = [dev_proxy, attrs] @@ -1616,18 +1616,18 @@ def _read_parallel(self): # deposit read requests tango_dev_channels = self.getTangoDevChannels(only_enabled=True) - for _, dev_data in tango_dev_channels.items(): + for _, dev_data in list(tango_dev_channels.items()): dev, attrs = dev_data if dev is None: continue try: dev_replies[dev] = dev.read_attributes_asynch( - attrs.keys()), attrs + list(attrs.keys())), attrs except: dev_replies[dev] = None, attrs # gather all replies - for dev, reply_data in dev_replies.items(): + for dev, reply_data in list(dev_replies.items()): reply, attrs = reply_data try: data = dev.read_attributes_reply(reply, 0) @@ -1639,7 +1639,7 @@ def _read_parallel(self): value = data_item.value ret[channel_data['full_name']] = value except: - for _, channel_data in attrs.items(): + for _, channel_data in list(attrs.items()): ret[channel_data['full_name']] = None return ret @@ -1648,10 +1648,10 @@ def _read(self): self.prepare() ret = CaselessDict(self.cache) tango_dev_channels = self.getTangoDevChannels(only_enabled=True) - for _, dev_data in tango_dev_channels.items(): + for _, dev_data in list(tango_dev_channels.items()): dev, attrs = dev_data try: - data = dev.read_attributes(attrs.keys()) + data = dev.read_attributes(list(attrs.keys())) for data_item in data: channel_data = attrs[data_item.name] if data_item.has_failed: @@ -1660,7 +1660,7 @@ def _read(self): value = data_item.value ret[channel_data['full_name']] = value except: - for _, channel_data in attrs.items(): + for _, channel_data in list(attrs.items()): ret[channel_data['full_name']] = None return ret @@ -2019,7 +2019,7 @@ def _enableChannels(self, channels, state): channel['enabled'] = state found[name] = True wrong_channels = [] - for ch, f in found.items(): + for ch, f in list(found.items()): if f is False: wrong_channels.append(ch) if len(wrong_channels) > 0: @@ -2321,7 +2321,7 @@ def getObj(self, name, elem_type=None): name = name.lower() for e_type in elem_types: elems = self.getElementsOfType(e_type) - for elem in elems.values(): + for elem in list(elems.values()): if elem.name.lower() == name: return elem elem = elems.get(name) @@ -2362,7 +2362,7 @@ def getMoveable(self, names): while True: name = "_mg_ms_{0}_{1}".format(pid, i) exists = False - for mg in mgs.values(): + for mg in list(mgs.values()): if mg.name == name: exists = True break @@ -2376,7 +2376,7 @@ def __findMotorGroupWithElems(self, names): names_lower = map(str.lower, names) len_names = len(names) mgs = self.getElementsOfType('MotorGroup') - for mg in mgs.values(): + for mg in list(mgs.values()): mg_elems = mg.elements if len(mg_elems) != len_names: continue diff --git a/src/sardana/taurus/core/tango/sardana/sardana.py b/src/sardana/taurus/core/tango/sardana/sardana.py index 4d96cd8640..34768736ac 100644 --- a/src/sardana/taurus/core/tango/sardana/sardana.py +++ b/src/sardana/taurus/core/tango/sardana/sardana.py @@ -183,7 +183,7 @@ def getElementsOfType(self, t): return elems def getElementNamesOfType(self, t): - return [e.name for e in self.getElementsOfType(t).values()] + return [e.name for e in list(self.getElementsOfType(t).values())] def getElementsWithInterface(self, interface): elems = self._interfaces_dict.get(interface, {}) @@ -196,18 +196,18 @@ def getElementsWithInterfaces(self, interfaces): return ret def getElementNamesWithInterface(self, interface): - return [e.name for e in self.getElementsWithInterface(interface).values()] + return [e.name for e in list(self.getElementsWithInterface(interface).values())] def hasElementName(self, elem_name): return self.getElement(elem_name) is not None def getElement(self, elem_name): elem_name = elem_name.lower() - for elems in self._type_elems_dict.values(): + for elems in list(self._type_elems_dict.values()): elem = elems.get(elem_name) # full_name? if elem is not None: return elem - for elem in elems.values(): + for elem in list(elems.values()): if elem.name.lower() == elem_name: return elem @@ -216,14 +216,14 @@ def getElementWithInterface(self, elem_name, interface): elems = self._interfaces_dict.get(interface, {}) if elem_name in elems: return elems[elem_name] - for elem in elems.values(): + for elem in list(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()) + for elems in list(self._type_elems_dict.values()): + ret.update(list(elems.values())) return ret def getInterfaces(self): @@ -647,7 +647,7 @@ def __init__(self, db): def refresh(self): self._sardanas = sardanas = {} services = self._db.get_service_list("Sardana/.*") - for service, dev in services.items(): + for service, dev in list(services.items()): service_type, service_instance = service.split("/", 1) try: sardanas[service_instance] = Sardana( diff --git a/src/sardana/taurus/core/tango/sardana/test/test_pool.py b/src/sardana/taurus/core/tango/sardana/test/test_pool.py index 3713df2c0e..a85f5d0543 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_pool.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_pool.py @@ -72,7 +72,7 @@ def count(self, elements): try: mg = Device(mg_name) _, values = mg.count(.1) - for channel_name, value in values.iteritems(): + for channel_name, value in values.items(): msg = "Value (%s) for %s is not numerical" % \ (value, channel_name) self.assertTrue(is_numerical(value), msg) @@ -111,7 +111,7 @@ def test_value_ref_enabled(self): _set_value_ref_enabled(conf, channel, True) mg.setConfiguration(conf) _, values = mg.count(.1) - for channel_name, value in values.iteritems(): + for channel_name, value in values.items(): msg = "ValueRef (%s) for %s is not string" %\ (value, channel_name) self.assertTrue(is_pure_str(value), msg) @@ -132,7 +132,7 @@ def test_value_ref_disabled(self): _set_value_ref_enabled(conf, channel, False) mg.setConfiguration(conf) _, values = mg.count(.1) - for channel_name, value in values.iteritems(): + for channel_name, value in values.items(): msg = "Value (%s) for %s is not numerical" %\ (value, channel_name) self.assertTrue(is_numerical(value), msg) diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py index 263458049a..34b4345b2b 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py @@ -113,7 +113,7 @@ def _elementsChanged(self): # one or more measurement group was deleted mntgrp_changed = len(self._mntgrps_connected) > len(mntgrps) new_mntgrps_connected = [] - for name, mg in mntgrps.items(): + for name, mg in list(mntgrps.items()): if name not in self._mntgrps_connected: mntgrp_changed = True # this measurement group is new obj = mg.getObj() diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/model.py b/src/sardana/taurus/qt/qtcore/tango/sardana/model.py index 0719bea8c1..97865798d8 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/model.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/model.py @@ -460,7 +460,7 @@ def setupModelData(self, data): env = dev.getEnvironment() root = self._rootItem - for key, value in env.items(): + for key, value in list(env.items()): if not self.accept(key): continue env_item = EnvironmentTreeItem(self, (key, value), root) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py index b00db939d3..8feaa5c639 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py @@ -109,7 +109,7 @@ def __loadMacroNames(self): if ms is None: return macros = ms.getElementsWithInterface('MacroCode') - macroNames = sorted([macro.name for macro in macros.values()]) + macroNames = sorted([macro.name for macro in list(macros.values())]) macroNames.insert(0, '') # adding blank item self.addItems(macroNames) self.updateStyle() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 5ea14cc7cf..fc32ff9bfa 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -521,7 +521,7 @@ def getParamItems(self, index): return None type = node.type() ms = self.getParentModelObj() - items = ms.getElementsWithInterface(type).keys() + items = list(ms.getElementsWithInterface(type).keys()) return items, type def nextValue(self, current): diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py index 61e858175d..52e733ea6f 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py @@ -368,7 +368,7 @@ def nodeFromIndex(self, index): def createIdIndexDictionary(self): d = self.sourceModel().createIdIndexDictionary() - for id, sourceIndex in d.iteritems(): + for id, sourceIndex in d.items(): proxyIndex = self.mapFromSource(sourceIndex) d[id] = Qt.QPersistentModelIndex(proxyIndex) return d diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 4bd2e92a04..621500029e 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -287,7 +287,7 @@ def prepareMacroIds(self): def prepareMacroProgresses(self): self._idIndexDict = self.model().createIdIndexDictionary() - for macroId in self._idIndexDict.iterkeys(): + for macroId in self._idIndexDict.keys(): self.setProgressForMacro(macroId, 0) def setProgressForMacro(self, macroId, progress): diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolioregister.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolioregister.py index 5a95b4d6d6..365a576f40 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolioregister.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolioregister.py @@ -247,7 +247,7 @@ def setModel(self, model): # Empty previous buttons # self.ui.lo_buttons_write. - for button in self.button_value_dict.keys(): + for button in list(self.button_value_dict.keys()): self.button.clicked.disconnect(self.writeValue) button.deleteLater() self.button_value_dict = {} diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index 3b46a6a152..1453daa948 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -456,13 +456,13 @@ def _reloadConf(self, force=False): self._dirtyMntGrps = set() # set a list of available channels avail_channels = {} - for ch_info in door.macro_server.getExpChannelElements().values(): + for ch_info in list(door.macro_server.getExpChannelElements().values()): avail_channels[ch_info.full_name] = ch_info.getData() self.ui.channelEditor.getQModel().setAvailableChannels(avail_channels) # set a list of available triggers avail_triggers = {'software': {"name": "software"}} tg_elements = door.macro_server.getElementsOfType('TriggerGate') - for tg_info in tg_elements.values(): + for tg_info in list(tg_elements.values()): avail_triggers[tg_info.full_name] = tg_info.getData() self.ui.channelEditor.getQModel().setAvailableTriggers(avail_triggers) self.experimentConfigurationChanged.emit(copy.deepcopy(conf)) @@ -501,7 +501,7 @@ def setLocalConfig(self, conf): # set the measurement group ComboBox self.ui.activeMntGrpCB.clear() mntGrpLabels = [] - for _, mntGrpConf in self._localConfig['MntGrpConfigs'].items(): + for _, mntGrpConf in list(self._localConfig['MntGrpConfigs'].items()): # get labels to visualize names with lower and upper case mntGrpLabels.append(mntGrpConf['label']) self.ui.activeMntGrpCB.addItems(sorted(mntGrpLabels)) @@ -544,7 +544,7 @@ def writeExperimentConfiguration(self, ask=True): conf = self.getLocalConfig() # make sure that no empty measurement groups are written - for mgname, mgconfig in conf.get('MntGrpConfigs', {}).items(): + for mgname, mgconfig in list(conf.get('MntGrpConfigs', {}).items()): if mgconfig is not None and not mgconfig.get('controllers'): mglabel = mgconfig['label'] Qt.QMessageBox.information(self, "Empty Measurement group", @@ -610,7 +610,7 @@ def createMntGrp(self): # 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()] + v.name for v in list(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, diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py b/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py index ab701163c3..ac0f4fcddb 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py @@ -186,7 +186,7 @@ def setupModelData(self, data): root = self._rootItem macro_modules = {} macro_dict = ms.getMacros() - for macro_name, macro in macro_dict.items(): + for macro_name, macro in list(macro_dict.items()): module_name = macro.module moduleNode = macro_modules.get(module_name) if moduleNode is None: diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index debb29bb6a..7d82054714 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -809,11 +809,11 @@ def setEditorData(self, editor, index): dataSource = model.dataSource() taurus_role = model.role(index.column()) if taurus_role == ChannelView.PlotType: - editor.addItems(PlotType.keys()) + editor.addItems(list(PlotType.keys())) current = model.data(index) editor.setCurrentIndex(editor.findText(current)) elif taurus_role == ChannelView.Normalization: - editor.addItems(Normalization.keys()) + editor.addItems(list(Normalization.keys())) current = model.data(index) editor.setCurrentIndex(editor.findText(current)) elif taurus_role in (ChannelView.Timer, ChannelView.Monitor): @@ -830,14 +830,14 @@ def setEditorData(self, editor, index): current = model.data(index) editor.setCurrentIndex(editor.findText(current)) else: - for ctrl_data in dataSource['controllers'].values(): + for ctrl_data in list(dataSource['controllers'].values()): if key in ctrl_data: channel = all_channels[ctrl_data[key]] editor.addItem(channel['name'], channel['full_name']) current = dataSource.get(key) # current global timer/monitor editor.setCurrentIndex(editor.findData(current)) elif taurus_role == ChannelView.Synchronization: - editor.addItems(AcqSynchType.keys()) + editor.addItems(list(AcqSynchType.keys())) current = model.data(index) editor.setCurrentIndex(editor.findText(current)) elif taurus_role == ChannelView.PlotAxes: @@ -849,7 +849,7 @@ def setEditorData(self, editor, index): elif taurus_role == ChannelView.Synchronizer: # add the triggergates to the editor all_triggers = model.getAvailableTriggers() - for full_name, tg_data in all_triggers.items(): + for full_name, tg_data in list(all_triggers.items()): editor.addItem(tg_data['name'], full_name) current = model.data(index) editor.setCurrentIndex(editor.findText(current)) @@ -950,7 +950,7 @@ def setModelData(self, editor, model, index): # get the affected channels affected = [] channels = ctrl_data.get('channels') - for _, ch_data in channels.items(): + for _, ch_data in list(channels.items()): affected.append(ch_data['name']) if len(affected) > 1: @@ -1047,7 +1047,7 @@ def addChannel(self, channel=None): if channel is None: 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() + clist = [ch_info['name'] for ch_name, ch_info in list(avail_channels.items()) if ch_name not in shown] clist = sorted(clist) + ['(Other...)'] chname, ok = Qt.QInputDialog.getItem( @@ -1064,7 +1064,7 @@ def addChannel(self, channel=None): qmodel.addChannel( chname=m, ctrlname='__tango__', external=True) else: - for ch_info in avail_channels.values(): + for ch_info in list(avail_channels.values()): if ch_info['name'] == chname: qmodel.addChannel(chinfo=ch_info) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py index 3a03d76fe6..ca3592d882 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py @@ -70,7 +70,7 @@ def __init__(self, namelist=None, pathlist=None): if pathlist is None: pathlist = [] self.pathlist = pathlist - self.previous_modules = sys.modules.keys() + self.previous_modules = list(sys.modules.keys()) def is_module_blacklisted(self, modname, modpath): for path in [sys.prefix] + self.pathlist: @@ -88,7 +88,7 @@ def run(self, verbose=False): Do not del C modules """ log = [] - for modname, module in sys.modules.items(): + for modname, module in list(sys.modules.items()): if modname not in self.previous_modules: modpath = getattr(module, '__file__', None) if modpath is None: diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 49809d72b9..bace31288d 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -187,7 +187,7 @@ def onExpConfChanged(self, expconf): trends1d = {} trends2d = {} - for chname, chdata in channels.items(): + for chname, chdata in list(channels.items()): ptype = chdata['plot_type'] if ptype == PlotType.No: continue @@ -254,7 +254,7 @@ def _updateTemporaryTrends1D(self, trends1d): ''' from taurus.qt.qtgui.plot import TaurusTrend # TODO: use tpg instead! newpanels = [] - for axes, plotables in trends1d.items(): + for axes, plotables in list(trends1d.items()): if not axes: continue if axes not in self._trends1d: @@ -311,7 +311,7 @@ def _updateTemporaryTrends2D(self, trends2d): return newpanels = [] - for axes, plotables in trends2d.items(): + for axes, plotables in list(trends2d.items()): for chname in plotables: pname = u'Trend2D - %s' % chname if pname in self._trends2d: @@ -632,7 +632,7 @@ def __onDoorAbort(self): door.command_inout('abort') # send stop/abort to all pools pools = door.macro_server.getElementsOfType('Pool') - for pool in pools.values(): + for pool in list(pools.values()): self.info('Sending %s command to %s' % (cmd, pool.getFullName())) try: pool.getObj().command_inout(cmd) diff --git a/src/sardana/tools/config/get_pool_config.py b/src/sardana/tools/config/get_pool_config.py index 903441f60d..d25e4e1902 100644 --- a/src/sardana/tools/config/get_pool_config.py +++ b/src/sardana/tools/config/get_pool_config.py @@ -80,10 +80,10 @@ def checkPoolElements(pool): config = json.loads(config) controllers = config['controllers'] elements = {} - for ctrl, c_data in controllers.items(): + for ctrl, c_data in list(controllers.items()): if c_data.has_key('units'): c_data = c_data['units']['0'] - for _, data in c_data['channels'].items(): + for _, data in list(c_data['channels'].items()): index = int(data['index']) if ctrl == '__tango__': elements[index] = data['full_name'] @@ -103,7 +103,7 @@ def checkPoolElements(pool): db = taurus.Database() pool_elements_detail = {} - for element_type in pool_elements.keys(): + for element_type in list(pool_elements.keys()): elements = pool_elements[element_type] for info in elements: info_splitted = json.loads(info) @@ -134,7 +134,7 @@ def checkPoolElements(pool): for attr, attr_dict in db.get_device_attribute_property( normal_name, map(str, attrs) - ).iteritems(): + ).items(): if len(attr_dict) > 0: pool_elements_detail[alias]['attr_dicts'][attr] = attr_dict else: @@ -148,7 +148,7 @@ def checkPoolElements(pool): # print pool_instruments # CHECK ELEMENTS WITHOUT INSTRUMENT - for element_type in pool_elements.keys(): + for element_type in list(pool_elements.keys()): elements = pool_elements[element_type] elements_with_no_instrument = [] for info in elements: @@ -244,7 +244,7 @@ def checkPoolElements(pool): row = '\t'.join(columns) parameters_sheet += row + '\n' - for ctrl_type, controllers in pool_controllers_by_type.iteritems(): + for ctrl_type, controllers in pool_controllers_by_type.items(): if len(controllers) == 0: continue for ctrl in controllers: @@ -260,7 +260,7 @@ def checkPoolElements(pool): ctrl_details['properties'] = '' else: properties = [] - for k, v in ctrl_details['properties'].iteritems(): + for k, v in ctrl_details['properties'].items(): properties.append(k + ':' + v) ctrl_details['properties'] = ';'.join(properties) @@ -279,7 +279,7 @@ def checkPoolElements(pool): elem_type = elem_details['type'] attr_dicts = elem_details['attr_dicts'] attribute_values = [] - for attr in attr_dicts.keys(): + for attr in list(attr_dicts.keys()): attr_dict = attr_dicts[attr] if attr_dict.has_key('__value'): # skip memorized values of DialPosition and Position @@ -317,7 +317,7 @@ def checkPoolElements(pool): 'event_period', [''])[0] elem_params['event'] = attr_dict.get('abs_change', [''])[0] - for k, v in elem_params.iteritems(): + for k, v in elem_params.items(): if v != '' and k not in ['pool', 'element', 'parameter']: params_row_template = '{pool}\t{element}\t{parameter}\t{label}\t{format}\t{min_value}\t{min_alarm}\t{min_warning}\t{max_warning}\t{max_alarm}\t{max_value}\t{unit}\t{polling}\t{event}' row = params_row_template.format(**elem_params) @@ -346,7 +346,7 @@ def checkPoolElements(pool): instruments_sheet += row + '\n' acq_row_template = '{type}\t{pool}\t{name}\tAutomatic\t{channels}' - for mg_name, mg_channels in pool_measurement_groups.iteritems(): + for mg_name, mg_channels in pool_measurement_groups.items(): mg_details = {} mg_details['type'] = 'MeasurementGroup' mg_details['pool'] = pool diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index efe158093b..7c11e2ef92 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -225,8 +225,8 @@ def print_ticks(d): child = spawn(command, timeout=timeout, maxread=2000, logfile=logfile, cwd=cwd, env=env) if events is not None: - patterns = events.keys() - responses = events.values() + patterns = list(events.keys()) + responses = list(events.values()) else: patterns = None # We assume that EOF or TIMEOUT will save us. responses = None diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index 6f31ee6e69..e69b76477d 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -1362,17 +1362,17 @@ def _preprocess(self): SimuIOLib, SimuIOClass = simType["IORegister"] simuMotorLibs = [mode["Motor"][0] - for mode in self.SimulationModes.values()] + for mode in list(self.SimulationModes.values())] simuCoTiLibs = [mode["CounterTimer"][0] - for mode in self.SimulationModes.values()] + for mode in list(self.SimulationModes.values())] simu0DLibs = [mode["ZeroDExpChannel"][0] - for mode in self.SimulationModes.values()] + for mode in list(self.SimulationModes.values())] simu1DLibs = [mode["OneDExpChannel"][0] - for mode in self.SimulationModes.values()] + for mode in list(self.SimulationModes.values())] simu2DLibs = [mode["TwoDExpChannel"][0] - for mode in self.SimulationModes.values()] + for mode in list(self.SimulationModes.values())] simuIOLibs = [mode["IORegister"][0] - for mode in self.SimulationModes.values()] + for mode in list(self.SimulationModes.values())] for poolserver in poolservers: simuMotorList = [] @@ -1788,19 +1788,19 @@ def prepareMSs(self): self._macServs[serv.getInstanceName()] = serv def _getServsShutdownOrder(self): - servs = self._macServs.values() - servs += self._devicePools.values() - servs += self._signalSims.values() - servs += self._coTiSims.values() - servs += self._motorSims.values() + servs = list(self._macServs.values()) + servs += list(self._devicePools.values()) + servs += list(self._signalSims.values()) + servs += list(self._coTiSims.values()) + servs += list(self._motorSims.values()) return servs def _getServsStartupOrder(self): - servs = self._motorSims.values() - servs += self._coTiSims.values() - servs += self._signalSims.values() - servs += self._devicePools.values() - servs += self._macServs.values() + servs = list(self._motorSims.values()) + servs += list(self._coTiSims.values()) + servs += list(self._signalSims.values()) + servs += list(self._devicePools.values()) + servs += list(self._macServs.values()) return servs def cleanUp(self): From 846720f22b82dff9f37608d392523a67775a547f Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 13:59:49 +0200 Subject: [PATCH 057/830] 2to3: except --- src/sardana/macroserver/macros/standard.py | 4 +-- .../macroserver/macros/test/sardemoenv.py | 2 +- .../macroserver/macros/test/test_expert.py | 4 +-- src/sardana/macroserver/msenvmanager.py | 2 +- src/sardana/macroserver/msmacromanager.py | 6 ++-- src/sardana/macroserver/msparameter.py | 4 +-- src/sardana/macroserver/recorders/storage.py | 6 ++-- src/sardana/macroserver/scan/gscan.py | 10 +++--- .../macroserver/scan/recorder/datarecorder.py | 2 +- .../macroserver/test/test_msparameter.py | 2 +- .../DummyTriggerGateController.py | 2 +- .../pool/poolcontrollers/TangoController.py | 2 +- src/sardana/spock/inputhandler.py | 2 +- src/sardana/spock/ipython_00_10/genutils.py | 8 ++--- src/sardana/spock/ipython_00_11/genutils.py | 16 ++++----- src/sardana/spock/ipython_01_00/genutils.py | 16 ++++----- src/sardana/spock/magic.py | 6 ++-- src/sardana/spock/spockms.py | 8 ++--- src/sardana/tango/core/util.py | 2 +- src/sardana/tango/macroserver/Door.py | 2 +- src/sardana/tango/macroserver/MacroServer.py | 4 +-- src/sardana/tango/macroserver/test/base.py | 4 +-- src/sardana/tango/pool/Motor.py | 2 +- src/sardana/tango/pool/MotorGroup.py | 2 +- src/sardana/tango/pool/PoolDevice.py | 2 +- src/sardana/tango/pool/test/base_sartest.py | 8 ++--- .../tango/pool/test/test_measurementgroup.py | 4 +-- .../taurus/core/tango/sardana/macroserver.py | 2 +- .../taurus/core/tango/sardana/motion.py | 6 ++-- src/sardana/taurus/core/tango/sardana/pool.py | 12 +++---- .../qtgui/extra_macroexecutor/macrobutton.py | 2 +- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 10 +++--- .../qtgui/extra_sardana/measurementgroup.py | 2 +- .../qtgui/extra_sardana/sardanabasewizard.py | 2 +- src/sardana/tools/config/pexpect23.py | 22 ++++++------ src/sardana/tools/config/sardana.py | 34 +++++++++---------- src/sardana/util/motion/motion.py | 2 +- 37 files changed, 113 insertions(+), 113 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index df600ec74e..63433ede6a 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -524,7 +524,7 @@ def _clean(self): posObj = motor.getPositionObj() try: posObj.unsubscribeEvent(self.positionChanged, motor) - except Exception, e: + except Exception as e: print str(e) raise e @@ -832,7 +832,7 @@ def run(self, timer): try: mnt_grp.setTimer(timer.getName()) - except Exception, e: + except Exception as e: self.output(str(e)) self.output( "%s is not a valid channel in the active measurement group" diff --git a/src/sardana/macroserver/macros/test/sardemoenv.py b/src/sardana/macroserver/macros/test/sardemoenv.py index 77eb3ae4e7..7c74777f32 100644 --- a/src/sardana/macroserver/macros/test/sardemoenv.py +++ b/src/sardana/macroserver/macros/test/sardemoenv.py @@ -188,7 +188,7 @@ def getElements(elem_type="all", fallback_name="element_not_defined", taurus.warning("The door %s is not running. " % (door_name) + "Ignore this message if you are building the documentation.") elements = [fallback_name] * fallback_elements_len - except Exception, e: + except Exception as e: import taurus taurus.debug(e) taurus.warning("It was not possible to retrieve the element. " + diff --git a/src/sardana/macroserver/macros/test/test_expert.py b/src/sardana/macroserver/macros/test/test_expert.py index c85c0c0c11..7a3e48d002 100644 --- a/src/sardana/macroserver/macros/test/test_expert.py +++ b/src/sardana/macroserver/macros/test/test_expert.py @@ -60,7 +60,7 @@ def test_expert(self): self.macro_runs(macro_name="udefctrl", macro_params=[CTRL_NAME], wait_timeout=1) - except Exception, e: + except Exception as e: import taurus taurus.warning("Your system may stay dirty due to an unexpected" " exception during the test.") @@ -78,7 +78,7 @@ def test_meas(self): macro_params=[MNTGRP_NAME, CT_NAME1, CT_NAME2]) self.macro_runs(macro_name="udefmeas", macro_params=[MNTGRP_NAME]) - except Exception, e: + except Exception as e: import taurus taurus.warning("Your system may stay dirty due to an unexpected" " exception during the test.") diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index 3a9afcc500..4b1dc8fe36 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -112,7 +112,7 @@ def setEnvironmentDb(self, f_name): try: self.info("Creating environment directory: %s" % dir_name) os.makedirs(dir_name) - except OSError, ose: + except OSError as ose: self.error("Creating environment: %s" % ose.strerror) self.debug("Details:", exc_info=1) raise ose diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 3c30ddb0dc..787dec00d2 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -762,7 +762,7 @@ def decodeMacroParameters(self, door, raw_params): type_manager = door.type_manager try: out_par_list = ParamDecoder(type_manager, params_def, raw_params) - except WrongParam, out_e: + except WrongParam as out_e: # only if raw params are passed as a list e.g. using macro API # execMacro("mv", mot01, 0.0) and parameters definition allows to # decode it from a flat list we give it a try @@ -772,7 +772,7 @@ def decodeMacroParameters(self, door, raw_params): try: out_par_list = FlatParamDecoder(type_manager, params_def, raw_params) - except WrongParam, in_e: + except WrongParam as in_e: msg = ("Either of: %s or %s made it impossible to decode" " parameters" % (out_e.message, in_e.message)) raise WrongParam, msg @@ -1602,7 +1602,7 @@ def runMacro(self, macro_obj): 'args': df.args, 'traceback': traceback.format_exc()} macro_exp = MacroServerException(exp_pars) - except Exception, err: + except Exception as err: exc_info = sys.exc_info() exp_pars = {'type': err.__class__.__name__, 'msg': str(err), diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py index 8c75105ecd..65d5b485ff 100644 --- a/src/sardana/macroserver/msparameter.py +++ b/src/sardana/macroserver/msparameter.py @@ -582,9 +582,9 @@ def decodeNormal(self, raw_params, params_def): par_str = raw_params[str_idx] try: val = par_type.getObj(par_str) - except ValueError, e: + except ValueError as e: raise WrongParamType, e.message - except UnknownParamObj, e: + except UnknownParamObj as e: raise WrongParam, e.message if val is None: msg = 'Could not create %s parameter "%s" for "%s"' % \ diff --git a/src/sardana/macroserver/recorders/storage.py b/src/sardana/macroserver/recorders/storage.py index 53e6e30d53..50c6d4bf89 100644 --- a/src/sardana/macroserver/recorders/storage.py +++ b/src/sardana/macroserver/recorders/storage.py @@ -788,7 +788,7 @@ def _populateInstrumentInfo(self): nid = self.fd.getdataID() self._createBranch(dd.instrument) self.fd.makelink(nid) - except Exception, e: + except Exception as e: self.warning( "Could not create link to '%s' in '%s'. Reason: %s", datapath, dd.instrument, repr(e)) @@ -802,7 +802,7 @@ def _populateInstrumentInfo(self): nid = self.fd.getdataID() self._createBranch(dd.instrument) self.fd.makelink(nid) - except Exception, e: + except Exception as e: self.warning( "Could not create link to '%s' in '%s'. Reason: %s", datapath, dd.instrument, repr(e)) @@ -898,7 +898,7 @@ def _addCustomData(self, value, name, nxpath=None, dtype=None, **kwargs): self._createBranch(nxpath) try: self._writeData(name, value, dtype) - except ValueError, e: + except ValueError as e: msg = "Error writing %s. Reason: %s" % (name, str(e)) self.warning(msg) self.macro.warning(msg) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 5085f3c352..97f9ac11e3 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -435,7 +435,7 @@ def _getExtraColumns(self): ret.append(TangoExtraData(**kw)) except InterruptException: raise - except Exception, colexcept: + except Exception as colexcept: colname = kw.get('label', str(i)) self.macro.warning("Extra column %s is invalid: %s", colname, str(colexcept)) @@ -540,7 +540,7 @@ def _getFileRecorders(self): file_recorders.append(file_recorder) except InterruptException: raise - except AmbiguousRecorderError, e: + except AmbiguousRecorderError as e: macro.error('Select recorder that you would like to use ' '(i.e. set ScanRecorder environment variable).') raise e @@ -1443,7 +1443,7 @@ def _restore_motors(self): deceleration=motor_backup["deceleration"]) try: self.configure_motor(motor, attributes) - except ScanException, e: + except ScanException as e: msg = "Error when restoring motor's backup (%s)" % e raise ScanException(msg) @@ -1466,7 +1466,7 @@ def _setFastMotions(self, motors=None): deceleration=self.get_min_dec_time(motor)) try: self.configure_motor(motor, attributes) - except ScanException, e: + except ScanException as e: msg = "Error when setting fast motion (%s)" % e raise ScanException(msg) @@ -2404,7 +2404,7 @@ def _go_through_waypoints(self): continue try: self.configure_motor(motor, attributes) - except ScanException, e: + except ScanException as e: msg = "Error when configuring scan motion (%s)" % e raise ScanException(msg) diff --git a/src/sardana/macroserver/scan/recorder/datarecorder.py b/src/sardana/macroserver/scan/recorder/datarecorder.py index 2cf9230fcc..802903a991 100644 --- a/src/sardana/macroserver/scan/recorder/datarecorder.py +++ b/src/sardana/macroserver/scan/recorder/datarecorder.py @@ -150,7 +150,7 @@ def setSaveMode(self, mode): def addCustomData(self, value, name, **kwargs): try: self._addCustomData(value, name, **kwargs) - except Exception, e: + except Exception as e: raise RuntimeError('%s can not process custom data: %s' % (self.__class__.__name__, e)) diff --git a/src/sardana/macroserver/test/test_msparameter.py b/src/sardana/macroserver/test/test_msparameter.py index 1e357d10e5..e28d7241d6 100644 --- a/src/sardana/macroserver/test/test_msparameter.py +++ b/src/sardana/macroserver/test/test_msparameter.py @@ -106,7 +106,7 @@ def decode(self, params_def, params_raw, expected_params=None, try: param_decoder = ParamDecoder(self.type_manager, params_def, params_raw) - except Exception, e: + except Exception as e: exception = e if expected_params: exception_message = getattr(exception, "message", None) diff --git a/src/sardana/pool/poolcontrollers/DummyTriggerGateController.py b/src/sardana/pool/poolcontrollers/DummyTriggerGateController.py index caf7841eca..68b828f4ed 100644 --- a/src/sardana/pool/poolcontrollers/DummyTriggerGateController.py +++ b/src/sardana/pool/poolcontrollers/DummyTriggerGateController.py @@ -68,7 +68,7 @@ def StateOne(self, axis): status = "Moving" self._log.debug('StateOne(%d): returning (%s, %s)' % (axis, sta, status)) - except Exception, e: + except Exception as e: print e return sta, status diff --git a/src/sardana/pool/poolcontrollers/TangoController.py b/src/sardana/pool/poolcontrollers/TangoController.py index f1dee4ac5a..c7521357e2 100644 --- a/src/sardana/pool/poolcontrollers/TangoController.py +++ b/src/sardana/pool/poolcontrollers/TangoController.py @@ -118,7 +118,7 @@ def set_extra_attribute_par(self, axis, name, value): if dev_info is None: try: proxy = PyTango.DeviceProxy(dev_name) - except PyTango.DevFailed, df: + except PyTango.DevFailed as df: if len(df): self._pending[axis] = df[0].reason + ": " + df[0].desc else: diff --git a/src/sardana/spock/inputhandler.py b/src/sardana/spock/inputhandler.py index f2b099f9c0..7a1f140f06 100644 --- a/src/sardana/spock/inputhandler.py +++ b/src/sardana/spock/inputhandler.py @@ -138,7 +138,7 @@ def safe_run(self, conn): # child process try: return self.run(conn) - except Exception, e: + except Exception as e: msgbox = TaurusMessageBox(*sys.exc_info()) conn.send((e, False)) msgbox.exec_() diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index f5e075984e..02d4273443 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -1094,22 +1094,22 @@ def start(user_ns=None): try: check_requirements() - except exception.SpockMissingRequirement, requirement: + except exception.SpockMissingRequirement as requirement: print str(requirement) sys.exit(-1) - except exception.SpockMissingRecommended, recommended: + except exception.SpockMissingRecommended as recommended: print str(recommended) user_ns = user_ns or {} try: user_ns.update(get_args(sys.argv)) - except exception.SpockException, e: + except exception.SpockException as e: print e.message print 'Starting normal IPython console' except KeyboardInterrupt: print "\nUser pressed Ctrl+C. Exiting..." sys.exit() - except Exception, e: + except Exception as e: print 'spock exited with an unmanaged exception: %s' % str(e) sys.exit(-2) diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py index 802d0c9774..dc4375dcc6 100644 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ b/src/sardana/spock/ipython_00_11/genutils.py @@ -211,9 +211,9 @@ def get_ipython_version(): except Exception: try: v = IPython.release.version - except Exception, e2: + except Exception as e2: print e2 - except Exception, e3: + except Exception as e3: print e3 return v @@ -1171,22 +1171,22 @@ def start(user_ns=None): try: check_requirements() - except exception.SpockMissingRequirement, requirement: + except exception.SpockMissingRequirement as requirement: print str(requirement) sys.exit(-1) - except exception.SpockMissingRecommended, recommended: + except exception.SpockMissingRecommended as recommended: print str(recommended) user_ns = user_ns or {} try: user_ns.update(get_args(sys.argv)) - except exception.SpockException, e: + except exception.SpockException as e: print e.message print 'Starting normal IPython console' except KeyboardInterrupt: print "\nUser pressed Ctrl+C. Exiting..." sys.exit() - except Exception, e: + except Exception as e: print 'spock exited with an unmanaged exception: %s' % str(e) sys.exit(-2) @@ -1289,10 +1289,10 @@ def _banner_default(self): try: check_requirements() - except exception.SpockMissingRequirement, requirement: + except exception.SpockMissingRequirement as requirement: print str(requirement) sys.exit(-1) - except exception.SpockMissingRecommended, recommended: + except exception.SpockMissingRecommended as recommended: print str(recommended) prepare_input_handler() diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index 080425497d..5db6f58ea1 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -218,9 +218,9 @@ def get_ipython_version(): except Exception: try: v = IPython.release.version - except Exception, e2: + except Exception as e2: print e2 - except Exception, e3: + except Exception as e3: print e3 return v @@ -1235,22 +1235,22 @@ def start(user_ns=None): try: check_requirements() - except exception.SpockMissingRequirement, requirement: + except exception.SpockMissingRequirement as requirement: print str(requirement) sys.exit(-1) - except exception.SpockMissingRecommended, recommended: + except exception.SpockMissingRecommended as recommended: print str(recommended) user_ns = user_ns or {} try: user_ns.update(get_args(sys.argv)) - except exception.SpockException, e: + except exception.SpockException as e: print e.message print 'Starting normal IPython console' except KeyboardInterrupt: print "\nUser pressed Ctrl+C. Exiting..." sys.exit() - except Exception, e: + except Exception as e: print 'spock exited with an unmanaged exception: %s' % str(e) sys.exit(-2) @@ -1361,10 +1361,10 @@ def _banner_default(self): try: check_requirements() - except exception.SpockMissingRequirement, requirement: + except exception.SpockMissingRequirement as requirement: print str(requirement) sys.exit(-1) - except exception.SpockMissingRecommended, recommended: + except exception.SpockMissingRecommended as recommended: print str(recommended) prepare_input_handler() diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index b659a11b41..fd18288e8c 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -177,7 +177,7 @@ def www(self, parameter_s=''): return exc = "".join(last_macro.exc_stack) door.write(exc) - except Exception, e: + except Exception as e: door.writeln("Unexpected exception occurred executing www:", stream=door.Error) door.writeln(str(e), stream=door.Error) @@ -267,7 +267,7 @@ def edmac(self, parameter_s=''): try: remote_fname, code, line_nb = ms.GetMacroCode(macro_info) - except PyTango.DevFailed, e: + except PyTango.DevFailed as e: PyTango.Except.print_exception(e) return @@ -288,7 +288,7 @@ def edmac(self, parameter_s=''): new_code = f.read() ms.SetMacroCode([remote_fname, new_code]) print MSG_DONE - except Exception, e: + except Exception as e: print MSG_FAILED print 'Reason:', str(e) f.close() diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index ed3a0b77c0..4bc2776311 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -189,7 +189,7 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): file_model = nexus_widget.model() title = file_model.getNodeFromIndex(title_index)[0] windowTitle += " - " + title - except Exception, e: + except Exception as e: print "Cannot plot scan:" print str(e) @@ -212,7 +212,7 @@ def plot(self): try: env = dict(door.getEnvironmentObj().read().value) - except Exception, e: + except Exception as e: print 'Unable to read environment. No plotting' print str(e) return @@ -241,7 +241,7 @@ def plot(self): try: mem = sps.attach(program, array) mem_ENV = sps.attach(program, array_ENV) - except Exception, e: + except Exception as e: print 'sps.attach error: %s. No plotting' % str(e) return @@ -362,7 +362,7 @@ def _runMacro(self, xml, **kwargs): self.block_lines = 0 self.stop() self.writeln("Done!") - except PyTango.DevFailed, e: + except PyTango.DevFailed as e: if is_non_str_seq(e.args) and \ not isinstance(e.args, (str, unicode)): reason, desc = e.args[0].reason, e.args[0].desc diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index e7ee850df8..1452d29ff7 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -1247,7 +1247,7 @@ def terminate(self): try: log_messages.extend(prepare_server(args, tango_args)) - except AbortException, e: + except AbortException as e: print e.message return except KeyboardInterrupt: diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py index c6cc888802..9c6b4d1f16 100644 --- a/src/sardana/tango/macroserver/Door.py +++ b/src/sardana/tango/macroserver/Door.py @@ -340,7 +340,7 @@ def read_RecordData(self, attr): macro_data = self.door.get_macro_data() codec = CodecFactory().getCodec('bz2_pickle') data = codec.encode(('', macro_data)) - except MacroServerException, mse: + except MacroServerException as mse: throw_sardana_exception(mse) attr.set_value(*data) diff --git a/src/sardana/tango/macroserver/MacroServer.py b/src/sardana/tango/macroserver/MacroServer.py index b8e1ff6adb..744fdb63a7 100644 --- a/src/sardana/tango/macroserver/MacroServer.py +++ b/src/sardana/tango/macroserver/MacroServer.py @@ -272,7 +272,7 @@ def ReloadMacro(self, macro_names): try: for macro_name in macro_names: self.macro_server.reload_macro(macro_name) - except MacroServerException, mse: + except MacroServerException as mse: Except.throw_exception(mse.type, mse.msg, 'ReloadMacro') return ['OK'] @@ -282,7 +282,7 @@ def ReloadMacroLib(self, lib_names): try: for lib_name in lib_names: self.macro_server.reload_macro_lib(lib_name) - except MacroServerException, mse: + except MacroServerException as mse: Except.throw_exception(mse.type, mse.msg, 'ReloadMacroLib') return ['OK'] diff --git a/src/sardana/tango/macroserver/test/base.py b/src/sardana/tango/macroserver/test/base.py index f6fccfefc7..6a9a85a47b 100644 --- a/src/sardana/tango/macroserver/test/base.py +++ b/src/sardana/tango/macroserver/test/base.py @@ -84,7 +84,7 @@ def setUp(self, properties=None): # start MS server self._msstarter.startDs() self.door = PyTango.DeviceProxy(self.door_name) - except Exception, e: + except Exception as e: # force tearDown in order to eliminate the MacroServer print e self.tearDown() @@ -112,7 +112,7 @@ def tearDown(self): ms_properties = os.path.normpath(ms_properties) try: os.remove(ms_properties) - except Exception, e: + except Exception as e: msg = "Not possible to remove macroserver environment file" print(msg) print("Details: %s" % e) diff --git a/src/sardana/tango/pool/Motor.py b/src/sardana/tango/pool/Motor.py index 13aa82ba8d..3279ffc9f1 100644 --- a/src/sardana/tango/pool/Motor.py +++ b/src/sardana/tango/pool/Motor.py @@ -475,7 +475,7 @@ def write_Position(self, attr): raise Exception("Cannot move: already in motion") try: self.motor.position = position - except PoolException, pe: + except PoolException as pe: throw_sardana_exception(pe) # manually store write dial position in the database diff --git a/src/sardana/tango/pool/MotorGroup.py b/src/sardana/tango/pool/MotorGroup.py index d5570f2782..f4ddb7b4db 100644 --- a/src/sardana/tango/pool/MotorGroup.py +++ b/src/sardana/tango/pool/MotorGroup.py @@ -190,7 +190,7 @@ def write_Position(self, attr): raise Exception("Cannot move: already in motion") try: self.motor_group.position = position - except PoolException, pe: + except PoolException as pe: throw_sardana_exception(pe) finally: self.in_write_position = False diff --git a/src/sardana/tango/pool/PoolDevice.py b/src/sardana/tango/pool/PoolDevice.py index 946a4076e8..b35abe4e16 100644 --- a/src/sardana/tango/pool/PoolDevice.py +++ b/src/sardana/tango/pool/PoolDevice.py @@ -430,7 +430,7 @@ def dev_status(self): ctrl_status = self.element.get_status(cache=use_cache, propagate=0) status = self.calculate_tango_status(ctrl_status) return status - except Exception, e: + except Exception as e: msg = "Exception trying to return status: %s" % str(e) self.error(msg) self.debug("Details:", exc_info=1) diff --git a/src/sardana/tango/pool/test/base_sartest.py b/src/sardana/tango/pool/test/base_sartest.py index 3ce5a8ac90..7bede8ea0d 100644 --- a/src/sardana/tango/pool/test/base_sartest.py +++ b/src/sardana/tango/pool/test/base_sartest.py @@ -112,7 +112,7 @@ def setUp(self, pool_properties=None): ctrl = PyTango.DeviceProxy(ctrl_name) # use the first trigger/gate element by default ctrl.write_attribute("Synchronizer", "_test_tg_1_1") - except Exception, e: + except Exception as e: print e msg = 'Impossible to create ctrl: "%s"' % (ctrl_name) raise Exception('Aborting SartestTestCase: %s' % (msg)) @@ -123,7 +123,7 @@ def setUp(self, pool_properties=None): try: self.pool.createElement( [sar_type, ctrl_name, str(axis), elem_name]) - except Exception, e: + except Exception as e: print e msg = 'Impossible to create element: "%s"' % ( elem_name) @@ -139,7 +139,7 @@ def setUp(self, pool_properties=None): argin.extend(roles) try: self.pool.CreateController(argin) - except Exception, e: + except Exception as e: print e msg = 'Impossible to create ctrl: "%s"' % (ctrl_name) raise Exception('Aborting SartestTestCase: %s' % (msg)) @@ -148,7 +148,7 @@ def setUp(self, pool_properties=None): elem = role.split("=")[1] if elem not in self.elem_list: self.elem_list.append(elem) - except Exception, e: + except Exception as e: # force tearDown in order to eliminate the Pool BasePoolTestCase.tearDown(self) print e diff --git a/src/sardana/tango/pool/test/test_measurementgroup.py b/src/sardana/tango/pool/test/test_measurementgroup.py index a007d7eac2..caa47e5763 100644 --- a/src/sardana/tango/pool/test/test_measurementgroup.py +++ b/src/sardana/tango/pool/test/test_measurementgroup.py @@ -100,7 +100,7 @@ def event_received(self, *args, **kwargs): pad = [None] * (idx[0] - expected_idx) channel_data.extend(pad + value) self.data[obj_fullname] = channel_data - except Exception, e: + except Exception as e: print e raise Exception('"data" event callback failed') @@ -262,7 +262,7 @@ def tearDown(self): try: # Delete the meas self.pool.DeleteElement(self.mg_name) - except Exception, e: + except Exception as e: print('Impossible to delete MeasurementGroup: %s' % (self.mg_name)) print e SarTestTestCase.tearDown(self) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 6e81ec7ea0..d3ec9d3a87 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -227,7 +227,7 @@ def get(self, cache=False): mnt_grp_configs[mnt_grp] = \ codec.decode(('json', reply.get_data().value), ensure_ascii=True)[1] - except Exception, e: + except Exception as e: from taurus.core.util.log import warning warning('Cannot load Measurement group "%s": %s', repr(mnt_grp), repr(e)) diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py index 87f71a5473..964d47aa1f 100644 --- a/src/sardana/taurus/core/tango/sardana/motion.py +++ b/src/sardana/taurus/core/tango/sardana/motion.py @@ -565,17 +565,17 @@ def test(): try: m = Motion(["m1", "m2"], [ms1, ms2, ms3], read_only=True) m.startMove([0.5, 20.4]) - except Exception, e: + except Exception as e: assert(e.message == "Trying to move read only motion") try: m = Motion(["m1", "m1"], [ms1, ms2, ms3]) - except Exception, e: + except Exception as e: assert(e.message == "Moveable item m1 appears more than once") try: m = Motion(["m1", "m999"], [ms1, ms2, ms3]) - except Exception, e: + except Exception as e: assert(e.message == "Moveable item m999 not found") if __name__ == "__main__": diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 537afb8d9d..efdfd0c566 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1074,7 +1074,7 @@ def _information(self, tab=' '): pos = str(position.value) if position.quality != AttrQuality.ATTR_VALID: pos += " [" + QUALITY[position.quality] + "]" - except DevFailed, df: + except DevFailed as df: if len(df.args): pos = df.args[0].desc else: @@ -1118,7 +1118,7 @@ def _start(self, *args, **kwargs): new_pos = new_pos[0] try: self.write_attribute('position', new_pos) - except DevFailed, df: + except DevFailed as df: for err in df: if err.reason == 'API_AttrNotAllowed': raise RuntimeError('%s is already moving' % self) @@ -1164,7 +1164,7 @@ def _information(self, tab=' '): pos = str(position.value) if position.quality != AttrQuality.ATTR_VALID: pos += " [" + QUALITY[position.quality] + "]" - except DevFailed, df: + except DevFailed as df: if len(df.args): pos = df.args[0].desc else: @@ -1210,7 +1210,7 @@ def _start(self, *args, **kwargs): new_pos = args[0] try: self.write_attribute('position', new_pos) - except DevFailed, df: + except DevFailed as df: for err in df: if err.reason == 'API_AttrNotAllowed': raise RuntimeError('%s is already moving' % self) @@ -1258,7 +1258,7 @@ def _information(self, tab=' '): pos = str(position.value) if position.quality != AttrQuality.ATTR_VALID: pos += " [" + QUALITY[position.quality] + "]" - except DevFailed, df: + except DevFailed as df: if len(df.args): pos = df.args[0].desc else: @@ -1301,7 +1301,7 @@ def set_info(self, info): data_type = info.data_type try: self.data_type = FROM_TANGO_TO_STR_TYPE[data_type] - except KeyError, e: + except KeyError as e: # For backwards compatibility: # starting from Taurus 4.3.0 DevVoid was added to the dict if data_type == PyTango.DevVoid: diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 95f1023880..4b16f5b9a4 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -299,7 +299,7 @@ def runMacro(self): sec_xml = self.door.getRunningXML() # get the id of the current running macro self.macro_id = sec_xml[0].get("id") - except Exception, e: + except Exception as e: self.ui.button.setChecked(False) raise e diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 647a3df6b4..a632469894 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -94,7 +94,7 @@ def setMotor(self, pool_motor_dev_name): # PENDING. self.has_limits = hasattr(self.motor_dev, 'Limit_Switches') self.has_encoder = hasattr(self.motor_dev, 'Encoder') - except Exception, e: + except Exception as e: taurus.warning('Exception Creating Motor Device %s', str(e)) def moveMotor(self, pos): @@ -668,14 +668,14 @@ def showEvent(self, event): TaurusWidget.showEvent(self, event) try: self.motor_dev.getAttribute('Position').enablePolling(force=True) - except AttributeError, e: + except AttributeError as e: self.debug('Error in showEvent: %s', repr(e)) def hideEvent(self, event): TaurusWidget.hideEvent(self, event) try: self.motor_dev.getAttribute('Position').disablePolling() - except AttributeError, e: + except AttributeError as e: self.debug('Error in hideEvent: %s', repr(e)) #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- @@ -748,7 +748,7 @@ def setModel(self, model): limits_visible = True self.ui.btnMin.setVisible(limits_visible) self.ui.btnMax.setVisible(limits_visible) - except Exception, e: + except Exception as e: self.ui.motorGroupBox.setEnabled(False) self.info('Error setting model "%s". Reason: %s' % (model, repr(e))) @@ -1523,7 +1523,7 @@ def setModel(self, model): self.motor_dev.getAttribute('Position').enablePolling(force=True) self.setExpertView(self._expertView) - except Exception, e: + except Exception as e: self.warning("Exception caught while setting model: %s", repr(e)) self.motor_dev = None return diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index 7d82054714..d8af354f9f 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -144,7 +144,7 @@ def createChannelDict(channel, index=None, **kwargs): # 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: + except Exception as e: print str(e) if value is not None: diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanabasewizard.py b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanabasewizard.py index 5d19af1efe..751c397e70 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanabasewizard.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanabasewizard.py @@ -64,7 +64,7 @@ def __getitem__(self, name): if isinstance(p, SardanaBasePage): try: return p[name]() - except Exception, e: + except Exception as e: pass return self._item_funcs[name]() return None diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 7c11e2ef92..bf4ddf633c 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -80,7 +80,7 @@ import errno import traceback import signal -except ImportError, e: +except ImportError as e: raise ImportError (str(e) + """ A critical module was not found. Probably this operating system does not @@ -252,10 +252,10 @@ def print_ticks(d): raise TypeError( 'The callback must be a string or function type.') event_count = event_count + 1 - except TIMEOUT, e: + except TIMEOUT as e: child_result_list.append(child.before) break - except EOF, e: + except EOF as e: child_result_list.append(child.before) break child_result = ''.join(child_result_list) @@ -540,7 +540,7 @@ def _spawn(self, command, args=[]): if self.use_native_pty_fork: try: self.pid, self.child_fd = pty.fork() - except OSError, e: + except OSError as e: raise ExceptionPexpect('Error! pty.fork() failed: ' + str(e)) else: # Use internal __fork_pty self.pid, self.child_fd = self.__fork_pty() @@ -841,7 +841,7 @@ def read_nonblocking(self, size=1, timeout=-1): if self.child_fd in r: try: s = os.read(self.child_fd, size) - except OSError, e: # Linux does this + except OSError as e: # Linux does this self.flag_eof = True raise EOF( 'End Of File (EOF) in read_nonblocking(). Exception style platform.') @@ -1076,7 +1076,7 @@ def terminate(self, force=False): else: return False return False - except OSError, e: + except OSError as e: # I think there are kernel timing issues that sometimes cause # this to happen. I think isalive() reports True, but the # process is dead to the kernel. @@ -1134,7 +1134,7 @@ def isalive(self): try: pid, status = os.waitpid(self.pid, waitpid_options) - except OSError, e: # No child processes + except OSError as e: # No child processes if e[0] == errno.ECHILD: raise ExceptionPexpect( 'isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?') @@ -1148,7 +1148,7 @@ def isalive(self): try: # os.WNOHANG) # Solaris! pid, status = os.waitpid(self.pid, waitpid_options) - except OSError, e: # This should never happen... + except OSError as e: # This should never happen... if e[0] == errno.ECHILD: raise ExceptionPexpect( 'isalive() encountered condition that should never happen. There was no child process. Did someone else call waitpid() on our process?') @@ -1385,7 +1385,7 @@ def expect_loop(self, searcher, timeout=-1, searchwindowsize=-1): incoming = incoming + c if timeout is not None: timeout = end_time - time.time() - except EOF, e: + except EOF as e: self.buffer = '' self.before = incoming self.after = EOF @@ -1398,7 +1398,7 @@ def expect_loop(self, searcher, timeout=-1, searchwindowsize=-1): self.match = None self.match_index = None raise EOF(str(e) + '\n' + str(self)) - except TIMEOUT, e: + except TIMEOUT as e: self.buffer = incoming self.before = incoming self.after = TIMEOUT @@ -1546,7 +1546,7 @@ def __select(self, iwtd, owtd, ewtd, timeout=None): while True: try: return select.select(iwtd, owtd, ewtd, timeout) - except select.error, e: + except select.error as e: if e[0] == errno.EINTR: # if we loop back we have to subtract the amount of time we # already waited. diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index e69b76477d..036d7b07ff 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -77,7 +77,7 @@ try: import pexpect23 as pexpect print "[WARNING]: pexpect module not found. Using local pexpect 2.3" - except Exception, e: + except Exception as e: print e print "The Sardana requires pexpect python module which was not found." print "This module can be found at http://www.noah.org/wiki/Pexpect" @@ -148,7 +148,7 @@ def start(self): idx = self._process.expect([Process.ReadyMsg, Process.AlreadyRunning, pexpect.EOF, pexpect.TIMEOUT], timeout=self.getMaxStartupTime()) - except Exception, e: + except Exception as e: self.on("[FAILED]") raise e @@ -204,7 +204,7 @@ def stop(self, max_shutdown_time=None): res = self.terminate() idx = self._process.expect(['Exiting', 'Exited', pexpect.EOF, pexpect.TIMEOUT], timeout=max_shutdown_time) - except Exception, e: + except Exception as e: self.on("[FAILED]") raise e @@ -224,7 +224,7 @@ def stop(self, max_shutdown_time=None): try: idx = self._process.expect(['Exited', pexpect.EOF, pexpect.TIMEOUT], timeout=5) - except Exception, e: + except Exception as e: self.on("[FAILED]") raise e @@ -240,7 +240,7 @@ def stop(self, max_shutdown_time=None): try: idx = self._process.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=5) - except Exception, e: + except Exception as e: self.on("[FAILED]") raise e @@ -253,7 +253,7 @@ def stop(self, max_shutdown_time=None): self.o(" (shutdown time exceeded). Forcing... ") self.kill() - except KeyboardInterrupt, ki: + except KeyboardInterrupt as ki: self.o("(Ctrl-C during stop). Forcing... ") self.kill() @@ -315,7 +315,7 @@ def __init__(self, instname, db=None, logfile=None): f, path, desc = imp.find_module('SimuMotor') if f: f.close() - except exceptions.ImportError, e: + except exceptions.ImportError as e: msg = "Could not find %s executable.\n" \ "Make sure PYTHONPATH points to the directory(ies) where " \ "SimuMotorCtrl.py and SimuMotor.py files are installed" % name @@ -336,7 +336,7 @@ def __init__(self, instname, db=None, logfile=None): f, fname, desc = imp.find_module('SimuCoTiCtrl') if f: f.close() - except exceptions.ImportError, e: + except exceptions.ImportError as e: msg = "Could not find %s executable.\n" \ "Make sure PYTHONPATH points to the directory(ies) where " \ "SimuCoTiCtrl.py file is installed" % name @@ -357,7 +357,7 @@ def __init__(self, instname, db=None, logfile=None): f, fname, desc = imp.find_module('PySignalSimulator') if f: f.close() - except exceptions.ImportError, e: + except exceptions.ImportError as e: msg = "Could not find %s executable.\n" \ "Make sure PYTHONPATH points to the directory where " \ "PySignalSimulator.py is installed" % name @@ -690,7 +690,7 @@ def handle_attributes(self, dev_name, node): v = self._item_node_to_value(tango_attr, v_node) try: dev.write_attribute(name, v) - except Exception, ex: + except Exception as ex: print 'SOME PROBLEMS SETTING ATTRIBUTE VALUE FOR DEVICE', dev_name, 'ATTRIBUTE', tango_attr.name, 'VALUE', str(v) print 'EXCEPTION:', ex @@ -752,7 +752,7 @@ def handle_attributes(self, dev_name, node): try: dev.set_attribute_config(attr_info) - except Exception, e: + except Exception as e: print 'COULD NOT SET THE FOLLOWING CONFIG FOR DEVICE', dev_name, 'ATTR', tango_attr.name print 'ATTRIBUTE INFO:', attr_info print 'EXCEPTION:', e @@ -764,7 +764,7 @@ def handle_attributes(self, dev_name, node): try: value = value.strip() dev.write_attribute('Instrument', value) - except Exception, ex: + except Exception as ex: print 'SOME PROBLEMS SETTING INSTRUMENT VALUE FOR DEVICE', dev_name, 'VALUE', value print 'EXCEPTION:', ex @@ -956,7 +956,7 @@ def loadPool(self): self.handle_attributes(aliasName, e) - except PyTango.DevFailed, df: + except PyTango.DevFailed as df: self.on("Exception creating %s: %s" % (aliasName, str(df))) # to flush any output generated by the pool @@ -1847,7 +1847,7 @@ def getRoot(self): try: opts, pargs = getopt.getopt( sys.argv[1:], 'vl', ['simulation=', 'cleanup=']) - except Exception, e: + except Exception as e: print "ERROR:", str(e) print print __doc__ @@ -1881,7 +1881,7 @@ def getRoot(self): try: import to_sar sar_doc = to_sar.transform(filename) - except Exception, e: + except Exception as e: print 'Sorry, but some problems found when trying to convert to SARDANA xml:' print str(e) @@ -1897,9 +1897,9 @@ def getRoot(self): sardana.setUp() print "Ready!" sardana.run() - except KeyboardInterrupt, e: + except KeyboardInterrupt as e: print "User pressed Ctrl+C..." - except Exception, e: + except Exception as e: traceback.print_exc() print "Shutting down!" diff --git a/src/sardana/util/motion/motion.py b/src/sardana/util/motion/motion.py index fab92e0eff..d2d160f437 100644 --- a/src/sardana/util/motion/motion.py +++ b/src/sardana/util/motion/motion.py @@ -698,7 +698,7 @@ def fromMotor(motor): decel_time = motor.getDeceleration() return Motor(min_vel=min_vel, max_vel=max_vel, accel_time=accel_time, decel_time=decel_time) - except Exception, e: + except Exception as e: print e return Motor._fromTangoMotor(motor) From bd1a0fb473bb23929f0e6be90d1ba45c70bfb7a4 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 14:02:00 +0200 Subject: [PATCH 058/830] 2to3: exec --- src/sardana/taurus/qt/qtgui/extra_sardana/startup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py index ca3592d882..17829871d9 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py @@ -161,7 +161,7 @@ def debugfile(filename, args=None, wdir=None): __commands__ = __run_init_commands() if __commands__: for command in __commands__.split(';'): - exec command + exec(command) else: __run_pythonstartup_script() From 450ee22b41ad9b18721ea2ae30de0386a92d2c0b Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 14:04:47 +0200 Subject: [PATCH 059/830] 2to3: execfile --- src/sardana/taurus/qt/qtgui/extra_sardana/startup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py index 17829871d9..c7c30865b9 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py @@ -36,7 +36,7 @@ def __run_pythonstartup_script(): import os filename = os.environ.get('PYTHONSTARTUP') if filename and os.path.isfile(filename): - execfile(filename) + exec(compile(open(filename).read(), filename, 'exec')) def __run_init_commands(): @@ -135,7 +135,7 @@ def runfile(filename, args=None, wdir=None): sys.argv.append(arg) if wdir is not None: os.chdir(wdir) - execfile(filename, glbs) + exec(compile(open(filename).read(), filename, 'exec'), glbs) sys.argv = [''] glbs.pop('__file__') From baa2b52a966330a5fbabfd4203fd58e09cedd71c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 14:08:49 +0200 Subject: [PATCH 060/830] 2to3: filter --- src/sardana/macroserver/macros/hkl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py index 78eb913f80..a69cd94c5c 100644 --- a/src/sardana/macroserver/macros/hkl.py +++ b/src/sardana/macroserver/macros/hkl.py @@ -1551,7 +1551,7 @@ def run(self): self.output("New directory %s not found" % newdir) return - res = filter(lambda x: x.endswith('.txt'), files) + res = [x for x in files if x.endswith('.txt')] if len(res) == 0: self.output("No crystals available in set directory. Nothing done") return From ef712e4e662598256970a0ed5b1d7b1233a5175c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 14:12:07 +0200 Subject: [PATCH 061/830] 2to3: funcattrs --- src/sardana/macroserver/macro.py | 2 +- src/sardana/macroserver/msmacromanager.py | 8 ++++---- src/sardana/sardanameta.py | 2 +- src/sardana/spock/spockms.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index a29fedf5b8..4e3b344e37 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -2483,7 +2483,7 @@ class MacroFunc(Macro): def __init__(self, *args, **kwargs): function = kwargs['function'] self._function = function - kwargs['as'] = self._function.func_name + kwargs['as'] = self._function.__name__ if function.param_def is not None: self.param_def = function.param_def if function.result_def is not None: diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 787dec00d2..854c11b7d4 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -123,18 +123,18 @@ def is_macro(macro, abs_file=None, logger=None): if logger: logger.debug("Could not add macro %s: Needs at least one " "parameter (usually called 'self')", - macro.func_name) + macro.__name__) return False if keywords is not None: if logger: logger.debug("Could not add macro %s: Unsupported keyword " - "parameters '%s'", macro.func_name, keywords) + "parameters '%s'", macro.__name__, keywords) return False if varargs and len(args) > 1: if logger: logger.debug("Could not add macro %s: Unsupported giving " "named parameters '%s' and varargs '%s'", - macro.func_name, args, varargs) + macro.__name__, args, varargs) return False else: return False @@ -599,7 +599,7 @@ def addMacroClass(self, macro_lib, klass, isoverwritten=False): self._macro_dict[macro_name] = macro_class def addMacroFunction(self, macro_lib, func, isoverwritten=False): - macro_name = func.func_name + macro_name = func.__name__ action = (macro_lib.has_macro(macro_name) and "Updating") or "Adding" self.debug("%s macro function %s" % (action, macro_name)) diff --git a/src/sardana/sardanameta.py b/src/sardana/sardanameta.py index a841c59dfb..9dab4333d0 100644 --- a/src/sardana/sardanameta.py +++ b/src/sardana/sardanameta.py @@ -524,7 +524,7 @@ class SardanaFunction(SardanaCode): def __init__(self, **kwargs): function = kwargs.pop('function') kwargs['code'] = function - kwargs['name'] = kwargs.pop('name', function.func_name) + kwargs['name'] = kwargs.pop('name', function.__name__) SardanaCode.__init__(self, **kwargs) @property diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 4bc2776311..4896d78b2a 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -627,7 +627,7 @@ def macro_fn(parameter_s='', name=macro_name): if macro is not None: # maybe none if macro was aborted return macro.getResult() - macro_fn.func_name = macro_name + macro_fn.__name__ = macro_name macro_fn.__doc__ = macro_info.doc + "\nWARNING: do not rely on the" \ " file path below\n" From b645a3d84fe7586f31dcde620a64942c8761b8a2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:20:50 +0200 Subject: [PATCH 062/830] 2to3: has_key --- src/sardana/macroserver/macro.py | 2 +- src/sardana/macroserver/msmacromanager.py | 2 +- src/sardana/macroserver/recorders/examples/xas.py | 2 +- src/sardana/macroserver/recorders/storage.py | 6 +++--- src/sardana/pool/poolcontroller.py | 8 ++++---- src/sardana/pool/poolcontrollermanager.py | 2 +- src/sardana/sardanacontainer.py | 4 ++-- src/sardana/sardanamodulemanager.py | 4 ++-- src/sardana/spock/ipython_00_10/genutils.py | 2 +- src/sardana/spock/spockms.py | 2 +- src/sardana/tango/pool/IORegister.py | 4 ++-- src/sardana/tango/pool/Pool.py | 10 +++++----- src/sardana/taurus/core/tango/sardana/motion.py | 2 +- src/sardana/taurus/core/tango/sardana/sardana.py | 2 +- .../taurus/qt/qtgui/extra_sardana/measurementgroup.py | 4 ++-- src/sardana/tools/config/get_pool_config.py | 4 ++-- src/sardana/tools/config/pexpect23.py | 2 +- src/sardana/tools/config/sardana.py | 2 +- 18 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 4e3b344e37..a6362ba4f6 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -1973,7 +1973,7 @@ def _getViewOption(self, name): the environment, sets it to a default value and returns it. ''' view_options = self._getViewOptions() - if not view_options.has_key(name): + if name not in view_options: ViewOption.reset_option(view_options, name) self.setEnv('_ViewOptions', view_options) return view_options[name] diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 854c11b7d4..2a82bb8bee 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1268,7 +1268,7 @@ def _createMacroObj(self, macro_name_or_meta, pars, init_opts={}): 'environment': self.macro_server } macro_opts.update(init_opts) - if not macro_opts.has_key('id'): + if 'id' not in macro_opts: macro_opts['id'] = str(self.getNewMacroID()) macroObj = self.macro_manager.createMacroObjFromMeta(macro_meta, pars, diff --git a/src/sardana/macroserver/recorders/examples/xas.py b/src/sardana/macroserver/recorders/examples/xas.py index f95d9a5ae1..c49a0a8b4c 100644 --- a/src/sardana/macroserver/recorders/examples/xas.py +++ b/src/sardana/macroserver/recorders/examples/xas.py @@ -190,7 +190,7 @@ def _writeRecord(self, record): rec_data, rec_nb = record.data, record.recordno for dd in self.datadesc: - if record.data.has_key(dd.name): + if dd.name in record.data: data = rec_data[dd.name] field = self.ddfieldsDict[dd.label] diff --git a/src/sardana/macroserver/recorders/storage.py b/src/sardana/macroserver/recorders/storage.py index 50c6d4bf89..9b69ea94be 100644 --- a/src/sardana/macroserver/recorders/storage.py +++ b/src/sardana/macroserver/recorders/storage.py @@ -137,7 +137,7 @@ def _startRecordList(self, recordlist): self.fd.write("!\n! Parameter\n!\n%p\n") self.fd.flush() env = self.macro().getAllEnv() - if env.has_key('FlagFioWriteMotorPositions') and env['FlagFioWriteMotorPositions']: + if 'FlagFioWriteMotorPositions' in env and env['FlagFioWriteMotorPositions']: all_motors = sorted( self.macro().findObjs('.*', type_class=Type.Motor)) for mot in all_motors: @@ -700,7 +700,7 @@ def _writeRecord(self, record): rec_data, rec_nb = record.data, record.recordno for dd in self.datadesc: - if record.data.has_key(dd.name): + if dd.name in record.data: data = rec_data[dd.name] fd.opendata(dd.label) @@ -768,7 +768,7 @@ def writeRecordList(self, recordlist): # if not all the records contain this field, we cannot write it # as a block.. so do it record by record (but only this field!) for record in recordlist.records: - if record.data.has_key(dd.label): + if dd.label in record.data: self.fd.putslab(record.data[dd.label], [ record.recordno] + [0] * len(dd.shape), [1] + list(dd.shape)) else: diff --git a/src/sardana/pool/poolcontroller.py b/src/sardana/pool/poolcontroller.py index f800908c44..57ef12af6b 100644 --- a/src/sardana/pool/poolcontroller.py +++ b/src/sardana/pool/poolcontroller.py @@ -118,9 +118,9 @@ def add_element(self, elem, propagate=1): def remove_element(self, elem, propagate=1): name, axis, eid = elem.get_name(), elem.get_axis(), elem.get_id() - f = self._element_ids.has_key(eid) + f = eid in self._element_ids if not f: - f = self._pending_element_ids.has_key(eid) + f = eid in self._pending_element_ids if not f: raise Exception("element '%s' is not in controller") del self._pending_element_ids[eid] @@ -162,9 +162,9 @@ def rename_element(self, old_name, new_name, propagate=1): elements) def remove_axis(self, axis, propagate=1): - f = self._element_axis.has_key(axis) + f = axis in self._element_axis if not f: - f = self._pending_element_axis.has_key(axis) + f = axis in self._pending_element_axis if not f: raise Exception("element '%s' is not in controller") elem = self._pending_element_axis[axis] diff --git a/src/sardana/pool/poolcontrollermanager.py b/src/sardana/pool/poolcontrollermanager.py index c1012ebecd..650631b6e3 100644 --- a/src/sardana/pool/poolcontrollermanager.py +++ b/src/sardana/pool/poolcontrollermanager.py @@ -413,7 +413,7 @@ def reloadControllerLib(self, module_name, path=None, reload=True): path.reverse() # if there was previous Controller Lib info remove it - if self._modules.has_key(module_name): + if module_name in self._modules: self._modules.pop(module_name) m, exc_info = None, None diff --git a/src/sardana/sardanacontainer.py b/src/sardana/sardanacontainer.py index 74efc71da2..14354e6378 100644 --- a/src/sardana/sardanacontainer.py +++ b/src/sardana/sardanacontainer.py @@ -132,11 +132,11 @@ def get_element(self, **kwargs): :throw: KeyError """ - if kwargs.has_key("id"): + if "id" in kwargs: id = kwargs.pop("id") return self.get_element_by_id(id, **kwargs) - if kwargs.has_key("full_name"): + if "full_name" in kwargs: full_name = kwargs.pop("full_name") return self.get_element_by_full_name(full_name, **kwargs) diff --git a/src/sardana/sardanamodulemanager.py b/src/sardana/sardanamodulemanager.py index 8c59feae56..8bba63a69f 100644 --- a/src/sardana/sardanamodulemanager.py +++ b/src/sardana/sardanamodulemanager.py @@ -285,9 +285,9 @@ def loadModule(self, module_name, path=None): def unloadModule(self, module_name): """Unloads the given module name""" - if self._modules.has_key(module_name): + if module_name in self._modules: self.debug("unloading module %s" % module_name) - assert(sys.modules.has_key(module_name)) + assert(module_name in sys.modules) self._modules.pop(module_name) del sys.modules[module_name] diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index 02d4273443..1d7277f3dc 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -1034,7 +1034,7 @@ def init_pre_spock(ip, macro_server, door): ip.user_ns['DOOR_STATE'] = "" ip.user_ns['spock_options'] = so - if ip.IP.alias_table.has_key('mv'): + if 'mv' in ip.IP.alias_table: del ip.IP.alias_table['mv'] v = release.version diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 4896d78b2a..23ecc9077c 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -297,7 +297,7 @@ class SpockBaseDoor(BaseDoor): def __init__(self, name, **kw): self._consoleReady = kw.get("consoleReady", False) - if not kw.has_key('silent'): + if 'silent' not in kw: kw['silent'] = False self._lines = [] self._spock_state = None diff --git a/src/sardana/tango/pool/IORegister.py b/src/sardana/tango/pool/IORegister.py index 7e9c2d6939..b6f4bd2378 100644 --- a/src/sardana/tango/pool/IORegister.py +++ b/src/sardana/tango/pool/IORegister.py @@ -202,10 +202,10 @@ def initialize_dynamic_attributes(self): non_detect_evts = () for attr_name in detect_evts: - if attrs.has_key(attr_name): + if attr_name in attrs: self.set_change_event(attr_name, True, True) for attr_name in non_detect_evts: - if attrs.has_key(attr_name): + if attr_name in attrs: self.set_change_event(attr_name, True, False) return diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index a0511281fa..42de1aa0db 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -814,15 +814,15 @@ def _format_CreateController_arguments(self, argin): raise Exception(msg) if len(argin) == 1: ret = self._format_create_json_arguments(argin) - if not ret.has_key('type'): + if 'type' not in ret: raise KeyError("Missing key 'type'") - if not ret.has_key('library'): + if 'library' not in ret: raise KeyError("Missing key 'library'") - if not ret.has_key('klass'): + if 'klass' not in ret: raise KeyError("Missing key 'klass'") - if not ret.has_key('name'): + if 'name' not in ret: raise KeyError("Missing key 'name'") - if not ret.has_key('properties'): + if 'properties' not in ret: ret['properties'] = CaselessDict() return ret diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py index 964d47aa1f..29eddb25b9 100644 --- a/src/sardana/taurus/core/tango/sardana/motion.py +++ b/src/sardana/taurus/core/tango/sardana/motion.py @@ -338,7 +338,7 @@ def getElemNamesByMoveableSource(self, names, moveable_sources, for moveable_source in moveable_sources: moveable = moveable_source.getMoveable([name]) if not moveable is None: - if not ms_elems.has_key(moveable_source): + if moveable_source not in ms_elems: ms_elems[moveable_source] = [] moveable_source_moveables = ms_elems.get(moveable_source) present = name in moveable_source_moveables diff --git a/src/sardana/taurus/core/tango/sardana/sardana.py b/src/sardana/taurus/core/tango/sardana/sardana.py index 34768736ac..5724e350a3 100644 --- a/src/sardana/taurus/core/tango/sardana/sardana.py +++ b/src/sardana/taurus/core/tango/sardana/sardana.py @@ -656,7 +656,7 @@ def refresh(self): pass def create_sardana(self, name, device_name): - if self._sardanas.has_key(name): + if name in self._sardanas: raise Exception("Sardana '%s' already exists" % name) self._db.register_service("Sardana", name, device_name) sardana = Sardana(self, name) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index d8af354f9f..408ae18412 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -631,7 +631,7 @@ def addChannel(self, chname=None, chinfo=None, ctrlname=None, external=False): # 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): + if ctrlname not in ctrlsdict: ctrlsdict[ctrlname] = ctrl = {'channels': {}} if not external and chinfo['type'] in ('CTExpChannel', 'OneDExpChannel', 'TwoDExpChannel'): ctrl['timer'] = chname @@ -641,7 +641,7 @@ def addChannel(self, chname=None, chinfo=None, ctrlname=None, external=False): else: ctrl = ctrlsdict[ctrlname] channelsdict = ctrl['channels'] - if channelsdict.has_key(chname): + if chname in channelsdict: self.error( 'Channel "%s" is already in the measurement group. It will not be added again' % chname) return diff --git a/src/sardana/tools/config/get_pool_config.py b/src/sardana/tools/config/get_pool_config.py index d25e4e1902..9b4d94936a 100644 --- a/src/sardana/tools/config/get_pool_config.py +++ b/src/sardana/tools/config/get_pool_config.py @@ -81,7 +81,7 @@ def checkPoolElements(pool): controllers = config['controllers'] elements = {} for ctrl, c_data in list(controllers.items()): - if c_data.has_key('units'): + if 'units' in c_data: c_data = c_data['units']['0'] for _, data in list(c_data['channels'].items()): index = int(data['index']) @@ -281,7 +281,7 @@ def checkPoolElements(pool): attribute_values = [] for attr in list(attr_dicts.keys()): attr_dict = attr_dicts[attr] - if attr_dict.has_key('__value'): + if '__value' in attr_dict: # skip memorized values of DialPosition and Position # DialPosition because it is read only attribute and the # current version of sardana script would not be able to se it diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index bf4ddf633c..545fd7ae32 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -1770,7 +1770,7 @@ def which(filename): if os.access(filename, os.X_OK): return filename - if not os.environ.has_key('PATH') or os.environ['PATH'] == '': + if 'PATH' not in os.environ or os.environ['PATH'] == '': p = os.defpath else: p = os.environ['PATH'] diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index 036d7b07ff..50c4aad493 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -1339,7 +1339,7 @@ def _preprocess(self): self.o("Preprocessing input... ") try: - if not self.SimulationModes.has_key(self._simulation): + if self._simulation not in self.SimulationModes: return motSimNb = 0 From c251c19a2c27d968be10b5a4a07b53f3d4933e3a Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:22:58 +0200 Subject: [PATCH 063/830] 2to3: idioms --- src/sardana/macroserver/msmacromanager.py | 2 +- src/sardana/pool/poolmeasurementgroup.py | 2 +- src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 2a82bb8bee..c7a184f59f 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -877,7 +877,7 @@ def __init__(self, param=None): def filter(self, record): allow = True if record.levelname == "DEBUG": - if type(record.msg) != str: + if not isinstance(record.msg, str): allow = False return allow if record.msg.find("[START]") != -1: diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 2d3bba6b6e..d5e1abcc02 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -1051,7 +1051,7 @@ def get_timer(self): def get_integration_time(self): integration_time = self._synchronization.active_time - if type(integration_time) == float: + if isinstance(integration_time, float): return integration_time elif len(integration_time) == 0: raise Exception("The synchronization group has not been" diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index 1453daa948..ef0163f992 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -147,7 +147,7 @@ def find_diff(first, second): idiff = 'Error on processing' if len(idiff) > 0: diff[key] = idiff - elif type(value1) == list and key.lower() not in SKIPLIST: + elif isinstance(value1, list) and key.lower() not in SKIPLIST: ldiff = [] for v1, v2 in zip(value1, value2): try: From 47b1fd8527b4abfcbb46133cbc3647528050a96c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:28:12 +0200 Subject: [PATCH 064/830] 2to3: import --- src/sardana/__init__.py | 4 ++-- src/sardana/macroserver/macros/test/__init__.py | 6 +++--- src/sardana/macroserver/scan/__init__.py | 4 ++-- src/sardana/pool/poolcontrollers/test/__init__.py | 2 +- src/sardana/spock/__init__.py | 2 +- src/sardana/spock/genutils.py | 6 +++--- src/sardana/tango/__init__.py | 6 +++--- src/sardana/tango/macroserver/test/__init__.py | 4 ++-- src/sardana/tango/pool/Controller.py | 2 +- src/sardana/tango/pool/test/__init__.py | 4 ++-- src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py | 6 +++--- .../qt/qtgui/extra_hkl/diffractometeralignment.py | 2 +- src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py | 2 +- src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py | 4 ++-- .../extra_macroexecutor/favouriteseditor/__init__.py | 4 ++-- .../favouriteseditor/favouriteseditor.py | 2 +- .../favouriteseditor/historyviewer.py | 2 +- .../macroparameterseditor/__init__.py | 4 ++-- .../macroparameterseditor/customeditors/__init__.py | 2 +- .../extra_macroexecutor/sequenceeditor/__init__.py | 2 +- src/sardana/taurus/qt/qtgui/extra_pool/__init__.py | 12 ++++++------ .../taurus/qt/qtgui/extra_pool/poolchannel.py | 2 +- .../taurus/qt/qtgui/extra_pool/poolioregister.py | 2 +- .../taurus/qt/qtgui/extra_sardana/sardanaeditor.py | 6 +++--- 24 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/sardana/__init__.py b/src/sardana/__init__.py index 7c72456212..732e499128 100644 --- a/src/sardana/__init__.py +++ b/src/sardana/__init__.py @@ -26,8 +26,8 @@ """This package provides the sardana library""" -import release as __release -import requirements as __requirements +from . import release as __release +from . import requirements as __requirements __requirements.check_requirements() diff --git a/src/sardana/macroserver/macros/test/__init__.py b/src/sardana/macroserver/macros/test/__init__.py index eab3a577a0..c827bdc1ee 100644 --- a/src/sardana/macroserver/macros/test/__init__.py +++ b/src/sardana/macroserver/macros/test/__init__.py @@ -23,7 +23,7 @@ ## ############################################################################## -from macroexecutor import BaseMacroExecutor, MacroExecutorFactory -from base import (macroTest, BaseMacroTestCase, RunMacroTestCase, +from .macroexecutor import BaseMacroExecutor, MacroExecutorFactory +from .base import (macroTest, BaseMacroTestCase, RunMacroTestCase, RunStopMacroTestCase, testRun, testFail, testStop) -from sardemoenv import * +from .sardemoenv import * diff --git a/src/sardana/macroserver/scan/__init__.py b/src/sardana/macroserver/scan/__init__.py index e8ff34706c..79e15c718d 100644 --- a/src/sardana/macroserver/scan/__init__.py +++ b/src/sardana/macroserver/scan/__init__.py @@ -27,5 +27,5 @@ __docformat__ = 'restructuredtext' -from scandata import * -from gscan import * +from .scandata import * +from .gscan import * diff --git a/src/sardana/pool/poolcontrollers/test/__init__.py b/src/sardana/pool/poolcontrollers/test/__init__.py index f7a77b2d69..9b5ed21c9e 100644 --- a/src/sardana/pool/poolcontrollers/test/__init__.py +++ b/src/sardana/pool/poolcontrollers/test/__init__.py @@ -1 +1 @@ -from base import * +from .base import * diff --git a/src/sardana/spock/__init__.py b/src/sardana/spock/__init__.py index 496c5b59f5..dbeef274e7 100644 --- a/src/sardana/spock/__init__.py +++ b/src/sardana/spock/__init__.py @@ -26,7 +26,7 @@ """This package provides spock""" -from genutils import load_ipython_extension, unload_ipython_extension, \ +from .genutils import load_ipython_extension, unload_ipython_extension, \ load_config, run diff --git a/src/sardana/spock/genutils.py b/src/sardana/spock/genutils.py index 9d21a3b2c3..b41840c247 100644 --- a/src/sardana/spock/genutils.py +++ b/src/sardana/spock/genutils.py @@ -66,8 +66,8 @@ def get_ipython_version_list(): ipv = get_ipython_version_list() if ipv >= [0, 10] and ipv < [0, 11]: - from ipython_00_10.genutils import * + from .ipython_00_10.genutils import * elif ipv >= [0, 11] and ipv < [1, 0]: - from ipython_00_11.genutils import * + from .ipython_00_11.genutils import * else: - from ipython_01_00.genutils import * + from .ipython_01_00.genutils import * diff --git a/src/sardana/tango/__init__.py b/src/sardana/tango/__init__.py index 5242b40b6e..597716b72b 100644 --- a/src/sardana/tango/__init__.py +++ b/src/sardana/tango/__init__.py @@ -31,14 +31,14 @@ def prepare_sardana(util): - import pool - import macroserver + from . import pool + from . import macroserver pool.prepare_pool(util) macroserver.prepare_macroserver(util) def main_sardana(args=None, start_time=None, mode=None): - import core.util + from . import core.util # pass server name so the scripts generated with setuptools work on Windows return core.util.run(prepare_sardana, args=args, start_time=start_time, mode=mode, name=SERVER_NAME) diff --git a/src/sardana/tango/macroserver/test/__init__.py b/src/sardana/tango/macroserver/test/__init__.py index 6d9650059e..de2a4fc1b9 100644 --- a/src/sardana/tango/macroserver/test/__init__.py +++ b/src/sardana/tango/macroserver/test/__init__.py @@ -23,5 +23,5 @@ ## ############################################################################## -from macroexecutor import TangoMacroExecutor -from base import * +from .macroexecutor import TangoMacroExecutor +from .base import * diff --git a/src/sardana/tango/pool/Controller.py b/src/sardana/tango/pool/Controller.py index dccbc3e13e..f4a221a9f1 100644 --- a/src/sardana/tango/pool/Controller.py +++ b/src/sardana/tango/pool/Controller.py @@ -46,7 +46,7 @@ from sardana.sardanaattribute import SardanaAttribute from sardana.tango.core.util import to_tango_attr_info -from PoolDevice import PoolDevice, PoolDeviceClass +from .PoolDevice import PoolDevice, PoolDeviceClass def to_bool(s): diff --git a/src/sardana/tango/pool/test/__init__.py b/src/sardana/tango/pool/test/__init__.py index 25b3ca6527..ad1cd75194 100644 --- a/src/sardana/tango/pool/test/__init__.py +++ b/src/sardana/tango/pool/test/__init__.py @@ -23,9 +23,9 @@ ## ############################################################################## -from base import (BasePoolTestCase, ControllerLoadsTestCase, +from .base import (BasePoolTestCase, ControllerLoadsTestCase, ControllerCreationTestCase, ElementCreationTestCase) -from base_sartest import SarTestTestCase +from .base_sartest import SarTestTestCase from .test_measurementgroup import * from .test_Motor import * from .test_persistence import * diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py b/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py index ecf90ecd97..11085946b3 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py @@ -27,6 +27,6 @@ __init__.py: """ -from hklscan import HKLScan -from ubmatrix import UBMatrixBase -from diffractometeralignment import DiffractometerAlignment +from .hklscan import HKLScan +from .ubmatrix import UBMatrixBase +from .diffractometeralignment import DiffractometerAlignment diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py index 560e4986ba..7f2b03a95b 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py @@ -50,7 +50,7 @@ from sardana.taurus.qt.qtgui.extra_macroexecutor import TaurusMacroConfigurationDialog -from selectsignal import SelectSignal +from .selectsignal import SelectSignal class EngineModesComboBox(Qt.QComboBox, TaurusBaseWidget): diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py index 3300f8f36e..a0508a94a6 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py @@ -39,7 +39,7 @@ from taurus.qt.qtcore.communication import SharedDataManager from taurus.qt.qtgui.input import TaurusValueLineEdit -from displayscanangles import DisplayScanAngles +from .displayscanangles import DisplayScanAngles import taurus.core.util.argparse import taurus.qt.qtgui.application diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py index bdb8a85d3e..daaf9306eb 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py @@ -30,8 +30,8 @@ import sardana from taurus.external.qt import Qt from taurus.qt.qtgui.container import TaurusWidget -from reflectionslist import ReflectionsList -from reflectionseditor import ReflectionsEditor +from .reflectionslist import ReflectionsList +from .reflectionseditor import ReflectionsEditor from taurus.qt.qtgui.base import TaurusBaseWidget from taurus.external.qt import QtCore, QtGui diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/__init__.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/__init__.py index 5d88d0fcce..d8bf71f660 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/__init__.py @@ -23,5 +23,5 @@ ## ############################################################################## -from favouriteseditor import FavouritesMacrosEditor -from historyviewer import HistoryMacrosViewer +from .favouriteseditor import FavouritesMacrosEditor +from .historyviewer import HistoryMacrosViewer diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py index c0200922d5..9787076695 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py @@ -32,7 +32,7 @@ from taurus.qt.qtgui.resource import getIcon from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtcore.configuration import BaseConfigurableClass -from model import MacrosListModel +from .model import MacrosListModel class FavouritesMacrosEditor(TaurusWidget): diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py index 9cb2c53b09..eb4026d7e2 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py @@ -32,7 +32,7 @@ from taurus.qt.qtgui.resource import getIcon from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtcore.configuration import BaseConfigurableClass -from model import MacrosListModel +from .model import MacrosListModel class HistoryMacrosViewer(TaurusWidget): diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/__init__.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/__init__.py index fe70da9c51..254043ad22 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/__init__.py @@ -26,5 +26,5 @@ """ __init__.py: """ -from macroparameterseditor import ParamEditorManager, StandardMacroParametersEditor -from model import ParamEditorModel +from .macroparameterseditor import ParamEditorManager, StandardMacroParametersEditor +from .model import ParamEditorModel diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/__init__.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/__init__.py index c470b50746..cde35af141 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/__init__.py @@ -23,4 +23,4 @@ ## ############################################################################## -from senv import SenvEditor +from .senv import SenvEditor diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/__init__.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/__init__.py index ce4abc642c..623433cbe7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/__init__.py @@ -27,4 +27,4 @@ __init__.py: """ -from sequenceeditor import TaurusSequencer, TaurusSequencerWidget, main +from .sequenceeditor import TaurusSequencer, TaurusSequencerWidget, main diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py b/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py index f604a8979e..4839ee5ae2 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py @@ -27,9 +27,9 @@ __init__.py: """ -from motor import TaurusMotorH, TaurusMotorH2, TaurusMotorV, TaurusMotorV2 -from poolmotor import PoolMotorSlim, LabelWidgetDragsDeviceAndAttribute -from poolmotor import PoolMotorTV, PoolMotorTVLabelWidget, PoolMotorTVReadWidget, PoolMotorTVWriteWidget, PoolMotorTVUnitsWidget -from poolmotor import PoolMotor -from poolchannel import PoolChannel, PoolChannelTV -from poolioregister import PoolIORegisterTV, PoolIORegisterReadWidget, PoolIORegisterWriteWidget, PoolIORegister, PoolIORegisterButtons +from .motor import TaurusMotorH, TaurusMotorH2, TaurusMotorV, TaurusMotorV2 +from .poolmotor import PoolMotorSlim, LabelWidgetDragsDeviceAndAttribute +from .poolmotor import PoolMotorTV, PoolMotorTVLabelWidget, PoolMotorTVReadWidget, PoolMotorTVWriteWidget, PoolMotorTVUnitsWidget +from .poolmotor import PoolMotor +from .poolchannel import PoolChannel, PoolChannelTV +from .poolioregister import PoolIORegisterTV, PoolIORegisterReadWidget, PoolIORegisterWriteWidget, PoolIORegister, PoolIORegisterButtons diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 1fb5d01af4..4c2cee4d21 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -33,7 +33,7 @@ from taurus.external.qt import Qt from taurus.qt.qtgui.panel import TaurusValue, TaurusDevButton from taurus.qt.qtgui.container import TaurusWidget -from poolmotor import LabelWidgetDragsDeviceAndAttribute +from .poolmotor import LabelWidgetDragsDeviceAndAttribute class _ParentDevButton(TaurusDevButton): diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolioregister.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolioregister.py index 365a576f40..c4d82fad48 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolioregister.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolioregister.py @@ -37,7 +37,7 @@ from taurus.qt.qtgui.panel import TaurusValue from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtgui.util.ui import UILoadable -from poolmotor import LabelWidgetDragsDeviceAndAttribute +from .poolmotor import LabelWidgetDragsDeviceAndAttribute import taurus diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py index 5bb434590a..02ac41e827 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py @@ -44,13 +44,13 @@ # consider adding an utility in taurusedirot to create actions from spyder.utils.qthelpers import create_action -from macrotree import MacroSelectionDialog -from elementtree import SardanaElementTreeWidget +from .macrotree import MacroSelectionDialog +from .elementtree import SardanaElementTreeWidget from sardana.taurus.qt.qtcore.tango.sardana.model import SardanaBaseProxyModel, \ SardanaElementTypeModel, SardanaTypeTreeItem, SardanaRootTreeItem -from sardanabasewizard import SardanaBaseWizard, SardanaBasePage +from .sardanabasewizard import SardanaBaseWizard, SardanaBasePage _MACRO_LIB_TEMPLATE = """#!/usr/bin/env python {copyright} From d68b8705b59d3781a6edf7960b8351f662bbd94f Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:34:21 +0200 Subject: [PATCH 065/830] 2to3: imports --- src/sardana/macroserver/macro.py | 4 ++-- src/sardana/pool/poolcontroller.py | 4 ++-- src/sardana/util/deepreload.py | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index a6362ba4f6..ad38aefc27 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -43,7 +43,7 @@ import ctypes import weakref import operator -import StringIO +import io import threading import traceback @@ -846,7 +846,7 @@ def print(self, *args, **kwargs): """ fd = kwargs.get('file', sys.stdout) if fd in (sys.stdout, sys.stderr): - out = StringIO.StringIO() + out = io.StringIO() kwargs['file'] = out end = kwargs.get('end', '\n') if end == '\n': diff --git a/src/sardana/pool/poolcontroller.py b/src/sardana/pool/poolcontroller.py index 57ef12af6b..14caa60205 100644 --- a/src/sardana/pool/poolcontroller.py +++ b/src/sardana/pool/poolcontroller.py @@ -33,7 +33,7 @@ import sys import weakref -import StringIO +import io import traceback import functools @@ -84,7 +84,7 @@ def get_ctrl_error_str(self): err = self._ctrl_error if err is None: return "" - sio = StringIO.StringIO() + sio = io.StringIO() traceback.print_exception(err[0], err[1], err[2], None, sio) s = sio.getvalue() sio.close() diff --git a/src/sardana/util/deepreload.py b/src/sardana/util/deepreload.py index 5a38302a8f..440818a992 100644 --- a/src/sardana/util/deepreload.py +++ b/src/sardana/util/deepreload.py @@ -42,7 +42,7 @@ re-implementation of hierarchical module import. """ -import __builtin__ +import builtins from contextlib import contextmanager import imp import sys @@ -50,26 +50,26 @@ from types import ModuleType from warnings import warn -original_import = __builtin__.__import__ +original_import = builtins.__import__ class DeepReload(object): def __enter__(self): - __builtin__.reload = reload + builtins.reload = reload def __exit__(self, etype, evalue, etraceback): - __builtin__.reload = original_reload + builtins.reload = original_reload @contextmanager def replace_import_hook(new_import): - saved_import = __builtin__.__import__ - __builtin__.__import__ = new_import + saved_import = builtins.__import__ + builtins.__import__ = new_import try: yield finally: - __builtin__.__import__ = saved_import + builtins.__import__ = saved_import def get_parent(globals, level): @@ -351,7 +351,7 @@ def deep_reload_hook(m): # Save the original hooks try: - original_reload = __builtin__.reload + original_reload = builtins.reload except AttributeError: original_reload = imp.reload # Python 3 From 1b0e12e11c91063cb0e1b48ab8bd21ffbbed757c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:37:02 +0200 Subject: [PATCH 066/830] 2to3: intern --- src/sardana/sardanabase.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sardana/sardanabase.py b/src/sardana/sardanabase.py index 5a462df28a..710dd05f2f 100644 --- a/src/sardana/sardanabase.py +++ b/src/sardana/sardanabase.py @@ -27,6 +27,7 @@ classes for Sardana object""" from __future__ import absolute_import +import sys __all__ = ["SardanaBaseObject", "SardanaObjectID"] @@ -53,8 +54,8 @@ def __init__(self, **kwargs): EventGenerator.__init__(self) EventReceiver.__init__(self) self._type = kwargs.pop('elem_type') - self._name = intern(kwargs.pop('name')) - self._full_name = intern(kwargs.pop('full_name')) + self._name = sys.intern(kwargs.pop('name')) + self._full_name = sys.intern(kwargs.pop('full_name')) self._frontend = None Logger.__init__(self, self._name) self._manager = weakref.ref(kwargs.pop('manager')) From f6ddd2e92779c654c99fd1809dc20cf5dcf16ec7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:39:29 +0200 Subject: [PATCH 067/830] 2to3: itertools --- src/sardana/macroserver/scan/recorder/storage.py | 2 +- src/sardana/tango/core/util.py | 2 +- src/sardana/taurus/core/tango/sardana/macroserver.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/scan/recorder/storage.py b/src/sardana/macroserver/scan/recorder/storage.py index a6208e90e2..ce61d847ca 100644 --- a/src/sardana/macroserver/scan/recorder/storage.py +++ b/src/sardana/macroserver/scan/recorder/storage.py @@ -104,7 +104,7 @@ def setFileName(self, filename): # obtain preferred nexus file mode for writing from the filename # extension (defaults to hdf5) extension = os.path.splitext(filename)[1] - inv_formats = dict(itertools.izip( + inv_formats = dict(zip( iter(self.formats.values()), iter(self.formats.keys()))) self.nxfilemode = inv_formats.get(extension.lower(), 'w5') self.currentlist = None diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 1452d29ff7..fe5d7434e0 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -915,7 +915,7 @@ def get_dev_from_class_server(db, classname, server): def pairwise(iterable): "s -> (s0, s1), (s2, s3), (s4, s5), ..." a = iter(iterable) - return itertools.izip(a, a) + return zip(a, a) devices = [] device_class_list = db.get_device_class_list(server) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index d3ec9d3a87..b30166c371 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -1226,7 +1226,7 @@ def __fillParamNodesValues(self, paramInfo, paramNode): if isinstance(paramType, list): for repeatNode in paramNode.children(): children = repeatNode.children() - for child, paramT in izip_longest(children, paramType): + for child, paramT in zip_longest(children, paramType): if child is None: node = ParamFactory(paramT, repeatNode) repeatNode.insertChild(node) From b1fea70cf701b2db35a73abcd9cc7b6a3bd0f648 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:40:38 +0200 Subject: [PATCH 068/830] 2to3: itertools_imports --- src/sardana/taurus/core/tango/sardana/macroserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index b30166c371..707f0d2942 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -57,7 +57,7 @@ SingleParamNode, ParamNode, createMacroNode from .sardana import BaseSardanaElementContainer, BaseSardanaElement from .pool import getChannelConfigs -from itertools import izip_longest +from itertools import zip_longest CHANGE_EVT_TYPES = TaurusEventType.Change, TaurusEventType.Periodic From 59f997619f4ca4d13174480dee1321342804c885 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:51:39 +0200 Subject: [PATCH 069/830] 2to3: long --- src/sardana/macroserver/recorders/sharedmemory.py | 4 ++-- src/sardana/sardanadefs.py | 6 +++--- src/sardana/sardanautils.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sardana/macroserver/recorders/sharedmemory.py b/src/sardana/macroserver/recorders/sharedmemory.py index 39df47b85f..cf002dac9a 100644 --- a/src/sardana/macroserver/recorders/sharedmemory.py +++ b/src/sardana/macroserver/recorders/sharedmemory.py @@ -143,7 +143,7 @@ def _writeRecord(self, record): for colname in self.labels: val = record.data.get(colname) - if (not val is None) and (operator.isNumberType(val) and (type(val) in [int, float, long])): + if (not val is None) and (operator.isNumberType(val) and (type(val) in [int, float, int])): vals.append(val) elif (not val is None) and (operator.isNumberType(val)): valsmca = [] @@ -271,7 +271,7 @@ def _writeRecord(self, record): for colname in self.labels: dim_list.append(0) val = record.data.get(colname) - if (not val is None) and (type(val) in [int, float, long]): + if (not val is None) and (type(val) in [int, float, int]): vals.append(val) myj = 0 diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index d6cb7094c5..0d17ce8f07 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -124,7 +124,7 @@ def __repr__(self): 'int': DataType.Integer, 'integer': DataType.Integer, int: DataType.Integer, - long: DataType.Integer, + int: DataType.Integer, 'long': DataType.Integer, DataType.Integer: DataType.Integer, 'float': DataType.Double, @@ -146,7 +146,7 @@ def __repr__(self): 'int': int, 'integer': int, int: int, - long: int, + int: int, 'long': int, DataType.Integer: int, 'float': float, @@ -482,7 +482,7 @@ def __expand_sardana_interface_data(si_map, name, curr_id): curr_id = __expand_sardana_interface_data( si_map, interface, curr_id) d |= si_map[interface] - si_map[name] = long(d | curr_id) + si_map[name] = int(d | curr_id) return 2 * curr_id diff --git a/src/sardana/sardanautils.py b/src/sardana/sardanautils.py index b3108f25e7..a7fced9270 100644 --- a/src/sardana/sardanautils.py +++ b/src/sardana/sardanautils.py @@ -58,10 +58,10 @@ __use_long = False try: - long + int __use_long = True - __int_klasses.append(long) - __DTYPE_MAP[long] = DataType.Integer + __int_klasses.append(int) + __DTYPE_MAP[int] = DataType.Integer except: pass From 39a087af13c44f9672761613b512d5d3d3d6a17b Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:53:59 +0200 Subject: [PATCH 070/830] Manual changes after 2to3: long These changes does not have sense. Remove the affected code. --- src/sardana/macroserver/recorders/sharedmemory.py | 4 ++-- src/sardana/sardanadefs.py | 2 -- src/sardana/sardanautils.py | 9 --------- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/sardana/macroserver/recorders/sharedmemory.py b/src/sardana/macroserver/recorders/sharedmemory.py index cf002dac9a..216a065b0a 100644 --- a/src/sardana/macroserver/recorders/sharedmemory.py +++ b/src/sardana/macroserver/recorders/sharedmemory.py @@ -143,7 +143,7 @@ def _writeRecord(self, record): for colname in self.labels: val = record.data.get(colname) - if (not val is None) and (operator.isNumberType(val) and (type(val) in [int, float, int])): + if (not val is None) and (operator.isNumberType(val) and (type(val) in [int, float])): vals.append(val) elif (not val is None) and (operator.isNumberType(val)): valsmca = [] @@ -271,7 +271,7 @@ def _writeRecord(self, record): for colname in self.labels: dim_list.append(0) val = record.data.get(colname) - if (not val is None) and (type(val) in [int, float, int]): + if (not val is None) and (type(val) in [int, float]): vals.append(val) myj = 0 diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index 0d17ce8f07..9819991794 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -124,7 +124,6 @@ def __repr__(self): 'int': DataType.Integer, 'integer': DataType.Integer, int: DataType.Integer, - int: DataType.Integer, 'long': DataType.Integer, DataType.Integer: DataType.Integer, 'float': DataType.Double, @@ -146,7 +145,6 @@ def __repr__(self): 'int': int, 'integer': int, int: int, - int: int, 'long': int, DataType.Integer: int, 'float': float, diff --git a/src/sardana/sardanautils.py b/src/sardana/sardanautils.py index a7fced9270..1fb5bd8360 100644 --- a/src/sardana/sardanautils.py +++ b/src/sardana/sardanautils.py @@ -56,15 +56,6 @@ except: pass -__use_long = False -try: - int - __use_long = True - __int_klasses.append(int) - __DTYPE_MAP[int] = DataType.Integer -except: - pass - __bool_klasses = [bool] + __int_klasses __str_klasses = tuple(__str_klasses) From d284082efa6a1ec9e3adc5f9e758bbfd23e5959d Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:56:14 +0200 Subject: [PATCH 071/830] 2to3: map --- src/sardana/macroserver/macro.py | 4 +-- src/sardana/macroserver/macros/demo.py | 2 +- src/sardana/macroserver/macros/discrete.py | 2 +- .../macroserver/macros/examples/plotting.py | 2 +- src/sardana/macroserver/macros/standard.py | 10 +++---- src/sardana/macroserver/macroserver.py | 2 +- src/sardana/macroserver/msmacromanager.py | 4 +-- src/sardana/macroserver/recorders/output.py | 2 +- src/sardana/macroserver/scan/scandata.py | 2 +- src/sardana/pool/pool.py | 2 +- src/sardana/pool/poolcontroller.py | 2 +- src/sardana/pool/poolmetacontroller.py | 2 +- src/sardana/sardanabase.py | 2 +- src/sardana/spock/ipython_00_10/genutils.py | 10 +++---- src/sardana/spock/ipython_00_11/genutils.py | 10 +++---- src/sardana/spock/ipython_01_00/genutils.py | 10 +++---- src/sardana/tango/core/util.py | 4 +-- src/sardana/tango/macroserver/MacroServer.py | 2 +- .../tango/macroserver/test/macroexecutor.py | 2 +- src/sardana/tango/pool/Controller.py | 4 +-- src/sardana/tango/pool/MotorGroup.py | 2 +- src/sardana/tango/pool/Pool.py | 4 +-- src/sardana/tango/pool/PoolDevice.py | 2 +- src/sardana/tango/pool/PseudoCounter.py | 2 +- src/sardana/tango/pool/PseudoMotor.py | 2 +- .../taurus/core/tango/sardana/macro.py | 2 +- .../taurus/core/tango/sardana/macroserver.py | 6 ++-- src/sardana/taurus/core/tango/sardana/pool.py | 16 +++++----- .../taurus/core/tango/sardana/sardana.py | 8 ++--- src/sardana/tools/config/get_pool_config.py | 2 +- src/sardana/tools/config/sardana.py | 30 +++++++++---------- 31 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index ad38aefc27..1c2df84a9b 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -2305,7 +2305,7 @@ def _reserveObjs(self, args): macro""" for obj in args: # isiterable - if not type(obj) in map(type, ([], ())): + if not type(obj) in list(map(type, ([], ()))): # if not operator.isSequenceType(obj) or type(obj) in # types.StringTypes: obj = (obj,) @@ -2362,7 +2362,7 @@ def __prepareResult(self, out): if out is None: out = () if operator.isSequenceType(out) and not type(out) in types.StringTypes: - out = map(str, out) + out = list(map(str, out)) else: out = (str(out),) return out diff --git a/src/sardana/macroserver/macros/demo.py b/src/sardana/macroserver/macros/demo.py index 9d4ae1289c..172f0e0256 100644 --- a/src/sardana/macroserver/macros/demo.py +++ b/src/sardana/macroserver/macros/demo.py @@ -209,7 +209,7 @@ def sar_demo(self): @macro([["motor", Type.Moveable, None, '']]) def mym2(self, pm): self.output(pm.getMotorNames()) - elements = map(self.getMoveable, pm.elements) + elements = list(map(self.getMoveable, pm.elements)) self.output(elements) self.output(type(pm)) self.output(type(elements[0])) diff --git a/src/sardana/macroserver/macros/discrete.py b/src/sardana/macroserver/macros/discrete.py index 77c34f3124..e35ad627c1 100644 --- a/src/sardana/macroserver/macros/discrete.py +++ b/src/sardana/macroserver/macros/discrete.py @@ -196,7 +196,7 @@ def run(self, pseudo): # Sort by position column value_list = sorted(value_list, key=lambda x: x[1]) # Transpose matrix - value_list = map(list, zip(*value_list)) + value_list = list(map(list, zip(*value_list))) # Extract sorted row headers row_head_str = value_list[0] # Extract sorted values diff --git a/src/sardana/macroserver/macros/examples/plotting.py b/src/sardana/macroserver/macros/examples/plotting.py index 750c2c56cc..97bf7621cd 100644 --- a/src/sardana/macroserver/macros/examples/plotting.py +++ b/src/sardana/macroserver/macros/examples/plotting.py @@ -19,7 +19,7 @@ def J0_plot(self): x = linspace(0, 20, 200) y = j0(x) x1 = x[::10] - y1 = map(j0i, x1) + y1 = list(map(j0i, x1)) self.pyplot.plot(x, y, label=r'$J_0(x)$') self.pyplot.plot(x1, y1, 'ro', label=r'$J_0^{integ}(x)$') self.pyplot.title( diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 63433ede6a..3317ba8d75 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -374,9 +374,9 @@ def run(self, motor_list): if show_ctrlaxis: valctrl = str_fmt % (ctrl_name) valaxis = str_fmt % str(axis_nb) - upos = map(str, [valctrl, valaxis, ' ', val2, val1, val3]) + upos = list(map(str, [valctrl, valaxis, ' ', val2, val1, val3])) else: - upos = map(str, ['', val2, val1, val3]) + upos = list(map(str, ['', val2, val1, val3])) pos_data = upos if show_dial: try: @@ -389,7 +389,7 @@ def run(self, motor_list): val2 = str_fmt % dPosObj.getMaxValue() val3 = str_fmt % dPosObj.getMinValue() - dpos = map(str, [val2, val1, val3]) + dpos = list(map(str, [val2, val1, val3])) pos_data += [''] + dpos motor_pos.append(pos_data) @@ -430,8 +430,8 @@ def run(self, motor_list): name = motor.getName() motor_names.append([name]) posObj = motor.getPositionObj() - upos = map(str, [posObj.getMaxValue(), motor.getPosition( - force=True), posObj.getMinValue()]) + upos = list(map(str, [posObj.getMaxValue(), motor.getPosition( + force=True), posObj.getMinValue()])) pos_data = [''] + upos motor_pos.append(pos_data) diff --git a/src/sardana/macroserver/macroserver.py b/src/sardana/macroserver/macroserver.py index 30b1be4a04..0bb206621e 100644 --- a/src/sardana/macroserver/macroserver.py +++ b/src/sardana/macroserver/macroserver.py @@ -740,7 +740,7 @@ def find_objects(self, param, type_class=All, subtype=All, pool=All): type_name_list = type_class obj_set = set() param = ['^%s$' % x for x in param] - re_objs = map(re.compile, param, len(param) * (re.IGNORECASE,)) + re_objs = list(map(re.compile, param, len(param) * (re.IGNORECASE,))) re_subtype = re.compile(subtype, re.IGNORECASE) for type_name in type_name_list: type_class_name = type_name diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index c7a184f59f..c0f91cbe99 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1191,7 +1191,7 @@ def __preprocessResult(self, result): if result is None: return () if is_non_str_seq(result): - result = map(str, result) + result = list(map(str, result)) else: result = (str(result),) return result @@ -1203,7 +1203,7 @@ def _composeMacroLine(self, macro_name, macro_params, macro_id): # recursive map to maintain the list objects structure params_str_list = recur_map(str, macro_params) # plain map to be able to perform join (only strings may be joined) - params_str_list = map(str, params_str_list) + params_str_list = list(map(str, params_str_list)) params_str = ', '.join(params_str_list) macro_id = macro_id # create macro_line - string representation of macro, its parameters diff --git a/src/sardana/macroserver/recorders/output.py b/src/sardana/macroserver/recorders/output.py index 527e671649..9d84244bd4 100644 --- a/src/sardana/macroserver/recorders/output.py +++ b/src/sardana/macroserver/recorders/output.py @@ -190,7 +190,7 @@ def _startRecordList(self, recordlist): label = [label] header_rows = max(header_rows, len(label)) labels.append(label) - col_size = max(col_width, max(map(len, label))) + col_size = max(col_width, max(list(map(len, label)))) header_len += col_size col_sizes.append(col_size) diff --git a/src/sardana/macroserver/scan/scandata.py b/src/sardana/macroserver/scan/scandata.py index ea0d722037..6fecb38979 100644 --- a/src/sardana/macroserver/scan/scandata.py +++ b/src/sardana/macroserver/scan/scandata.py @@ -491,7 +491,7 @@ def isRecordCompleted(self, recordno): return True def addRecords(self, records): - map(self.addRecord, records) + list(map(self.addRecord, records)) def end(self): start = self.currentIndex diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py index e6f1c432df..20eb596407 100644 --- a/src/sardana/pool/pool.py +++ b/src/sardana/pool/pool.py @@ -366,7 +366,7 @@ def get_acquisition_elements_info(self): return ret def get_acquisition_elements_str_info(self): - return map(self.str_object, self.get_acquisition_elements_info()) + return list(map(self.str_object, self.get_acquisition_elements_info())) def create_controller(self, **kwargs): ctrl_type = kwargs['type'] diff --git a/src/sardana/pool/poolcontroller.py b/src/sardana/pool/poolcontroller.py index 14caa60205..299dd233da 100644 --- a/src/sardana/pool/poolcontroller.py +++ b/src/sardana/pool/poolcontroller.py @@ -71,7 +71,7 @@ def get_ctrl_types(self): raise NotImplementedError def get_ctrl_type_names(self): - return map(ElementType.whatis, self.get_ctrl_types()) + return list(map(ElementType.whatis, self.get_ctrl_types())) def is_online(self): return True diff --git a/src/sardana/pool/poolmetacontroller.py b/src/sardana/pool/poolmetacontroller.py index ac34119bd2..d75b276c3e 100644 --- a/src/sardana/pool/poolmetacontroller.py +++ b/src/sardana/pool/poolmetacontroller.py @@ -302,7 +302,7 @@ def __init__(self, **kwargs): axis_attrs[k] = DataInfo.toDataInfo(k, v) self.types = types = self.__build_types() - self.type_names = map(ElementType.whatis, types) + self.type_names = list(map(ElementType.whatis, types)) if ElementType.PseudoMotor in types: self.motor_roles = tuple(klass.motor_roles) diff --git a/src/sardana/sardanabase.py b/src/sardana/sardanabase.py index 710dd05f2f..9a78e73391 100644 --- a/src/sardana/sardanabase.py +++ b/src/sardana/sardanabase.py @@ -163,7 +163,7 @@ def get_interface_names(self): The sequence of interfaces this object implements. :rtype: sequence<:obj:`str`>""" - return map(Interface.get, self.get_interfaces()) + return list(map(Interface.get, self.get_interfaces())) def serialize(self, *args, **kwargs): kwargs['name'] = self.name diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index 1d7277f3dc..de18fd622b 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -545,11 +545,11 @@ def get_taurus_core_version_number(): def check_requirements(): r = requirements - minPyTango, recPyTango = map(translate_version_str2int, r["PyTango"]) - minIPython, recIPython = map(translate_version_str2int, r["IPython"]) - minPython, recPython = map(translate_version_str2int, r["Python"]) - minTaurusCore, recTaurusCore = map( - translate_version_str2int, r["taurus.core"]) + minPyTango, recPyTango = list(map(translate_version_str2int, r["PyTango"])) + minIPython, recIPython = list(map(translate_version_str2int, r["IPython"])) + minPython, recPython = list(map(translate_version_str2int, r["Python"])) + minTaurusCore, recTaurusCore = list(map( + translate_version_str2int, r["taurus.core"])) currPython = get_python_version_number() currIPython = get_ipython_version_number() diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py index dc4375dcc6..d933bc514c 100644 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ b/src/sardana/spock/ipython_00_11/genutils.py @@ -498,11 +498,11 @@ def get_taurus_core_version_number(): def check_requirements(): r = requirements - minPyTango, recPyTango = map(translate_version_str2int, r["PyTango"]) - minIPython, recIPython = map(translate_version_str2int, r["IPython"]) - minPython, recPython = map(translate_version_str2int, r["Python"]) - minTaurusCore, recTaurusCore = map( - translate_version_str2int, r["taurus.core"]) + minPyTango, recPyTango = list(map(translate_version_str2int, r["PyTango"])) + minIPython, recIPython = list(map(translate_version_str2int, r["IPython"])) + minPython, recPython = list(map(translate_version_str2int, r["Python"])) + minTaurusCore, recTaurusCore = list(map( + translate_version_str2int, r["taurus.core"])) currPython = get_python_version_number() currIPython = get_ipython_version_number() diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index 5db6f58ea1..b44b4b361f 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -514,11 +514,11 @@ def get_taurus_core_version_number(): def check_requirements(): r = requirements - minPyTango, recPyTango = map(translate_version_str2int, r["PyTango"]) - minIPython, recIPython = map(translate_version_str2int, r["IPython"]) - minPython, recPython = map(translate_version_str2int, r["Python"]) - minTaurusCore, recTaurusCore = map( - translate_version_str2int, r["taurus.core"]) + minPyTango, recPyTango = list(map(translate_version_str2int, r["PyTango"])) + minIPython, recIPython = list(map(translate_version_str2int, r["IPython"])) + minPython, recPython = list(map(translate_version_str2int, r["Python"])) + minTaurusCore, recTaurusCore = list(map( + translate_version_str2int, r["taurus.core"])) currPython = get_python_version_number() currIPython = get_ipython_version_number() diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index fe5d7434e0..1d434dd4a8 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -743,7 +743,7 @@ def prepare_server(args, tango_args): pool_alias = pool[2] if pool_alias is not None: all_pools.append(pool_alias) - all_pools = map(str.lower, all_pools) + all_pools = list(map(str.lower, all_pools)) pools_for_choosing = [] for i in pools: pools_for_choosing.append(pools[i][3]) @@ -789,7 +789,7 @@ def prepare_server(args, tango_args): def exists_server_instance(db, server_name, server_instance): - known_inst = map(str.lower, db.get_instance_name_list(server_name)) + known_inst = list(map(str.lower, db.get_instance_name_list(server_name))) return server_instance.lower() in known_inst diff --git a/src/sardana/tango/macroserver/MacroServer.py b/src/sardana/tango/macroserver/MacroServer.py index 744fdb63a7..b55926cacb 100644 --- a/src/sardana/tango/macroserver/MacroServer.py +++ b/src/sardana/tango/macroserver/MacroServer.py @@ -290,7 +290,7 @@ def GetMacroCode(self, argin): """GetMacroCode( [, ]) -> full filename, code, line_nb """ ret = self.macro_server.get_or_create_macro_lib(*argin) - return map(str, ret) + return list(map(str, ret)) def SetMacroCode(self, argin): lib_name, code = argin[:2] diff --git a/src/sardana/tango/macroserver/test/macroexecutor.py b/src/sardana/tango/macroserver/test/macroexecutor.py index 7a1b25625e..80494fab35 100644 --- a/src/sardana/tango/macroserver/test/macroexecutor.py +++ b/src/sardana/tango/macroserver/test/macroexecutor.py @@ -96,7 +96,7 @@ def push_event(self, *args, **kwargs): attr_value = getattr(event_data, 'attr_value') if attr_value is None: return - v = map(str, attr_value.value) + v = list(map(str, attr_value.value)) if not len(v[1]): return fmt = v[0] diff --git a/src/sardana/tango/pool/Controller.py b/src/sardana/tango/pool/Controller.py index f4a221a9f1..d721999234 100644 --- a/src/sardana/tango/pool/Controller.py +++ b/src/sardana/tango/pool/Controller.py @@ -109,7 +109,7 @@ def get_role_ids(self): 'counter_role_ids'] if len(role_ids) == 0: role_ids = self.Role_ids - role_ids = map(int, role_ids) + role_ids = list(map(int, role_ids)) return role_ids @@ -151,7 +151,7 @@ def _get_ctrl_properties(self): op = float elif dtype == DataType.Boolean: op = to_bool - prop_value = map(op, prop_value) + prop_value = list(map(op, prop_value)) if dformat == DataFormat.Scalar: prop_value = prop_value[0] ret[prop_name] = prop_value diff --git a/src/sardana/tango/pool/MotorGroup.py b/src/sardana/tango/pool/MotorGroup.py index f4ddb7b4db..fdf63a6c66 100644 --- a/src/sardana/tango/pool/MotorGroup.py +++ b/src/sardana/tango/pool/MotorGroup.py @@ -77,7 +77,7 @@ def init_device(self): detect_evts = "position", non_detect_evts = "elementlist", self.set_change_events(detect_evts, non_detect_evts) - self.Elements = map(int, self.Elements) + self.Elements = list(map(int, self.Elements)) motor_group = self.motor_group if motor_group is None: full_name = self.get_full_name() diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index 42de1aa0db..fb05d0fc6d 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -383,7 +383,7 @@ def CreateController(self, argin): info = dict(id=pm_id, name=pm_name, ctrl_name=name, axis=i + 1, type='PseudoMotor', elements=motor_ids) if pm_name.count(',') > 0: - n, fn = map(str.strip, pm_name.split(',', 1)) + n, fn = list(map(str.strip, pm_name.split(',', 1))) info['name'], info['full_name'] = n, fn pseudo_motor_infos[klass_pseudo_role] = info pseudo_motor_ids.append(pm_id) @@ -423,7 +423,7 @@ def CreateController(self, argin): info = dict(id=pc_id, name=pc_name, ctrl_name=name, axis=i + 1, type='PseudoCounter', elements=counter_ids) if pc_name.count(',') > 0: - n, fn = map(str.strip, pc_name.split(',', 1)) + n, fn = list(map(str.strip, pc_name.split(',', 1))) info['name'], info['full_name'] = n, fn pseudo_counter_infos[klass_pseudo_role] = info pseudo_counter_ids.append(pc_id) diff --git a/src/sardana/tango/pool/PoolDevice.py b/src/sardana/tango/pool/PoolDevice.py index b35abe4e16..19a5a23d1f 100644 --- a/src/sardana/tango/pool/PoolDevice.py +++ b/src/sardana/tango/pool/PoolDevice.py @@ -219,7 +219,7 @@ def remove_unwanted_dynamic_attributes(self, new_std_attrs, new_dyn_attrs): dev_class = self.get_device_class() multi_attr = self.get_device_attr() multi_class_attr = dev_class.get_class_attr() - static_attr_names = map(str.lower, list(dev_class.attr_list.keys())) + static_attr_names = list(map(str.lower, list(dev_class.attr_list.keys()))) static_attr_names.extend(('state', 'status')) new_attrs = CaselessDict(new_std_attrs) diff --git a/src/sardana/tango/pool/PseudoCounter.py b/src/sardana/tango/pool/PseudoCounter.py index 66720e3d58..f1c422295a 100644 --- a/src/sardana/tango/pool/PseudoCounter.py +++ b/src/sardana/tango/pool/PseudoCounter.py @@ -76,7 +76,7 @@ def delete_device(self): def init_device(self): PoolExpChannelDevice.init_device(self) - self.Elements = map(int, self.Elements) + self.Elements = list(map(int, self.Elements)) pseudo_counter = self.pseudo_counter if pseudo_counter is None: full_name = self.get_full_name() diff --git a/src/sardana/tango/pool/PseudoMotor.py b/src/sardana/tango/pool/PseudoMotor.py index e3363e0526..562b36b162 100644 --- a/src/sardana/tango/pool/PseudoMotor.py +++ b/src/sardana/tango/pool/PseudoMotor.py @@ -78,7 +78,7 @@ def delete_device(self): def init_device(self): PoolElementDevice.init_device(self) - self.Elements = map(int, self.Elements) + self.Elements = list(map(int, self.Elements)) pseudo_motor = self.pseudo_motor if self.pseudo_motor is None: full_name = self.get_full_name() diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 72c633570c..654c91dd39 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -1024,7 +1024,7 @@ def toRun(self): def toSpockCommand(self): values, alerts = self.toRun() - values = map(str, values) + values = list(map(str, values)) return "%s %s" % (self.name(), str.join(' ', values)) def value(self): diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 707f0d2942..663aab0d23 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -513,7 +513,7 @@ def stop(self, synch=True): def _clearRunMacro(self): # Clear the log buffer - map(LogAttr.clearLogBuffer, list(self._log_attr.values())) + list(map(LogAttr.clearLogBuffer, list(self._log_attr.values()))) self._running_macros = None self._running_macro = None self._user_xml = None @@ -669,7 +669,7 @@ def _processRecordData(self, data): return # 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) - data = map(str, data.value) + data = list(map(str, data.value)) size = len(data[1]) if size == 0: @@ -690,7 +690,7 @@ def macroStatusReceived(self, s, t, v): # 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 = map(str, v.value) + v = list(map(str, v.value)) if not len(v[1]): return format = v[0] diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index efdfd0c566..8088985534 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -691,7 +691,7 @@ def __init__(self, name, **kw): self._value_ref_buffer_codec = CodecFactory().getCodec(codec_name) def isReferable(self): - if "valueref" in map(str.lower, self.get_attribute_list()): + if "valueref" in list(map(str.lower, self.get_attribute_list())): return True return False @@ -1193,7 +1193,7 @@ def getMotorNames(self): return self.getPoolData()['elements'] def hasMotor(self, name): - motor_names = map(str.lower, self.getMotorNames()) + motor_names = list(map(str.lower, self.getMotorNames())) return name.lower() in motor_names def getPosition(self, force=False): @@ -1242,7 +1242,7 @@ def getSize(self): def getIndex(self, name): try: - motor_names = map(str.lower, self.getMotorNames()) + motor_names = list(map(str.lower, self.getMotorNames())) return motor_names.index(name.lower()) except: return -1 @@ -1875,7 +1875,7 @@ def valueBufferChanged(self, channel, value_buffer): _, value_buffer = self._value_buffer_codec.decode(value_buffer) values = value_buffer["value"] if isinstance(values[0], list): - np_values = map(numpy.array, values) + np_values = list(map(numpy.array, values)) value_buffer["value"] = np_values self._value_buffer_cb(channel, value_buffer) @@ -2373,7 +2373,7 @@ def getMoveable(self, names): return moveable def __findMotorGroupWithElems(self, names): - names_lower = map(str.lower, names) + names_lower = list(map(str.lower, names)) len_names = len(names) mgs = self.getElementsOfType('MotorGroup') for mg in list(mgs.values()): @@ -2414,14 +2414,14 @@ def _wait_for_element_in_container(self, container, elem_name, timeout=0.5, time.sleep(nap) def createMotorGroup(self, mg_name, elements): - params = [mg_name, ] + map(str, elements) + params = [mg_name, ] + list(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) + params = [mg_name, ] + list(map(str, elements)) self.debug('trying to create measurement group: %s', params) self.command_inout('CreateMeasurementGroup', params) elements_info = self.getElementsInfo() @@ -2466,7 +2466,7 @@ def createController(self, class_name, name, *props): raise Exception("Controller class %s not found" % class_name) cmd = "CreateController" pars = [ctrl_class.types[0], ctrl_class.file_name, class_name, name] - pars.extend(map(str, props)) + pars.extend(list(map(str, props))) self.command_inout(cmd, pars) elements_info = self.getElementsInfo() return self._wait_for_element_in_container(elements_info, name) diff --git a/src/sardana/taurus/core/tango/sardana/sardana.py b/src/sardana/taurus/core/tango/sardana/sardana.py index 5724e350a3..2145f71867 100644 --- a/src/sardana/taurus/core/tango/sardana/sardana.py +++ b/src/sardana/taurus/core/tango/sardana/sardana.py @@ -532,8 +532,8 @@ def _init(self): pass elif dev_class_name == "MacroServer": ms_dev_name = dev_name - ms_prop_list = map( - str.lower, db.get_device_property_list(ms_dev_name, "*")) + ms_prop_list = list(map( + str.lower, db.get_device_property_list(ms_dev_name, "*"))) ms_props = db.get_device_property(ms_dev_name, ms_prop_list) ms_name = dev_info.server().serverInstance() ms_alias = dev_info.alias() @@ -541,8 +541,8 @@ def _init(self): ms_props.get("version"), ms_alias, ms_dev_name) self._macroservers.append(ms) for pool_dev_name in ms_props.get("poolnames", ()): - pool_prop_list = map( - str.lower, db.get_device_property_list(pool_dev_name, "*")) + pool_prop_list = list(map( + str.lower, db.get_device_property_list(pool_dev_name, "*"))) pool_props = db.get_device_property( pool_dev_name, pool_prop_list) pool_dev_info = cache.devices()[pool_dev_name] diff --git a/src/sardana/tools/config/get_pool_config.py b/src/sardana/tools/config/get_pool_config.py index 9b4d94936a..c3cad7cba7 100644 --- a/src/sardana/tools/config/get_pool_config.py +++ b/src/sardana/tools/config/get_pool_config.py @@ -133,7 +133,7 @@ def checkPoolElements(pool): attrs = element_dev.get_attribute_list() for attr, attr_dict in db.get_device_attribute_property( normal_name, - map(str, attrs) + list(map(str, attrs)) ).items(): if len(attr_dict) > 0: pool_elements_detail[alias]['attr_dicts'][attr] = attr_dict diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index 50c4aad493..012905d767 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -842,7 +842,7 @@ def loadPool(self): pars.append(p.get("name")) pars.append(p.text or '\n'.join( [i.text for i in p.findall("Item")])) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) pool_dp.command_inout("CreateController", pars) # to flush any output generated by the pool self.run(step=True) @@ -876,7 +876,7 @@ def loadPool(self): pars = ["Motor", name, axis, aliasName] if deviceName.count('/') == 2: pars.append(deviceName) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) pool_dp.command_inout("CreateElement", pars) self.handle_attributes(aliasName, e) @@ -914,7 +914,7 @@ def loadPool(self): pars = ["CTExpChannel", name, axis, aliasName] if deviceName.count('/') == 2: pars.append(deviceName) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) pool_dp.command_inout("CreateElement", pars) self.handle_attributes(aliasName, e) @@ -950,7 +950,7 @@ def loadPool(self): pars = ["ZeroDExpChannel", name, axis, aliasName] if deviceName.count('/') == 2: pars.append(deviceName) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) try: pool_dp.command_inout("CreateElement", pars) @@ -990,7 +990,7 @@ def loadPool(self): pars = ["OneDExpChannel", name, axis, aliasName] if deviceName.count('/') == 2: pars.append(deviceName) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) pool_dp.command_inout("CreateElement", pars) self.handle_attributes(aliasName, e) @@ -1026,7 +1026,7 @@ def loadPool(self): pars = ["TwoDExpChannel", name, axis, aliasName] if deviceName.count('/') == 2: pars.append(deviceName) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) pool_dp.command_inout("CreateElement", pars) self.handle_attributes(aliasName, e) @@ -1063,7 +1063,7 @@ def loadPool(self): pars = ["IORegister", name, axis, aliasName] if deviceName.count('/') == 2: pars.append(deviceName) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) pool_dp.command_inout("CreateElement", pars) self.handle_attributes(aliasName, e) @@ -1124,7 +1124,7 @@ def loadPool(self): pars.append(p.text or '\n'.join( [i.text for i in p.findall("Item")])) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) pool_dp.command_inout("CreateController", pars) pm_ctrl_count += 1 @@ -1182,7 +1182,7 @@ def loadPool(self): pars.append(p.text or '\n'.join( [i.text for i in p.findall("Item")])) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) pool_dp.command_inout("CreateController", pars) pc_ctrl_count += 1 @@ -1214,7 +1214,7 @@ def loadPool(self): pars = [aliasName] for channel in channels: pars.append(channel.get("name")) - pars = map(str.strip, pars) + pars = list(map(str.strip, pars)) pool_dp.command_inout("CreateMeasurementGroup", pars) self.handle_attributes(aliasName, mg) @@ -1424,7 +1424,7 @@ def _preprocess(self): # change the pool XML nodes to refer to simulator lib # instead of real lib - map(ctrl.remove, ctrl.findall("Property")) + list(map(ctrl.remove, ctrl.findall("Property"))) ctrl.set("lib", SimuMotorLib) ctrl.set("class", SimuMotorClass) @@ -1505,7 +1505,7 @@ def _preprocess(self): # change the pool XML nodes to refer to simulator lib # instead of real lib - map(ctrl.remove, ctrl.findall("Property")) + list(map(ctrl.remove, ctrl.findall("Property"))) ctrl.set("lib", SimuCoTiLib) ctrl.set("class", SimuCoTiClass) if self._simulation == "Best": @@ -1561,7 +1561,7 @@ def _preprocess(self): # change the pool XML nodes to refer to simulator lib # instead of real lib - map(ctrl.remove, ctrl.findall("Property")) + list(map(ctrl.remove, ctrl.findall("Property"))) ctrl.set("lib", Simu0DLib) ctrl.set("class", Simu0DClass) if self._simulation == "Best": @@ -1618,7 +1618,7 @@ def _preprocess(self): # change the pool XML nodes to refer to simulator lib # instead of real lib - map(ctrl.remove, ctrl.findall("Property")) + list(map(ctrl.remove, ctrl.findall("Property"))) ctrl.set("lib", Simu1DLib) ctrl.set("class", Simu1DClass) if self._simulation == "Best": @@ -1678,7 +1678,7 @@ def _preprocess(self): # change the pool XML nodes to refer to simulator lib # instead of real lib - map(ctrl.remove, ctrl.findall("Property")) + list(map(ctrl.remove, ctrl.findall("Property"))) ctrl.set("lib", SimuIOLib) ctrl.set("class", SimuIOClass) if self._simulation == "Best": From bc691ab8ddf84537e461757bc9fe91f6adb3bcd7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 15:57:08 +0200 Subject: [PATCH 072/830] 2to3: metaclass --- src/sardana/macroserver/msoptions.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sardana/macroserver/msoptions.py b/src/sardana/macroserver/msoptions.py index d6c3dadeb0..9b90dca990 100644 --- a/src/sardana/macroserver/msoptions.py +++ b/src/sardana/macroserver/msoptions.py @@ -37,9 +37,7 @@ def ViewOptionMeta(name, bases, attrs): return type(name, bases, attrs) -class ViewOption(object): - __metaclass__ = ViewOptionMeta - +class ViewOption(object, metaclass=ViewOptionMeta): _DEFAULT_VIEW_OPTIONS = { 'ShowDial': True, 'ShowCtrlAxis': False, From 1b61a589a817f66dd27c13f7ac882e5b81a3b9c0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:02:39 +0200 Subject: [PATCH 073/830] 2to3: next --- src/sardana/macroserver/macros/scan.py | 2 +- src/sardana/macroserver/msenvmanager.py | 2 +- src/sardana/macroserver/scan/gscan.py | 6 +++--- src/sardana/tools/config/pexpect23.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index a18ba9de4b..24d4955368 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -302,7 +302,7 @@ def getTimeEstimation(self): # calculate motion time max_step0_time, max_step_time = 0.0, 0.0 # first motion takes longer, all others should be "equal" - step0 = it.next() + step0 = next(it) for v_motor, start, stop, length in zip(v_motors, curr_pos, step0['positions'], self.interv_sizes): diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index 4b1dc8fe36..c714d760ca 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -373,7 +373,7 @@ def getDoorMacroEnv(self, door_name, macro_name, keys=None): return ret def _pairwise(self, iterable): - itnext = iter(iterable).next + itnext = iter(iterable).__next__ while True: yield itnext(), itnext() diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 97f9ac11e3..14358c0bc2 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -861,7 +861,7 @@ def _estimate(self, max_iter=None): v_motors = self.get_virtual_motors() motion_time, acq_time = 0.0, 0.0 while point_nb < max_iter: - step = iterator.next() + step = next(iterator) end_pos = step['positions'] max_path_duration = 0.0 for v_motor, start, stop in zip(v_motors, @@ -882,7 +882,7 @@ def _estimate(self, max_iter=None): else: try: while point_nb < max_iter: - step = iterator.next() + step = next(iterator) point_nb += 1 finally: total_time = self.macro.getTimeEstimation() @@ -1860,7 +1860,7 @@ def scan_loop(self): macro.checkPoint() try: - point_nb, step = period_steps.next() + point_nb, step = next(period_steps) except StopIteration: self._all_waypoints_finished = True break diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 545fd7ae32..0c5130e7ba 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -913,7 +913,7 @@ def __iter__(self): # File-like object. return self - def next(self): # File-like object. + def __next__(self): # File-like object. """This is to support iterators over a file-like object. """ From 8d71a01049a32f42a75d4bd8a40155ea928a8d4c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:05:32 +0200 Subject: [PATCH 074/830] 2to3: numliterals --- src/sardana/pool/pool.py | 2 +- src/sardana/spock/ipython_00_10/genutils.py | 2 +- src/sardana/spock/ipython_00_11/genutils.py | 2 +- src/sardana/spock/ipython_01_00/genutils.py | 2 +- src/sardana/tango/core/util.py | 2 +- src/sardana/tools/config/pexpect23.py | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py index 20eb596407..651dc0129f 100644 --- a/src/sardana/pool/pool.py +++ b/src/sardana/pool/pool.py @@ -154,7 +154,7 @@ def init_local_logging(self): log_file_name = os.path.join(path, 'controller.log.txt') try: if not os.path.exists(path): - os.makedirs(path, 0777) + os.makedirs(path, 0o777) f_h = logging.handlers.RotatingFileHandler(log_file_name, maxBytes=1E7, backupCount=5) diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index de18fd622b..c9aeaa513a 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -433,7 +433,7 @@ def print_dev_from_class(classname, dft=None): db = get_tango_db() pytg_ver = get_pytango_version_number() - if pytg_ver >= 030004: + if pytg_ver >= 0o30004: server_wildcard = '*' try: exp_dev_list = db.get_device_exported_for_class(classname) diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py index d933bc514c..55a455e437 100644 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ b/src/sardana/spock/ipython_00_11/genutils.py @@ -384,7 +384,7 @@ def print_dev_from_class(classname, dft=None): db = get_tango_db() pytg_ver = get_pytango_version_number() - if pytg_ver >= 030004: + if pytg_ver >= 0o30004: server_wildcard = '*' try: exp_dev_list = db.get_device_exported_for_class(classname) diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index b44b4b361f..0af109f5ce 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -392,7 +392,7 @@ def print_dev_from_class(classname, dft=None): db = get_tango_db() pytg_ver = get_pytango_version_number() - if pytg_ver >= 030004: + if pytg_ver >= 0o30004: server_wildcard = '*' try: exp_dev_list = db.get_device_exported_for_class(classname) diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 1d434dd4a8..57be46e2f8 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -1108,7 +1108,7 @@ def prepare_logging(options, args, tango_args, start_time=None, taurus._handlers = handlers = [] try: if not os.path.exists(path): - os.makedirs(path, 0777) + os.makedirs(path, 0o777) from sardana import sardanacustomsettings maxBytes = getattr(sardanacustomsettings, 'LOG_FILES_SIZE', 1E7) diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 0c5130e7ba..987afde2b9 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -1422,7 +1422,7 @@ def getwinsize(self): """This returns the terminal window size of the child tty. The return value is a tuple of (rows, cols). """ - TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912L) + TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912) s = struct.pack('HHHH', 0, 0, 0, 0) x = fcntl.ioctl(self.fileno(), TIOCGWINSZ, s) return struct.unpack('HHHH', x)[0:2] @@ -1443,7 +1443,7 @@ def setwinsize(self, r, c): # Newer versions of Linux have totally different values for TIOCSWINSZ. # Note that this fix is a hack. TIOCSWINSZ = getattr(termios, 'TIOCSWINSZ', -2146929561) - if TIOCSWINSZ == 2148037735L: # L is not required in Python >= 2.2. + if TIOCSWINSZ == 2148037735: # L is not required in Python >= 2.2. TIOCSWINSZ = -2146929561 # Same bits, but with sign. # Note, assume ws_xpixel and ws_ypixel are zero. s = struct.pack('HHHH', r, c, 0, 0) From fe77b98c9503e8c75ab7173a526dae7712b3b189 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:07:39 +0200 Subject: [PATCH 075/830] 2to3: operator --- src/sardana/macroserver/macro.py | 14 ++++++++------ src/sardana/macroserver/msenvmanager.py | 5 +++-- src/sardana/macroserver/msmetamacro.py | 5 +++-- src/sardana/macroserver/recorders/output.py | 10 ++++++---- src/sardana/macroserver/recorders/sharedmemory.py | 5 +++-- src/sardana/macroserver/scan/gscan.py | 5 +++-- src/sardana/sardanadefs.py | 5 +++-- src/sardana/tango/core/attributehandler.py | 3 ++- src/sardana/tango/pool/Pool.py | 7 ++++--- src/sardana/taurus/core/tango/sardana/pool.py | 7 ++++--- 10 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 1c2df84a9b..c9d35502bc 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -28,6 +28,8 @@ from __future__ import with_statement from __future__ import print_function +import collections +import numbers __all__ = ["OverloadPrint", "PauseEvent", "Hookable", "ExecMacroHook", "MacroFinder", "Macro", "macro", "iMacro", "imacro", @@ -1331,7 +1333,7 @@ def execMacro(self, *args, **kwargs): if type(arg0) in types.StringTypes: # dealing with sth like args = ('ascan th 0 100 10 1.0',) macro_name = arg0.split()[0] - elif operator.isSequenceType(arg0): + elif isinstance(arg0, collections.Sequence): # dealing with sth like args = (['ascan', 'th', '0', '100', # '10', '1.0'],) macro_name = arg0[0] @@ -1390,7 +1392,7 @@ def outputBlock(self, line): :param :obj:`str` line: line to be sent""" if isinstance(line, (str, unicode)): o = line - elif operator.isSequenceType(line): + elif isinstance(line, collections.Sequence): o = "\n".join(line) else: o = str(line) @@ -2056,7 +2058,7 @@ def _outputBlock(self, line): :param :obj:`str` line: line to be sent""" if isinstance(line, (str, unicode)): o = line - elif operator.isSequenceType(line): + elif isinstance(line, collections.Sequence): o = "\n".join(line) else: o = str(line) @@ -2332,14 +2334,14 @@ def exec_(self): if isinstance(res, types.GeneratorType): it = iter(res) for i in it: - if operator.isMappingType(i): + if isinstance(i, collections.Mapping): new_range = i.get('range') if new_range is not None: macro_status['range'] = new_range new_step = i.get('step') if new_step is not None: macro_status['step'] = new_step - elif operator.isNumberType(i): + elif isinstance(i, numbers.Number): macro_status['step'] = i macro_status['state'] = 'step' yield macro_status @@ -2361,7 +2363,7 @@ def __prepareResult(self, out): """ if out is None: out = () - if operator.isSequenceType(out) and not type(out) in types.StringTypes: + if isinstance(out, collections.Sequence) and not type(out) in types.StringTypes: out = list(map(str, out)) else: out = (str(out),) diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index c714d760ca..e5966f3c51 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -38,6 +38,7 @@ from sardana.macroserver.msmanager import MacroServerManager from sardana.macroserver.msexception import UnknownEnv +import collections class EnvironmentManager(MacroServerManager): @@ -453,10 +454,10 @@ def setEnvObj(self, obj): @return a dict representing the added environment""" - if operator.isSequenceType(obj) and \ + if isinstance(obj, collections.Sequence) and \ not isinstance(obj, (str, unicode)): obj = self._dictFromSequence(obj) - elif not operator.isMappingType(obj): + elif not isinstance(obj, collections.Mapping): raise TypeError("obj parameter must be a sequence or a map") obj = self._encode(obj) diff --git a/src/sardana/macroserver/msmetamacro.py b/src/sardana/macroserver/msmetamacro.py index 8e869304de..e5adfe9d44 100644 --- a/src/sardana/macroserver/msmetamacro.py +++ b/src/sardana/macroserver/msmetamacro.py @@ -36,6 +36,7 @@ from sardana import InvalidId, ElementType from sardana.sardanameta import SardanaLibrary, SardanaClass, SardanaFunction from sardana.macroserver.msparameter import Type, ParamRepeat +import collections MACRO_TEMPLATE = """class @macro_name@(Macro): \"\"\"@macro_name@ description.\"\"\" @@ -161,8 +162,8 @@ def _build_parameter(self, param_def): if isinstance(t, ParamRepeat): t = t.obj() - if operator.isSequenceType(t) and not isinstance(t, (str, unicode)): - if operator.isMappingType(t[-1]): + if isinstance(t, collections.Sequence) and not isinstance(t, (str, unicode)): + if isinstance(t[-1], collections.Mapping): ret_p.update(t[-1]) t = self._build_parameter(t[:-1]) else: diff --git a/src/sardana/macroserver/recorders/output.py b/src/sardana/macroserver/recorders/output.py index 9d84244bd4..4378fcd1e9 100644 --- a/src/sardana/macroserver/recorders/output.py +++ b/src/sardana/macroserver/recorders/output.py @@ -40,6 +40,8 @@ from sardana.macroserver.scan.recorder.datarecorder import DataRecorder from sardana.macroserver.scan.recorder.storage import BaseFileRecorder +import collections +import numbers class JsonRecorder(DataRecorder): @@ -135,10 +137,10 @@ def __init__(self, stream, cols=None, number_fmt='%8.4f', col_width=8, self._number_fmt = number_fmt self._col_sep = col_sep self._col_width = col_width - if operator.isSequenceType(cols) and \ + if isinstance(cols, collections.Sequence) and \ not isinstance(cols, (str, unicode)): cols = CaselessList(cols) - elif operator.isNumberType(cols): + elif isinstance(cols, numbers.Number): cols = cols else: cols = None @@ -178,9 +180,9 @@ def _startRecordList(self, recordlist): if not getattr(column, 'output', True): continue name = column.name - if operator.isSequenceType(cols) and name not in cols: + if isinstance(cols, collections.Sequence) and name not in cols: continue - if operator.isNumberType(cols) and col >= cols: + if isinstance(cols, numbers.Number) and col >= cols: break col_names.append(name) label = column.label.strip() diff --git a/src/sardana/macroserver/recorders/sharedmemory.py b/src/sardana/macroserver/recorders/sharedmemory.py index 216a065b0a..20d6e3f036 100644 --- a/src/sardana/macroserver/recorders/sharedmemory.py +++ b/src/sardana/macroserver/recorders/sharedmemory.py @@ -35,6 +35,7 @@ from sardana.macroserver.scan.recorder import BaseSharedMemoryRecorder from sardana.macroserver.scan.recorder.datarecorder import DataRecorder +import numbers class SPSRecorder(BaseSharedMemoryRecorder): @@ -143,9 +144,9 @@ def _writeRecord(self, record): for colname in self.labels: val = record.data.get(colname) - if (not val is None) and (operator.isNumberType(val) and (type(val) in [int, float])): + if (not val is None) and (isinstance(val, numbers.Number) and (type(val) in [int, float])): vals.append(val) - elif (not val is None) and (operator.isNumberType(val)): + elif (not val is None) and (isinstance(val, numbers.Number)): valsmca = [] for i in range(0, len(val)): valsmca.append(val[i]) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 14358c0bc2..a7d7dde9f6 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -41,6 +41,7 @@ import PyTango import taurus +import collections try: from collections import OrderedDict @@ -515,14 +516,14 @@ def _getFileRecorders(self): if isinstance(file_names, (str, unicode)): file_names = (file_names,) - elif not operator.isSequenceType(file_names): + elif not isinstance(file_names, collections.Sequence): scan_file_t = type(file_names).__name__ raise TypeError("ScanFile MUST be string or sequence of strings." " It is '%s'" % scan_file_t) if isinstance(scan_recorders, (str, unicode)): scan_recorders = (scan_recorders,) - elif not operator.isSequenceType(scan_recorders): + elif not isinstance(scan_recorders, collections.Sequence): scan_recorders_t = type(scan_recorders).__name__ raise TypeError("ScanRecorder MUST be string or sequence of " "strings. It is '%s'" % scan_recorders_t) diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index 9819991794..97fbd52695 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -26,6 +26,7 @@ """This module contains the most generic sardana constants and enumerations""" from __future__ import absolute_import +import collections __all__ = ["EpsilonError", "SardanaServer", "ServerRunMode", "State", "DataType", "DataFormat", "DataAccess", "DTYPE_MAP", "R_DTYPE_MAP", @@ -234,14 +235,14 @@ def to_dtype_dformat(data): dtype, dformat = data, DataFormat.Scalar if isinstance(data, (str, unicode)): dtype, dformat = from_dtype_str(data) - elif operator.isSequenceType(data): + elif isinstance(data, collections.Sequence): dformat = DataFormat.OneD dtype = data[0] if isinstance(dtype, str): dtype, dformat2 = from_dtype_str(dtype) if dformat2 == DataFormat.OneD: dformat = DataFormat.TwoD - elif operator.isSequenceType(dtype): + elif isinstance(dtype, collections.Sequence): dformat = DataFormat.TwoD dtype = dtype[0] if isinstance(dtype, str): diff --git a/src/sardana/tango/core/attributehandler.py b/src/sardana/tango/core/attributehandler.py index 9c130a3a30..14aefafba7 100644 --- a/src/sardana/tango/core/attributehandler.py +++ b/src/sardana/tango/core/attributehandler.py @@ -34,6 +34,7 @@ import operator from taurus.core.util.containers import LIFO +import collections class AttributeLogHandler(logging.Handler): @@ -70,7 +71,7 @@ def clearBuffer(self): self._buff.clear() def appendBuffer(self, d): - if operator.isSequenceType(d): + if isinstance(d, collections.Sequence): if isinstance(d, (str, unicode)): self._buff.append(d) else: diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index fb05d0fc6d..c9ac7c6525 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -45,6 +45,7 @@ from sardana.pool.pool import Pool as POOL from sardana.pool.poolmetacontroller import TYPE_MAP_OBJ from sardana.tango.core.util import get_tango_version_number +import collections class Pool(PyTango.Device_4Impl, Logger): @@ -786,7 +787,7 @@ def on_pool_changed(self, evt_src, evt_type, evt_value): def _format_create_json_arguments(self, argin): elems, ret = json.loads(argin[0]), [] - if operator.isMappingType(elems): + if isinstance(elems, collections.Mapping): elems = [elems] for elem in elems: d = {} @@ -861,7 +862,7 @@ def _format_CreateMotorGroup_arguments(self, argin): elems = json.loads(argin[0]) except: elems = argin - if operator.isMappingType(elems): + if isinstance(elems, collections.Mapping): elems = [elems] for elem in elems: d = {} @@ -886,7 +887,7 @@ def _format_CreateMeasurementGroup_arguments(self, argin): elems = json.loads(argin[0]) except: elems = argin - if operator.isMappingType(elems): + if isinstance(elems, collections.Mapping): elems = [elems] for elem in elems: d = {} diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 8088985534..378ee3b548 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -27,6 +27,7 @@ It contains specific part of sardana device pool""" from __future__ import absolute_import +import collections __all__ = ["InterruptException", "StopException", "AbortException", "BaseElement", "ControllerClass", "ControllerLibrary", @@ -982,7 +983,7 @@ def setSign(self, value): def _start(self, *args, **kwargs): new_pos = args[0] - if operator.isSequenceType(new_pos): + if isinstance(new_pos, collections.Sequence): new_pos = new_pos[0] try: self.write_attribute('position', new_pos) @@ -1009,7 +1010,7 @@ def go(self, *args, **kwargs): @reservedOperation def iterMove(self, new_pos, timeout=None): - if operator.isSequenceType(new_pos): + if isinstance(new_pos, collections.Sequence): new_pos = new_pos[0] state, pos = self.getAttribute("state"), self.getAttribute("position") @@ -1114,7 +1115,7 @@ def getDialPositionObj(self): def _start(self, *args, **kwargs): new_pos = args[0] - if operator.isSequenceType(new_pos): + if isinstance(new_pos, collections.Sequence): new_pos = new_pos[0] try: self.write_attribute('position', new_pos) From 4eb3668e4017d2eead609a742b797e6c70a113f9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:08:48 +0200 Subject: [PATCH 076/830] 2to3: paren --- src/sardana/taurus/core/tango/sardana/macroserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 663aab0d23..ed85ba632f 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -794,7 +794,7 @@ def refresh(self): self.macro_path = mp = self._ms().get_property("MacroPath")[ "MacroPath"] self.base_macro_path = osp.commonprefix(self.macro_path) - self.rel_macro_path = [osp.relpath for p in mp, self.base_macro_path] + self.rel_macro_path = [osp.relpath for p in (mp, self.base_macro_path)] class Environment(dict): From 55460a7ac1fa94b6c9934daad5b86353a8cd3647 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:14:59 +0200 Subject: [PATCH 077/830] 2to3: print --- .../macros/examples/specific_experiments.py | 2 +- src/sardana/macroserver/macros/hkl.py | 2 +- src/sardana/macroserver/macros/scan.py | 2 +- src/sardana/macroserver/macros/standard.py | 2 +- .../macroserver/macros/test/macroexecutor.py | 2 +- .../macroserver/macros/test/sardemoenv.py | 22 ++--- .../macroserver/recorders/sharedmemory.py | 2 +- src/sardana/macroserver/scan/test/helper.py | 2 +- .../poolcontrollers/DummyMotorController.py | 34 ++++---- .../DummyTriggerGateController.py | 2 +- src/sardana/pool/poolcontrollers/test/base.py | 2 +- .../pool/test/test_measurementgroup.py | 6 +- src/sardana/requirements.py | 12 +-- src/sardana/spock/inputhandler.py | 6 +- src/sardana/spock/ipython_00_10/genutils.py | 34 ++++---- src/sardana/spock/ipython_00_11/genutils.py | 42 +++++----- src/sardana/spock/ipython_01_00/genutils.py | 42 +++++----- src/sardana/spock/magic.py | 42 +++++----- src/sardana/spock/spockms.py | 62 +++++++------- src/sardana/tango/core/util.py | 20 ++--- src/sardana/tango/macroserver/Door.py | 2 +- src/sardana/tango/macroserver/test/base.py | 6 +- src/sardana/tango/pool/test/base_sartest.py | 8 +- .../tango/pool/test/test_measurementgroup.py | 10 +-- .../taurus/core/tango/sardana/macroserver.py | 4 +- .../taurus/qt/qtgui/extra_hkl/hklscan.py | 2 +- .../taurus/qt/qtgui/extra_hkl/selectsignal.py | 2 +- .../qtgui/extra_macroexecutor/macrobutton.py | 2 +- .../sequenceeditor/sequenceeditor.py | 2 +- .../qt/qtgui/extra_sardana/controllertree.py | 2 +- .../qt/qtgui/extra_sardana/macrotree.py | 2 +- .../qtgui/extra_sardana/measurementgroup.py | 2 +- .../taurus/qt/qtgui/extra_sardana/startup.py | 4 +- src/sardana/test/testsuite.py | 4 +- src/sardana/tools/config/fods_to_sar.py | 4 +- src/sardana/tools/config/get_pool_config.py | 82 +++++++++---------- src/sardana/tools/config/sar_to_fods.py | 2 +- src/sardana/tools/config/sardana.py | 56 ++++++------- src/sardana/tools/config/to_sar.py | 2 +- src/sardana/tools/config/xls_to_sar.py | 4 +- src/sardana/util/deepreload.py | 2 +- src/sardana/util/motion/motion.py | 42 +++++----- 42 files changed, 293 insertions(+), 293 deletions(-) diff --git a/src/sardana/macroserver/macros/examples/specific_experiments.py b/src/sardana/macroserver/macros/examples/specific_experiments.py index 2f209a1938..b420e0875a 100644 --- a/src/sardana/macroserver/macros/examples/specific_experiments.py +++ b/src/sardana/macroserver/macros/examples/specific_experiments.py @@ -72,7 +72,7 @@ def prepare(self, start, final, nr_interv, integ_time, **opts): for n, e in self.getElementsWithInterface('Instrument').items(): inst = e.getObj() # ,inst.getElements() - print n, e.name, inst.getFullName(), type(e), type(inst), type(inst.getPoolObj()) + print(n, e.name, inst.getFullName(), type(e), type(inst), type(inst.getPoolObj())) # maybe I should use the instrument interface to obtain the right # counters diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py index a69cd94c5c..c505b6d0ba 100644 --- a/src/sardana/macroserver/macros/hkl.py +++ b/src/sardana/macroserver/macros/hkl.py @@ -136,7 +136,7 @@ def on_stop(self): def check_collinearity(self, h0, k0, l0, h1, k1, l1): - print h0 + print(h0) cpx = k0 * l1 - l0 * k1 cpy = l0 * h1 - h0 * l1 cpz = h0 * k1 - k0 * h1 diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 24d4955368..b6122d50ab 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -993,7 +993,7 @@ def run(self, scan_number): try: hist = self.getEnv("ScanHistory") except UnknownEnv: - print "No scan recorded in history" + print("No scan recorded in history") return if scan_number < 0: self.show_all(hist) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 3317ba8d75..737f2b513d 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -525,7 +525,7 @@ def _clean(self): try: posObj.unsubscribeEvent(self.positionChanged, motor) except Exception as e: - print str(e) + print(str(e)) raise e def positionChanged(self, motor, position): diff --git a/src/sardana/macroserver/macros/test/macroexecutor.py b/src/sardana/macroserver/macros/test/macroexecutor.py index 71854b0e72..f52b98d3dc 100644 --- a/src/sardana/macroserver/macros/test/macroexecutor.py +++ b/src/sardana/macroserver/macros/test/macroexecutor.py @@ -322,4 +322,4 @@ def _getTangoMacroExecutor(self, door_name): if __name__ == '__main__': from sardana import sardanacustomsettings door_name = getattr(sardanacustomsettings, 'UNITTEST_DOOR_NAME') - print MacroExecutorFactory().getMacroExecutor(door_name) + print(MacroExecutorFactory().getMacroExecutor(door_name)) diff --git a/src/sardana/macroserver/macros/test/sardemoenv.py b/src/sardana/macroserver/macros/test/sardemoenv.py index 7c74777f32..2000486025 100644 --- a/src/sardana/macroserver/macros/test/sardemoenv.py +++ b/src/sardana/macroserver/macros/test/sardemoenv.py @@ -235,14 +235,14 @@ def getIORs(): if __name__ == '__main__': s = SarDemoEnv() - print s.env - print s.getControllers() - print s.getCTs() - print s.getMotors() - print s.getPseudoMotors() - print s.getZerods() - print s.getOneds() - print s.getTwods() - print s.getElements('Moveable') - print s.getMoveables() - print s.getElements() + print(s.env) + print(s.getControllers()) + print(s.getCTs()) + print(s.getMotors()) + print(s.getPseudoMotors()) + print(s.getZerods()) + print(s.getOneds()) + print(s.getTwods()) + print(s.getElements('Moveable')) + print(s.getMoveables()) + print(s.getElements()) diff --git a/src/sardana/macroserver/recorders/sharedmemory.py b/src/sardana/macroserver/recorders/sharedmemory.py index 20d6e3f036..e0eb9e31fc 100644 --- a/src/sardana/macroserver/recorders/sharedmemory.py +++ b/src/sardana/macroserver/recorders/sharedmemory.py @@ -235,7 +235,7 @@ def _startRecordList(self, recordlist): self.sps.create(self.progname, self.shm_id_env, self.maxenv, self.envlen, self.sps.STRING) - print "Starting new SHM recording" + print("Starting new SHM recording") self.putenv('title', recordlist.getEnvironValue('title')) diff --git a/src/sardana/macroserver/scan/test/helper.py b/src/sardana/macroserver/scan/test/helper.py index e62ed2b6cc..19c2357a2b 100644 --- a/src/sardana/macroserver/scan/test/helper.py +++ b/src/sardana/macroserver/scan/test/helper.py @@ -119,7 +119,7 @@ def main(): f = nxs.load(file_name) m = f['entry1']['measurement'] ch1 = m['ch1'] - print ch1.nxdata + print(ch1.nxdata) if __name__ == "__main__": main() diff --git a/src/sardana/pool/poolcontrollers/DummyMotorController.py b/src/sardana/pool/poolcontrollers/DummyMotorController.py index 64eb53b2e3..c5c6017efd 100644 --- a/src/sardana/pool/poolcontrollers/DummyMotorController.py +++ b/src/sardana/pool/poolcontrollers/DummyMotorController.py @@ -472,23 +472,23 @@ def setPower(self, power): self.power = power def info(self): - print "Small movement =", self.small_motion - print "length =", self.dsplmnt - print "position where maximum velocity will be reached =", self.curr_max_vel_pos - print "necessary displacement to reach maximum velocity =", self.curr_dsplmnt_reach_max_vel - print "necessary displacement to stop from maximum velocity =", self.curr_dsplmnt_reach_min_vel - print "maximum velocity possible =", self.curr_max_vel - print "time at top velocity =", self.curr_at_max_vel_time - print "displacement at top velocity =", self.curr_at_max_vel_dsplmnt - print "time to reach maximum velocity =", self.curr_max_vel_time - print "time to reach minimum velocity =", self.curr_min_vel_time - print "time the motion will take =", self.duration - print "instant when maximum velocity should be reached =", self.curr_max_vel_instant - print "instant when should start decelerating =", self.curr_min_vel_instant - print "instant the motion will end", self.final_instant - print "" - print "For long movements (where top vel is possible), necessary displacement to reach maximum velocity =", self.dsplmnt_reach_max_vel - print "For long movements (where top vel is possible), necessary displacement to stop from maximum velocity =", self.dsplmnt_reach_min_vel + print("Small movement =", self.small_motion) + print("length =", self.dsplmnt) + print("position where maximum velocity will be reached =", self.curr_max_vel_pos) + print("necessary displacement to reach maximum velocity =", self.curr_dsplmnt_reach_max_vel) + print("necessary displacement to stop from maximum velocity =", self.curr_dsplmnt_reach_min_vel) + print("maximum velocity possible =", self.curr_max_vel) + print("time at top velocity =", self.curr_at_max_vel_time) + print("displacement at top velocity =", self.curr_at_max_vel_dsplmnt) + print("time to reach maximum velocity =", self.curr_max_vel_time) + print("time to reach minimum velocity =", self.curr_min_vel_time) + print("time the motion will take =", self.duration) + print("instant when maximum velocity should be reached =", self.curr_max_vel_instant) + print("instant when should start decelerating =", self.curr_min_vel_instant) + print("instant the motion will end", self.final_instant) + print("") + print("For long movements (where top vel is possible), necessary displacement to reach maximum velocity =", self.dsplmnt_reach_max_vel) + print("For long movements (where top vel is possible), necessary displacement to stop from maximum velocity =", self.dsplmnt_reach_min_vel) class BasicDummyMotorController(MotorController): diff --git a/src/sardana/pool/poolcontrollers/DummyTriggerGateController.py b/src/sardana/pool/poolcontrollers/DummyTriggerGateController.py index 68b828f4ed..f2795057b6 100644 --- a/src/sardana/pool/poolcontrollers/DummyTriggerGateController.py +++ b/src/sardana/pool/poolcontrollers/DummyTriggerGateController.py @@ -69,7 +69,7 @@ def StateOne(self, axis): self._log.debug('StateOne(%d): returning (%s, %s)' % (axis, sta, status)) except Exception as e: - print e + print(e) return sta, status def PreStartAll(self): diff --git a/src/sardana/pool/poolcontrollers/test/base.py b/src/sardana/pool/poolcontrollers/test/base.py index 081a42e0f4..c11a5175b1 100644 --- a/src/sardana/pool/poolcontrollers/test/base.py +++ b/src/sardana/pool/poolcontrollers/test/base.py @@ -298,7 +298,7 @@ def calc_cycletocycle(self): i += 1 if len(periods) > 0: periods_array = numpy.array(periods) - print periods_array + print(periods_array) c2c = numpy.diff(periods_array) mean_c2c = c2c.mean() std_c2c = c2c.std() diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index b317333b4e..4734e248c8 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -83,10 +83,10 @@ def acq_asserts(self, channel_names, repetitions): # printing acquisition records table = self.attr_listener.get_table() header = table.dtype.names - print header + print(header) n_rows = table.shape[0] for row in xrange(n_rows): - print row, table[row] + print(row, table[row]) # checking if any of data was acquired self.assertTrue(self.attr_listener.data, 'no data were acquired') # checking if all channels produced data @@ -216,7 +216,7 @@ def meas_cont_stop_acquisition(self, config, synchronization, self.assertEqual(numBW, 0, msg) # print the acquisition records for i, record in enumerate(zip(*list(self.attr_listener.data.values()))): - print i, record + print(i, record) def meas_contpos_acquisition(self, config, synchronization, moveable, second_config=None): diff --git a/src/sardana/requirements.py b/src/sardana/requirements.py index 8ed81ce2c6..87c759d6c3 100644 --- a/src/sardana/requirements.py +++ b/src/sardana/requirements.py @@ -58,7 +58,7 @@ def check_requirements(exec_name=None): pyver_str = ".".join(map(str, pyver)) if pyver < pyver_: - print "Sardana requires python %s. Installed version is %s" % (pyver_str_, pyver_str) + print("Sardana requires python %s. Installed version is %s" % (pyver_str_, pyver_str)) sys.exit(-1) pytangover = None @@ -71,11 +71,11 @@ def check_requirements(exec_name=None): pytangover = tuple(map(int, PyTango.__version__.split('.', 3))) if pytangover is None: - print "%s requires PyTango %s. No version installed" % (exec_name, pytangover_str_,) + print("%s requires PyTango %s. No version installed" % (exec_name, pytangover_str_,)) sys.exit(-1) if pytangover < pytangover_: pytangover_str = ".".join(map(str, pytangover)) - print "%s requires PyTango %s. Installed version is %s" % (exec_name, pytangover_str_, pytangover_str) + print("%s requires PyTango %s. Installed version is %s" % (exec_name, pytangover_str_, pytangover_str)) sys.exit(-1) # TODO: add itango as runtime dependency of spock @@ -92,15 +92,15 @@ def check_requirements(exec_name=None): taurusver = tuple(map(int, taurus.Release.version.split('.', 3))) if taurusver is None: - print "%s requires taurus %s. No version installed" % (exec_name, taurusver_str_,) + print("%s requires taurus %s. No version installed" % (exec_name, taurusver_str_,)) sys.exit(-1) if taurusver < taurusver_: taurusver_str = ".".join(map(str, taurusver)) - print "%s requires taurus %s. Installed version is %s" % (exec_name, taurusver_str_, taurusver_str) + print("%s requires taurus %s. Installed version is %s" % (exec_name, taurusver_str_, taurusver_str)) sys.exit(-1) try: from lxml import etree except: - print "Could not find any suitable XML library" + print("Could not find any suitable XML library") sys.exit(-1) diff --git a/src/sardana/spock/inputhandler.py b/src/sardana/spock/inputhandler.py index 7a1f140f06..b22494ce27 100644 --- a/src/sardana/spock/inputhandler.py +++ b/src/sardana/spock/inputhandler.py @@ -54,7 +54,7 @@ def input(self, input_data=None): prompt = input_data.get('prompt') if 'data_type' in input_data: if input_data['data_type'] != 'String': - print("Accepted input: %s" % input_data['data_type']) + print(("Accepted input: %s" % input_data['data_type'])) ret = dict(input=None, cancel=False) try: if prompt is None: @@ -66,7 +66,7 @@ def input(self, input_data=None): return ret def input_timeout(self, input_data): - print "SpockInputHandler input timeout" + print("SpockInputHandler input timeout") class MessageHandler(Qt.QObject): @@ -154,7 +154,7 @@ def run(self, conn): TaurusManager().addJob(self.run_forever, None) app.exec_() conn.close() - print "Quit input handler" + print("Quit input handler") def run_forever(self): # child process diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index c9aeaa513a..ef6d6c053a 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -365,7 +365,7 @@ def get_device_from_user(expected_class, dft=None): try: full_name, name, alias = from_name_to_tango(from_user) except: - print "Warning: the given %s does not exist" % expected_class + print("Warning: the given %s does not exist" % expected_class) return name try: @@ -373,10 +373,10 @@ def get_device_from_user(expected_class, dft=None): cl_name = db.get_class_for_device(name) class_correct = cl_name == expected_class if not class_correct: - print "Warning: the given name is not a %s (it is a %s)" % (expected_class, cl_name) + print("Warning: the given name is not a %s (it is a %s)" % (expected_class, cl_name)) except Exception as e: - print "Warning: unable to confirm if '%s' is valid" % name - print str(e) + print("Warning: unable to confirm if '%s' is valid" % name) + print(str(e)) return full_name @@ -426,7 +426,7 @@ def get_tango_host_from_user(): except: exp = "Invalid tango host. Must be in format :" exp = "Invalid tango host. %s " % exp - print exp + print(exp) def print_dev_from_class(classname, dft=None): @@ -446,7 +446,7 @@ def print_dev_from_class(classname, dft=None): res = None dev_list = list(db.get_device_name(server_wildcard, classname)) tg_host = "%s:%s" % (db.get_db_host(), db.get_db_port()) - print "Available", classname, "devices from", tg_host, ":" + print("Available", classname, "devices from", tg_host, ":") list_devices_with_alias = [] list_devices_with_no_alias = [] @@ -475,7 +475,7 @@ def print_dev_from_class(classname, dft=None): out = "%-25s" % out if dev_name in exp_dev_list: out += " (running)" - print out + print(out) if dft: if dft.lower() == name.lower(): @@ -817,9 +817,9 @@ def check_for_upgrade(ipy_profile_file, ipythondir, session, profile): alpha_in_spock_profile == alpha_in_spock_lib: return if spocklib_ver < spock_profile_ver: - print '%sYour spock profile (%s) is newer than your spock version ' \ - '(%s)!' % (TermColors.Brown, spock_profile_ver_str, spock_lib_ver_str) - print 'Please upgrade spock or delete the current profile %s' % TermColors.Normal + print('%sYour spock profile (%s) is newer than your spock version ' \ + '(%s)!' % (TermColors.Brown, spock_profile_ver_str, spock_lib_ver_str)) + print('Please upgrade spock or delete the current profile %s' % TermColors.Normal) sys.exit(1) # there was no version track of spock profiles since spock 0.2.0 so change @@ -829,7 +829,7 @@ def check_for_upgrade(ipy_profile_file, ipythondir, session, profile): msg = 'Your current spock door extension profile has been created with spock %s.\n' \ 'Your current spock door extension version is %s, therefore a profile upgrade is needed.\n' \ % (spock_profile_ver_str, spock_lib_ver_str) - print msg + print(msg) prompt = 'Do you wish to upgrade now (warn: this will shutdown the current spock session) ([y]/n)? ' r = raw_input(prompt) or 'y' if r.lower() == 'y': @@ -1095,22 +1095,22 @@ def start(user_ns=None): try: check_requirements() except exception.SpockMissingRequirement as requirement: - print str(requirement) + print(str(requirement)) sys.exit(-1) except exception.SpockMissingRecommended as recommended: - print str(recommended) + print(str(recommended)) user_ns = user_ns or {} try: user_ns.update(get_args(sys.argv)) except exception.SpockException as e: - print e.message - print 'Starting normal IPython console' + print(e.message) + print('Starting normal IPython console') except KeyboardInterrupt: - print "\nUser pressed Ctrl+C. Exiting..." + print("\nUser pressed Ctrl+C. Exiting...") sys.exit() except Exception as e: - print 'spock exited with an unmanaged exception: %s' % str(e) + print('spock exited with an unmanaged exception: %s' % str(e)) sys.exit(-2) return IPython.Shell.start(user_ns=user_ns) diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py index 55a455e437..05f8f3fb47 100644 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ b/src/sardana/spock/ipython_00_11/genutils.py @@ -212,9 +212,9 @@ def get_ipython_version(): try: v = IPython.release.version except Exception as e2: - print e2 + print(e2) except Exception as e3: - print e3 + print(e3) return v @@ -315,7 +315,7 @@ def get_device_from_user(expected_class, dft=None): try: full_name, name, _ = from_name_to_tango(from_user) except: - print "Warning: the given %s does not exist" % expected_class + print("Warning: the given %s does not exist" % expected_class) return name try: @@ -323,10 +323,10 @@ def get_device_from_user(expected_class, dft=None): cl_name = db.get_class_for_device(name) class_correct = cl_name == expected_class if not class_correct: - print "Warning: the given name is not a %s (it is a %s)" % (expected_class, cl_name) + print("Warning: the given name is not a %s (it is a %s)" % (expected_class, cl_name)) except Exception as e: - print "Warning: unable to confirm if '%s' is valid" % name - print str(e) + print("Warning: unable to confirm if '%s' is valid" % name) + print(str(e)) return full_name @@ -377,7 +377,7 @@ def get_tango_host_from_user(): except: exp = "Invalid tango host. Must be in format :" exp = "Invalid tango host. %s " % exp - print exp + print(exp) def print_dev_from_class(classname, dft=None): @@ -397,7 +397,7 @@ def print_dev_from_class(classname, dft=None): res = None dev_list = list(db.get_device_name(server_wildcard, classname)) tg_host = "%s:%s" % (db.get_db_host(), db.get_db_port()) - print "Available", classname, "devices from", tg_host, ":" + print("Available", classname, "devices from", tg_host, ":") list_devices_with_alias = [] list_devices_with_no_alias = [] @@ -426,7 +426,7 @@ def print_dev_from_class(classname, dft=None): out = "%-25s" % out if dev_name in exp_dev_list: out += " (running)" - print out + print(out) if dft: if dft.lower() == name.lower(): @@ -805,10 +805,10 @@ def check_for_upgrade(ipy_profile_dir): alpha_in_spock_profile == alpha_in_spock_lib: return if spocklib_ver < spock_profile_ver: - print '%sYour spock profile (%s) is newer than your spock version ' \ + print('%sYour spock profile (%s) is newer than your spock version ' \ '(%s)!' % (SpockTermColors.Brown, - spock_profile_ver_str, spock_lib_ver_str) - print 'Please upgrade spock or delete the current profile %s' % SpockTermColors.Normal + spock_profile_ver_str, spock_lib_ver_str)) + print('Please upgrade spock or delete the current profile %s' % SpockTermColors.Normal) sys.exit(1) # there was no version track of spock profiles since spock 0.2.0 so change @@ -818,7 +818,7 @@ def check_for_upgrade(ipy_profile_dir): msg = 'Your current spock door extension profile has been created with spock %s.\n' \ 'Your current spock door extension version is %s, therefore a profile upgrade is needed.\n' \ % (spock_profile_ver_str, spock_lib_ver_str) - print msg + print(msg) prompt = 'Do you wish to upgrade now (warn: this will shutdown the current spock session) ([y]/n)? ' r = raw_input(prompt) or 'y' if r.lower() == 'y': @@ -1172,22 +1172,22 @@ def start(user_ns=None): try: check_requirements() except exception.SpockMissingRequirement as requirement: - print str(requirement) + print(str(requirement)) sys.exit(-1) except exception.SpockMissingRecommended as recommended: - print str(recommended) + print(str(recommended)) user_ns = user_ns or {} try: user_ns.update(get_args(sys.argv)) except exception.SpockException as e: - print e.message - print 'Starting normal IPython console' + print(e.message) + print('Starting normal IPython console') except KeyboardInterrupt: - print "\nUser pressed Ctrl+C. Exiting..." + print("\nUser pressed Ctrl+C. Exiting...") sys.exit() except Exception as e: - print 'spock exited with an unmanaged exception: %s' % str(e) + print('spock exited with an unmanaged exception: %s' % str(e)) sys.exit(-2) app = TerminalIPythonApp.instance() @@ -1290,10 +1290,10 @@ def _banner_default(self): try: check_requirements() except exception.SpockMissingRequirement as requirement: - print str(requirement) + print(str(requirement)) sys.exit(-1) except exception.SpockMissingRecommended as recommended: - print str(recommended) + print(str(recommended)) prepare_input_handler() prepare_cmdline() diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index 0af109f5ce..781b2dc014 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -219,9 +219,9 @@ def get_ipython_version(): try: v = IPython.release.version except Exception as e2: - print e2 + print(e2) except Exception as e3: - print e3 + print(e3) return v @@ -323,7 +323,7 @@ def get_device_from_user(expected_class, dft=None): try: full_name, name, _ = from_name_to_tango(from_user) except: - print "Warning: the given %s does not exist" % expected_class + print("Warning: the given %s does not exist" % expected_class) return name try: @@ -331,10 +331,10 @@ def get_device_from_user(expected_class, dft=None): cl_name = db.get_class_for_device(name) class_correct = cl_name == expected_class if not class_correct: - print "Warning: the given name is not a %s (it is a %s)" % (expected_class, cl_name) + print("Warning: the given name is not a %s (it is a %s)" % (expected_class, cl_name)) except Exception as e: - print "Warning: unable to confirm if '%s' is valid" % name - print str(e) + print("Warning: unable to confirm if '%s' is valid" % name) + print(str(e)) return full_name @@ -385,7 +385,7 @@ def get_tango_host_from_user(): except: exp = "Invalid tango host. Must be in format :" exp = "Invalid tango host. %s " % exp - print exp + print(exp) def print_dev_from_class(classname, dft=None): @@ -405,7 +405,7 @@ def print_dev_from_class(classname, dft=None): res = None dev_list = list(db.get_device_name(server_wildcard, classname)) tg_host = "%s:%s" % (db.get_db_host(), db.get_db_port()) - print "Available", classname, "devices from", tg_host, ":" + print("Available", classname, "devices from", tg_host, ":") list_devices_with_alias = [] list_devices_with_no_alias = [] @@ -434,7 +434,7 @@ def print_dev_from_class(classname, dft=None): out = "%-25s" % out if dev_name in exp_dev_list: out += " (running)" - print out + print(out) if dft: if dft.lower() == name.lower(): @@ -850,10 +850,10 @@ def check_for_upgrade(ipy_profile_dir): alpha_in_spock_profile == alpha_in_spock_lib: return if spocklib_ver < spock_profile_ver: - print '%sYour spock profile (%s) is newer than your spock version ' \ + print('%sYour spock profile (%s) is newer than your spock version ' \ '(%s)!' % (SpockTermColors.Brown, - spock_profile_ver_str, spock_lib_ver_str) - print 'Please upgrade spock or delete the current profile %s' % SpockTermColors.Normal + spock_profile_ver_str, spock_lib_ver_str)) + print('Please upgrade spock or delete the current profile %s' % SpockTermColors.Normal) sys.exit(1) # there was no version track of spock profiles since spock 0.2.0 so change @@ -863,7 +863,7 @@ def check_for_upgrade(ipy_profile_dir): msg = 'Your current spock door extension profile has been created with spock %s.\n' \ 'Your current spock door extension version is %s, therefore a profile upgrade is needed.\n' \ % (spock_profile_ver_str, spock_lib_ver_str) - print msg + print(msg) prompt = 'Do you wish to upgrade now (warn: this will shutdown the current spock session) ([y]/n)? ' r = raw_input(prompt) or 'y' if r.lower() == 'y': @@ -1236,22 +1236,22 @@ def start(user_ns=None): try: check_requirements() except exception.SpockMissingRequirement as requirement: - print str(requirement) + print(str(requirement)) sys.exit(-1) except exception.SpockMissingRecommended as recommended: - print str(recommended) + print(str(recommended)) user_ns = user_ns or {} try: user_ns.update(get_args(sys.argv)) except exception.SpockException as e: - print e.message - print 'Starting normal IPython console' + print(e.message) + print('Starting normal IPython console') except KeyboardInterrupt: - print "\nUser pressed Ctrl+C. Exiting..." + print("\nUser pressed Ctrl+C. Exiting...") sys.exit() except Exception as e: - print 'spock exited with an unmanaged exception: %s' % str(e) + print('spock exited with an unmanaged exception: %s' % str(e)) sys.exit(-2) app = TerminalIPythonApp.instance() @@ -1362,10 +1362,10 @@ def _banner_default(self): try: check_requirements() except exception.SpockMissingRequirement as requirement: - print str(requirement) + print(str(requirement)) sys.exit(-1) except exception.SpockMissingRecommended as recommended: - print str(recommended) + print(str(recommended)) prepare_input_handler() prepare_cmdline() diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index fd18288e8c..080a146bf1 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -42,8 +42,8 @@ def expconf(self, parameter_s=''): try: from sardana.taurus.qt.qtgui.extra_sardana import ExpDescriptionEditor except: - print "Error importing ExpDescriptionEditor " \ - "(hint: is taurus extra_sardana installed?)" + print("Error importing ExpDescriptionEditor " \ + "(hint: is taurus extra_sardana installed?)") return try: doorname = get_door().name() @@ -93,8 +93,8 @@ def showscan(self, parameter_s=''): ShowScanOnline except Exception as e: - print "Error importing ShowScanOnline" - print e + print("Error importing ShowScanOnline") + print(e) return try: doorname = get_door().name() @@ -145,17 +145,17 @@ def debug(self, parameter_s=''): door = get_door() if len(params) == 0: s = door.getDebugMode() and 'on' or 'off' - print "debug mode is %s" % s + print("debug mode is %s" % s) return elif len(params) == 1: s = params[0].lower() if s not in ('off', 'on'): - print "Usage: debug [on|off]" + print("Usage: debug [on|off]") return door.setDebugMode(s == 'on') - print "debug mode is now %s" % s + print("debug mode is now %s" % s) else: - print "Usage: debug [on|off]" + print("Usage: debug [on|off]") def www(self, parameter_s=''): @@ -249,7 +249,7 @@ def edmac(self, parameter_s=''): macro_info_obj = ms.getMacroInfoObj(macro_name) if not is_new_macro: if macro_info_obj is None: - print "Macro '%s' could not be found" % macro_name + print("Macro '%s' could not be found" % macro_name) return macro_lib = macro_info_obj.module @@ -259,11 +259,11 @@ def edmac(self, parameter_s=''): ' override the already existing macro in module "%s"' % (macro_name, macro_lib, macro_info_obj.module)) if not ask_yes_no(msg, 'y'): - print "Aborting edition..." + print("Aborting edition...") return macro_info = (macro_lib, macro_name) - print 'Opening %s.%s...' % macro_info + print('Opening %s.%s...' % macro_info) try: remote_fname, code, line_nb = ms.GetMacroCode(macro_info) @@ -281,23 +281,23 @@ def edmac(self, parameter_s=''): ip.magic(cmd) if ask_yes_no('Do you want to apply the new code on the server?', 'y'): - print 'Storing...', + print('Storing...', end=' ') try: f = file(local_fname) try: new_code = f.read() ms.SetMacroCode([remote_fname, new_code]) - print MSG_DONE + print(MSG_DONE) except Exception as e: - print MSG_FAILED - print 'Reason:', str(e) + print(MSG_FAILED) + print('Reason:', str(e)) f.close() except: - print 'Could not open file \'%s\' for safe transfer to the ' \ - 'server' % local_fname - print 'Did you forget to save?' + print('Could not open file \'%s\' for safe transfer to the ' \ + 'server' % local_fname) + print('Did you forget to save?') else: - print "Discarding changes..." + print("Discarding changes...") # if os.path.exists(local_fname): # if ask_yes_no('Delete temporary file \'%s\'?' % local_fname, 'y'): @@ -317,7 +317,7 @@ def spock_late_startup_hook(self): except: import traceback - print "Exception in spock_late_startup_hook:" + print("Exception in spock_late_startup_hook:") traceback.print_exc() @@ -327,7 +327,7 @@ def spock_pre_prompt_hook(self): except: import traceback - print "Exception in spock_pre_prompt_hook:" + print("Exception in spock_pre_prompt_hook:") traceback.print_exc() # def spock_pre_runcode_hook(self): diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 23ecc9077c..1030e66019 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -116,8 +116,8 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): return break else: - print "Cannot plot scan:" - print "No scan in scan history was saved into a file" + print("Cannot plot scan:") + print("No scan in scan history was saved into a file") return else: for scan in reversed(scan_history_info): @@ -125,15 +125,15 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): scan_dir = scan.get('ScanDir') scan_file = scan.get('ScanFile') if scan_dir is None or scan_file is None: - print "Cannot plot scan:" - print "Scan %d was not saved into a file" % (scan_nb,) + print("Cannot plot scan:") + print("Scan %d was not saved into a file" % (scan_nb,)) return if not isinstance(scan_file, (str, unicode)): scan_file = scan_file[0] break else: - print "Cannot plot scan:" - print "Scan %d not found in scan history" % (scan_nb,) + print("Cannot plot scan:") + print("Scan %d not found in scan history" % (scan_nb,)) return remote_file = os.path.join(scan_dir, scan_file) @@ -159,16 +159,16 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): local_file = os.path.join(local_directory, scan_file) break if local_file is None: - print "Cannot plot scan:" - print "Could not find %s in any of the following locations:" % (scan_file,) - print "\n".join(locations) + print("Cannot plot scan:") + print("Could not find %s in any of the following locations:" % (scan_file,)) + print("\n".join(locations)) return import taurus.qt.qtgui.extra_nexus taurus_nexus_widget = taurus.qt.qtgui.extra_nexus.TaurusNeXusBrowser() taurus_nexus_widget.setMinimumSize(800, 600) - print "Trying to open local scan file %s..." % (local_file,) + print("Trying to open local scan file %s..." % (local_file,)) taurus_nexus_widget.openFile(local_file) taurus_nexus_widget.show() nexus_widget = taurus_nexus_widget.neXusWidget() @@ -190,8 +190,8 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): title = file_model.getNodeFromIndex(title_index)[0] windowTitle += " - " + title except Exception as e: - print "Cannot plot scan:" - print str(e) + print("Cannot plot scan:") + print(str(e)) taurus_nexus_widget.setWindowTitle(windowTitle) @@ -199,13 +199,13 @@ def plot(self): try: import sps except: - print 'sps module not available. No plotting' + print('sps module not available. No plotting') return try: import pylab except: - print "pylab not available (try running 'spock -pylab'). No plotting" + print("pylab not available (try running 'spock -pylab'). No plotting") return door = genutils.get_door() @@ -213,8 +213,8 @@ def plot(self): try: env = dict(door.getEnvironmentObj().read().value) except Exception as e: - print 'Unable to read environment. No plotting' - print str(e) + print('Unable to read environment. No plotting') + print(str(e)) return program = door.getNormalName().replace('/', '').replace('_', '') @@ -223,26 +223,26 @@ def plot(self): '/', '').replace('_', '').upper() + "0D" array_ENV = '%s_ENV' % array except: - print 'ActiveMntGrp not defined. No plotting' + print('ActiveMntGrp not defined. No plotting') return if not program in sps.getspeclist(): - print '%s not found. No plotting' % program + print('%s not found. No plotting' % program) return if not array in sps.getarraylist(program): - print '%s not found in %s. No plotting' % (array, program) + print('%s not found in %s. No plotting' % (array, program)) return if not array_ENV in sps.getarraylist(program): - print '%s not found in %s. No plotting' % (array_ENV, program) + print('%s not found in %s. No plotting' % (array_ENV, program)) return try: mem = sps.attach(program, array) mem_ENV = sps.attach(program, array_ENV) except Exception as e: - print 'sps.attach error: %s. No plotting' % str(e) + print('sps.attach error: %s. No plotting' % str(e)) return # reconstruct the environment @@ -259,7 +259,7 @@ def plot(self): col_nb = len(labels) if col_nb < 4: - print 'No data columns available in sps' + print('No data columns available in sps') return rows = int(env['nopts']) @@ -350,7 +350,7 @@ def runMacro(self, obj, parameters=[], synch=False): def _runMacro(self, xml, **kwargs): # kwargs like 'synch' are ignored in this re-implementation if self._spock_state != RUNNING_STATE: - print "Unable to run macro: No connection to door '%s'" % self.getSimpleName() + print("Unable to run macro: No connection to door '%s'" % self.getSimpleName()) raise Exception("Unable to run macro: No connection") if xml is None: xml = self.getRunningXML() @@ -368,21 +368,21 @@ def _runMacro(self, xml, **kwargs): reason, desc = e.args[0].reason, e.args[0].desc macro_obj = self.getRunningMacro() if reason == 'MissingParam': - print "Missing parameter:", desc - print macro_obj.getInfo().doc + print("Missing parameter:", desc) + print(macro_obj.getInfo().doc) elif reason == 'WrongParam': - print "Wrong parameter:", desc - print macro_obj.getInfo().doc + print("Wrong parameter:", desc) + print(macro_obj.getInfo().doc) elif reason == 'UnkownParamObj': - print "Unknown parameter:", desc + print("Unknown parameter:", desc) elif reason == 'MissingEnv': - print "Missing environment:", desc + print("Missing environment:", desc) elif reason in ('API_CantConnectToDevice', 'API_DeviceNotExported'): self._updateState(self._old_sw_door_state, TaurusSWDevState.Shutdown, silent=True) - print "Unable to run macro: No connection to door '%s'" % self.getSimpleName() + print("Unable to run macro: No connection to door '%s'" % self.getSimpleName()) else: - print "Unable to run macro:", reason, desc + print("Unable to run macro:", reason, desc) def _getMacroResult(self, macro): ret = None diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 57be46e2f8..772a8c92ca 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -534,11 +534,11 @@ def ask_yes_no(prompt, default=None): if not ans: # response was an empty string ans = default except KeyboardInterrupt: - print + print() except EOFError: if default in list(answers.keys()): ans = default - print + print() else: raise @@ -706,8 +706,8 @@ def prepare_server(args, tango_args): nodb = "-nodb" in tango_args if nodb and not hasattr(DeviceClass, "device_name_factory"): - print "In order to start %s with 'nodb' you need PyTango >= 7.2.3" %\ - server_name + print("In order to start %s with 'nodb' you need PyTango >= 7.2.3" %\ + server_name) sys.exit(1) if len(tango_args) < 2: @@ -720,7 +720,7 @@ def prepare_server(args, tango_args): out = ''.join([c for c in inst_name if c not in valid_set]) valid = len(inst_name) > 0 and len(out) == 0 if not valid: - print "We only accept alphanumeric combinations" + print("We only accept alphanumeric combinations") args.append(inst_name) tango_args.append(inst_name) else: @@ -756,7 +756,7 @@ def prepare_server(args, tango_args): else: print("\nAvailable Pools:") for pool in pools_for_choosing: - print pool + print(pool) print("") while True: msg = "Please select the Pool to connect to " \ @@ -771,12 +771,12 @@ def prepare_server(args, tango_args): break # user ended loop with some pools selected elif len(elem) == 0: - print("\nMacroServer %s has been connected to " - "Pool/s %s\n" % (inst_name, pool_names)) + print(("\nMacroServer %s has been connected to " + "Pool/s %s\n" % (inst_name, pool_names))) break # user entered unknown pool elif elem.lower() not in all_pools: - print "Unknown pool element" + print("Unknown pool element") else: pool_names.append(elem) log_messages += register_sardana(db, server_name, inst_name, @@ -1248,7 +1248,7 @@ def terminate(self): try: log_messages.extend(prepare_server(args, tango_args)) except AbortException as e: - print e.message + print(e.message) return except KeyboardInterrupt: print("\nInterrupted by keyboard") diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py index 9c6b4d1f16..ccb4e2dd57 100644 --- a/src/sardana/tango/macroserver/Door.py +++ b/src/sardana/tango/macroserver/Door.py @@ -369,7 +369,7 @@ def is_Abort_allowed(self): def PauseMacro(self): macro = self.getRunningMacro() if macro is None: - print "Unable to pause Null macro" + print("Unable to pause Null macro") return self.macro_executor.pause() diff --git a/src/sardana/tango/macroserver/test/base.py b/src/sardana/tango/macroserver/test/base.py index 6a9a85a47b..f4d987a02b 100644 --- a/src/sardana/tango/macroserver/test/base.py +++ b/src/sardana/tango/macroserver/test/base.py @@ -86,7 +86,7 @@ def setUp(self, properties=None): self.door = PyTango.DeviceProxy(self.door_name) except Exception as e: # force tearDown in order to eliminate the MacroServer - print e + print(e) self.tearDown() def tearDown(self): @@ -115,11 +115,11 @@ def tearDown(self): except Exception as e: msg = "Not possible to remove macroserver environment file" print(msg) - print("Details: %s" % e) + print(("Details: %s" % e)) if __name__ == '__main__': bms = BaseMacroServerTestCase() bms.setUp() - print bms.door, bms.macroserver + print(bms.door, bms.macroserver) bms.tearDown() diff --git a/src/sardana/tango/pool/test/base_sartest.py b/src/sardana/tango/pool/test/base_sartest.py index 7bede8ea0d..dfa32ad2bf 100644 --- a/src/sardana/tango/pool/test/base_sartest.py +++ b/src/sardana/tango/pool/test/base_sartest.py @@ -113,7 +113,7 @@ def setUp(self, pool_properties=None): # use the first trigger/gate element by default ctrl.write_attribute("Synchronizer", "_test_tg_1_1") except Exception as e: - print e + print(e) msg = 'Impossible to create ctrl: "%s"' % (ctrl_name) raise Exception('Aborting SartestTestCase: %s' % (msg)) self.ctrl_list.append(ctrl_name) @@ -124,7 +124,7 @@ def setUp(self, pool_properties=None): self.pool.createElement( [sar_type, ctrl_name, str(axis), elem_name]) except Exception as e: - print e + print(e) msg = 'Impossible to create element: "%s"' % ( elem_name) raise Exception('Aborting SartestTestCase: %s' % (msg)) @@ -140,7 +140,7 @@ def setUp(self, pool_properties=None): try: self.pool.CreateController(argin) except Exception as e: - print e + print(e) msg = 'Impossible to create ctrl: "%s"' % (ctrl_name) raise Exception('Aborting SartestTestCase: %s' % (msg)) self.ctrl_list.append(ctrl_name) @@ -151,7 +151,7 @@ def setUp(self, pool_properties=None): except Exception as e: # force tearDown in order to eliminate the Pool BasePoolTestCase.tearDown(self) - print e + print(e) def tearDown(self): """Remove the elements and the controllers diff --git a/src/sardana/tango/pool/test/test_measurementgroup.py b/src/sardana/tango/pool/test/test_measurementgroup.py index caa47e5763..3adce2524b 100644 --- a/src/sardana/tango/pool/test/test_measurementgroup.py +++ b/src/sardana/tango/pool/test/test_measurementgroup.py @@ -101,7 +101,7 @@ def event_received(self, *args, **kwargs): channel_data.extend(pad + value) self.data[obj_fullname] = channel_data except Exception as e: - print e + print(e) raise Exception('"data" event callback failed') @@ -222,7 +222,7 @@ def meas_cont_acquisition(self, params, config): # Do acquisition self.meas.Start() while self.meas.State() == PyTango.DevState.MOVING: - print "Acquiring..." + print("Acquiring...") time.sleep(0.1) time.sleep(1) repetitions = params['synchronization'][0][SynchParam.Repeats] @@ -238,7 +238,7 @@ def stop_meas_cont_acquisition(self, params, config): # starting timer (0.2 s) which will stop the measurement group threading.Timer(0.2, self.stopMeas).start() while self.meas.State() == PyTango.DevState.MOVING: - print "Acquiring..." + print("Acquiring...") time.sleep(0.1) state = self.meas.State() desired_state = PyTango.DevState.ON @@ -263,8 +263,8 @@ def tearDown(self): # Delete the meas self.pool.DeleteElement(self.mg_name) except Exception as e: - print('Impossible to delete MeasurementGroup: %s' % (self.mg_name)) - print e + print(('Impossible to delete MeasurementGroup: %s' % (self.mg_name))) + print(e) SarTestTestCase.tearDown(self) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index ed85ba632f..e42c07b72a 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -166,7 +166,7 @@ def input(self, input_data=None): return ret def input_timeout(self, input_data): - print "input timeout" + print("input timeout") class MacroServerDevice(TangoDevice): @@ -1240,7 +1240,7 @@ def __fillParamNodesValues(self, paramInfo, paramNode): def printTree(self, nodes, tabs=0): tabs = tabs + 1 for node in nodes: - print ('\t'*tabs) + str(type(node)) + str(node) + print(('\t'*tabs) + str(type(node)) + str(node)) if isinstance(node, SingleParamNode): pass else: diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py index a0508a94a6..57c0ab83f6 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py @@ -378,7 +378,7 @@ def main(): if len(args) > 1: w.onDoorChanged(args[1]) else: - print "WARNING: Not door name supplied. Connection to MacroServer/Door not automatically done" + print("WARNING: Not door name supplied. Connection to MacroServer/Door not automatically done") w.show() sys.exit(app.exec_()) diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/selectsignal.py b/src/sardana/taurus/qt/qtgui/extra_hkl/selectsignal.py index 4ac80a56b5..d351b17f25 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/selectsignal.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/selectsignal.py @@ -90,7 +90,7 @@ def update_signals(self, doorname=''): if self.doorName != doorname: self.doorName = doorname self.door_device = taurus.Device(self.doorName) - print "Create door_device with name " + str(self.doorName) + print("Create door_device with name " + str(self.doorName)) if self.doorName is not None: signals = [] diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 4b16f5b9a4..069ab0d4af 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -422,7 +422,7 @@ def getMacroInfo(self, macro_name): try: pars = door.macro_server.getMacroInfoObj(macro_name).parameters except AttributeError as e: - print "Macro %s does not exists!" % macro_name + print("Macro %s does not exists!" % macro_name) return None param_names = [] diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 621500029e..7b94592390 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -672,7 +672,7 @@ def onSaveSequence(self): "Error while saving macros sequence", "There was a problem while writing to the file: %s" % fileName) - print e + print(e) finally: if not file is None: file.close() diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py b/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py index d49fd19c48..7aefded30e 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py @@ -315,7 +315,7 @@ def main_ControllerClassSelecionDialog(pool, perspective=PoolControllerView.Cont model_name=pool, perspective=perspective) if w.result() == Qt.QDialog.Accepted: - print w.getSelectedMacros() + print(w.getSelectedMacros()) return w diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py b/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py index ac0f4fcddb..07c0a6a537 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py @@ -287,7 +287,7 @@ def main_MacroSelecionDialog(ms, perspective=MacroView.MacroModule): w = MacroSelectionDialog(model_name=ms, perspective=perspective) if w.result() == Qt.QDialog.Accepted: - print w.getSelectedMacros() + print(w.getSelectedMacros()) return w diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index 408ae18412..82b0a05b58 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -145,7 +145,7 @@ def createChannelDict(channel, index=None, **kwargs): if attrconf.data_format != PyTango.AttrDataFormat.SCALAR: value = attrproxy.read().value except Exception as e: - print str(e) + print(str(e)) if value is not None: shape = list(numpy.shape(value)) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py index c7c30865b9..eb8f8f7eb6 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/startup.py @@ -100,8 +100,8 @@ def run(self, verbose=False): log.append(modname) del sys.modules[modname] if verbose and log: - print "\x1b[4;33m%s\x1b[24m%s\x1b[0m" % ("UMD has deleted", - ": " + ", ".join(log)) + print("\x1b[4;33m%s\x1b[24m%s\x1b[0m" % ("UMD has deleted", + ": " + ", ".join(log))) __umd__ = None diff --git a/src/sardana/test/testsuite.py b/src/sardana/test/testsuite.py index 087b7192a6..c230b82aef 100644 --- a/src/sardana/test/testsuite.py +++ b/src/sardana/test/testsuite.py @@ -47,7 +47,7 @@ def _filter_suite(suite, exclude_pattern, ret=None): for e in suite: if isinstance(e, unittest.TestCase): if re.match(exclude_pattern, e.id()): - print "Excluded %s" % e.id() + print("Excluded %s" % e.id()) continue ret.addTest(e) else: @@ -108,7 +108,7 @@ def main(): args = parser.parse_args() if args.version: - print Release.version + print(Release.version) sys.exit(0) ret = run(exclude_pattern=args.exclude_pattern) diff --git a/src/sardana/tools/config/fods_to_sar.py b/src/sardana/tools/config/fods_to_sar.py index 579c1c7c6d..45d5578a71 100644 --- a/src/sardana/tools/config/fods_to_sar.py +++ b/src/sardana/tools/config/fods_to_sar.py @@ -30,12 +30,12 @@ def transform(f): def main(): if len(sys.argv) < 2: - print __doc__ + print(__doc__) sys.exit(1) filename = sys.argv[1] t = transform(filename) - print etree.tostring(t, pretty_print=True) + print(etree.tostring(t, pretty_print=True)) if __name__ == "__main__": main() diff --git a/src/sardana/tools/config/get_pool_config.py b/src/sardana/tools/config/get_pool_config.py index c3cad7cba7..fad35df2f6 100644 --- a/src/sardana/tools/config/get_pool_config.py +++ b/src/sardana/tools/config/get_pool_config.py @@ -37,8 +37,8 @@ def checkPoolElements(pool): try: ctrl_library = pool_ctrl_classes[ctrl_class][0] except KeyError: - print ("#WARNING: There is no controller class %s for controller %s" % - (ctrl_class, ctrl_name)) + print(("#WARNING: There is no controller class %s for controller %s" % + (ctrl_class, ctrl_name))) continue ctrl_type = str(info['main_type']) # sardana script is not compatible with the new type CTExpChannel @@ -139,7 +139,7 @@ def checkPoolElements(pool): pool_elements_detail[alias]['attr_dicts'][attr] = attr_dict else: if attr.lower() in ['position', 'value']: - print '***', specific_element_type, alias, attr, 'NO MEMORIZED ATTRIBUTES OR ATTRIBUTE CONFIGURATIONS ***' + print('***', specific_element_type, alias, attr, 'NO MEMORIZED ATTRIBUTES OR ATTRIBUTE CONFIGURATIONS ***') # print '\n' # print '----------------------------------------------------------------' @@ -164,8 +164,8 @@ def checkPoolElements(pool): try: ctrl = pool_controllers[ctrl_name] except KeyError: - print ("#WARNING: There is no controller %s for element %s" % - (ctrl_name, alias)) + print(("#WARNING: There is no controller %s for element %s" % + (ctrl_name, alias))) continue ctrl['ctrl_pool_elements'].append(alias) if specific_element_type in ['PseudoMotor', 'PseudoCounter']: @@ -355,46 +355,46 @@ def checkPoolElements(pool): row = acq_row_template.format(**mg_details) acquisition_sheet += row + '\n' - print '\n' * 2 - print '################################ CONTROLLERS ################################\n' * 4 - print '\n' * 2 - print controllers_sheet - print '\n' * 2 - print '################################ INSTRUMENTS ################################\n' * 4 - print '\n' * 2 - print instruments_sheet - print '\n' * 2 - print '################################ MOTORS ################################\n' * 4 - print '\n' * 2 - print motors_sheet - print '\n' * 2 - print '################################ IOREGS ################################\n' * 4 - print '\n' * 2 - print ioregs_sheet - print '\n' * 2 - print '################################ CHANNELS ################################\n' * 4 - print '\n' * 2 - print channels_sheet - print '\n' * 2 - print '################################ ACQUISITION ################################\n' * 4 - print '\n' * 2 - print acquisition_sheet - print '\n' * 2 - print '################################ PARAMETERS ################################\n' * 4 - print '\n' * 2 - print parameters_sheet + print('\n' * 2) + print('################################ CONTROLLERS ################################\n' * 4) + print('\n' * 2) + print(controllers_sheet) + print('\n' * 2) + print('################################ INSTRUMENTS ################################\n' * 4) + print('\n' * 2) + print(instruments_sheet) + print('\n' * 2) + print('################################ MOTORS ################################\n' * 4) + print('\n' * 2) + print(motors_sheet) + print('\n' * 2) + print('################################ IOREGS ################################\n' * 4) + print('\n' * 2) + print(ioregs_sheet) + print('\n' * 2) + print('################################ CHANNELS ################################\n' * 4) + print('\n' * 2) + print(channels_sheet) + print('\n' * 2) + print('################################ ACQUISITION ################################\n' * 4) + print('\n' * 2) + print(acquisition_sheet) + print('\n' * 2) + print('################################ PARAMETERS ################################\n' * 4) + print('\n' * 2) + print(parameters_sheet) if __name__ == '__main__': if len(sys.argv) != 2 or sys.argv[1] == '?': - print '----------------------------------------' - print 'Invalid number of arguments.' - print '' - print 'Example of usage:' - print ' python get_pool_config pool' - print '' - print ' where pool is the device name of the pool' - print '----------------------------------------' + print('----------------------------------------') + print('Invalid number of arguments.') + print('') + print('Example of usage:') + print(' python get_pool_config pool') + print('') + print(' where pool is the device name of the pool') + print('----------------------------------------') pool = sys.argv[1] checkPoolElements(pool) diff --git a/src/sardana/tools/config/sar_to_fods.py b/src/sardana/tools/config/sar_to_fods.py index af46c270b1..ddd9520a28 100644 --- a/src/sardana/tools/config/sar_to_fods.py +++ b/src/sardana/tools/config/sar_to_fods.py @@ -17,7 +17,7 @@ def transform(f): def main(): filename = sys.argv[1] t = transform(filename) - print etree.tostring(t, pretty_print=True) + print(etree.tostring(t, pretty_print=True)) if __name__ == "__main__": main() diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index 012905d767..a84fc9d41d 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -76,11 +76,11 @@ except: try: import pexpect23 as pexpect - print "[WARNING]: pexpect module not found. Using local pexpect 2.3" + print("[WARNING]: pexpect module not found. Using local pexpect 2.3") except Exception as e: - print e - print "The Sardana requires pexpect python module which was not found." - print "This module can be found at http://www.noah.org/wiki/Pexpect" + print(e) + print("The Sardana requires pexpect python module which was not found.") + print("This module can be found at http://www.noah.org/wiki/Pexpect") sys.exit(2) @@ -449,8 +449,8 @@ def __init__(self, servNode, bl, createProc=False, log=False): server_host_ip = socket.gethostbyname_ex(server_host)[2][0] pytango_host_ip = socket.gethostbyname_ex(pytango_host)[2][0] if (server_host_ip != pytango_host_ip) or (server_port != pytango_port): - print '\t!!! WARNING !!! %s TANGO_HOST is not the PyTango default. You may erase the WRONG sardana definition.' % self._complete_name - print '\tServer: %s PyTango: %s' % (server_tango_host, pytango_tango_host) + print('\t!!! WARNING !!! %s TANGO_HOST is not the PyTango default. You may erase the WRONG sardana definition.' % self._complete_name) + print('\tServer: %s PyTango: %s' % (server_tango_host, pytango_tango_host)) ans = raw_input('\tDo you _really_ want to continue? [y|N] ') if ans.lower() not in ['y', 'yes']: raise Exception( @@ -470,7 +470,7 @@ def __init__(self, servNode, bl, createProc=False, log=False): cmd = 'TANGO_HOST=%s jive-save-config %s %s &>/dev/null' % ( server_tango_host, self._complete_name, config_file_name) os.system(cmd) - print 'There is a backup of the deleted server config in: %s' % config_file_name + print('There is a backup of the deleted server config in: %s' % config_file_name) except: pass ####################################################################### @@ -691,8 +691,8 @@ def handle_attributes(self, dev_name, node): try: dev.write_attribute(name, v) except Exception as ex: - print 'SOME PROBLEMS SETTING ATTRIBUTE VALUE FOR DEVICE', dev_name, 'ATTRIBUTE', tango_attr.name, 'VALUE', str(v) - print 'EXCEPTION:', ex + print('SOME PROBLEMS SETTING ATTRIBUTE VALUE FOR DEVICE', dev_name, 'ATTRIBUTE', tango_attr.name, 'VALUE', str(v)) + print('EXCEPTION:', ex) c_node = attr.find("Configuration") if not c_node is None: @@ -748,14 +748,14 @@ def handle_attributes(self, dev_name, node): period = int(p_node.get("period") or 0) dev.poll_attribute(name, period) except: - print dev_name, tango_attr.name + print(dev_name, tango_attr.name) try: dev.set_attribute_config(attr_info) except Exception as e: - print 'COULD NOT SET THE FOLLOWING CONFIG FOR DEVICE', dev_name, 'ATTR', tango_attr.name - print 'ATTRIBUTE INFO:', attr_info - print 'EXCEPTION:', e + print('COULD NOT SET THE FOLLOWING CONFIG FOR DEVICE', dev_name, 'ATTR', tango_attr.name) + print('ATTRIBUTE INFO:', attr_info) + print('EXCEPTION:', e) intrument_node = node.find("InstrumentRef") if not intrument_node is None: @@ -765,8 +765,8 @@ def handle_attributes(self, dev_name, node): value = value.strip() dev.write_attribute('Instrument', value) except Exception as ex: - print 'SOME PROBLEMS SETTING INSTRUMENT VALUE FOR DEVICE', dev_name, 'VALUE', value - print 'EXCEPTION:', ex + print('SOME PROBLEMS SETTING INSTRUMENT VALUE FOR DEVICE', dev_name, 'VALUE', value) + print('EXCEPTION:', ex) def loadPool(self): start_load_time = datetime.datetime.now() @@ -1136,7 +1136,7 @@ def loadPool(self): self.run(step=True) except: self.on("[FAILED]") - print ctrl_class_info + print(ctrl_class_info) raise self.on(" (%d ctrls; %d pmotors) [DONE]" % ( pm_ctrl_count, pm_count)) @@ -1848,14 +1848,14 @@ def getRoot(self): opts, pargs = getopt.getopt( sys.argv[1:], 'vl', ['simulation=', 'cleanup=']) except Exception as e: - print "ERROR:", str(e) - print - print __doc__ + print("ERROR:", str(e)) + print() + print(__doc__) sys.exit(3) if not len(pargs): - print "ERROR: Please provide XML filename" - print + print("ERROR: Please provide XML filename") + print() sys.exit(3) filename = pargs[0] @@ -1875,32 +1875,32 @@ def getRoot(self): elif opt == '-v': just_output_and_exit = True else: - print __doc__ + print(__doc__) sys.exit(3) try: import to_sar sar_doc = to_sar.transform(filename) except Exception as e: - print 'Sorry, but some problems found when trying to convert to SARDANA xml:' - print str(e) + print('Sorry, but some problems found when trying to convert to SARDANA xml:') + print(str(e)) sardana = Sardana(sar_doc, simulation=simulation, log=activate_logging, cleanup=cleanup) if just_output_and_exit: sardana.prepare() - print etree.tostring(sardana.getRoot(), pretty_print=True) + print(etree.tostring(sardana.getRoot(), pretty_print=True)) sys.exit(0) try: sardana.setUp() - print "Ready!" + print("Ready!") sardana.run() except KeyboardInterrupt as e: - print "User pressed Ctrl+C..." + print("User pressed Ctrl+C...") except Exception as e: traceback.print_exc() - print "Shutting down!" + print("Shutting down!") sardana.tearDown() diff --git a/src/sardana/tools/config/to_sar.py b/src/sardana/tools/config/to_sar.py index 434e08b33b..486e6dc8a6 100644 --- a/src/sardana/tools/config/to_sar.py +++ b/src/sardana/tools/config/to_sar.py @@ -31,7 +31,7 @@ def transform(f): def main(): filename = sys.argv[1] t = transform(filename) - print etree.tostring(t, pretty_print=True) + print(etree.tostring(t, pretty_print=True)) if __name__ == "__main__": main() diff --git a/src/sardana/tools/config/xls_to_sar.py b/src/sardana/tools/config/xls_to_sar.py index 1ee2a59a9d..9acf655a29 100644 --- a/src/sardana/tools/config/xls_to_sar.py +++ b/src/sardana/tools/config/xls_to_sar.py @@ -30,12 +30,12 @@ def transform(f): def main(): if len(sys.argv) < 2: - print __doc__ + print(__doc__) sys.exit(1) filename = sys.argv[1] t = transform(filename) - print etree.tostring(t, pretty_print=True) + print(etree.tostring(t, pretty_print=True)) if __name__ == "__main__": main() diff --git a/src/sardana/util/deepreload.py b/src/sardana/util/deepreload.py index 440818a992..20f4c58508 100644 --- a/src/sardana/util/deepreload.py +++ b/src/sardana/util/deepreload.py @@ -198,7 +198,7 @@ def import_submodule(mod, subname, fullname): if fullname in found_now and fullname in sys.modules: m = sys.modules[fullname] else: - print 'Reloading', fullname + print('Reloading', fullname) found_now[fullname] = 1 oldm = sys.modules.get(fullname, None) diff --git a/src/sardana/util/motion/motion.py b/src/sardana/util/motion/motion.py index d2d160f437..dca5ab323c 100644 --- a/src/sardana/util/motion/motion.py +++ b/src/sardana/util/motion/motion.py @@ -251,27 +251,27 @@ def _calculateMotionPath(self): self.duration = duration def info(self): - print "Small movement =", self.small_motion - print "length =", self.displacement - print "position where maximum velocity will be reached =", \ - self.max_vel_pos - print "necessary displacement to reach maximum velocity =", \ - self.displacement_reach_max_vel - print "necessary displacement to stop from maximum velocity =", \ - self.displacement_reach_min_vel - print "maximum velocity possible =", self.max_vel - print "time at top velocity =", self.at_max_vel_time - print "displacement at top velocity =", self.at_max_vel_displacement - print "time to reach maximum velocity =", self.max_vel_time - print "time to reach minimum velocity =", self.min_vel_time - print "time the motion will take =", self.duration - print "" - print "For long movements (where top vel is possible), necessary " \ + print("Small movement =", self.small_motion) + print("length =", self.displacement) + print("position where maximum velocity will be reached =", \ + self.max_vel_pos) + print("necessary displacement to reach maximum velocity =", \ + self.displacement_reach_max_vel) + print("necessary displacement to stop from maximum velocity =", \ + self.displacement_reach_min_vel) + print("maximum velocity possible =", self.max_vel) + print("time at top velocity =", self.at_max_vel_time) + print("displacement at top velocity =", self.at_max_vel_displacement) + print("time to reach maximum velocity =", self.max_vel_time) + print("time to reach minimum velocity =", self.min_vel_time) + print("time the motion will take =", self.duration) + print("") + print("For long movements (where top vel is possible), necessary " \ "displacement to reach maximum velocity =", \ - self.displacement_reach_max_vel - print "For long movements (where top vel is possible), necessary " \ + self.displacement_reach_max_vel) + print("For long movements (where top vel is possible), necessary " \ "displacement to stop from maximum velocity =", \ - self.displacement_reach_min_vel + self.displacement_reach_min_vel) class Motion(object): @@ -536,7 +536,7 @@ def setPower(self, power): def info(self): if self.current_motion is not None: - print self.current_motion.info() + print(self.current_motion.info()) class Motor(BaseMotor): @@ -699,7 +699,7 @@ def fromMotor(motor): return Motor(min_vel=min_vel, max_vel=max_vel, accel_time=accel_time, decel_time=decel_time) except Exception as e: - print e + print(e) return Motor._fromTangoMotor(motor) @staticmethod From 2c892b4b613eb529a5c789c659b278b201c253fe Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:20:25 +0200 Subject: [PATCH 078/830] 2to3: raise --- src/sardana/macroserver/msmacromanager.py | 2 +- src/sardana/macroserver/msparameter.py | 20 ++++++++++---------- src/sardana/spock/ipython_00_10/genutils.py | 4 ++-- src/sardana/spock/ipython_00_11/genutils.py | 4 ++-- src/sardana/spock/ipython_01_00/genutils.py | 4 ++-- src/sardana/spock/parameter.py | 2 +- src/sardana/tools/config/pexpect23.py | 10 +++++----- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index c0f91cbe99..773d575248 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -775,7 +775,7 @@ def decodeMacroParameters(self, door, raw_params): except WrongParam as in_e: msg = ("Either of: %s or %s made it impossible to decode" " parameters" % (out_e.message, in_e.message)) - raise WrongParam, msg + raise WrongParam(msg) else: raise out_e return macro_meta, raw_params, out_par_list diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py index 65d5b485ff..a6613af933 100644 --- a/src/sardana/macroserver/msparameter.py +++ b/src/sardana/macroserver/msparameter.py @@ -385,7 +385,7 @@ def decode(self): if len(raw_params) > len_params_def: msg = ("%r are supernumerary with respect to definition" % raw_params[len_params_def:]) - raise SupernumeraryParam, msg + raise SupernumeraryParam(msg) # iterate over definition since missing values may just mean using # the default values for i, param_def in enumerate(params_def): @@ -466,7 +466,7 @@ def decodeRepeat(self, raw_param_repeat, param_repeat_def): if min_rep and len_rep < min_rep: msg = 'Found %d repetitions of param %s, min is %d' % \ (len_rep, name, min_rep) - raise MissingRepeat, msg + raise MissingRepeat(msg) if max_rep and len_rep > max_rep: msg = 'Found %d repetitions of param %s, max is %d' % \ (len_rep, name, max_rep) @@ -498,7 +498,7 @@ def decodeRepeat(self, raw_param_repeat, param_repeat_def): # to indicate default value elif isinstance(raw_repeat, list) and len(raw_repeat) > 0: msg = 'Repetitions of just one member must not be lists' - raise WrongParam, msg + raise WrongParam(msg) repeat = self.decodeNormal(raw_repeat, param_type[0]) param_repeat.append(repeat) return param_repeat @@ -524,7 +524,7 @@ def __init__(self, type_manager, params_def, raw_params): if not self.isPossible(params_def): msg = ("%s parameter definition is not compatible with" " FlatParamDecoder" % params_def) - raise AttributeError, msg + raise AttributeError(msg) self.decode() @staticmethod @@ -561,13 +561,13 @@ def decodeNormal(self, raw_params, params_def): if str_idx == str_len: if def_val is None: if not isinstance(type_class, list): - raise MissingParam, "'%s' not specified" % name + raise MissingParam("'%s' not specified" % name) elif isinstance(type_class, list): min_rep = par_def['min'] if min_rep > 0: msg = "'%s' demands at least %d values" %\ (name, min_rep) - raise WrongParam, msg + raise WrongParam(msg) if not def_val is None: new_obj = def_val else: @@ -583,13 +583,13 @@ def decodeNormal(self, raw_params, params_def): try: val = par_type.getObj(par_str) except ValueError as e: - raise WrongParamType, e.message + raise WrongParamType(e.message) except UnknownParamObj as e: - raise WrongParam, e.message + raise WrongParam(e.message) if val is None: msg = 'Could not create %s parameter "%s" for "%s"' % \ (par_type.getName(), name, par_str) - raise WrongParam, msg + raise WrongParam(msg) dec_token = 1 new_obj = val str_idx += dec_token @@ -618,7 +618,7 @@ def decodeRepeat(self, raw_params, par_def): if rep_nr < min_rep: msg = 'Found %d repetitions of param %s, min is %d' % \ (rep_nr, name, min_rep) - raise MissingRepeat, msg + raise MissingRepeat(msg) return dec_token, obj_list def getParamList(self): diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index ef6d6c053a..ab3156af92 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -629,10 +629,10 @@ def check_requirements(): if errMsg: errMsg += warnMsg - raise exception.SpockMissingRequirement, errMsg + raise exception.SpockMissingRequirement(errMsg) if warnMsg: - raise exception.SpockMissingRecommended, warnMsg + raise exception.SpockMissingRecommended(warnMsg) return True diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py index 05f8f3fb47..d046d30a58 100644 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ b/src/sardana/spock/ipython_00_11/genutils.py @@ -586,10 +586,10 @@ def check_requirements(): if errMsg: errMsg += warnMsg - raise exception.SpockMissingRequirement, errMsg + raise exception.SpockMissingRequirement(errMsg) if warnMsg: - raise exception.SpockMissingRecommended, warnMsg + raise exception.SpockMissingRecommended(warnMsg) return True diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index 781b2dc014..37254f0564 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -602,10 +602,10 @@ def check_requirements(): if errMsg: errMsg += warnMsg - raise exception.SpockMissingRequirement, errMsg + raise exception.SpockMissingRequirement(errMsg) if warnMsg: - raise exception.SpockMissingRecommended, warnMsg + raise exception.SpockMissingRecommended(warnMsg) return True diff --git a/src/sardana/spock/parameter.py b/src/sardana/spock/parameter.py index 8352a665c0..731f59e4d1 100644 --- a/src/sardana/spock/parameter.py +++ b/src/sardana/spock/parameter.py @@ -93,7 +93,7 @@ def __init__(self, name=None, desc=None, opts=None, param_def=None, type_name = from_array.read() if type_name != 'ParamRepeat': msg = 'Expecting "ParamRepeat" type, got ' + type_name - raise ValueError, msg + raise ValueError(msg) self.desc = from_array.read() opt_str = from_array.read() opt_list = opt_str.split(', ') diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 987afde2b9..221c89e1c0 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -595,11 +595,11 @@ def __fork_pty(self): parent_fd, child_fd = os.openpty() if parent_fd < 0 or child_fd < 0: - raise ExceptionPexpect, "Error! Could not open pty with os.openpty()." + raise ExceptionPexpect("Error! Could not open pty with os.openpty().") pid = os.fork() if pid < 0: - raise ExceptionPexpect, "Error! Failed os.fork()." + raise ExceptionPexpect("Error! Failed os.fork().") elif pid == 0: # Child. os.close(parent_fd) @@ -636,7 +636,7 @@ def __pty_make_controlling_tty(self, tty_fd): fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) if fd >= 0: os.close(fd) - raise ExceptionPexpect, "Error! We are not disconnected from a controlling tty." + raise ExceptionPexpect("Error! We are not disconnected from a controlling tty.") except: # Good! We are disconnected from a controlling tty. pass @@ -644,14 +644,14 @@ def __pty_make_controlling_tty(self, tty_fd): # Verify we can open child pty. fd = os.open(child_name, os.O_RDWR) if fd < 0: - raise ExceptionPexpect, "Error! Could not open child pty, " + child_name + raise ExceptionPexpect("Error! Could not open child pty, " + child_name) else: os.close(fd) # Verify we now have a controlling tty. fd = os.open("/dev/tty", os.O_WRONLY) if fd < 0: - raise ExceptionPexpect, "Error! Could not open controlling tty, /dev/tty" + raise ExceptionPexpect("Error! Could not open controlling tty, /dev/tty") else: os.close(fd) From ee411175d746885fbb10e49aa4e40b25d518c672 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:21:55 +0200 Subject: [PATCH 079/830] 2to3: raw_input --- src/sardana/spock/ipython_00_10/genutils.py | 8 ++++---- src/sardana/spock/ipython_00_11/genutils.py | 10 +++++----- src/sardana/spock/ipython_01_00/genutils.py | 12 ++++++------ src/sardana/tango/core/util.py | 6 +++--- src/sardana/tools/config/sardana.py | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index ab3156af92..0d31d8461b 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -359,7 +359,7 @@ def get_device_from_user(expected_class, dft=None): if not dft is None: prompt += "[%s]" % dft prompt += "? " - from_user = raw_input(prompt).strip() or dft + from_user = input(prompt).strip() or dft name = '' try: @@ -406,7 +406,7 @@ def get_tango_host_from_user(): while True: prompt = "Please enter a valid tango host (:): " - from_user = raw_input(prompt).strip() + from_user = input(prompt).strip() try: host, port = from_user.split(':') @@ -831,7 +831,7 @@ def check_for_upgrade(ipy_profile_file, ipythondir, session, profile): % (spock_profile_ver_str, spock_lib_ver_str) print(msg) prompt = 'Do you wish to upgrade now (warn: this will shutdown the current spock session) ([y]/n)? ' - r = raw_input(prompt) or 'y' + r = input(prompt) or 'y' if r.lower() == 'y': create_spock_profile(ipythondir, session, profile, door_name) sys.exit(0) @@ -881,7 +881,7 @@ def get_args(argv): while not r in ['y', 'n']: prompt = 'Profile \'%s\' does not exist. Do you want to create '\ 'one now ([y]/n)? ' % profile - r = raw_input(prompt) or 'y' + r = input(prompt) or 'y' if r.lower() == 'y': create_spock_profile(ipythondir, session, profile) else: diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py index d046d30a58..1c6ca2295c 100644 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ b/src/sardana/spock/ipython_00_11/genutils.py @@ -309,7 +309,7 @@ def get_device_from_user(expected_class, dft=None): if not dft is None: prompt += "[%s]" % dft prompt += "? " - from_user = raw_input(prompt).strip() or dft + from_user = input(prompt).strip() or dft name = '' try: @@ -357,7 +357,7 @@ def get_tango_host_from_user(): import PyTango while True: prompt = "Please enter a valid tango host (:): " - from_user = raw_input(prompt).strip() + from_user = input(prompt).strip() try: host, port = from_user.split(':') @@ -820,7 +820,7 @@ def check_for_upgrade(ipy_profile_dir): % (spock_profile_ver_str, spock_lib_ver_str) print(msg) prompt = 'Do you wish to upgrade now (warn: this will shutdown the current spock session) ([y]/n)? ' - r = raw_input(prompt) or 'y' + r = input(prompt) or 'y' if r.lower() == 'y': upgrade_spock_profile(ipy_profile_dir, door_name) sys.exit(0) @@ -855,7 +855,7 @@ def get_args(argv): while not r in ('y', 'n'): prompt = 'Profile \'%s\' does not exist. Do you want to create '\ 'one now ([y]/n)? ' % profile - r = raw_input(prompt) or 'y' + r = input(prompt) or 'y' if r.lower() == 'y': create_spock_profile(ipython_dir, profile) else: @@ -1238,7 +1238,7 @@ def prepare_cmdline(argv=None): while not r in ('y', 'n'): prompt = "Profile '%s' does not exist. Do you want to create "\ "one now ([y]/n)? " % profile - r = raw_input(prompt) or 'y' + r = input(prompt) or 'y' if r.lower() == 'y': create_spock_profile(ipython_dir, profile) else: diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index 37254f0564..121005f33c 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -161,7 +161,7 @@ def ask_yes_no(prompt, default=None): def spock_input(prompt='', ps2='... '): - return raw_input(prompt) + return input(prompt) def translate_version_str2int(version_str): @@ -317,7 +317,7 @@ def get_device_from_user(expected_class, dft=None): if not dft is None: prompt += "[%s]" % dft prompt += "? " - from_user = raw_input(prompt).strip() or dft + from_user = input(prompt).strip() or dft name = None try: @@ -365,7 +365,7 @@ def get_tango_host_from_user(): import PyTango while True: prompt = "Please enter a valid tango host (:): " - from_user = raw_input(prompt).strip() + from_user = input(prompt).strip() try: host, port = from_user.split(':') @@ -865,7 +865,7 @@ def check_for_upgrade(ipy_profile_dir): % (spock_profile_ver_str, spock_lib_ver_str) print(msg) prompt = 'Do you wish to upgrade now (warn: this will shutdown the current spock session) ([y]/n)? ' - r = raw_input(prompt) or 'y' + r = input(prompt) or 'y' if r.lower() == 'y': upgrade_spock_profile(ipy_profile_dir, door_name) sys.exit(0) @@ -900,7 +900,7 @@ def get_args(argv): while not r in ('y', 'n'): prompt = 'Profile \'%s\' does not exist. Do you want to create '\ 'one now ([y]/n)? ' % profile - r = raw_input(prompt) or 'y' + r = input(prompt) or 'y' if r.lower() == 'y': create_spock_profile(ipython_dir, profile) else: @@ -1302,7 +1302,7 @@ def prepare_cmdline(argv=None): while not r in ('y', 'n'): prompt = "Profile '%s' does not exist. Do you want to create "\ "one now ([y]/n)? " % profile - r = raw_input(prompt) or 'y' + r = input(prompt) or 'y' if r.lower() == 'y': create_spock_profile(ipython_dir, profile) else: diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 772a8c92ca..49693a3d20 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -530,7 +530,7 @@ def ask_yes_no(prompt, default=None): while ans not in list(answers.keys()): try: - ans = raw_input(prompt + ' ').lower() + ans = input(prompt + ' ').lower() if not ans: # response was an empty string ans = default except KeyboardInterrupt: @@ -713,7 +713,7 @@ def prepare_server(args, tango_args): if len(tango_args) < 2: valid = False while not valid: - inst_name = raw_input( + inst_name = input( "Please indicate %s instance name: " % server_name) # should be a instance name validator. valid_set = string.letters + string.digits + '_' + '-' @@ -764,7 +764,7 @@ def prepare_server(args, tango_args): # user may abort it with Ctrl+C - this will not # register anything in the database and the # KeyboardInterrupt will be raised - elem = raw_input(msg).strip() + elem = input(msg).strip() # no pools selected and user ended loop if len(elem) == 0 and len(pool_names) == 0: print(no_pool_msg) diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index a84fc9d41d..e1e8d45caa 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -451,7 +451,7 @@ def __init__(self, servNode, bl, createProc=False, log=False): if (server_host_ip != pytango_host_ip) or (server_port != pytango_port): print('\t!!! WARNING !!! %s TANGO_HOST is not the PyTango default. You may erase the WRONG sardana definition.' % self._complete_name) print('\tServer: %s PyTango: %s' % (server_tango_host, pytango_tango_host)) - ans = raw_input('\tDo you _really_ want to continue? [y|N] ') + ans = input('\tDo you _really_ want to continue? [y|N] ') if ans.lower() not in ['y', 'yes']: raise Exception( 'User cancelled the creation of %s server' % self._complete_name) From 6e3574f387dd7a51dee5fcb874aebbdcd2c8b777 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:25:13 +0200 Subject: [PATCH 080/830] 2to3: renames --- src/sardana/spock/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/spock/parameter.py b/src/sardana/spock/parameter.py index 731f59e4d1..78ecff0d68 100644 --- a/src/sardana/spock/parameter.py +++ b/src/sardana/spock/parameter.py @@ -174,8 +174,8 @@ def getParamCount(self): nb = 0 for par in self.pars: local_nb = par.getParamCount() - if local_nb == sys.maxint: - return sys.maxint + if local_nb == sys.maxsize: + return sys.maxsize nb += local_nb return nb From 1bbd1ebd199a1ced69dce5acc90065f128838746 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:31:53 +0200 Subject: [PATCH 081/830] 2to3: set_literal --- src/sardana/macroserver/macroserver.py | 4 +- src/sardana/pool/pool.py | 4 +- src/sardana/sardanadefs.py | 110 ++++++++++++------------- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/sardana/macroserver/macroserver.py b/src/sardana/macroserver/macroserver.py index 0bb206621e..742b014ff9 100644 --- a/src/sardana/macroserver/macroserver.py +++ b/src/sardana/macroserver/macroserver.py @@ -455,8 +455,8 @@ def reload_macro_lib(self, lib_name): new_elements.append(new_lib) else: changed_elements.append(new_lib) - new_names = set([macro.name for macro in new_lib.get_macros()]) - old_names = set([macro.name for macro in old_lib.get_macros()]) + new_names = {macro.name for macro in new_lib.get_macros()} + old_names = {macro.name for macro in old_lib.get_macros()} changed_names = set.intersection(new_names, old_names) deleted_names = old_names.difference(new_names) new_names = new_names.difference(old_names) diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py index 651dc0129f..ebdd86ffd2 100644 --- a/src/sardana/pool/pool.py +++ b/src/sardana/pool/pool.py @@ -688,8 +688,8 @@ def reload_controller_lib(self, lib_name): new_elements.extend(new_lib.get_controllers()) new_elements.append(new_lib) else: - new_names = set([ctrl.name for ctrl in new_lib.get_controllers()]) - old_names = set([ctrl.name for ctrl in old_lib.get_controllers()]) + new_names = {ctrl.name for ctrl in new_lib.get_controllers()} + old_names = {ctrl.name for ctrl in old_lib.get_controllers()} changed_names = set.intersection(new_names, old_names) deleted_names = old_names.difference(new_names) new_names = new_names.difference(old_names) diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index 97fbd52695..2742a7c255 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -308,50 +308,50 @@ def to_daccess(daccess): #: a set containning all "controllable" element types. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` -TYPE_ELEMENTS = set((ET.Motor, ET.CTExpChannel, ET.ZeroDExpChannel, +TYPE_ELEMENTS = {ET.Motor, ET.CTExpChannel, ET.ZeroDExpChannel, ET.OneDExpChannel, ET.TwoDExpChannel, ET.TriggerGate, ET.ComChannel, ET.IORegister, ET.PseudoMotor, - ET.PseudoCounter, ET.Constraint)) + ET.PseudoCounter, ET.Constraint} #: a set containing all group element types. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` -TYPE_GROUP_ELEMENTS = set((ET.MotorGroup, ET.MeasurementGroup)) +TYPE_GROUP_ELEMENTS = {ET.MotorGroup, ET.MeasurementGroup} #: a set containing the type of elements which are moveable. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` -TYPE_MOVEABLE_ELEMENTS = set((ET.Motor, ET.PseudoMotor, ET.MotorGroup)) +TYPE_MOVEABLE_ELEMENTS = {ET.Motor, ET.PseudoMotor, ET.MotorGroup} #: a set containing the possible types of physical elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` -TYPE_PHYSICAL_ELEMENTS = set((ET.Motor, ET.CTExpChannel, ET.ZeroDExpChannel, +TYPE_PHYSICAL_ELEMENTS = {ET.Motor, ET.CTExpChannel, ET.ZeroDExpChannel, ET.OneDExpChannel, ET.TwoDExpChannel, ET.TriggerGate, - ET.ComChannel, ET.IORegister)) + ET.ComChannel, ET.IORegister} #: a set containing the possible types of acquirable elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` -TYPE_ACQUIRABLE_ELEMENTS = set((ET.Motor, ET.CTExpChannel, ET.ZeroDExpChannel, +TYPE_ACQUIRABLE_ELEMENTS = {ET.Motor, ET.CTExpChannel, ET.ZeroDExpChannel, ET.OneDExpChannel, ET.TwoDExpChannel, ET.ComChannel, ET.IORegister, ET.PseudoMotor, - ET.PseudoCounter)) + ET.PseudoCounter} #: a set containing the possible measure-able elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` -TYPE_COUNTABLE_ELEMENTS = set((ET.CTExpChannel, ET.OneDExpChannel, - ET.TwoDExpChannel, ET.MeasurementGroup)) +TYPE_COUNTABLE_ELEMENTS = {ET.CTExpChannel, ET.OneDExpChannel, + ET.TwoDExpChannel, ET.MeasurementGroup} #: a set containing the possible types of experimental channel elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` -TYPE_EXP_CHANNEL_ELEMENTS = set((ET.CTExpChannel, ET.ZeroDExpChannel, - ET.OneDExpChannel, ET.TwoDExpChannel, ET.PseudoCounter)) +TYPE_EXP_CHANNEL_ELEMENTS = {ET.CTExpChannel, ET.ZeroDExpChannel, + ET.OneDExpChannel, ET.TwoDExpChannel, ET.PseudoCounter} #: a set containing the possible timer-able elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` -TYPE_TIMERABLE_ELEMENTS = set((ET.CTExpChannel, ET.OneDExpChannel, - ET.TwoDExpChannel)) +TYPE_TIMERABLE_ELEMENTS = {ET.CTExpChannel, ET.OneDExpChannel, + ET.TwoDExpChannel} #: a set containing the possible types of pseudo elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` -TYPE_PSEUDO_ELEMENTS = set((ET.PseudoMotor, ET.PseudoCounter)) +TYPE_PSEUDO_ELEMENTS = {ET.PseudoMotor, ET.PseudoCounter} # : An enumeration describing the all possible sardana interfaces # SardanaInterface = Enumeration("SardanaInterface", ( \ @@ -395,51 +395,51 @@ def to_daccess(daccess): INTERFACES = { "Meta": (set(), "A generic sardana meta object"), "Object": (set(), "A generic sardana object"), - "Element": (set(("Object",)), "A generic sardana element"), - "Class": (set(("Object",)), "A generic sardana class"), - "Function": (set(("Object",)), "A generic sardana function"), - "Library": (set(("Object",)), "A generic sardana library"), - "PoolObject": (set(("Object",)), "A Pool object"), - "PoolElement": (set(("Element", "PoolObject")), "A Pool element"), - "Pool": (set(("PoolElement",)), "A Pool"), - "Controller": (set(("PoolElement",)), "A controller"), - "Moveable": (set(("PoolElement",)), "A moveable element"), - "Acquirable": (set(("PoolElement",)), "An acquirable element"), - "Countable": (set(("PoolElement",)), "A countable element"), - "Instrument": (set(("PoolElement",)), "An instrument"), - "Motor": (set(("Moveable", "Acquirable")), "a motor"), - "PseudoMotor": (set(("Moveable", "Acquirable")), "A pseudo motor"), - "IORegister": (set(("Acquirable",)), "An IO register"), - "ExpChannel": (set(("Acquirable",)), "A generic experimental channel"), - "CTExpChannel": (set(("ExpChannel", "Countable")), + "Element": ({"Object"}, "A generic sardana element"), + "Class": ({"Object"}, "A generic sardana class"), + "Function": ({"Object"}, "A generic sardana function"), + "Library": ({"Object"}, "A generic sardana library"), + "PoolObject": ({"Object"}, "A Pool object"), + "PoolElement": ({"Element", "PoolObject"}, "A Pool element"), + "Pool": ({"PoolElement"}, "A Pool"), + "Controller": ({"PoolElement"}, "A controller"), + "Moveable": ({"PoolElement"}, "A moveable element"), + "Acquirable": ({"PoolElement"}, "An acquirable element"), + "Countable": ({"PoolElement"}, "A countable element"), + "Instrument": ({"PoolElement"}, "An instrument"), + "Motor": ({"Moveable", "Acquirable"}, "a motor"), + "PseudoMotor": ({"Moveable", "Acquirable"}, "A pseudo motor"), + "IORegister": ({"Acquirable"}, "An IO register"), + "ExpChannel": ({"Acquirable"}, "A generic experimental channel"), + "CTExpChannel": ({"ExpChannel", "Countable"}, "A counter/timer experimental channel"), - "ZeroDExpChannel": (set(("ExpChannel",)), "A 0D experimental channel"), - "OneDExpChannel": (set(("ExpChannel", "Countable")), + "ZeroDExpChannel": ({"ExpChannel"}, "A 0D experimental channel"), + "OneDExpChannel": ({"ExpChannel", "Countable"}, "A 1D experimental channel"), - "TwoDExpChannel": (set(("ExpChannel", "Countable")), + "TwoDExpChannel": ({"ExpChannel", "Countable"}, "A 2D experimental channel"), - "TriggerGate": (set(("PoolElement",)), "A trigger/gate"), - "PseudoCounter": (set(("ExpChannel",)), "A pseudo counter"), - "ComChannel": (set(("PoolElement",)), "A communication channel"), + "TriggerGate": ({"PoolElement"}, "A trigger/gate"), + "PseudoCounter": ({"ExpChannel"}, "A pseudo counter"), + "ComChannel": ({"PoolElement"}, "A communication channel"), "MotorGroup": (set(("PoolElement",),), "A motor group"), - "MeasurementGroup": (set(("PoolElement", "Countable")), + "MeasurementGroup": ({"PoolElement", "Countable"}, "A measurement group"), - "ControllerLibrary": (set(("Library", "PoolObject")), "A controller library"), - "ControllerClass": (set(("Class", "PoolObject")), "A controller class"), - "Constraint": (set(("PoolObject",)), "A constraint"), - "External": (set(("Object",)), "An external object"), - - "MacroServerObject": (set(("Object",)), "A generic macro server object"), - "MacroServerElement": (set(("Element", "MacroServerObject")), "A generic macro server element"), - "MacroServer": (set(("MacroServerElement",)), "A MacroServer"), - "Door": (set(("MacroServerElement",)), "A macro server door"), - "MacroLibrary": (set(("Library", "MacroServerObject")), "A macro server library"), - "MacroCode": (set(("MacroServerObject",)), "A macro server macro code"), - "MacroClass": (set(("Class", "MacroCode")), "A macro server macro class"), - "MacroFunction": (set(("Function", "MacroCode")), "A macro server macro function"), - "Macro": (set(("MacroClass", "MacroFunction")), "A macro server macro"), - - "ParameterType": (set(("Meta",)), "A generic macro server parameter type"), + "ControllerLibrary": ({"Library", "PoolObject"}, "A controller library"), + "ControllerClass": ({"Class", "PoolObject"}, "A controller class"), + "Constraint": ({"PoolObject"}, "A constraint"), + "External": ({"Object"}, "An external object"), + + "MacroServerObject": ({"Object"}, "A generic macro server object"), + "MacroServerElement": ({"Element", "MacroServerObject"}, "A generic macro server element"), + "MacroServer": ({"MacroServerElement"}, "A MacroServer"), + "Door": ({"MacroServerElement"}, "A macro server door"), + "MacroLibrary": ({"Library", "MacroServerObject"}, "A macro server library"), + "MacroCode": ({"MacroServerObject"}, "A macro server macro code"), + "MacroClass": ({"Class", "MacroCode"}, "A macro server macro class"), + "MacroFunction": ({"Function", "MacroCode"}, "A macro server macro function"), + "Macro": ({"MacroClass", "MacroFunction"}, "A macro server macro"), + + "ParameterType": ({"Meta"}, "A generic macro server parameter type"), } #: a dictionary containing the *all* interfaces supported by each type From 14f63f84c114633343f6e715cb87bcb0c47edbe3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:36:15 +0200 Subject: [PATCH 082/830] 2to3: tuple_params --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index a632469894..d920a9b935 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -859,8 +859,7 @@ def __init__(self, parent=None, designMode=False): # I don't like this approach, there should be something like # self.lbl_alias.addAction(...) - self.lbl_alias.contextMenuEvent = lambda( - event): self.contextMenuEvent(event) + self.lbl_alias.contextMenuEvent = lambda event: self.contextMenuEvent(event) # I' don't like this approach, there should be something like # self.lbl_alias.addToolTipCallback(self.calculate_extra_tooltip) From d5a0b31d13fe7684ab432c948a99222d4b3d68e4 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:38:43 +0200 Subject: [PATCH 083/830] 2to3: types --- src/sardana/macroserver/macro.py | 4 ++-- src/sardana/pool/poolcontrollermanager.py | 2 +- src/sardana/pool/poolmetacontroller.py | 2 +- src/sardana/taurus/core/tango/sardana/macro.py | 6 +++--- src/sardana/tools/config/fods_to_sar.py | 2 +- src/sardana/tools/config/pexpect23.py | 12 ++++++------ src/sardana/tools/config/sar_to_fods.py | 2 +- src/sardana/tools/config/sardana.py | 2 +- src/sardana/tools/config/to_sar.py | 2 +- src/sardana/tools/config/xls_to_sar.py | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index c9d35502bc..5f10ae6da1 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -1330,7 +1330,7 @@ def execMacro(self, *args, **kwargs): macro_name = None arg0 = args[0] if len(args) == 1: - if type(arg0) in types.StringTypes: + if type(arg0) in str: # dealing with sth like args = ('ascan th 0 100 10 1.0',) macro_name = arg0.split()[0] elif isinstance(arg0, collections.Sequence): @@ -2363,7 +2363,7 @@ def __prepareResult(self, out): """ if out is None: out = () - if isinstance(out, collections.Sequence) and not type(out) in types.StringTypes: + if isinstance(out, collections.Sequence) and not type(out) in str: out = list(map(str, out)) else: out = (str(out),) diff --git a/src/sardana/pool/poolcontrollermanager.py b/src/sardana/pool/poolcontrollermanager.py index 650631b6e3..a9f972ffdd 100644 --- a/src/sardana/pool/poolcontrollermanager.py +++ b/src/sardana/pool/poolcontrollermanager.py @@ -546,7 +546,7 @@ def decodeControllerParameters(self, in_par_list): raise RuntimeError('Controller name not specified') controller_name_or_klass = in_par_list[0] controller_class = controller_name_or_klass - if type(controller_class) in types.StringTypes: + if type(controller_class) in str: controller_class = self.getControllerClass(controller_class) if controller_class is None: raise UnknownController("Unknown controller %s" % diff --git a/src/sardana/pool/poolmetacontroller.py b/src/sardana/pool/poolmetacontroller.py index d75b276c3e..c2ef2be82c 100644 --- a/src/sardana/pool/poolmetacontroller.py +++ b/src/sardana/pool/poolmetacontroller.py @@ -206,7 +206,7 @@ def toDataInfo(klass, name, info): fget = info.get(FGet) fset = info.get(FSet) if default_value is not None and dtype != DataType.String: - if type(default_value) in types.StringTypes: + if type(default_value) in str: default_value = eval(default_value) return DataInfo(name, dtype, dformat=dformat, access=daccess, description=description, default_value=default_value, diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 654c91dd39..278658a1db 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -103,13 +103,13 @@ def _isParamComplex(self, p): return not self._isParamAtomic(p) def _isParamAtomic(self, p): - return type(p['type']) in types.StringTypes + return type(p['type']) in str def _buildParameterLine(self, parameters): l = [] for p in parameters: t = p['type'] - if type(t) in types.StringTypes: + if type(t) in str: # Simple parameter l.append('<%s>' % p['name']) else: @@ -122,7 +122,7 @@ def _buildParameterDescription(self, parameters): l = [] for p in parameters: t = p['type'] - if type(t) in types.StringTypes: + if type(t) in str: # Simple parameter l.append('{name} : ({type}) {description}'.format(**p)) else: diff --git a/src/sardana/tools/config/fods_to_sar.py b/src/sardana/tools/config/fods_to_sar.py index 45d5578a71..717b623d0b 100644 --- a/src/sardana/tools/config/fods_to_sar.py +++ b/src/sardana/tools/config/fods_to_sar.py @@ -21,7 +21,7 @@ def transform(f): xslt_filename = os.path.join(directory, "FODS_TO_SAR.xslt") t = etree.XSLT(etree.parse(xslt_filename)) - if type(f) in types.StringTypes: + if type(f) in str: doc = etree.parse(f) else: doc = f diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 221c89e1c0..52153483ab 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -235,16 +235,16 @@ def print_ticks(d): while True: try: index = child.expect(patterns) - if type(child.after) in types.StringTypes: + if type(child.after) in str: child_result_list.append(child.before + child.after) else: # child.after may have been a TIMEOUT or EOF, so don't cat those. child_result_list.append(child.before) - if type(responses[index]) in types.StringTypes: + if type(responses[index]) in str: child.send(responses[index]) elif isinstance(responses[index], types.FunctionType): callback_result = responses[index](locals()) sys.stdout.flush() - if type(callback_result) in types.StringTypes: + if type(callback_result) in str: child.send(callback_result) elif callback_result: break @@ -1216,7 +1216,7 @@ def compile_pattern_list(self, patterns): if patterns is None: return [] - if not isinstance(patterns, types.ListType): + if not isinstance(patterns, list): patterns = [patterns] compile_flags = re.DOTALL # Allow dot to match \n @@ -1224,7 +1224,7 @@ def compile_pattern_list(self, patterns): compile_flags = compile_flags | re.IGNORECASE compiled_pattern_list = [] for p in patterns: - if type(p) in types.StringTypes: + if type(p) in str: compiled_pattern_list.append(re.compile(p, compile_flags)) elif p is EOF: compiled_pattern_list.append(EOF) @@ -1343,7 +1343,7 @@ def expect_exact(self, pattern_list, timeout=-1, searchwindowsize=-1): This method is also useful when you don't want to have to worry about escaping regular expression characters that you want to match.""" - if type(pattern_list) in types.StringTypes or pattern_list in (TIMEOUT, EOF): + if type(pattern_list) in str or pattern_list in (TIMEOUT, EOF): pattern_list = [pattern_list] return self.expect_loop(searcher_string(pattern_list), timeout, searchwindowsize) diff --git a/src/sardana/tools/config/sar_to_fods.py b/src/sardana/tools/config/sar_to_fods.py index ddd9520a28..e90d5db141 100644 --- a/src/sardana/tools/config/sar_to_fods.py +++ b/src/sardana/tools/config/sar_to_fods.py @@ -7,7 +7,7 @@ def transform(f): t = etree.XSLT(etree.parse("SAR_TO_FODS.xslt")) - if type(f) in types.StringTypes: + if type(f) in str: doc = etree.parse(f) else: doc = f diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index e1e8d45caa..5bfcdfe871 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -1280,7 +1280,7 @@ class Sardana: def __init__(self, source, simulation="Best", cleanup=True, log=False): - if type(source) in types.StringTypes: + if type(source) in str: self._filename = source self._xmldoc = None else: diff --git a/src/sardana/tools/config/to_sar.py b/src/sardana/tools/config/to_sar.py index 486e6dc8a6..2bf2364be9 100644 --- a/src/sardana/tools/config/to_sar.py +++ b/src/sardana/tools/config/to_sar.py @@ -8,7 +8,7 @@ def transform(f): - if type(f) in types.StringTypes: + if type(f) in str: doc = etree.parse(f) else: doc = f diff --git a/src/sardana/tools/config/xls_to_sar.py b/src/sardana/tools/config/xls_to_sar.py index 9acf655a29..934f4f03b5 100644 --- a/src/sardana/tools/config/xls_to_sar.py +++ b/src/sardana/tools/config/xls_to_sar.py @@ -21,7 +21,7 @@ def transform(f): xslt_filename = os.path.join(directory, "XLS_TO_SAR.xslt") t = etree.XSLT(etree.parse(xslt_filename)) - if type(f) in types.StringTypes: + if type(f) in str: doc = etree.parse(f) else: doc = f From df8fb39c77e6efa42b78482c63ce98953b76c2a1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 16:40:38 +0200 Subject: [PATCH 084/830] 2to3: unicode --- src/sardana/macroserver/macro.py | 6 +++--- src/sardana/macroserver/macros/lists.py | 2 +- src/sardana/macroserver/macros/test/base.py | 4 ++-- src/sardana/macroserver/msdoor.py | 4 ++-- src/sardana/macroserver/msenvmanager.py | 8 ++++---- src/sardana/macroserver/msmacromanager.py | 4 ++-- src/sardana/macroserver/msmetamacro.py | 2 +- src/sardana/macroserver/recorders/h5storage.py | 2 +- src/sardana/macroserver/recorders/output.py | 4 ++-- src/sardana/macroserver/recorders/storage.py | 10 +++++----- src/sardana/macroserver/scan/gscan.py | 10 +++++----- src/sardana/sardanadefs.py | 10 +++++----- src/sardana/sardanautils.py | 6 +++--- src/sardana/spock/inputhandler.py | 2 +- src/sardana/spock/spockms.py | 8 ++++---- src/sardana/tango/core/attributehandler.py | 2 +- src/sardana/taurus/core/tango/sardana/macroserver.py | 4 ++-- src/sardana/taurus/core/tango/sardana/motion.py | 2 +- src/sardana/taurus/core/tango/sardana/pool.py | 6 +++--- src/sardana/taurus/core/tango/sardana/sardana.py | 2 +- .../taurus/qt/qtgui/extra_sardana/measurementgroup.py | 2 +- .../taurus/qt/qtgui/macrolistener/macrolistener.py | 4 ++-- 22 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 5f10ae6da1..edc5b7e888 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -1390,7 +1390,7 @@ def outputBlock(self, line): """**Macro API**. Sends an line tagged as a block to the output :param :obj:`str` line: line to be sent""" - if isinstance(line, (str, unicode)): + if isinstance(line, (str, str)): o = line elif isinstance(line, collections.Sequence): o = "\n".join(line) @@ -1472,7 +1472,7 @@ def getObj(self, name, type_class=All, subtype=All, pool=All, reserve=True): automatically reserve the object for this macro [default: True] :return: the object or None if no compatible object is found""" - if not isinstance(name, (str, unicode)): + if not isinstance(name, (str, str)): raise self._buildWrongParamExp("getObj", "name", "string", str(type(name))) @@ -2056,7 +2056,7 @@ def _outputBlock(self, line): Sends a line tagged as a block to the output :param :obj:`str` line: line to be sent""" - if isinstance(line, (str, unicode)): + if isinstance(line, (str, str)): o = line elif isinstance(line, collections.Sequence): o = "\n".join(line) diff --git a/src/sardana/macroserver/macros/lists.py b/src/sardana/macroserver/macros/lists.py index a89741ee75..780e4f6ccd 100644 --- a/src/sardana/macroserver/macros/lists.py +++ b/src/sardana/macroserver/macros/lists.py @@ -146,7 +146,7 @@ def run(self, filter): nb = len(objs) if nb is 0: if self.subtype is Macro.All: - if isinstance(self.type, (str, unicode)): + if isinstance(self.type, (str, str)): t = self.type.lower() else: t = ", ".join(self.type).lower() diff --git a/src/sardana/macroserver/macros/test/base.py b/src/sardana/macroserver/macros/test/base.py index f583327cf5..660cec9c8c 100644 --- a/src/sardana/macroserver/macros/test/base.py +++ b/src/sardana/macroserver/macros/test/base.py @@ -178,7 +178,7 @@ class RunMacroTestCase(BaseMacroTestCase): def assertFinished(self, msg): """Asserts that macro has finished. """ - finishStates = [u'finish'] + finishStates = ['finish'] state = self.macro_executor.getState() msg = msg + ';\nState: %s' % state exception_str = self.macro_executor.getExceptionStr() @@ -266,7 +266,7 @@ class RunStopMacroTestCase(RunMacroTestCase): def assertStopped(self, msg): """Asserts that macro was stopped """ - stoppedStates = [u'stop'] + stoppedStates = ['stop'] state = self.macro_executor.getState() # TODO buffer is just for debugging, attach only the last state state_buffer = self.macro_executor.getStateBuffer() diff --git a/src/sardana/macroserver/msdoor.py b/src/sardana/macroserver/msdoor.py index 26a3cd0e85..d9ff0206b3 100644 --- a/src/sardana/macroserver/msdoor.py +++ b/src/sardana/macroserver/msdoor.py @@ -209,7 +209,7 @@ def input(self, msg, *args, **kwargs): input_data = dict(prompt=msg, type='input') input_data.update(kwargs) data_type = kwargs['data_type'] - is_seq = not isinstance(data_type, (str, unicode)) and \ + is_seq = not isinstance(data_type, (str, str)) and \ isinstance(data_type, collections.Sequence) if is_seq: handle = self._handle_seq_input @@ -355,7 +355,7 @@ def get_macro_proxies(self): return self._macro_proxy_cache def run_macro(self, par_str_list, asynch=False): - if isinstance(par_str_list, (str, unicode)): + if isinstance(par_str_list, (str, str)): par_str_list = par_str_list, if not hasattr(self, "Output"): diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index e5966f3c51..6e5864bee0 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -347,7 +347,7 @@ def getDoorMacroEnv(self, door_name, macro_name, keys=None): if keys is None: return self.getAllDoorMacroEnv(door_name, macro_name) - if isinstance(keys, (str, unicode)): + if isinstance(keys, (str, str)): keys = (keys,) door_name = door_name.lower() @@ -384,7 +384,7 @@ def _dictFromSequence(self, seq): def _encode(self, d): ret = {} for k, v in d.items(): - if isinstance(v, (str, unicode)): + if isinstance(v, (str, str)): try: v = eval(v) except: @@ -455,7 +455,7 @@ def setEnvObj(self, obj): @return a dict representing the added environment""" if isinstance(obj, collections.Sequence) and \ - not isinstance(obj, (str, unicode)): + not isinstance(obj, (str, str)): obj = self._dictFromSequence(obj) elif not isinstance(obj, collections.Mapping): raise TypeError("obj parameter must be a sequence or a map") @@ -481,7 +481,7 @@ def unsetEnv(self, key): :param key: the key for the environment to be unset :return: the sequence of keys which have been removed""" - if isinstance(key, (str, unicode)): + if isinstance(key, (str, str)): key = (key,) self._unsetEnv(key) return key diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 773d575248..00325f7806 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -723,7 +723,7 @@ def getMacroFunctionCode(self, macro_name): return self.getMacroFunction(macro_name).function def getMacroInfo(self, macro_names, format='json'): - if isinstance(macro_names, (str, unicode)): + if isinstance(macro_names, (str, str)): macro_names = [macro_names] ret = [] json_codec = CodecFactory().getCodec(format) @@ -1260,7 +1260,7 @@ def _prepareXMLMacro(self, xml_macro, parent_macro=None): def _createMacroObj(self, macro_name_or_meta, pars, init_opts={}): macro_meta = macro_name_or_meta - if isinstance(macro_meta, (str, unicode)): + if isinstance(macro_meta, (str, str)): macro_meta = self.macro_manager.getMacro(macro_meta) macro_opts = { diff --git a/src/sardana/macroserver/msmetamacro.py b/src/sardana/macroserver/msmetamacro.py index e5adfe9d44..3229429a63 100644 --- a/src/sardana/macroserver/msmetamacro.py +++ b/src/sardana/macroserver/msmetamacro.py @@ -162,7 +162,7 @@ def _build_parameter(self, param_def): if isinstance(t, ParamRepeat): t = t.obj() - if isinstance(t, collections.Sequence) and not isinstance(t, (str, unicode)): + if isinstance(t, collections.Sequence) and not isinstance(t, (str, str)): if isinstance(t[-1], collections.Mapping): ret_p.update(t[-1]) t = self._build_parameter(t[:-1]) diff --git a/src/sardana/macroserver/recorders/h5storage.py b/src/sardana/macroserver/recorders/h5storage.py index 7b1f66786f..2e51b6b6ad 100644 --- a/src/sardana/macroserver/recorders/h5storage.py +++ b/src/sardana/macroserver/recorders/h5storage.py @@ -66,7 +66,7 @@ class NXscanH5_FileRecorder(BaseFileRecorder): """ formats = {'h5': '.h5'} # from http://docs.h5py.org/en/latest/strings.html - str_dt = h5py.special_dtype(vlen=unicode) # Variable-length UTF-8 (PY2) + str_dt = h5py.special_dtype(vlen=str) # Variable-length UTF-8 (PY2) byte_dt = h5py.special_dtype(vlen=bytes) # Variable-length UTF-8 (PY2) supported_dtypes = ('float32', 'float64', 'int8', 'int16', 'int32', 'int64', 'uint8', diff --git a/src/sardana/macroserver/recorders/output.py b/src/sardana/macroserver/recorders/output.py index 4378fcd1e9..45106cf977 100644 --- a/src/sardana/macroserver/recorders/output.py +++ b/src/sardana/macroserver/recorders/output.py @@ -138,7 +138,7 @@ def __init__(self, stream, cols=None, number_fmt='%8.4f', col_width=8, self._col_sep = col_sep self._col_width = col_width if isinstance(cols, collections.Sequence) and \ - not isinstance(cols, (str, unicode)): + not isinstance(cols, (str, str)): cols = CaselessList(cols) elif isinstance(cols, numbers.Number): cols = cols @@ -258,7 +258,7 @@ def _writeRecord(self, record): cell = str(cell_data.shape) elif cell_data is None: cell = "" - elif isinstance(cell_data, (str, unicode)): + elif isinstance(cell_data, (str, str)): # TODO: for SEP2 needs strings are enabled for visualizing # value refs, previously "" was printed. This may # have side effects e.g. alignment of columns etc.. Check diff --git a/src/sardana/macroserver/recorders/storage.py b/src/sardana/macroserver/recorders/storage.py index 9b69ea94be..89bbca0ae1 100644 --- a/src/sardana/macroserver/recorders/storage.py +++ b/src/sardana/macroserver/recorders/storage.py @@ -391,7 +391,7 @@ def _startRecordList(self, recordlist): header += '#L %(labels)s\n' self.fd = io.open(self.filename, 'a', newline='\n') - self.fd.write(unicode(header % data)) + self.fd.write(str(header % data)) self.fd.flush() os.fsync(self.fd.fileno()) @@ -470,7 +470,7 @@ def _writeRecord(self, record): str_data += '%s' % data outstr = '@A %s' % str_data outstr += '\n' - fd.write(unicode(outstr)) + fd.write(str(outstr)) for c in names: data = record.data.get(c) @@ -480,7 +480,7 @@ def _writeRecord(self, record): outstr = ' '.join(d) outstr += '\n' - fd.write(unicode(outstr)) + fd.write(str(outstr)) fd.flush() os.fsync(self.fd.fileno()) @@ -491,7 +491,7 @@ def _endRecordList(self, recordlist): env = recordlist.getEnviron() end_time = env['endtime'].ctime() - self.fd.write(unicode("#C Acquisition ended at %s\n" % end_time)) + self.fd.write(str("#C Acquisition ended at %s\n" % end_time)) self.fd.flush() self.fd.close() @@ -525,7 +525,7 @@ def _addCustomData(self, value, name, **kwargs): self.info( 'Custom data "%s" will not be stored in SPEC file. Reason: cannot open file', name) return - self.fd.write(unicode('#C %s : %s\n' % (name, v))) + self.fd.write(str('#C %s : %s\n' % (name, v))) self.fd.flush() if fileWasClosed: self.fd.close() # leave the file descriptor as found diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index a7d7dde9f6..2b295d269d 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -302,7 +302,7 @@ def __init__(self, macro, generator=None, moveables=[], env={}, macro.info("ActiveMntGrp not defined. Using %s", mnt_grp) macro.setEnv('ActiveMntGrp', mnt_grp.getName()) else: - if not isinstance(mnt_grp_name, (str, unicode)): + if not isinstance(mnt_grp_name, (str, str)): t = type(mnt_grp_name).__name__ raise TypeError("ActiveMntGrp MUST be string. It is '%s'" % t) @@ -492,7 +492,7 @@ def _getFileRecorders(self): 'ScanDir ") to enable it') return () - if not isinstance(scan_dir, (str, unicode)): + if not isinstance(scan_dir, (str, str)): scan_dir_t = type(scan_dir).__name__ raise TypeError("ScanDir MUST be string. It is '%s'" % scan_dir_t) @@ -514,14 +514,14 @@ def _getFileRecorders(self): except UnknownEnv: pass - if isinstance(file_names, (str, unicode)): + if isinstance(file_names, (str, str)): file_names = (file_names,) elif not isinstance(file_names, collections.Sequence): scan_file_t = type(file_names).__name__ raise TypeError("ScanFile MUST be string or sequence of strings." " It is '%s'" % scan_file_t) - if isinstance(scan_recorders, (str, unicode)): + if isinstance(scan_recorders, (str, str)): scan_recorders = (scan_recorders,) elif not isinstance(scan_recorders, collections.Sequence): scan_recorders_t = type(scan_recorders).__name__ @@ -971,7 +971,7 @@ def end(self): scan_history = [] scan_file = env['ScanFile'] - if isinstance(scan_file, (str, unicode)): + if isinstance(scan_file, (str, str)): scan_file = scan_file, names = [col.name for col in env['datadesc']] diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index 2742a7c255..78fba36cd7 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -192,7 +192,7 @@ def from_dtype_str(dtype): dformat = DataFormat.Scalar if dtype is None: dtype = 'float' - elif isinstance(dtype, (str, unicode)): + elif isinstance(dtype, (str, str)): dtype = dtype.lower() if dtype.startswith("pytango."): dtype = dtype[len("pytango."):] @@ -214,7 +214,7 @@ def from_access_str(access): :type dtype: :obj:`str` :return: a simple string for the given access :rtype: :obj:`str`""" - if isinstance(access, (str, unicode)): + if isinstance(access, (str, str)): access = access.lower() if access.startswith("pytango."): access = access[len("pytango."):] @@ -233,7 +233,7 @@ def to_dtype_dformat(data): """ import operator dtype, dformat = data, DataFormat.Scalar - if isinstance(data, (str, unicode)): + if isinstance(data, (str, str)): dtype, dformat = from_dtype_str(data) elif isinstance(data, collections.Sequence): dformat = DataFormat.OneD @@ -261,7 +261,7 @@ def to_daccess(daccess): :rtype: :obj:`DataAccess`""" if daccess is None: daccess = DataAccess.ReadWrite - elif isinstance(daccess, (str, unicode)): + elif isinstance(daccess, (str, str)): daccess = DACCESS_MAP.get( from_access_str(daccess), DataAccess.ReadWrite) return daccess @@ -449,7 +449,7 @@ def to_daccess(daccess): def __expand(name): direct_expansion, _ = INTERFACES[name] - if isinstance(direct_expansion, (str, unicode)): + if isinstance(direct_expansion, (str, str)): direct_expansion = direct_expansion, exp = set(direct_expansion) for e in direct_expansion: diff --git a/src/sardana/sardanautils.py b/src/sardana/sardanautils.py index 1fb5bd8360..f327290839 100644 --- a/src/sardana/sardanautils.py +++ b/src/sardana/sardanautils.py @@ -49,10 +49,10 @@ __use_unicode = False try: - unicode + str __use_unicode = True - __str_klasses.append(unicode) - __DTYPE_MAP[unicode] = DataType.String + __str_klasses.append(str) + __DTYPE_MAP[str] = DataType.String except: pass diff --git a/src/sardana/spock/inputhandler.py b/src/sardana/spock/inputhandler.py index b22494ce27..a44ccc5ab0 100644 --- a/src/sardana/spock/inputhandler.py +++ b/src/sardana/spock/inputhandler.py @@ -120,7 +120,7 @@ def init(self, *args, **kwargs): def input(self, input_data=None): # parent process data_type = input_data.get('data_type', 'String') - if isinstance(data_type, (str, unicode)): + if isinstance(data_type, (str, str)): ms = genutils.get_macro_server() interfaces = ms.getInterfaces() if data_type in interfaces: diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 1030e66019..4ef4149639 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -92,7 +92,7 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): scan_file = scan.get('ScanFile') if scan_dir is None or scan_file is None: continue - if not isinstance(scan_file, (str, unicode)): + if not isinstance(scan_file, (str, str)): scan_files = scan_file scan_file = None for fname in scan_files: @@ -128,7 +128,7 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): print("Cannot plot scan:") print("Scan %d was not saved into a file" % (scan_nb,)) return - if not isinstance(scan_file, (str, unicode)): + if not isinstance(scan_file, (str, str)): scan_file = scan_file[0] break else: @@ -148,7 +148,7 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): local_directories = directory_map[scan_dir] # local_directories may be either a string or a list of strings # the further logic is unified to work on a list, so convert it - if isinstance(local_directories, (str, unicode)): + if isinstance(local_directories, (str, str)): local_directories = [local_directories] locations = local_directories if scan_dir not in locations: @@ -364,7 +364,7 @@ def _runMacro(self, xml, **kwargs): self.writeln("Done!") except PyTango.DevFailed as e: if is_non_str_seq(e.args) and \ - not isinstance(e.args, (str, unicode)): + not isinstance(e.args, (str, str)): reason, desc = e.args[0].reason, e.args[0].desc macro_obj = self.getRunningMacro() if reason == 'MissingParam': diff --git a/src/sardana/tango/core/attributehandler.py b/src/sardana/tango/core/attributehandler.py index 14aefafba7..7175e8e140 100644 --- a/src/sardana/tango/core/attributehandler.py +++ b/src/sardana/tango/core/attributehandler.py @@ -72,7 +72,7 @@ def clearBuffer(self): def appendBuffer(self, d): if isinstance(d, collections.Sequence): - if isinstance(d, (str, unicode)): + if isinstance(d, (str, str)): self._buff.append(d) else: self._buff.extend(d) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index e42c07b72a..2b057e59e1 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -198,7 +198,7 @@ def get(self, cache=False): scan_file = env.get('ScanFile') if scan_file is None: scan_file = [] - elif isinstance(scan_file, (str, unicode)): + elif isinstance(scan_file, (str, str)): scan_file = [scan_file] ret['ScanFile'] = scan_file mnt_grps = macro_server.getElementsOfType("MeasurementGroup") @@ -537,7 +537,7 @@ def preRunMacro(self, obj, parameters): self._clearRunMacro() xml_root = None - if isinstance(obj, (str, unicode)): + if isinstance(obj, (str, str)): if obj.startswith('<') and not parameters: xml_root = etree.fromstring(obj) else: diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py index 29eddb25b9..d9702ccf2b 100644 --- a/src/sardana/taurus/core/tango/sardana/motion.py +++ b/src/sardana/taurus/core/tango/sardana/motion.py @@ -136,7 +136,7 @@ def __init__(self, elements, moveable_srcs, allow_repeat=False, first_elem = elements[0] - if isinstance(first_elem, (str, unicode)): + if isinstance(first_elem, (str, str)): self.init_by_names(elements, moveable_srcs, allow_repeat, allow_unknown) else: diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 378ee3b548..74ffecae59 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1363,7 +1363,7 @@ def getChannelConfigs(mgconfig, ctrls=None, sort=True): class MGConfiguration(object): def __init__(self, mg, data): self._mg = weakref.ref(mg) - if isinstance(data, (str, unicode)): + if isinstance(data, (str, str)): data = CodecFactory().decode(('json', data), ensure_ascii=True) self.raw_data = data self.__dict__.update(data) @@ -2315,7 +2315,7 @@ def getElementWithInterface(self, elem_name, interface): def getObj(self, name, elem_type=None): if elem_type is None: return self.getElementInfo(name) - elif isinstance(elem_type, (str, unicode)): + elif isinstance(elem_type, (str, str)): elem_types = elem_type, else: elem_types = elem_type @@ -2345,7 +2345,7 @@ def getMoveable(self, names): 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)): + if isinstance(names, (str, str)): names = names, if len(names) == 1: diff --git a/src/sardana/taurus/core/tango/sardana/sardana.py b/src/sardana/taurus/core/tango/sardana/sardana.py index 2145f71867..fc8586aa8e 100644 --- a/src/sardana/taurus/core/tango/sardana/sardana.py +++ b/src/sardana/taurus/core/tango/sardana/sardana.py @@ -111,7 +111,7 @@ def getType(self): def getTypes(self): elem_types = self.type - if isinstance(elem_types, (str, unicode)): + if isinstance(elem_types, (str, str)): return [elem_types] return elem_types diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index 82b0a05b58..0afc3035b8 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -89,7 +89,7 @@ def createChannelDict(channel, index=None, **kwargs): import PyTango import numpy - if isinstance(channel, (str, unicode)): + if isinstance(channel, (str, str)): #@fixme: to make things uglier, I lazily assume Tango attribute namin dev_name, attr_name = channel.rsplit('/', 1) name = attr_name diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index bace31288d..cc18a5b042 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -263,7 +263,7 @@ def _updateTemporaryTrends1D(self, trends1d): w.setScanDoor(self.getModelObj().getFullName()) # TODO: use a standard key for and w.setScansXDataKey(axes[0]) - pname = u'Trend1D - %s' % ":".join(axes) + pname = 'Trend1D - %s' % ":".join(axes) panel = self.createPanel(w, pname, registerconfig=False, permanent=False) try: # if the panel is a dockwidget, raise it @@ -313,7 +313,7 @@ def _updateTemporaryTrends2D(self, trends2d): newpanels = [] for axes, plotables in list(trends2d.items()): for chname in plotables: - pname = u'Trend2D - %s' % chname + pname = 'Trend2D - %s' % chname if pname in self._trends2d: self._trends2d[pname].widget().trendItem.clearTrend() else: From f5317910ff9b9d8b93af5f8d2d4a3156e777aa82 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 18:03:36 +0200 Subject: [PATCH 085/830] Manual changes after 2to3: unicode These changes does not have sense. Remove the affected code. --- src/sardana/macroserver/macro.py | 6 +++--- src/sardana/macroserver/macros/lists.py | 2 +- src/sardana/macroserver/msdoor.py | 4 ++-- src/sardana/macroserver/msenvmanager.py | 8 ++++---- src/sardana/macroserver/msmacromanager.py | 4 ++-- src/sardana/macroserver/msmetamacro.py | 2 +- src/sardana/macroserver/recorders/output.py | 4 ++-- src/sardana/macroserver/scan/gscan.py | 10 +++++----- src/sardana/sardanadefs.py | 10 +++++----- src/sardana/sardanautils.py | 9 --------- src/sardana/spock/inputhandler.py | 2 +- src/sardana/spock/spockms.py | 8 ++++---- src/sardana/tango/core/attributehandler.py | 2 +- src/sardana/taurus/core/tango/sardana/macroserver.py | 4 ++-- src/sardana/taurus/core/tango/sardana/motion.py | 2 +- src/sardana/taurus/core/tango/sardana/pool.py | 6 +++--- src/sardana/taurus/core/tango/sardana/sardana.py | 2 +- .../taurus/qt/qtgui/extra_sardana/measurementgroup.py | 2 +- 18 files changed, 39 insertions(+), 48 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index edc5b7e888..6167aedb92 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -1390,7 +1390,7 @@ def outputBlock(self, line): """**Macro API**. Sends an line tagged as a block to the output :param :obj:`str` line: line to be sent""" - if isinstance(line, (str, str)): + if isinstance(line, str): o = line elif isinstance(line, collections.Sequence): o = "\n".join(line) @@ -1472,7 +1472,7 @@ def getObj(self, name, type_class=All, subtype=All, pool=All, reserve=True): automatically reserve the object for this macro [default: True] :return: the object or None if no compatible object is found""" - if not isinstance(name, (str, str)): + if not isinstance(name, str): raise self._buildWrongParamExp("getObj", "name", "string", str(type(name))) @@ -2056,7 +2056,7 @@ def _outputBlock(self, line): Sends a line tagged as a block to the output :param :obj:`str` line: line to be sent""" - if isinstance(line, (str, str)): + if isinstance(line, str): o = line elif isinstance(line, collections.Sequence): o = "\n".join(line) diff --git a/src/sardana/macroserver/macros/lists.py b/src/sardana/macroserver/macros/lists.py index 780e4f6ccd..a89998ef26 100644 --- a/src/sardana/macroserver/macros/lists.py +++ b/src/sardana/macroserver/macros/lists.py @@ -146,7 +146,7 @@ def run(self, filter): nb = len(objs) if nb is 0: if self.subtype is Macro.All: - if isinstance(self.type, (str, str)): + if isinstance(self.type, str): t = self.type.lower() else: t = ", ".join(self.type).lower() diff --git a/src/sardana/macroserver/msdoor.py b/src/sardana/macroserver/msdoor.py index d9ff0206b3..1eb06fd24d 100644 --- a/src/sardana/macroserver/msdoor.py +++ b/src/sardana/macroserver/msdoor.py @@ -209,7 +209,7 @@ def input(self, msg, *args, **kwargs): input_data = dict(prompt=msg, type='input') input_data.update(kwargs) data_type = kwargs['data_type'] - is_seq = not isinstance(data_type, (str, str)) and \ + is_seq = not isinstance(data_type, str) and \ isinstance(data_type, collections.Sequence) if is_seq: handle = self._handle_seq_input @@ -355,7 +355,7 @@ def get_macro_proxies(self): return self._macro_proxy_cache def run_macro(self, par_str_list, asynch=False): - if isinstance(par_str_list, (str, str)): + if isinstance(par_str_list, str): par_str_list = par_str_list, if not hasattr(self, "Output"): diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index 6e5864bee0..cccdc2e2ef 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -347,7 +347,7 @@ def getDoorMacroEnv(self, door_name, macro_name, keys=None): if keys is None: return self.getAllDoorMacroEnv(door_name, macro_name) - if isinstance(keys, (str, str)): + if isinstance(keys, str): keys = (keys,) door_name = door_name.lower() @@ -384,7 +384,7 @@ def _dictFromSequence(self, seq): def _encode(self, d): ret = {} for k, v in d.items(): - if isinstance(v, (str, str)): + if isinstance(v, str): try: v = eval(v) except: @@ -455,7 +455,7 @@ def setEnvObj(self, obj): @return a dict representing the added environment""" if isinstance(obj, collections.Sequence) and \ - not isinstance(obj, (str, str)): + not isinstance(obj, str): obj = self._dictFromSequence(obj) elif not isinstance(obj, collections.Mapping): raise TypeError("obj parameter must be a sequence or a map") @@ -481,7 +481,7 @@ def unsetEnv(self, key): :param key: the key for the environment to be unset :return: the sequence of keys which have been removed""" - if isinstance(key, (str, str)): + if isinstance(key, str): key = (key,) self._unsetEnv(key) return key diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 00325f7806..b762e5d199 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -723,7 +723,7 @@ def getMacroFunctionCode(self, macro_name): return self.getMacroFunction(macro_name).function def getMacroInfo(self, macro_names, format='json'): - if isinstance(macro_names, (str, str)): + if isinstance(macro_names, str): macro_names = [macro_names] ret = [] json_codec = CodecFactory().getCodec(format) @@ -1260,7 +1260,7 @@ def _prepareXMLMacro(self, xml_macro, parent_macro=None): def _createMacroObj(self, macro_name_or_meta, pars, init_opts={}): macro_meta = macro_name_or_meta - if isinstance(macro_meta, (str, str)): + if isinstance(macro_meta, str): macro_meta = self.macro_manager.getMacro(macro_meta) macro_opts = { diff --git a/src/sardana/macroserver/msmetamacro.py b/src/sardana/macroserver/msmetamacro.py index 3229429a63..9128358823 100644 --- a/src/sardana/macroserver/msmetamacro.py +++ b/src/sardana/macroserver/msmetamacro.py @@ -162,7 +162,7 @@ def _build_parameter(self, param_def): if isinstance(t, ParamRepeat): t = t.obj() - if isinstance(t, collections.Sequence) and not isinstance(t, (str, str)): + if isinstance(t, collections.Sequence) and not isinstance(t, str): if isinstance(t[-1], collections.Mapping): ret_p.update(t[-1]) t = self._build_parameter(t[:-1]) diff --git a/src/sardana/macroserver/recorders/output.py b/src/sardana/macroserver/recorders/output.py index 45106cf977..b6d1e8bb58 100644 --- a/src/sardana/macroserver/recorders/output.py +++ b/src/sardana/macroserver/recorders/output.py @@ -138,7 +138,7 @@ def __init__(self, stream, cols=None, number_fmt='%8.4f', col_width=8, self._col_sep = col_sep self._col_width = col_width if isinstance(cols, collections.Sequence) and \ - not isinstance(cols, (str, str)): + not isinstance(cols, str): cols = CaselessList(cols) elif isinstance(cols, numbers.Number): cols = cols @@ -258,7 +258,7 @@ def _writeRecord(self, record): cell = str(cell_data.shape) elif cell_data is None: cell = "" - elif isinstance(cell_data, (str, str)): + elif isinstance(cell_data, str): # TODO: for SEP2 needs strings are enabled for visualizing # value refs, previously "" was printed. This may # have side effects e.g. alignment of columns etc.. Check diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 2b295d269d..871557233e 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -302,7 +302,7 @@ def __init__(self, macro, generator=None, moveables=[], env={}, macro.info("ActiveMntGrp not defined. Using %s", mnt_grp) macro.setEnv('ActiveMntGrp', mnt_grp.getName()) else: - if not isinstance(mnt_grp_name, (str, str)): + if not isinstance(mnt_grp_name, str): t = type(mnt_grp_name).__name__ raise TypeError("ActiveMntGrp MUST be string. It is '%s'" % t) @@ -492,7 +492,7 @@ def _getFileRecorders(self): 'ScanDir ") to enable it') return () - if not isinstance(scan_dir, (str, str)): + if not isinstance(scan_dir, str): scan_dir_t = type(scan_dir).__name__ raise TypeError("ScanDir MUST be string. It is '%s'" % scan_dir_t) @@ -514,14 +514,14 @@ def _getFileRecorders(self): except UnknownEnv: pass - if isinstance(file_names, (str, str)): + if isinstance(file_names, str): file_names = (file_names,) elif not isinstance(file_names, collections.Sequence): scan_file_t = type(file_names).__name__ raise TypeError("ScanFile MUST be string or sequence of strings." " It is '%s'" % scan_file_t) - if isinstance(scan_recorders, (str, str)): + if isinstance(scan_recorders, str): scan_recorders = (scan_recorders,) elif not isinstance(scan_recorders, collections.Sequence): scan_recorders_t = type(scan_recorders).__name__ @@ -971,7 +971,7 @@ def end(self): scan_history = [] scan_file = env['ScanFile'] - if isinstance(scan_file, (str, str)): + if isinstance(scan_file, str): scan_file = scan_file, names = [col.name for col in env['datadesc']] diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index 78fba36cd7..2474fd9d72 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -192,7 +192,7 @@ def from_dtype_str(dtype): dformat = DataFormat.Scalar if dtype is None: dtype = 'float' - elif isinstance(dtype, (str, str)): + elif isinstance(dtype, str): dtype = dtype.lower() if dtype.startswith("pytango."): dtype = dtype[len("pytango."):] @@ -214,7 +214,7 @@ def from_access_str(access): :type dtype: :obj:`str` :return: a simple string for the given access :rtype: :obj:`str`""" - if isinstance(access, (str, str)): + if isinstance(access, str): access = access.lower() if access.startswith("pytango."): access = access[len("pytango."):] @@ -233,7 +233,7 @@ def to_dtype_dformat(data): """ import operator dtype, dformat = data, DataFormat.Scalar - if isinstance(data, (str, str)): + if isinstance(data, str): dtype, dformat = from_dtype_str(data) elif isinstance(data, collections.Sequence): dformat = DataFormat.OneD @@ -261,7 +261,7 @@ def to_daccess(daccess): :rtype: :obj:`DataAccess`""" if daccess is None: daccess = DataAccess.ReadWrite - elif isinstance(daccess, (str, str)): + elif isinstance(daccess, str): daccess = DACCESS_MAP.get( from_access_str(daccess), DataAccess.ReadWrite) return daccess @@ -449,7 +449,7 @@ def to_daccess(daccess): def __expand(name): direct_expansion, _ = INTERFACES[name] - if isinstance(direct_expansion, (str, str)): + if isinstance(direct_expansion, str): direct_expansion = direct_expansion, exp = set(direct_expansion) for e in direct_expansion: diff --git a/src/sardana/sardanautils.py b/src/sardana/sardanautils.py index f327290839..a326e6e2e1 100644 --- a/src/sardana/sardanautils.py +++ b/src/sardana/sardanautils.py @@ -47,15 +47,6 @@ __DTYPE_MAP = dict(DTYPE_MAP) -__use_unicode = False -try: - str - __use_unicode = True - __str_klasses.append(str) - __DTYPE_MAP[str] = DataType.String -except: - pass - __bool_klasses = [bool] + __int_klasses __str_klasses = tuple(__str_klasses) diff --git a/src/sardana/spock/inputhandler.py b/src/sardana/spock/inputhandler.py index a44ccc5ab0..03dca9f0ae 100644 --- a/src/sardana/spock/inputhandler.py +++ b/src/sardana/spock/inputhandler.py @@ -120,7 +120,7 @@ def init(self, *args, **kwargs): def input(self, input_data=None): # parent process data_type = input_data.get('data_type', 'String') - if isinstance(data_type, (str, str)): + if isinstance(data_type, str): ms = genutils.get_macro_server() interfaces = ms.getInterfaces() if data_type in interfaces: diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 4ef4149639..957124d9d0 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -92,7 +92,7 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): scan_file = scan.get('ScanFile') if scan_dir is None or scan_file is None: continue - if not isinstance(scan_file, (str, str)): + if not isinstance(scan_file, str): scan_files = scan_file scan_file = None for fname in scan_files: @@ -128,7 +128,7 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): print("Cannot plot scan:") print("Scan %d was not saved into a file" % (scan_nb,)) return - if not isinstance(scan_file, (str, str)): + if not isinstance(scan_file, str): scan_file = scan_file[0] break else: @@ -148,7 +148,7 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): local_directories = directory_map[scan_dir] # local_directories may be either a string or a list of strings # the further logic is unified to work on a list, so convert it - if isinstance(local_directories, (str, str)): + if isinstance(local_directories, str): local_directories = [local_directories] locations = local_directories if scan_dir not in locations: @@ -364,7 +364,7 @@ def _runMacro(self, xml, **kwargs): self.writeln("Done!") except PyTango.DevFailed as e: if is_non_str_seq(e.args) and \ - not isinstance(e.args, (str, str)): + not isinstance(e.args, str): reason, desc = e.args[0].reason, e.args[0].desc macro_obj = self.getRunningMacro() if reason == 'MissingParam': diff --git a/src/sardana/tango/core/attributehandler.py b/src/sardana/tango/core/attributehandler.py index 7175e8e140..3517c0769e 100644 --- a/src/sardana/tango/core/attributehandler.py +++ b/src/sardana/tango/core/attributehandler.py @@ -72,7 +72,7 @@ def clearBuffer(self): def appendBuffer(self, d): if isinstance(d, collections.Sequence): - if isinstance(d, (str, str)): + if isinstance(d, str): self._buff.append(d) else: self._buff.extend(d) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 2b057e59e1..e19ac43b22 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -198,7 +198,7 @@ def get(self, cache=False): scan_file = env.get('ScanFile') if scan_file is None: scan_file = [] - elif isinstance(scan_file, (str, str)): + elif isinstance(scan_file, str): scan_file = [scan_file] ret['ScanFile'] = scan_file mnt_grps = macro_server.getElementsOfType("MeasurementGroup") @@ -537,7 +537,7 @@ def preRunMacro(self, obj, parameters): self._clearRunMacro() xml_root = None - if isinstance(obj, (str, str)): + if isinstance(obj, str): if obj.startswith('<') and not parameters: xml_root = etree.fromstring(obj) else: diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py index d9702ccf2b..948df83b27 100644 --- a/src/sardana/taurus/core/tango/sardana/motion.py +++ b/src/sardana/taurus/core/tango/sardana/motion.py @@ -136,7 +136,7 @@ def __init__(self, elements, moveable_srcs, allow_repeat=False, first_elem = elements[0] - if isinstance(first_elem, (str, str)): + if isinstance(first_elem, str): self.init_by_names(elements, moveable_srcs, allow_repeat, allow_unknown) else: diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 74ffecae59..8030402c26 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1363,7 +1363,7 @@ def getChannelConfigs(mgconfig, ctrls=None, sort=True): class MGConfiguration(object): def __init__(self, mg, data): self._mg = weakref.ref(mg) - if isinstance(data, (str, str)): + if isinstance(data, str): data = CodecFactory().decode(('json', data), ensure_ascii=True) self.raw_data = data self.__dict__.update(data) @@ -2315,7 +2315,7 @@ def getElementWithInterface(self, elem_name, interface): def getObj(self, name, elem_type=None): if elem_type is None: return self.getElementInfo(name) - elif isinstance(elem_type, (str, str)): + elif isinstance(elem_type, str): elem_types = elem_type, else: elem_types = elem_type @@ -2345,7 +2345,7 @@ def getMoveable(self, names): 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, str)): + if isinstance(names, str): names = names, if len(names) == 1: diff --git a/src/sardana/taurus/core/tango/sardana/sardana.py b/src/sardana/taurus/core/tango/sardana/sardana.py index fc8586aa8e..0edebf445f 100644 --- a/src/sardana/taurus/core/tango/sardana/sardana.py +++ b/src/sardana/taurus/core/tango/sardana/sardana.py @@ -111,7 +111,7 @@ def getType(self): def getTypes(self): elem_types = self.type - if isinstance(elem_types, (str, str)): + if isinstance(elem_types, str): return [elem_types] return elem_types diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index 0afc3035b8..1b767fb3af 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -89,7 +89,7 @@ def createChannelDict(channel, index=None, **kwargs): import PyTango import numpy - if isinstance(channel, (str, str)): + if isinstance(channel, str): #@fixme: to make things uglier, I lazily assume Tango attribute namin dev_name, attr_name = channel.rsplit('/', 1) name = attr_name From 62000f63ad034e0b5c033f37acd13f25d4cd21b3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 18:05:21 +0200 Subject: [PATCH 086/830] 2to3: xrange --- src/sardana/macroserver/macros/examples/hooks.py | 2 +- src/sardana/macroserver/macros/examples/scans.py | 16 ++++++++-------- .../macros/examples/specific_experiments.py | 2 +- .../macroserver/macros/examples/submacros.py | 2 +- src/sardana/macroserver/macros/scan.py | 6 +++--- src/sardana/macroserver/scan/gscan.py | 4 ++-- src/sardana/macroserver/scan/test/helper.py | 2 +- .../pool/poolcontrollers/DummyTwoDController.py | 2 +- .../pool/poolcontrollers/DummyZeroDController.py | 2 +- src/sardana/pool/test/test_measurementgroup.py | 2 +- src/sardana/spock/spockms.py | 2 +- .../taurus/core/tango/sardana/macroserver.py | 2 +- src/sardana/tools/config/pexpect23.py | 4 ++-- src/sardana/tools/config/sardana.py | 12 ++++++------ src/sardana/util/deepreload.py | 2 +- src/sardana/util/funcgenerator.py | 4 ++-- src/sardana/util/test/test_funcgenerator.py | 6 +++--- 17 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/sardana/macroserver/macros/examples/hooks.py b/src/sardana/macroserver/macros/examples/hooks.py index dcfc176144..83dca9fd5f 100644 --- a/src/sardana/macroserver/macros/examples/hooks.py +++ b/src/sardana/macroserver/macros/examples/hooks.py @@ -44,7 +44,7 @@ class loop(Macro, Hookable): def run(self, start, stop, step): self.info("Starting loop") - for i in xrange(start, stop, step): + for i in range(start, stop, step): self.output("At step %d" % i) self.flushOutput() for hook, hints in self.hooks: diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index a263240ad5..63f4e45203 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -84,7 +84,7 @@ def _generator(self): step = {} # integ_time is the same for all steps step["integ_time"] = self.integ_time - for point_no in xrange(self.nr_points): + for point_no in range(self.nr_points): step["positions"] = self.start + point_no * \ self.interv_size # note that this is a numpy array step["point_id"] = point_no @@ -160,10 +160,10 @@ def _generator(self): step["check_func"] = [] extrainfo = {"repetition": 0} # !!! step['extrainfo'] = extrainfo # !!! - for point_no in xrange(self.nr_points): + for point_no in range(self.nr_points): step["positions"] = self.starts + point_no * self.interv_sizes step["point_id"] = point_no - for i in xrange(self.repeat): + for i in range(self.repeat): extrainfo["repetition"] = i # !!! yield step @@ -245,18 +245,18 @@ def _generator(self): step["check_func"] = [] extrainfo = {"cycle": None, "interval": None, "sample": None, } step['extrainfo'] = extrainfo - halfcycle1 = range(self.nr_interv + 1) + halfcycle1 = list(range(self.nr_interv + 1)) halfcycle2 = halfcycle1[1:-1] halfcycle2.reverse() intervallist = halfcycle1 + halfcycle2 point_no = 0 - for cycle in xrange(self.nr_cycles): + for cycle in range(self.nr_cycles): extrainfo["cycle"] = cycle for interval in intervallist: extrainfo["interval"] = interval step["positions"] = numpy.array( [self.start_pos + (interval) * self.interv_size], dtype='d') - for sample in xrange(self.nr_samples): + for sample in range(self.nr_samples): extrainfo["sample"] = sample step["point_id"] = point_no yield step @@ -265,7 +265,7 @@ def _generator(self): # last step for closing the loop extrainfo["interval"] = 0 step["positions"] = numpy.array([self.start_pos], dtype='d') - for sample in xrange(self.nr_samples): + for sample in range(self.nr_samples): extrainfo["sample"] = sample step["point_id"] = point_no yield step @@ -616,7 +616,7 @@ def run(self, motor, start_pos, final_pos, nr_interv, integ_time, **opts): # "//custom_data") if none given dh.addCustomData('Hello world1', 'dummyChar1') # you can pass arrays (but not all recorders will handle them) - dh.addCustomData(range(10), 'dummyArray1') + dh.addCustomData(list(range(10)), 'dummyArray1') # you can pass a custom nxpath *relative* to the current entry dh.addCustomData('Hello world2', 'dummyChar2', nxpath='sample:NXsample') diff --git a/src/sardana/macroserver/macros/examples/specific_experiments.py b/src/sardana/macroserver/macros/examples/specific_experiments.py index b420e0875a..80fb4259b5 100644 --- a/src/sardana/macroserver/macros/examples/specific_experiments.py +++ b/src/sardana/macroserver/macros/examples/specific_experiments.py @@ -108,7 +108,7 @@ def _generator(self): "post-acq-hooks"] = self.getHooks('post-acq') + self.getHooks('_NOHINT_') step["post-step-hooks"] = self.getHooks('post-step') step["check_func"] = [] - for point_no in xrange(self.nr_points): + for point_no in range(self.nr_points): step["positions"] = self.starts + point_no * self.interv_sizes step["point_id"] = point_no yield step diff --git a/src/sardana/macroserver/macros/examples/submacros.py b/src/sardana/macroserver/macros/examples/submacros.py index d447c7070c..9f6eb62411 100644 --- a/src/sardana/macroserver/macros/examples/submacros.py +++ b/src/sardana/macroserver/macros/examples/submacros.py @@ -184,7 +184,7 @@ def run(self, mot): data = dscan.data len_data = len(data) - for point_nb in xrange(len_data): + for point_nb in range(len_data): position = data[point_nb].data[mot.getName()] positions.append(position) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index b6122d50ab..d99560a6c5 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -224,7 +224,7 @@ def _stepGenerator(self): step["post-step-hooks"] = self.getHooks('post-step') step["check_func"] = [] - for point_no in xrange(self.nr_points): + for point_no in range(self.nr_points): step["positions"] = self.starts + point_no * self.interv_sizes step["point_id"] = point_no yield step @@ -235,7 +235,7 @@ def _waypoint_generator(self): step["post-move-hooks"] = self.getHooks('post-move') step["check_func"] = [] step["slow_down"] = self.slow_down - for point_no in xrange(self.nr_waypoints): + for point_no in range(self.nr_waypoints): step["positions"] = self.starts + point_no * self.way_lengths step["waypoint_id"] = point_no yield step @@ -946,7 +946,7 @@ def _generator(self): step["post-step-hooks"] = self.getHooks('post-step') step["check_func"] = [] - for i in xrange(self.nr_points): + for i in range(self.nr_points): step["positions"] = self.paths[:, i] step["integ_time"] = self.integ_time[i] step["point_id"] = i diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 871557233e..b25c7bc396 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2063,7 +2063,7 @@ def generate_timestamps(synchronization, initial_timestamp=0): timestamp += delay ret[index] = dict(timestamp=timestamp) index += 1 - for _ in xrange(1, repeats): + for _ in range(1, repeats): timestamp += total ret[index] = dict(timestamp=timestamp) index += 1 @@ -2085,7 +2085,7 @@ def generate_positions(motors, starts, finals, nr_points): # convert to numpy array for easier handling table = np.array(zip(*moveable_positions), dtype=dtype_spec) n_rows = table.shape[0] - for i in xrange(n_rows): + for i in range(n_rows): row = dict() for label in table.dtype.names: row[label] = table[label][i] diff --git a/src/sardana/macroserver/scan/test/helper.py b/src/sardana/macroserver/scan/test/helper.py index 19c2357a2b..d3bdefb8f7 100644 --- a/src/sardana/macroserver/scan/test/helper.py +++ b/src/sardana/macroserver/scan/test/helper.py @@ -36,7 +36,7 @@ def run(self): i = 0 for v, t in zip(self.values, self.intervals): try: - idx = range(i, i + len(v)) + idx = list(range(i, i + len(v))) i += len(v) skip = float('NaN') in v except TypeError: # if v is not a list diff --git a/src/sardana/pool/poolcontrollers/DummyTwoDController.py b/src/sardana/pool/poolcontrollers/DummyTwoDController.py index 0c78168cd7..043d757aed 100644 --- a/src/sardana/pool/poolcontrollers/DummyTwoDController.py +++ b/src/sardana/pool/poolcontrollers/DummyTwoDController.py @@ -522,7 +522,7 @@ def _updateChannelValue(self, axis, elapsed_time): channel.buffer_values.extend([img] * nb_new_acq) if channel.value_ref_enabled: start = self.start_idx * self.repetitions + channel.acq_idx - for img_idx in xrange(start, start + nb_new_acq): + for img_idx in range(start, start + nb_new_acq): value_ref_pattern = channel.value_ref_pattern scheme, path, dataset_name, msg = generate_ref( value_ref_pattern, img_idx) diff --git a/src/sardana/pool/poolcontrollers/DummyZeroDController.py b/src/sardana/pool/poolcontrollers/DummyZeroDController.py index 656e588e16..86107e115b 100644 --- a/src/sardana/pool/poolcontrollers/DummyZeroDController.py +++ b/src/sardana/pool/poolcontrollers/DummyZeroDController.py @@ -47,7 +47,7 @@ class DummyZeroDController(ZeroDController): def __init__(self, inst, props, *args, **kwargs): ZeroDController.__init__(self, inst, props, *args, **kwargs) - self.channels = [Channel(i + 1) for i in xrange(self.MaxDevice)] + self.channels = [Channel(i + 1) for i in range(self.MaxDevice)] self.read_channels = {} def AddDevice(self, ind): diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index 4734e248c8..e1fa769714 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -85,7 +85,7 @@ def acq_asserts(self, channel_names, repetitions): header = table.dtype.names print(header) n_rows = table.shape[0] - for row in xrange(n_rows): + for row in range(n_rows): print(row, table[row]) # checking if any of data was acquired self.assertTrue(self.attr_listener.data, 'no data were acquired') diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 957124d9d0..5d99060099 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -270,7 +270,7 @@ def plot(self): colors = 'bgrcmyk' col_nb = min(col_nb, len(colors) + 3) # skip point_nb, motor and timer columns - for i in xrange(3, col_nb): + for i in range(3, col_nb): y = m[i][:rows] line, = pylab.plot(x, y, label=labels[i]) line.linestyle = '-' diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index e19ac43b22..dfe6b8eddf 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -724,7 +724,7 @@ def logReceived(self, log_name, output): if not self._debug: if line == self.BlockStart: self._in_block = True - for i in xrange(self._block_lines): + for i in range(self._block_lines): if max_chrs == float('inf'): nb_lines = 1 else: diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 52153483ab..30042f0d23 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -1603,7 +1603,7 @@ def __init__(self, strings): self.eof_index = -1 self.timeout_index = -1 self._strings = [] - for n, s in zip(range(len(strings)), strings): + for n, s in zip(list(range(len(strings))), strings): if s is EOF: self.eof_index = n continue @@ -1700,7 +1700,7 @@ def __init__(self, patterns): self.eof_index = -1 self.timeout_index = -1 self._searches = [] - for n, s in zip(range(len(patterns)), patterns): + for n, s in zip(list(range(len(patterns))), patterns): if s is EOF: self.eof_index = n continue diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index 5bfcdfe871..eb88b77d50 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -1554,7 +1554,7 @@ def _preprocess(self): pSimAttributes.set("name", "DynamicAttributes") pSimAttributes.set("type", "DevVarStringArray") simAttrTempl = "zerod%03d=float(100.0+10.0*random())" - for i in xrange(len(zerods)): + for i in range(len(zerods)): pSimAttributeItem = etree.SubElement( pSimAttributes, "Item") pSimAttributeItem.text = simAttrTempl % (i + 1) @@ -1568,7 +1568,7 @@ def _preprocess(self): attributeNames = etree.SubElement(ctrl, "Property") attributeNames.set("name", "AttributeNames") simAttrTempl = "%s/zerod%%03d" % simu0DCtrlName - for i in xrange(len(zerods)): + for i in range(len(zerods)): attributeNameItem = etree.SubElement( attributeNames, "Item") attributeNameItem.text = simAttrTempl % (i + 1) @@ -1611,7 +1611,7 @@ def _preprocess(self): pSimAttributes.set("type", "DevVarStringArray") simAttrTempl = "oned%03d=DevVarLongArray([10*sin(0.01*x) for x in xrange(100)])" - for i in xrange(len(oneds)): + for i in range(len(oneds)): pSimAttributeItem = etree.SubElement( pSimAttributes, "Item") pSimAttributeItem.text = simAttrTempl % (i + 1) @@ -1627,7 +1627,7 @@ def _preprocess(self): attributeNamesV = etree.SubElement( attributeNames, "Item") simAttrTempl = "%s/oned%%03d" % simu1DCtrlName - for i in xrange(len(oneds)): + for i in range(len(oneds)): attributeNameItem = etree.SubElement( attributeNames, "Item") attributeNameItem.text = simAttrTempl % (i + 1) @@ -1670,7 +1670,7 @@ def _preprocess(self): pSimAttributes.set("type", "DevVarStringArray") simAttrTempl = "ior%03d=int(READ and VAR('ior%03d') or WRITE and VAR('ior%03d',VALUE))" - for i in xrange(len(iors)): + for i in range(len(iors)): pSimAttributeItem = etree.SubElement( pSimAttributes, "Item") pSimAttributeItem.text = simAttrTempl % ( @@ -1685,7 +1685,7 @@ def _preprocess(self): attributeNames = etree.SubElement(ctrl, "Property") attributeNames.set("name", "AttributeNames") simAttrTempl = "%s/ior%%03d" % simuIOCtrlName - for i in xrange(len(iors)): + for i in range(len(iors)): attributeNameItem = etree.SubElement( attributeNames, "Item") attributeNameItem.text = simAttrTempl % (i + 1) diff --git a/src/sardana/util/deepreload.py b/src/sardana/util/deepreload.py index 20f4c58508..c2c84a318e 100644 --- a/src/sardana/util/deepreload.py +++ b/src/sardana/util/deepreload.py @@ -120,7 +120,7 @@ def get_parent(globals, level): globals['__package__'] = name = modname[:lastdot] dot = len(name) - for x in xrange(level, 1, -1): + for x in range(level, 1, -1): try: dot = name.rindex('.', 0, dot) except ValueError: diff --git a/src/sardana/util/funcgenerator.py b/src/sardana/util/funcgenerator.py index f72bac46a7..ff3f771e35 100644 --- a/src/sardana/util/funcgenerator.py +++ b/src/sardana/util/funcgenerator.py @@ -209,7 +209,7 @@ def sleep(self, period): nap = 0 else: nap = period / necessary_naps - for _ in xrange(necessary_naps): + for _ in range(necessary_naps): if self.is_stopped(): break time.sleep(nap) @@ -348,7 +348,7 @@ def set_configuration(self, configuration): total_param = group[Total] total_in_initial_domain = total_param[initial_domain_in_use] total_in_active_domain = total_param[active_domain_in_use] - for _ in xrange(repeats): + for _ in range(repeats): passive_event = active_event_in_active_domain + active active_events.append(active_event_in_initial_domain) passive_events.append(passive_event) diff --git a/src/sardana/util/test/test_funcgenerator.py b/src/sardana/util/test/test_funcgenerator.py index b117fec21d..a221b0444a 100644 --- a/src/sardana/util/test/test_funcgenerator.py +++ b/src/sardana/util/test/test_funcgenerator.py @@ -127,7 +127,7 @@ def test_run_time(self): self.thread_pool.add(self.func_generator.run, self._done) self.event.wait(100) active_event_ids = self.listener.active_event_ids - active_event_ids_ok = range(0, 10) + active_event_ids_ok = list(range(0, 10)) msg = "Received active event ids: %s, expected: %s" % ( active_event_ids, active_event_ids_ok) self.assertListEqual(active_event_ids, active_event_ids_ok, msg) @@ -163,7 +163,7 @@ def test_run_position_negative(self): self.event.wait(3) position.remove_listener(self.func_generator) active_event_ids = self.listener.active_event_ids - active_event_ids_ok = range(0, 10) + active_event_ids_ok = list(range(0, 10)) msg = "Received active event ids: %s, expected: %s" % (active_event_ids, active_event_ids_ok) self.assertListEqual(active_event_ids, active_event_ids_ok, msg) @@ -183,7 +183,7 @@ def test_run_position_positive(self): self.event.wait(3) position.remove_listener(self.func_generator) active_event_ids = self.listener.active_event_ids - active_event_ids_ok = range(0, 10) + active_event_ids_ok = list(range(0, 10)) msg = "Received active event ids: %s, expected: %s" % (active_event_ids, active_event_ids_ok) self.assertListEqual(active_event_ids, active_event_ids_ok, msg) From eb56b44bc7ee20fc9fb8305198b64871a3920962 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 18:07:52 +0200 Subject: [PATCH 087/830] 2to3: zip --- src/sardana/macroserver/macros/discrete.py | 2 +- src/sardana/macroserver/macros/scan.py | 2 +- src/sardana/macroserver/scan/gscan.py | 2 +- src/sardana/macroserver/scan/recorder/storage.py | 4 ++-- src/sardana/pool/test/util.py | 2 +- src/sardana/tango/core/util.py | 2 +- src/sardana/taurus/core/tango/sardana/macroserver.py | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sardana/macroserver/macros/discrete.py b/src/sardana/macroserver/macros/discrete.py index e35ad627c1..f8544f9336 100644 --- a/src/sardana/macroserver/macros/discrete.py +++ b/src/sardana/macroserver/macros/discrete.py @@ -196,7 +196,7 @@ def run(self, pseudo): # Sort by position column value_list = sorted(value_list, key=lambda x: x[1]) # Transpose matrix - value_list = list(map(list, zip(*value_list))) + value_list = list(map(list, list(zip(*value_list)))) # Extract sorted row headers row_head_str = value_list[0] # Extract sorted values diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index d99560a6c5..85212a96e7 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -876,7 +876,7 @@ def prepare(self, *args, **opts): self.motors = [item[0] for item in args[2]] self.funcstrings = [item[1] for item in args[2]] - globals_lst = [dict(zip(indepvars, values)) + globals_lst = [dict(list(zip(indepvars, values))) for values in zip(*list(indepvars.values()))] self.paths = [[SafeEvaluator(globals).eval( func) for globals in globals_lst] for func in self.funcstrings] diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index b25c7bc396..a38d216a26 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2083,7 +2083,7 @@ def generate_positions(motors, starts, finals, nr_points): label = motor.getName() dtype_spec.append((label, 'float64')) # convert to numpy array for easier handling - table = np.array(zip(*moveable_positions), dtype=dtype_spec) + table = np.array(list(zip(*moveable_positions)), dtype=dtype_spec) n_rows = table.shape[0] for i in range(n_rows): row = dict() diff --git a/src/sardana/macroserver/scan/recorder/storage.py b/src/sardana/macroserver/scan/recorder/storage.py index ce61d847ca..7a6c818c7c 100644 --- a/src/sardana/macroserver/scan/recorder/storage.py +++ b/src/sardana/macroserver/scan/recorder/storage.py @@ -104,8 +104,8 @@ def setFileName(self, filename): # obtain preferred nexus file mode for writing from the filename # extension (defaults to hdf5) extension = os.path.splitext(filename)[1] - inv_formats = dict(zip( - iter(self.formats.values()), iter(self.formats.keys()))) + inv_formats = dict(list(zip( + iter(self.formats.values()), iter(self.formats.keys())))) self.nxfilemode = inv_formats.get(extension.lower(), 'w5') self.currentlist = None diff --git a/src/sardana/pool/test/util.py b/src/sardana/pool/test/util.py index 5cb2b77592..301cf28d55 100644 --- a/src/sardana/pool/test/util.py +++ b/src/sardana/pool/test/util.py @@ -73,5 +73,5 @@ def get_table(self): v.extend([None] * (max_len - len(v))) table.append(v) dtype_spec.append((k, self.dtype)) - a = numpy.array(zip(*table), dtype=dtype_spec) + a = numpy.array(list(zip(*table)), dtype=dtype_spec) return a diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 49693a3d20..e24d15c39d 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -915,7 +915,7 @@ def get_dev_from_class_server(db, classname, server): def pairwise(iterable): "s -> (s0, s1), (s2, s3), (s4, s5), ..." a = iter(iterable) - return zip(a, a) + return list(zip(a, a)) devices = [] device_class_list = db.get_device_class_list(server) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index dfe6b8eddf..723a32eaaf 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -256,7 +256,7 @@ def set(self, conf, mnt_grps=None): except Exception: # if the mnt_grp did not already exist, create it now chconfigs = getChannelConfigs(mnt_grp_cfg) - chnames, chinfos = zip(*chconfigs) # unzipping + chnames, chinfos = list(zip(*chconfigs)) # unzipping # We assume that all the channels belong to the same # pool! pool = self._getPoolOfElement(chnames[0]) From 3a00a88eca08df16ed70d7db180bd228b036dc3a Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 18:09:26 +0200 Subject: [PATCH 088/830] 2to3: future --- src/sardana/macroserver/macro.py | 4 ++-- src/sardana/macroserver/macros/demo.py | 2 +- src/sardana/macroserver/macros/examples/funcs.py | 2 +- src/sardana/macroserver/macros/expert.py | 2 +- src/sardana/macroserver/macroserver.py | 2 +- src/sardana/macroserver/msoptions.py | 4 ++-- src/sardana/pool/pool.py | 4 ++-- src/sardana/requirements.py | 2 +- src/sardana/sardanaattribute.py | 2 +- src/sardana/sardanabase.py | 2 +- src/sardana/sardanabuffer.py | 2 +- src/sardana/sardanacontainer.py | 2 +- src/sardana/sardanadefs.py | 2 +- src/sardana/sardanaevent.py | 2 +- src/sardana/sardanaexception.py | 2 +- src/sardana/sardanalock.py | 2 +- src/sardana/sardanamanager.py | 2 +- src/sardana/sardanameta.py | 2 +- src/sardana/sardanamodulemanager.py | 4 ++-- src/sardana/sardanathreadpool.py | 4 ++-- src/sardana/sardanautils.py | 2 +- src/sardana/sardanavalue.py | 2 +- src/sardana/tango/core/SardanaDevice.py | 2 +- src/sardana/taurus/core/tango/sardana/macro.py | 4 ++-- src/sardana/taurus/core/tango/sardana/pool.py | 2 +- src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py | 2 +- 26 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 6167aedb92..c2a8ffd9f5 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -26,8 +26,8 @@ """This module contains the class definition for the MacroServer generic scan""" -from __future__ import with_statement -from __future__ import print_function + + import collections import numbers diff --git a/src/sardana/macroserver/macros/demo.py b/src/sardana/macroserver/macros/demo.py index 172f0e0256..f58f101159 100644 --- a/src/sardana/macroserver/macros/demo.py +++ b/src/sardana/macroserver/macros/demo.py @@ -23,7 +23,7 @@ """This is the demo macro module""" -from __future__ import print_function + __all__ = ["sar_demo", "clear_sar_demo", "sar_demo_hkl", "clear_sar_demo_hkl"] diff --git a/src/sardana/macroserver/macros/examples/funcs.py b/src/sardana/macroserver/macros/examples/funcs.py index 2d3d69fecc..375920097c 100644 --- a/src/sardana/macroserver/macros/examples/funcs.py +++ b/src/sardana/macroserver/macros/examples/funcs.py @@ -23,7 +23,7 @@ """Examples of macro functions""" -from __future__ import print_function + __all__ = ["mfunc1", "mfunc2", "mfunc3", "mfunc4", "mfunc5"] diff --git a/src/sardana/macroserver/macros/expert.py b/src/sardana/macroserver/macros/expert.py index 880bd248f7..36c6b52630 100644 --- a/src/sardana/macroserver/macros/expert.py +++ b/src/sardana/macroserver/macros/expert.py @@ -23,7 +23,7 @@ """Expert macros""" -from __future__ import print_function + __all__ = ["addctrllib", "addmaclib", "commit_ctrllib", "defctrl", "defelem", "defm", "defmeas", "edctrlcls", "edctrllib", "prdef", diff --git a/src/sardana/macroserver/macroserver.py b/src/sardana/macroserver/macroserver.py index 742b014ff9..721e9a9c38 100644 --- a/src/sardana/macroserver/macroserver.py +++ b/src/sardana/macroserver/macroserver.py @@ -23,7 +23,7 @@ ## ############################################################################## -from __future__ import with_statement + import os import re diff --git a/src/sardana/macroserver/msoptions.py b/src/sardana/macroserver/msoptions.py index 9b90dca990..b73b242bf4 100644 --- a/src/sardana/macroserver/msoptions.py +++ b/src/sardana/macroserver/msoptions.py @@ -25,8 +25,8 @@ """This module contains a definition for ViewOptions""" -from __future__ import with_statement -from __future__ import print_function + + __all__ = ['ViewOption'] diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py index ebdd86ffd2..b6bde77976 100644 --- a/src/sardana/pool/pool.py +++ b/src/sardana/pool/pool.py @@ -25,8 +25,8 @@ """This module contains the main pool class""" -from __future__ import print_function -from __future__ import with_statement + + __all__ = ["Pool"] diff --git a/src/sardana/requirements.py b/src/sardana/requirements.py index 87c759d6c3..12b3e361b5 100644 --- a/src/sardana/requirements.py +++ b/src/sardana/requirements.py @@ -25,7 +25,7 @@ """ """ -from __future__ import absolute_import + __docformat__ = 'restructuredtext' diff --git a/src/sardana/sardanaattribute.py b/src/sardana/sardanaattribute.py index c1cca95693..c980786b51 100644 --- a/src/sardana/sardanaattribute.py +++ b/src/sardana/sardanaattribute.py @@ -26,7 +26,7 @@ """This module is part of the Python Sardana libray. It defines the base classes for Sardana attributes""" -from __future__ import absolute_import + __all__ = ["SardanaAttribute", "SardanaSoftwareAttribute", "ScalarNumberAttribute", "SardanaAttributeConfiguration"] diff --git a/src/sardana/sardanabase.py b/src/sardana/sardanabase.py index 9a78e73391..1d5fc7c223 100644 --- a/src/sardana/sardanabase.py +++ b/src/sardana/sardanabase.py @@ -26,7 +26,7 @@ """This module is part of the Python Sardana library. It defines the base classes for Sardana object""" -from __future__ import absolute_import + import sys __all__ = ["SardanaBaseObject", "SardanaObjectID"] diff --git a/src/sardana/sardanabuffer.py b/src/sardana/sardanabuffer.py index 31e7585566..c481441e65 100644 --- a/src/sardana/sardanabuffer.py +++ b/src/sardana/sardanabuffer.py @@ -26,7 +26,7 @@ """This module is part Sardana Python library. It defines the base clases for Sardana buffers""" -from __future__ import absolute_import + __all__ = ["SardanaBuffer", "LateValueException", "EarlyValueException"] diff --git a/src/sardana/sardanacontainer.py b/src/sardana/sardanacontainer.py index 14354e6378..5248ca0f7d 100644 --- a/src/sardana/sardanacontainer.py +++ b/src/sardana/sardanacontainer.py @@ -26,7 +26,7 @@ """This module is part of the Python Pool libray. It defines the base classes for a pool container element""" -from __future__ import absolute_import + __all__ = ["SardanaContainer"] diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index 2474fd9d72..385db4dcdd 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -25,7 +25,7 @@ """This module contains the most generic sardana constants and enumerations""" -from __future__ import absolute_import + import collections __all__ = ["EpsilonError", "SardanaServer", "ServerRunMode", "State", diff --git a/src/sardana/sardanaevent.py b/src/sardana/sardanaevent.py index b9e6f7991b..1348affdb8 100644 --- a/src/sardana/sardanaevent.py +++ b/src/sardana/sardanaevent.py @@ -26,7 +26,7 @@ """This module is part of the Python Pool libray. It defines the base classes for pool event mechanism""" -from __future__ import absolute_import + __all__ = ["EventGenerator", "EventReceiver", "EventType"] diff --git a/src/sardana/sardanaexception.py b/src/sardana/sardanaexception.py index 9bef60311c..cbcf6fa0e2 100644 --- a/src/sardana/sardanaexception.py +++ b/src/sardana/sardanaexception.py @@ -26,7 +26,7 @@ """This module is part of the Python Sardana libray. It defines the base classes for sardana exceptions""" -from __future__ import absolute_import + __all__ = ["AbortException", "SardanaException", "SardanaExceptionList", "UnknownCode", "UnknownLibrary", "LibraryError", diff --git a/src/sardana/sardanalock.py b/src/sardana/sardanalock.py index 02690129f9..94dd3d940a 100644 --- a/src/sardana/sardanalock.py +++ b/src/sardana/sardanalock.py @@ -26,7 +26,7 @@ """This module is part of the Python Sardana libray. It defines a *slow* lock class that provides additional debugging information""" -from __future__ import absolute_import + __all__ = ["SardanaLock"] diff --git a/src/sardana/sardanamanager.py b/src/sardana/sardanamanager.py index 96e749b3be..15932ef4b9 100644 --- a/src/sardana/sardanamanager.py +++ b/src/sardana/sardanamanager.py @@ -26,7 +26,7 @@ """This module is part of the Python Sardana libray. It defines the base class for Sardana manager""" -from __future__ import absolute_import + __all__ = ["SardanaElementManager", "SardanaIDManager"] diff --git a/src/sardana/sardanameta.py b/src/sardana/sardanameta.py index 9dab4333d0..23611fc91b 100644 --- a/src/sardana/sardanameta.py +++ b/src/sardana/sardanameta.py @@ -26,7 +26,7 @@ """This module is part of the Python Sardana libray. It defines the base classes for MetaLibrary and MetaClass""" -from __future__ import absolute_import + __all__ = ["SardanaLibrary", "SardanaClass", "SardanaFunction"] diff --git a/src/sardana/sardanamodulemanager.py b/src/sardana/sardanamodulemanager.py index 8bba63a69f..680cd7e494 100644 --- a/src/sardana/sardanamodulemanager.py +++ b/src/sardana/sardanamodulemanager.py @@ -26,8 +26,8 @@ """This module is part of the Python Sardana library. It defines the base classes for module manager""" -from __future__ import with_statement -from __future__ import absolute_import + + __all__ = ["ModuleManager"] diff --git a/src/sardana/sardanathreadpool.py b/src/sardana/sardanathreadpool.py index 66faf4e29e..4950d00550 100644 --- a/src/sardana/sardanathreadpool.py +++ b/src/sardana/sardanathreadpool.py @@ -25,8 +25,8 @@ """This module contains the function to access sardana thread pool""" -from __future__ import with_statement -from __future__ import absolute_import + + __all__ = ["get_thread_pool"] diff --git a/src/sardana/sardanautils.py b/src/sardana/sardanautils.py index a326e6e2e1..9586db2a8a 100644 --- a/src/sardana/sardanautils.py +++ b/src/sardana/sardanautils.py @@ -26,7 +26,7 @@ """This module is part of the Python Sardana library. It defines some utility methods""" -from __future__ import absolute_import + __all__ = ["is_pure_str", "is_non_str_seq", "is_integer", "is_number", "is_bool", "check_type", "assert_type", "str_to_value", diff --git a/src/sardana/sardanavalue.py b/src/sardana/sardanavalue.py index f002c88ce3..449706fe7a 100644 --- a/src/sardana/sardanavalue.py +++ b/src/sardana/sardanavalue.py @@ -26,7 +26,7 @@ """This module is part of the Python Sardana libray. It defines the base classes for Sardana values""" -from __future__ import absolute_import + __all__ = ["SardanaValue"] diff --git a/src/sardana/tango/core/SardanaDevice.py b/src/sardana/tango/core/SardanaDevice.py index 93cd40e7b2..9044a447d5 100644 --- a/src/sardana/tango/core/SardanaDevice.py +++ b/src/sardana/tango/core/SardanaDevice.py @@ -25,7 +25,7 @@ """Generic Sardana Tango device module""" -from __future__ import with_statement + __all__ = ["SardanaDevice", "SardanaDeviceClass"] diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 278658a1db..624483c920 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from __future__ import absolute_import + ############################################################################## ## # This file is part of Sardana @@ -24,7 +24,7 @@ ############################################################################## """The macro submodule.""" -from __future__ import absolute_import + __all__ = ["MacroInfo", "Macro", "MacroNode", "ParamFactory", "MacroRunException"] diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 8030402c26..05ed3c670d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -26,7 +26,7 @@ """The device pool submodule. It contains specific part of sardana device pool""" -from __future__ import absolute_import + import collections __all__ = ["InterruptException", "StopException", "AbortException", diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index cc18a5b042..b9eec33f33 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -35,7 +35,7 @@ `taurus.qt.qtgui.taurusgui.macrolistener` """ -from __future__ import print_function + from builtins import object From 810cf70d775368c3da02ad4df570b4f9468e9024 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 23:00:41 +0200 Subject: [PATCH 089/830] py3: fix sardana.Release Class's __dict__ (mapping proxy) can not be updated in py3. Use setattr instead of update. --- src/sardana/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sardana/__init__.py b/src/sardana/__init__.py index 732e499128..a355e5fa36 100644 --- a/src/sardana/__init__.py +++ b/src/sardana/__init__.py @@ -35,7 +35,9 @@ class Release: pass -Release.__dict__.update(__release.__dict__) + +for attr, value in __release.__dict__.items(): + setattr(Release, attr, value) Release.__doc__ = __release.__doc__ from .sardanadefs import * From 137ee136241eff792e788e605e81ba5fa7c5bba7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 23:03:10 +0200 Subject: [PATCH 090/830] py3: Fix mistake of 2to3 types fixer d5a0b31d13 did not work correctly. types.StringTypes is a tuple (str, unicode) and is now converted to simple str. Comparison of type(foo) in str does not work. Fix it. --- src/sardana/macroserver/macro.py | 2 +- src/sardana/pool/poolcontrollermanager.py | 2 +- src/sardana/pool/poolmetacontroller.py | 2 +- src/sardana/taurus/core/tango/sardana/macro.py | 6 +++--- src/sardana/tools/config/fods_to_sar.py | 2 +- src/sardana/tools/config/pexpect23.py | 10 +++++----- src/sardana/tools/config/sar_to_fods.py | 2 +- src/sardana/tools/config/sardana.py | 2 +- src/sardana/tools/config/to_sar.py | 2 +- src/sardana/tools/config/xls_to_sar.py | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index c2a8ffd9f5..174f86b56f 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -1330,7 +1330,7 @@ def execMacro(self, *args, **kwargs): macro_name = None arg0 = args[0] if len(args) == 1: - if type(arg0) in str: + if isinstance(arg0, str): # dealing with sth like args = ('ascan th 0 100 10 1.0',) macro_name = arg0.split()[0] elif isinstance(arg0, collections.Sequence): diff --git a/src/sardana/pool/poolcontrollermanager.py b/src/sardana/pool/poolcontrollermanager.py index a9f972ffdd..a1c25da17c 100644 --- a/src/sardana/pool/poolcontrollermanager.py +++ b/src/sardana/pool/poolcontrollermanager.py @@ -546,7 +546,7 @@ def decodeControllerParameters(self, in_par_list): raise RuntimeError('Controller name not specified') controller_name_or_klass = in_par_list[0] controller_class = controller_name_or_klass - if type(controller_class) in str: + if isinstance(controller_class, str): controller_class = self.getControllerClass(controller_class) if controller_class is None: raise UnknownController("Unknown controller %s" % diff --git a/src/sardana/pool/poolmetacontroller.py b/src/sardana/pool/poolmetacontroller.py index c2ef2be82c..93a5733cd6 100644 --- a/src/sardana/pool/poolmetacontroller.py +++ b/src/sardana/pool/poolmetacontroller.py @@ -206,7 +206,7 @@ def toDataInfo(klass, name, info): fget = info.get(FGet) fset = info.get(FSet) if default_value is not None and dtype != DataType.String: - if type(default_value) in str: + if isinstance(default_value, str): default_value = eval(default_value) return DataInfo(name, dtype, dformat=dformat, access=daccess, description=description, default_value=default_value, diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 624483c920..2ac6932ccc 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -103,13 +103,13 @@ def _isParamComplex(self, p): return not self._isParamAtomic(p) def _isParamAtomic(self, p): - return type(p['type']) in str + return isinstance(p['type'], str) def _buildParameterLine(self, parameters): l = [] for p in parameters: t = p['type'] - if type(t) in str: + if isinstance(t, str): # Simple parameter l.append('<%s>' % p['name']) else: @@ -122,7 +122,7 @@ def _buildParameterDescription(self, parameters): l = [] for p in parameters: t = p['type'] - if type(t) in str: + if isinstance(t, str): # Simple parameter l.append('{name} : ({type}) {description}'.format(**p)) else: diff --git a/src/sardana/tools/config/fods_to_sar.py b/src/sardana/tools/config/fods_to_sar.py index 717b623d0b..5e1b6b92bb 100644 --- a/src/sardana/tools/config/fods_to_sar.py +++ b/src/sardana/tools/config/fods_to_sar.py @@ -21,7 +21,7 @@ def transform(f): xslt_filename = os.path.join(directory, "FODS_TO_SAR.xslt") t = etree.XSLT(etree.parse(xslt_filename)) - if type(f) in str: + if isinstance(f, str): doc = etree.parse(f) else: doc = f diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 30042f0d23..20cad4026e 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -235,16 +235,16 @@ def print_ticks(d): while True: try: index = child.expect(patterns) - if type(child.after) in str: + if isinstance(child.after, str): child_result_list.append(child.before + child.after) else: # child.after may have been a TIMEOUT or EOF, so don't cat those. child_result_list.append(child.before) - if type(responses[index]) in str: + if isinstance(responses[index], str): child.send(responses[index]) elif isinstance(responses[index], types.FunctionType): callback_result = responses[index](locals()) sys.stdout.flush() - if type(callback_result) in str: + if isinstance(callback_result, str): child.send(callback_result) elif callback_result: break @@ -1224,7 +1224,7 @@ def compile_pattern_list(self, patterns): compile_flags = compile_flags | re.IGNORECASE compiled_pattern_list = [] for p in patterns: - if type(p) in str: + if isinstance(p, str): compiled_pattern_list.append(re.compile(p, compile_flags)) elif p is EOF: compiled_pattern_list.append(EOF) @@ -1343,7 +1343,7 @@ def expect_exact(self, pattern_list, timeout=-1, searchwindowsize=-1): This method is also useful when you don't want to have to worry about escaping regular expression characters that you want to match.""" - if type(pattern_list) in str or pattern_list in (TIMEOUT, EOF): + if isinstance(pattern_list, str) or pattern_list in (TIMEOUT, EOF): pattern_list = [pattern_list] return self.expect_loop(searcher_string(pattern_list), timeout, searchwindowsize) diff --git a/src/sardana/tools/config/sar_to_fods.py b/src/sardana/tools/config/sar_to_fods.py index e90d5db141..3dbfc810f2 100644 --- a/src/sardana/tools/config/sar_to_fods.py +++ b/src/sardana/tools/config/sar_to_fods.py @@ -7,7 +7,7 @@ def transform(f): t = etree.XSLT(etree.parse("SAR_TO_FODS.xslt")) - if type(f) in str: + if isinstance(f, str): doc = etree.parse(f) else: doc = f diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index eb88b77d50..92f52f44a9 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -1280,7 +1280,7 @@ class Sardana: def __init__(self, source, simulation="Best", cleanup=True, log=False): - if type(source) in str: + if isinstance(source, str): self._filename = source self._xmldoc = None else: diff --git a/src/sardana/tools/config/to_sar.py b/src/sardana/tools/config/to_sar.py index 2bf2364be9..a5a4c87007 100644 --- a/src/sardana/tools/config/to_sar.py +++ b/src/sardana/tools/config/to_sar.py @@ -8,7 +8,7 @@ def transform(f): - if type(f) in str: + if isinstance(f, str): doc = etree.parse(f) else: doc = f diff --git a/src/sardana/tools/config/xls_to_sar.py b/src/sardana/tools/config/xls_to_sar.py index 934f4f03b5..c12fb79710 100644 --- a/src/sardana/tools/config/xls_to_sar.py +++ b/src/sardana/tools/config/xls_to_sar.py @@ -21,7 +21,7 @@ def transform(f): xslt_filename = os.path.join(directory, "XLS_TO_SAR.xslt") t = etree.XSLT(etree.parse(xslt_filename)) - if type(f) in str: + if isinstance(f, str): doc = etree.parse(f) else: doc = f From 10e1360bcbced55b0ed28b679e175214f7fe15c8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 23:45:50 +0200 Subject: [PATCH 091/830] py3: use explicit old division to preserve behavior of py2 Whenever two integers are divided in py2 use old division operator "//" in py3 in order to preserver the behavior. --- src/sardana/macroserver/macros/examples/scans.py | 6 +++--- src/sardana/spock/spockms.py | 2 +- .../taurus/qt/qtgui/extra_hkl/diffractometeralignment.py | 4 ++-- src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py | 2 +- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index 63f4e45203..d0105bf151 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -304,7 +304,7 @@ def prepare(self, motor, integ_time, start_pos, regions, **opts): self.integ_time = integ_time self.start_pos = start_pos self.regions = regions - self.regions_count = len(self.regions) / 2 + self.regions_count = len(self.regions) // 2 generator = self._generator moveables = [motor] @@ -365,7 +365,7 @@ def prepare(self, motor1, motor2, integ_time, start_pos, regions, **opts): self.integ_time = integ_time self.start_pos = start_pos self.regions = regions - self.regions_count = len(self.regions) / 2 + self.regions_count = len(self.regions) // 2 generator = self._generator moveables = [motor1, motor2] @@ -430,7 +430,7 @@ def prepare(self, motor1, motor2, motor3, integ_time, start_pos, regions, **opts self.integ_time = integ_time self.start_pos = start_pos self.regions = regions - self.regions_count = len(self.regions) / 2 + self.regions_count = len(self.regions) // 2 generator = self._generator moveables = [motor1, motor2, motor3] diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 5d99060099..2cdd00daa9 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -522,7 +522,7 @@ def _processRecordData(self, data): value = data.value size = len(value[1]) if size > self._RECORD_DATA_THRESOLD: - sizekb = size / 1024 + sizekb = size // 1024 self.logReceived(self.Info, ['Received long data record (%d Kb)' % sizekb, 'It may take some time to process. Please wait...']) return BaseDoor._processRecordData(self, data) diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py index 7f2b03a95b..be0c7ef55c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py @@ -146,7 +146,7 @@ def setModel(self, model): angles_taurus_label = [] angles_taurus_input = [] - gap_x = 650 / self.nb_motors + gap_x = 650 // self.nb_motors try: self.angles_names = self.device.motorroles @@ -216,7 +216,7 @@ def setModel(self, model): tomax_functions = [self.tomax_scan1, self.tomax_scan2, self.tomax_scan3, self.tomax_scan4, self.tomax_scan5, self.tomax_scan6] - gap_x = 650 / self.nb_motors + gap_x = 650 // self.nb_motors for i in range(0, self.nb_motors): scan_buttons.append(QtGui.QPushButton(self)) diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py index 57c0ab83f6..bb03f30a5b 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py @@ -121,7 +121,7 @@ def setModel(self, model): angles_names = [] angles_taurus_label = [] - gap_x = 800 / self.nb_motors + gap_x = 800 // self.nb_motors try: angles_names = self.device.motorroles diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index d920a9b935..f7ce85404c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -107,7 +107,7 @@ def moveInc(self, inc): @Qt.pyqtSlot() def jogNeg(self): - neg_limit = -((self.maxint_in_32_bits / 2) - 1) + neg_limit = -((self.maxint_in_32_bits // 2) - 1) # THERE IS A BUG IN THE ICEPAP THAT DOES NOT ALLOW MOVE ABSOLUTE FURTHER THAN 32 BIT # SO IF THERE ARE STEPS PER UNIT, max_int HAS TO BE REDUCED if hasattr(self.motor_dev, 'step_per_unit'): @@ -122,7 +122,7 @@ def jogNeg(self): @Qt.pyqtSlot() def jogPos(self): - pos_limit = (self.maxint_in_32_bits / 2) - 1 + pos_limit = (self.maxint_in_32_bits // 2) - 1 # THERE IS A BUG IN THE ICEPAP THAT DOES NOT ALLOW MOVE ABSOLUTE FURTHER THAN 32 BIT # SO IF THERE ARE STEPS PER UNIT, max_int HAS TO BE REDUCED if hasattr(self.motor_dev, 'step_per_unit'): From 223cdf712ec59172bcf67f119b68843ac7218b67 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jul 2019 23:46:59 +0200 Subject: [PATCH 092/830] py3: remove float/int casts in divisions (no need in py3) This is an optional commit. These changes are not strictly mandatory. --- src/sardana/macroserver/macros/examples/plotting.py | 5 ++--- src/sardana/macroserver/macros/examples/scans.py | 10 +++++----- src/sardana/macroserver/macros/standard.py | 2 +- src/sardana/macroserver/recorders/output.py | 4 ++-- src/sardana/macroserver/scan/gscan.py | 4 ++-- .../pool/poolcontrollers/DummyOneDController.py | 2 +- src/sardana/pool/poolcontrollers/IoverI0.py | 2 +- src/sardana/pool/poolcontrollers/Slit.py | 6 +++--- src/sardana/pool/poolzerodexpchannel.py | 2 +- src/sardana/tango/pool/Motor.py | 2 +- src/sardana/tango/pool/Pool.py | 4 ++-- src/sardana/taurus/core/tango/sardana/pool.py | 4 ++-- 12 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/sardana/macroserver/macros/examples/plotting.py b/src/sardana/macroserver/macros/examples/plotting.py index 97bf7621cd..103dcc34d8 100644 --- a/src/sardana/macroserver/macros/examples/plotting.py +++ b/src/sardana/macroserver/macros/examples/plotting.py @@ -10,7 +10,7 @@ def j0i(x): """Integral form of J_0(x)""" def integrand(phi): return math.cos(x * math.sin(phi)) - return (1.0 / math.pi) * quad(integrand, 0, math.pi)[0] + return (1 / math.pi) * quad(integrand, 0, math.pi)[0] @macro() @@ -55,10 +55,9 @@ def mandelbrot(self, interactions, density): fractal = numpy.zeros(z.shape, dtype=numpy.uint8) + 255 - finteractions = float(interactions) for n in range(interactions): z *= z z += c mask = (fractal == 255) & (abs(z) > 10) - fractal[mask] = 254 * n / finteractions + fractal[mask] = 254 * n / interactions self.pyplot.imshow(fractal) diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index d0105bf151..a07b9d7468 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -517,11 +517,11 @@ def _generator(self): positions_m2 = numpy.linspace(start2, end2, interv2 + 1) if interv1 > interv2: - positions_m2 = start2 + (float(end2 - start2) / interv2) * ( - numpy.arange(interv1 + 1) // (float(interv1) / float(interv2))) + positions_m2 = start2 + ((end2 - start2) / interv2) * ( + numpy.arange(interv1 + 1) // (interv1 / interv2)) elif interv2 > interv1: - positions_m1 = start1 + (float(end1 - start1) / interv1) * ( - numpy.arange(interv2 + 1) // (float(interv2) / float(interv1))) + positions_m1 = start1 + ((end1 - start1) / interv1) * ( + numpy.arange(interv2 + 1) // (interv2 / interv1)) point_id = 0 for pos1, pos2 in zip(positions_m1, positions_m2): @@ -663,7 +663,7 @@ def run(self, *args, **kwargs): first_position = positions[0] second_position = positions[1] positive_direction = second_position > first_position - shift = abs(second_position - first_position) / 2. + shift = abs(second_position - first_position) / 2 if positive_direction: mid_positions = positions + shift else: diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 737f2b513d..fc8433da18 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -919,7 +919,7 @@ def run(self, nr, macro_name_params): else: for i in range(nr): self.__loop() - progress = ((i + 1) / float(nr)) * 100 + progress = ((i + 1) / nr) * 100 yield progress diff --git a/src/sardana/macroserver/recorders/output.py b/src/sardana/macroserver/recorders/output.py index b6d1e8bb58..c981d2505a 100644 --- a/src/sardana/macroserver/recorders/output.py +++ b/src/sardana/macroserver/recorders/output.py @@ -243,8 +243,8 @@ def _endRecordList(self, recordlist): endts = recordlist.getEnvironValue('endts') startts = recordlist.getEnvironValue('startts') totaltimets = endts - startts - deadtime_perc = deadtime * 100.0 / totaltimets - motiontime_perc = motiontime * 100.0 / totaltimets + deadtime_perc = deadtime * 100 / totaltimets + motiontime_perc = motiontime * 100 / totaltimets info_string = 'Scan #%s ended at %s, taking %s. ' + \ 'Dead time %.1f%% (motion dead time %.1f%%)' self._stream().info(info_string % (serialno, endtime, totaltime, diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index a38d216a26..8c0660f191 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1076,7 +1076,7 @@ def scan_loop(self): self.stepUp(i, step, lstep) lstep = step if scream: - yield ((i + 1) / nr_points) * 100.0 + yield ((i + 1) / nr_points) * 100 if not scream: yield 100.0 @@ -1933,7 +1933,7 @@ def scan_loop(self): self.data.addRecord(data_line) if scream: - yield ((point_nb + 1) / nr_points) * 100.0 + yield ((point_nb + 1) / nr_points) * 100 else: break old_curr_time = curr_time diff --git a/src/sardana/pool/poolcontrollers/DummyOneDController.py b/src/sardana/pool/poolcontrollers/DummyOneDController.py index 8d32a6e62e..669a814d86 100644 --- a/src/sardana/pool/poolcontrollers/DummyOneDController.py +++ b/src/sardana/pool/poolcontrollers/DummyOneDController.py @@ -174,7 +174,7 @@ def _updateChannelValue(self, axis, elapsed_time): elif self._synchronization in (AcqSynch.HardwareTrigger, AcqSynch.HardwareGate): if self.integ_time is not None: - n = int(t / self.integ_time) + n = t // self.integ_time cp = 0 if n > self._repetitions: cp = n - self._repetitions diff --git a/src/sardana/pool/poolcontrollers/IoverI0.py b/src/sardana/pool/poolcontrollers/IoverI0.py index ee262017f6..9103e6eb9e 100644 --- a/src/sardana/pool/poolcontrollers/IoverI0.py +++ b/src/sardana/pool/poolcontrollers/IoverI0.py @@ -44,7 +44,7 @@ class IoverI0(PseudoCounterController): def Calc(self, axis, counter_values): i, i0 = counter_values try: - i = float(i / i0) + i = i / i0 except ZeroDivisionError: pass return i diff --git a/src/sardana/pool/poolcontrollers/Slit.py b/src/sardana/pool/poolcontrollers/Slit.py index 04e0671935..1593c0afc4 100644 --- a/src/sardana/pool/poolcontrollers/Slit.py +++ b/src/sardana/pool/poolcontrollers/Slit.py @@ -59,7 +59,7 @@ def __init__(self, inst, props, *args, **kwargs): self._example = {} def CalcPhysical(self, index, pseudo_pos, curr_physical_pos): - half_gap = pseudo_pos[0] / 2.0 + half_gap = pseudo_pos[0] / 2 if index == 1: ret = self.sign * (pseudo_pos[1] + half_gap) else: @@ -73,7 +73,7 @@ def CalcPseudo(self, index, physical_pos, curr_pseudo_pos): if index == 1: ret = self.sign * gap else: - ret = self.sign * (physical_pos[0] - gap / 2.0) + ret = self.sign * (physical_pos[0] - gap / 2) return ret def CalcAllPseudo(self, physical_pos, curr_pseudo_pos): @@ -81,7 +81,7 @@ def CalcAllPseudo(self, physical_pos, curr_pseudo_pos): pseudo motor system from the positions of the physical motors.""" gap = physical_pos[1] + physical_pos[0] return (self.sign * gap, - self.sign * (physical_pos[0] - gap / 2.0)) + self.sign * (physical_pos[0] - gap / 2)) # def CalcAllPhysical(self, pseudo_pos, curr_physical_pos): # """Calculates the positions of all motors that belong to the pseudo diff --git a/src/sardana/pool/poolzerodexpchannel.py b/src/sardana/pool/poolzerodexpchannel.py index cd2fe1a7e9..9f97d362dc 100644 --- a/src/sardana/pool/poolzerodexpchannel.py +++ b/src/sardana/pool/poolzerodexpchannel.py @@ -118,7 +118,7 @@ def update_value(self, value, timestamp): else: last_value, last_timestamp = self.last_value dt = timestamp - last_timestamp - self.sum += dt * (last_value + value) / 2.0 + self.sum += dt * (last_value + value) / 2 total_dt = timestamp - self.start_time self.value = self.sum / total_dt self.last_value = value, timestamp diff --git a/src/sardana/tango/pool/Motor.py b/src/sardana/tango/pool/Motor.py index 3279ffc9f1..1bb33f93ba 100644 --- a/src/sardana/tango/pool/Motor.py +++ b/src/sardana/tango/pool/Motor.py @@ -349,7 +349,7 @@ def init_device(self): pass if self.Sleep_bef_last_read > 0: - motor.set_instability_time(self.Sleep_bef_last_read / 1000.0) + motor.set_instability_time(self.Sleep_bef_last_read / 1000) motor.add_listener(self.on_motor_changed) self.set_state(DevState.ON) diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index c9ac7c6525..3f67a7f328 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -117,10 +117,10 @@ def init_device(self): p = self.pool p.set_python_path(self.PythonPath) p.set_path(self.PoolPath) - p.set_motion_loop_sleep_time(self.MotionLoop_SleepTime / 1000.0) + p.set_motion_loop_sleep_time(self.MotionLoop_SleepTime / 1000) p.set_motion_loop_states_per_position( self.MotionLoop_StatesPerPosition) - p.set_acq_loop_sleep_time(self.AcqLoop_SleepTime / 1000.0) + p.set_acq_loop_sleep_time(self.AcqLoop_SleepTime / 1000) p.set_acq_loop_states_per_value(self.AcqLoop_StatesPerValue) p.set_drift_correction(self.DriftCorrection) if self.RemoteLog is None: diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 05ed3c670d..f9ce1e9866 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -485,7 +485,7 @@ def waitFinish(self, timeout=None, id=None): # Due to taurus-org/taurus #573 we need to divide the timeout # in two intervals if timeout is not None: - timeout = timeout / 2. + timeout = timeout / 2 if id is not None: id = id[0] evt_wait = self._getEventWait() @@ -2397,7 +2397,7 @@ def _wait_for_element_in_container(self, container, elem_name, timeout=0.5, cond = True nap = 0.01 if timeout: - nap = timeout / 10. + nap = timeout / 10 while cond: elem = container.getElement(elem_name) if contains: From 7c3eef4ecbfdff1f3cf49ce34a85369131b5945a Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 9 Jul 2019 16:24:05 +0200 Subject: [PATCH 093/830] Fix erroneous test parameters This has nothing to do with py3 migration. --- src/sardana/pool/test/test_measurementgroup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index e1fa769714..5d589ed245 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -118,7 +118,7 @@ def meas_double_acquisition(self, config, synchronization, moveable=None): self.remove_attribute_listener() synchronization = [{SynchParam.Delay: {SynchDomain.Time: 0}, SynchParam.Active: {SynchDomain.Time: 0.1}, - SynchParam.Total: {SynchDomain.Time: 0}, + SynchParam.Total: {SynchDomain.Time: 0.2}, SynchParam.Repeats: 1}] self.pmg.synchronization = synchronization self.acquire() From 3353787f5b2e2fc124646e9af920926c9608d48a Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 9 Jul 2019 17:18:15 +0200 Subject: [PATCH 094/830] py3: use open instead of file file built-in function disappeared in py3. Use open instead. All the occurrences are text files. --- src/sardana/macroserver/msmacromanager.py | 4 ++-- src/sardana/pool/poolcontrollermanager.py | 4 ++-- src/sardana/spock/ipython_00_10/genutils.py | 2 +- src/sardana/spock/ipython_00_11/genutils.py | 4 ++-- src/sardana/spock/ipython_01_00/genutils.py | 4 ++-- src/sardana/spock/magic.py | 2 +- .../taurus/qt/qtgui/extra_sardana/sardanaeditor.py | 10 +++++----- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index b762e5d199..5fe348da29 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -326,7 +326,7 @@ def getOrCreateMacroLib(self, lib_name, macro_name=None): f_name, code = self.createMacroLib(lib_name), '' else: f_name = macro_lib.file_path - f = file(f_name) + f = open(f_name) code = f.read() f.close() else: @@ -341,7 +341,7 @@ def getOrCreateMacroLib(self, lib_name, macro_name=None): else: _, line_nb = macro.code f_name = macro.file_path - f = file(f_name) + f = open(f_name) code = f.read() f.close() diff --git a/src/sardana/pool/poolcontrollermanager.py b/src/sardana/pool/poolcontrollermanager.py index a1c25da17c..7ce19cdd69 100644 --- a/src/sardana/pool/poolcontrollermanager.py +++ b/src/sardana/pool/poolcontrollermanager.py @@ -238,7 +238,7 @@ def getOrCreateControllerLib(self, lib_name, controller_name=None): f_name, code = self.createControllerLib(lib_name), '' else: f_name = controller_lib.get_file_name() - f = file(f_name) + f = open(f_name) code = f.read() f.close() else: @@ -254,7 +254,7 @@ def getOrCreateControllerLib(self, lib_name, controller_name=None): else: _, line_nb = controller.getCode() f_name = controller.getFileName() - f = file(f_name) + f = open(f_name) code = f.read() f.close() diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index 0d31d8461b..cb7fd87906 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -274,7 +274,7 @@ def get_spock_profiles(ipython_profiles=None): if not os.path.isfile(profile_f): continue try: - for i, l in enumerate(file(profile_f)): + for i, l in enumerate(open(profile_f)): if i > 10: break if l.find("spock_creation_version") >= 0: diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py index 1c6ca2295c..d0e0873d3c 100644 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ b/src/sardana/spock/ipython_00_11/genutils.py @@ -741,7 +741,7 @@ def _create_config_file(location, door_name=None): sys.stdout.write('Storing %s in %s... ' % (config_file_name, location)) sys.stdout.flush() - with file(abs_config_file_name, "w") as f: + with open(abs_config_file_name, "w") as f: f.write(dest_data) f.close() sys.stdout.write(MSG_DONE + '\n') @@ -785,7 +785,7 @@ def check_for_upgrade(ipy_profile_dir): abs_config_file_name = os.path.join(ipy_profile_dir, config_file_name) # search for version and door inside the ipy_profile file - with file(abs_config_file_name, "r") as ipy_config_file: + with open(abs_config_file_name, "r") as ipy_config_file: for i, line in enumerate(ipy_config_file): if i > 20: break # give up after 20 lines diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index 121005f33c..973554be89 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -768,7 +768,7 @@ def _create_config_file(location, door_name=None): sys.stdout.write('Storing %s in %s... ' % (config_file_name, location)) sys.stdout.flush() - with file(abs_config_file_name, "w") as f: + with open(abs_config_file_name, "w") as f: f.write(dest_data) f.close() sys.stdout.write(MSG_DONE + '\n') @@ -830,7 +830,7 @@ def check_for_upgrade(ipy_profile_dir): abs_config_file_name = os.path.join(ipy_profile_dir, config_file_name) # search for version and door inside the ipy_profile file - with file(abs_config_file_name, "r") as ipy_config_file: + with open(abs_config_file_name, "r") as ipy_config_file: for i, line in enumerate(ipy_config_file): if i > 20: break # give up after 20 lines diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index 080a146bf1..edffb1cb18 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -283,7 +283,7 @@ def edmac(self, parameter_s=''): if ask_yes_no('Do you want to apply the new code on the server?', 'y'): print('Storing...', end=' ') try: - f = file(local_fname) + f = open(local_fname) try: new_code = f.read() ms.SetMacroCode([remote_fname, new_code]) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py index 02ac41e827..5f9d402ed7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py @@ -289,7 +289,7 @@ def new_macro(self, template): self.debug("Creating local file %s...", local_filename) fname, lib_code, line = macro_server.GetMacroCode( (macro_lib_name,)) - fd = file(local_filename, "w") + fd = open(local_filename, "w") fd.write(lib_code) fd.close() self.debug("Loading local file %s...", local_filename) @@ -345,7 +345,7 @@ def new_macro_library(self): # transform into relative path rel_filename = filename[filename.index(osp.sep) + 1:] local_filename = osp.join(self._tmp_dir, rel_filename) - f = file(local_filename, "w") + f = open(local_filename, "w") # TODO: ask for additional imports # TODO: check if door environment has copyright variable @@ -407,7 +407,7 @@ def open_macros(self, macros): _, code, line = self.get_macro_code(module, name) line = int(line) self.debug("Creating local file %s...", local_filename) - fd = file(local_filename, "w") + fd = open(local_filename, "w") fd.write(code) fd.close() if idx is None: @@ -460,7 +460,7 @@ def open_macro_libraries(self, macro_libraries): _, code, line = self.get_macro_code(module) line = int(line) self.debug("Creating local file %s...", local_filename) - fd = file(local_filename, "w") + fd = open(local_filename, "w") fd.write(code) fd.close() if idx is None: @@ -484,7 +484,7 @@ def on_save(self, checked, apply=True): if not res: return local_filename = file_info.filename - fd = file(local_filename, "r") + fd = open(local_filename, "r") code = fd.read() fd.close() remote_filename = local_filename[len(self._tmp_dir):] From f31218dfad0b29e2cb79065d5218c29c000ffd9f Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 9 Jul 2019 17:40:24 +0200 Subject: [PATCH 095/830] Add py2_round to sardana utils The behavior of round has changed in Python 3. In Python 2, rounding of halfway cases was away from zero, and round() would always return a float. In Python 3 rounding of halfway cases are now always towards the nearest even. This is standard practice, as it will make a set of evenly distributed roundings average out. --- src/sardana/sardanautils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sardana/sardanautils.py b/src/sardana/sardanautils.py index 9586db2a8a..a0115e74f2 100644 --- a/src/sardana/sardanautils.py +++ b/src/sardana/sardanautils.py @@ -31,10 +31,11 @@ __all__ = ["is_pure_str", "is_non_str_seq", "is_integer", "is_number", "is_bool", "check_type", "assert_type", "str_to_value", "is_callable", "translate_version_str2int", - "translate_version_str2list"] + "translate_version_str2list", "py2_round"] __docformat__ = 'restructuredtext' +import math import numpy import numbers import collections @@ -187,3 +188,11 @@ def translate_version_str2list(version_str, depth=2): i = 0 ver.append(i) return ver + + +def py2_round(x, d=0): + p = 10 ** d + if x > 0: + return float(math.floor((x * p) + 0.5)) / p + else: + return float(math.ceil((x * p) - 0.5)) / p From 1e8947739c61412777869b994a7720514206facd Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 9 Jul 2019 17:41:55 +0200 Subject: [PATCH 096/830] py3: be conservative and apply py2 round behavior Add TODO to evaluate it in the future. --- src/sardana/macroserver/macros/hkl.py | 7 ++++--- src/sardana/pool/poolmotor.py | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py index c505b6d0ba..2e5443a3b3 100644 --- a/src/sardana/macroserver/macros/hkl.py +++ b/src/sardana/macroserver/macros/hkl.py @@ -47,6 +47,7 @@ import re import numpy as np +from sardana.sardanautils import py2_round from sardana.macroserver.macro import Macro, iMacro, Type from sardana.macroserver.macros.scan import aNscan from sardana.macroserver.msexception import UnknownEnv @@ -1605,7 +1606,7 @@ def run(self, parameter): self.output("Old lattice parameter %s = %s" % (parameter, a0)) h0 = self.h_device.position - h1 = round(h0) + h1 = py2_round(h0) # TODO: check if round would be fine? a1 = h1 / h0 * a0 self.output("New lattice parameter %s = %s" % (parameter, a1)) @@ -1615,7 +1616,7 @@ def run(self, parameter): self.output("Old lattice parameter %s = %s" % (parameter, a0)) h0 = self.k_device.position - h1 = round(h0) + h1 = py2_round(h0) # TODO: check if round would be fine? a1 = h1 / h0 * a0 self.output("New lattice parameter %s = %s" % (parameter, a1)) @@ -1625,7 +1626,7 @@ def run(self, parameter): self.output("Old lattice parameter %s = %s" % (parameter, a0)) h0 = self.l_device.position - h1 = round(h0) + h1 = py2_round(h0) # TODO: check if round would be fine? a1 = h1 / h0 * a0 self.output("New lattice parameter %s = %s" % (parameter, a1)) diff --git a/src/sardana/pool/poolmotor.py b/src/sardana/pool/poolmotor.py index 64dcec8885..3991190960 100644 --- a/src/sardana/pool/poolmotor.py +++ b/src/sardana/pool/poolmotor.py @@ -37,7 +37,7 @@ from sardana.sardanaattribute import SardanaAttribute, ScalarNumberAttribute, \ SardanaSoftwareAttribute from sardana.sardanaevent import EventType -from sardana.sardanautils import assert_type, is_number +from sardana.sardanautils import assert_type, is_number, py2_round from sardana.pool.poolelement import PoolElement from sardana.pool.poolmotion import PoolMotion, MotionState @@ -167,7 +167,8 @@ def calc_motion(self, new_position): # compute a rounding value if necessary if ctrl.wants_rounding(): - nb_step = round(new_dial * step_per_unit) + # TODO: check if round would be fine + nb_step = py2_round(new_dial * step_per_unit) new_dial = nb_step / step_per_unit backlash_position = new_dial @@ -751,7 +752,8 @@ def calculate_motion(self, new_position, items=None, calculated=None): # compute a rounding value if necessary if ctrl.wants_rounding(): - nb_step = round(new_dial * step_per_unit) + # TODO: check if round would be fine + nb_step = py2_round(new_dial * step_per_unit) new_dial = nb_step / step_per_unit backlash_position = new_dial From ff8dfffe63e5e12d3a11be740b8b1d0772b7cbc1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 9 Jul 2019 18:16:24 +0200 Subject: [PATCH 097/830] py3: implement __lt__ instead of __cmp__ Python 3 removed cmp() built-in function and __cmp__() method. Implement __lt__ method which is enough for sorting. --- src/sardana/sardanameta.py | 4 ++-- src/sardana/taurus/core/tango/sardana/pool.py | 23 ++++++++----------- .../taurus/core/tango/sardana/sardana.py | 4 ++-- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/sardana/sardanameta.py b/src/sardana/sardanameta.py index 23611fc91b..46d793d5a1 100644 --- a/src/sardana/sardanameta.py +++ b/src/sardana/sardanameta.py @@ -131,8 +131,8 @@ def __init__(self, **kwargs): kwargs['full_name'] = file_path or name SardanaBaseObject.__init__(self, **kwargs) - def __cmp__(self, o): - return cmp(self.full_name, o.full_name) + def __lt__(self, o): + return self.full_name < o.full_name def __str__(self): return self.name diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index f9ce1e9866..9f4550aa53 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -130,9 +130,8 @@ def str(self, n=0): return CodecFactory.encode(('json'), self.serialize()) return self._str_tuple[:n] - def __cmp__(self, o): - return cmp(self.getPoolData()['full_name'], - o.getPoolData()['full_name']) + def __lt__(self, o): + return self.getPoolData()['full_name'] < o.getPoolData()['full_name'] def getName(self): return self.getPoolData()['name'] @@ -183,14 +182,12 @@ def getModel(self): def getOrganization(self): return self.organization - def __cmp__(self, o): - t = cmp(self.getType(), o.getType()) - if t != 0: - return t - t = cmp(self.getGender(), o.getGender()) - if t != 0: - return t - return cmp(self.getClassName(), o.getClassName()) + def __lt__(self, o): + if self.getType() != o.getType(): + return self.getType() < o.getType() + if self.getGender() != o.getGender(): + return self.getGender() < o.getGender() + return self.getClassName() < o.getClassName() class ControllerLibrary(BaseElement): @@ -662,8 +659,8 @@ def getLastUsedAxis(self): return None return max(used_axes) - def __cmp__(self, o): - return cmp(self.getName(), o.getName()) + def __lt__(self, o): + return self.getName() < o.getName() class ComChannel(PoolElement): diff --git a/src/sardana/taurus/core/tango/sardana/sardana.py b/src/sardana/taurus/core/tango/sardana/sardana.py index 0edebf445f..33c74bbf24 100644 --- a/src/sardana/taurus/core/tango/sardana/sardana.py +++ b/src/sardana/taurus/core/tango/sardana/sardana.py @@ -94,8 +94,8 @@ def __str__(self): def __getattr__(self, name): return getattr(self.getObj(), name) - def __cmp__(self, elem): - return cmp(self.name, elem.name) + def __lt__(self, elem): + return self.name < elem.name def getData(self): return self._data From 4e8d66a5cb2a0ed7a5e741f0d62dbeab6b081fea Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 9 Jul 2019 18:18:36 +0200 Subject: [PATCH 098/830] py3: use key kwarg for sorted cmp kwarg, a function with two arguments and returning either of -1, 0, 1 is removed in Python 3. key kwarg, a function with one argument and returning the key to compare, is used instead. --- src/sardana/taurus/core/tango/sardana/pool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 9f4550aa53..e00293b8c5 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1564,7 +1564,7 @@ def getChannelsInfoList(self, only_enabled=False): ret = [] for _, (_, _, ch_info) in list(channels_info.items()): ret.append(ch_info) - ret = sorted(ret, lambda x, y: cmp(x.index, y.index)) + ret = sorted(ret, key=lambda x: x.index) return ret def getCountersInfoList(self): From a37e3a65318f1b1c63670b29a2a14586d7dc7e8f Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jul 2019 09:39:35 +0200 Subject: [PATCH 099/830] py3: fix wrong relative import done by 2to3 --- src/sardana/tango/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/tango/__init__.py b/src/sardana/tango/__init__.py index 597716b72b..770d4bf471 100644 --- a/src/sardana/tango/__init__.py +++ b/src/sardana/tango/__init__.py @@ -38,7 +38,7 @@ def prepare_sardana(util): def main_sardana(args=None, start_time=None, mode=None): - from . import core.util + from . import core # pass server name so the scripts generated with setuptools work on Windows return core.util.run(prepare_sardana, args=args, start_time=start_time, mode=mode, name=SERVER_NAME) From 3f6bd4e2f0bd6d99e1d97d2fb9067a5c4e86e0c0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jul 2019 11:04:12 +0200 Subject: [PATCH 100/830] py3: fix assignment of docstring to function In python3 __func__ attribute of an unbound method does not exist. Actually unbound method does not exists - these are directly functions. Assign doc to function directly. --- src/sardana/tango/pool/Pool.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index 3f67a7f328..17bb051e61 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -1129,7 +1129,7 @@ def SetControllerCode(self, argin): {1} """.format(CREATE_MOTOR_GROUP_PAR_IN_DOC, CREATE_MOTOR_GROUP_PAR_OUT_DOC) -Pool.CreateMotorGroup.__func__.__doc__ = CREATE_MOTOR_GROUP_DOC +Pool.CreateMotorGroup.__doc__ = CREATE_MOTOR_GROUP_DOC CREATE_MEASUREMENT_GROUP_PAR_IN_DOC = """\ Must give either: @@ -1300,18 +1300,18 @@ def SetControllerCode(self, argin): {1} """.format(SEND_TO_CONTROLLER_PAR_IN_DOC, SEND_TO_CONTROLLER_PAR_OUT_DOC) -Pool.CreateController.__func__.__doc__ = CREATE_CONTROLLER_DOC -Pool.CreateElement.__func__.__doc__ = CREATE_ELEMENT_DOC -Pool.CreateInstrument.__func__.__doc__ = CREATE_INSTRUMENT_DOC -Pool.CreateMotorGroup.__func__.__doc__ = CREATE_MOTOR_GROUP_DOC -Pool.CreateMeasurementGroup.__func__.__doc__ = CREATE_MEASUREMENT_GROUP_DOC -Pool.DeleteElement.__func__.__doc__ = DELETE_ELEMENT_DOC -Pool.GetControllerClassInfo.__func__.__doc__ = GET_CONTROLLER_CLASS_INFO_DOC -Pool.ReloadControllerLib.__func__.__doc__ = RELOAD_CONTROLLER_LIB_INFO_DOC -Pool.ReloadControllerClass.__func__.__doc__ = RELOAD_CONTROLLER_CLASS_INFO_DOC -Pool.RenameElement.__func__.__doc__ = RENAME_ELEMENT_CLASS_INFO_DOC -Pool.Stop.__func__.__doc__ = STOP_DOC -Pool.Abort.__func__.__doc__ = ABORT_DOC +Pool.CreateController.__doc__ = CREATE_CONTROLLER_DOC +Pool.CreateElement.__doc__ = CREATE_ELEMENT_DOC +Pool.CreateInstrument.__doc__ = CREATE_INSTRUMENT_DOC +Pool.CreateMotorGroup.__doc__ = CREATE_MOTOR_GROUP_DOC +Pool.CreateMeasurementGroup.__doc__ = CREATE_MEASUREMENT_GROUP_DOC +Pool.DeleteElement.__doc__ = DELETE_ELEMENT_DOC +Pool.GetControllerClassInfo.__doc__ = GET_CONTROLLER_CLASS_INFO_DOC +Pool.ReloadControllerLib.__doc__ = RELOAD_CONTROLLER_LIB_INFO_DOC +Pool.ReloadControllerClass.__doc__ = RELOAD_CONTROLLER_CLASS_INFO_DOC +Pool.RenameElement.__doc__ = RENAME_ELEMENT_CLASS_INFO_DOC +Pool.Stop.__doc__ = STOP_DOC +Pool.Abort.__doc__ = ABORT_DOC class PoolClass(PyTango.DeviceClass): From a99f272e67327ce3d606421316f080475bf9aecf Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jul 2019 11:31:01 +0200 Subject: [PATCH 101/830] py3: Use DevFailed.args for indexing PEP352 removed backwards compatibility for indexing of BaseException. Now one needs to use args member get item based on index. More details in https://www.tango-controls.org/community/forum/c/development/python/devfailed-does-not-support-indexing/ --- src/sardana/macroserver/msmacromanager.py | 4 ++-- src/sardana/pool/poolcontrollers/TangoController.py | 3 ++- src/sardana/tango/core/SardanaDevice.py | 6 +++--- src/sardana/taurus/core/tango/sardana/pool.py | 12 ++++++------ 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 5fe348da29..27aeb3374d 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1597,8 +1597,8 @@ def runMacro(self, macro_obj): mse.traceback = traceback.format_exc() except DevFailed as df: exc_info = sys.exc_info() - exp_pars = {'type': df[0].reason, - 'msg': df[0].desc, + exp_pars = {'type': df.args[0].reason, + 'msg': df.args[0].desc, 'args': df.args, 'traceback': traceback.format_exc()} macro_exp = MacroServerException(exp_pars) diff --git a/src/sardana/pool/poolcontrollers/TangoController.py b/src/sardana/pool/poolcontrollers/TangoController.py index c7521357e2..d9bb2975a9 100644 --- a/src/sardana/pool/poolcontrollers/TangoController.py +++ b/src/sardana/pool/poolcontrollers/TangoController.py @@ -120,7 +120,8 @@ def set_extra_attribute_par(self, axis, name, value): proxy = PyTango.DeviceProxy(dev_name) except PyTango.DevFailed as df: if len(df): - self._pending[axis] = df[0].reason + ": " + df[0].desc + self._pending[axis] = df.args[0].reason + ": " + \ + df.args[0].desc else: self._pending[ axis] = "Unknwon PyTango Error: " + str(df) diff --git a/src/sardana/tango/core/SardanaDevice.py b/src/sardana/tango/core/SardanaDevice.py index 9044a447d5..3e496be7a2 100644 --- a/src/sardana/tango/core/SardanaDevice.py +++ b/src/sardana/tango/core/SardanaDevice.py @@ -254,12 +254,12 @@ def set_write_attribute(self, attr, w_value): try: attr.set_write_value(w_value) except DevFailed as df: - df0 = df[0] + df0 = df.args[0] reason = df0.reason # if outside limit prefix the description with the device name if reason == PyTango.constants.API_WAttrOutsideLimit: desc = self.alias + ": " + df0.desc - _df = DevFailed(*df[1:]) + _df = DevFailed(*df.args[1:]) PyTango.Except.re_throw_exception( _df, df0.reason, desc, df0.origin) raise df @@ -405,7 +405,7 @@ def _set_attribute_push(self, attr, value=None, w_value=None, timestamp=None, try: attr.set_write_value(w_value) except DevFailed as df: - error = df[0] + error = df.args[0] reason = error.reason if reason == PyTango.constants.API_WAttrOutsideLimit and\ attr_name == 'position': diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index e00293b8c5..0717c70882 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1020,7 +1020,7 @@ def iterMove(self, new_pos, timeout=None): try: self.getPositionObj().write(new_pos) except DevFailed as err_traceback: - for err in err_traceback: + for err in err_traceback.args: if err.reason == 'API_AttrNotAllowed': raise RuntimeError('%s is already moving' % self) else: @@ -1117,7 +1117,7 @@ def _start(self, *args, **kwargs): try: self.write_attribute('position', new_pos) except DevFailed as df: - for err in df: + for err in df.args: if err.reason == 'API_AttrNotAllowed': raise RuntimeError('%s is already moving' % self) else: @@ -1209,7 +1209,7 @@ def _start(self, *args, **kwargs): try: self.write_attribute('position', new_pos) except DevFailed as df: - for err in df: + for err in df.args: if err.reason == 'API_AttrNotAllowed': raise RuntimeError('%s is already moving' % self) else: @@ -2032,7 +2032,7 @@ def _start(self, *args, **kwargs): except DevFailed as e: # TODO: Workaround for CORBA timeout on measurement group start # remove it whenever sardana-org/sardana#93 gets implemented - if e[-1].reason == "API_DeviceTimedOut": + if e.args[-1].reason == "API_DeviceTimedOut": self.error("start timed out, trying to stop") self.stop() self.debug("stopped") @@ -2161,7 +2161,7 @@ def startWriteValue(self, new_value, timeout=None): self.getValueObj().write(new_value) self.final_val = new_value except DevFailed as err_traceback: - for err in err_traceback: + for err in err_traceback.args: if err.reason == 'API_AttrNotAllowed': raise RuntimeError('%s is already chaging' % self) else: @@ -2237,7 +2237,7 @@ def on_elements_changed(self, evt_src, evt_type, evt_value): if evt_type == TaurusEventType.Error: msg = evt_value if isinstance(msg, DevFailed): - d = msg[0] + d = msg.args[0] # skip configuration errors if d.reason == "API_BadConfigurationProperty": return From 042c3655cfe5e2c272b0e214794fe4810a18dd2f Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jul 2019 12:01:03 +0200 Subject: [PATCH 102/830] py3: make ControllerClass comparable In python3 ControllerClass is not comparable anymore. One needs to implement at least the __lt__ method. Implement it based on the one already existing for taurus.core.tango.sardana.pool.ControllerClass. --- src/sardana/pool/poolmetacontroller.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sardana/pool/poolmetacontroller.py b/src/sardana/pool/poolmetacontroller.py index 93a5733cd6..1278617aec 100644 --- a/src/sardana/pool/poolmetacontroller.py +++ b/src/sardana/pool/poolmetacontroller.py @@ -327,6 +327,15 @@ def __init__(self, **kwargs): if init_args.varargs is None or init_args.keywords is None: self.api_version = 0 + def __lt__(self, o): + main_type = self.types[0] + o_main_type = o.types[0] + if main_type != o_main_type: + return main_type < o_main_type + if self.gender != o.gender: + return self.gender < o.gender + return self.name < o.name + def __build_types(self): types = [] klass = self.klass From 31665b45801fbeff856966e6106f83e991653448 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jul 2019 18:10:09 +0200 Subject: [PATCH 103/830] Remove code necessary for PyTango 7.1.4 (PyTango > 7.2.3 is required) This code is no more necessary and it breaks decoding from json in py3. Remove it. --- src/sardana/tango/macroserver/test/macroexecutor.py | 7 +------ src/sardana/taurus/core/tango/sardana/macroserver.py | 11 ++--------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/sardana/tango/macroserver/test/macroexecutor.py b/src/sardana/tango/macroserver/test/macroexecutor.py index 80494fab35..9459301336 100644 --- a/src/sardana/tango/macroserver/test/macroexecutor.py +++ b/src/sardana/tango/macroserver/test/macroexecutor.py @@ -90,21 +90,16 @@ def push_event(self, *args, **kwargs): if event_data.err: self._state_buffer = event_data.errors self._tango_macro_executor._done_event.set() - # 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) attr_value = getattr(event_data, 'attr_value') if attr_value is None: return - v = list(map(str, attr_value.value)) + v = attr_value.value if not len(v[1]): return fmt = v[0] codec = CodecFactory().getCodec(fmt) - # 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]) fmt, data = codec.decode(v) for macro_status in data: state = macro_status['state'] diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 723a32eaaf..72c1d5bfdf 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -667,9 +667,7 @@ def recordDataReceived(self, s, t, v): def _processRecordData(self, data): if data is None or data.value is None: return - # 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) - data = list(map(str, data.value)) + data = data.value size = len(data[1]) if size == 0: @@ -688,17 +686,12 @@ def macroStatusReceived(self, s, t, v): if t not in CHANGE_EVT_TYPES: return - # 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 = list(map(str, v.value)) + v = v.value if not len(v[1]): 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]) fmt, data = codec.decode(v) for macro_status in data: id = macro_status.get('id') From 3bb22a4f5f18bdf31500d2fea422b07c7ada24d1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jul 2019 18:12:31 +0200 Subject: [PATCH 104/830] py3: fix Optional serialization with json iteritems is removed in Py3 and items should be used instead. Py3 json serialization uses items so we need to preserve this method. Preserve it istead of iteritems. --- src/sardana/macroserver/msparameter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py index a6613af933..7c54261865 100644 --- a/src/sardana/macroserver/msparameter.py +++ b/src/sardana/macroserver/msparameter.py @@ -51,9 +51,9 @@ def __init__(self, obj): attributes = dir(self) for attr in attributes: - # iteritems is necessary fo python 2.6 implementation of json - if attr in ['__setattr__', '__repr__', 'raise_error', '__class__', - '__dict__', '__weakref__', 'iteritems']: + # items is necessary fo python 3.5 implementation of json + if attr in ['__setattr__', 'raise_error', '__class__', + '__dict__', '__weakref__', 'items']: continue self.__setattr__(attr, self.raise_error) self.__setattr__ = self.raise_error From 664ba41849f9885bdbc6ac133a9a20b443af408c Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jul 2019 18:14:01 +0200 Subject: [PATCH 105/830] py3: make param decoders iterable In Python 2 it was enough to expose all methods of an iterable member with the __getattr__. In Python 3 one needs to explicitly implement the __iter__ method. Do it for both ParamDecoder and FlatParamDecoder. --- src/sardana/macroserver/msparameter.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py index 7c54261865..5fc0bfa5f6 100644 --- a/src/sardana/macroserver/msparameter.py +++ b/src/sardana/macroserver/msparameter.py @@ -506,6 +506,9 @@ def decodeRepeat(self, raw_param_repeat, param_repeat_def): def getParamList(self): return self.params + def __iter__(self): + return iter(self.params) + def __getattr__(self, name): return getattr(self.params, name) @@ -624,5 +627,8 @@ def decodeRepeat(self, raw_params, par_def): def getParamList(self): return self.params + def __iter__(self): + return iter(self.params) + def __getattr__(self, name): return getattr(self.params, name) From e67a0a60d040464648e941c840cee6a61b8dc4ea Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jul 2019 18:15:19 +0200 Subject: [PATCH 106/830] py3: fix recur_map on string objects In Python 3 string objects have __iter__ member. Exclude them from the recursion - we don't want to map them - these are alredy atomic objects. --- src/sardana/macroserver/msmacromanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 27aeb3374d..cb3d21ca61 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -148,7 +148,7 @@ def recur_map(fun, data, keep_none=False): :param data: the same purpose as in map function :param keep_none: keep None elements without applying fun """ - if hasattr(data, "__iter__"): + if hasattr(data, "__iter__") and not isinstance(data, str): return [recur_map(fun, elem, keep_none) for elem in data] else: if keep_none is True and data is None: From 77a7aa91fddbd55112cf94feb7f47d30cc509fcd Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jul 2019 22:38:10 +0200 Subject: [PATCH 107/830] py3: don't use Exception.message Either use `raise from` or directly cast exception to str --- src/sardana/macroserver/msmacromanager.py | 2 +- src/sardana/macroserver/msparameter.py | 8 ++++---- src/sardana/spock/ipython_00_10/genutils.py | 2 +- src/sardana/spock/ipython_00_11/genutils.py | 2 +- src/sardana/spock/ipython_01_00/genutils.py | 2 +- src/sardana/tango/core/util.py | 2 +- src/sardana/taurus/core/tango/sardana/macro.py | 4 ++-- src/sardana/taurus/core/tango/sardana/motion.py | 6 +++--- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index cb3d21ca61..b34ec2cf31 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -774,7 +774,7 @@ def decodeMacroParameters(self, door, raw_params): raw_params) except WrongParam as in_e: msg = ("Either of: %s or %s made it impossible to decode" - " parameters" % (out_e.message, in_e.message)) + " parameters" % (out_e, in_e)) raise WrongParam(msg) else: raise out_e diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py index 5fc0bfa5f6..f10c2bc968 100644 --- a/src/sardana/macroserver/msparameter.py +++ b/src/sardana/macroserver/msparameter.py @@ -434,9 +434,9 @@ def decodeNormal(self, raw_param, param_def): param = param_type.getObj(str(value)) except ValueError as e: - raise WrongParamType(e.message) + raise WrongParamType from e except UnknownParamObj as e: - raise WrongParam(e.message) + raise WrongParam from e if param is None and not optional_param: msg = 'Could not create %s parameter "%s" for "%s"' % \ (param_type.getName(), name, raw_param) @@ -586,9 +586,9 @@ def decodeNormal(self, raw_params, params_def): try: val = par_type.getObj(par_str) except ValueError as e: - raise WrongParamType(e.message) + raise WrongParamType from e except UnknownParamObj as e: - raise WrongParam(e.message) + raise WrongParam from e if val is None: msg = 'Could not create %s parameter "%s" for "%s"' % \ (par_type.getName(), name, par_str) diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index cb7fd87906..71a989c4d1 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -1104,7 +1104,7 @@ def start(user_ns=None): try: user_ns.update(get_args(sys.argv)) except exception.SpockException as e: - print(e.message) + print(e) print('Starting normal IPython console') except KeyboardInterrupt: print("\nUser pressed Ctrl+C. Exiting...") diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py index d0e0873d3c..9a168472c6 100644 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ b/src/sardana/spock/ipython_00_11/genutils.py @@ -1181,7 +1181,7 @@ def start(user_ns=None): try: user_ns.update(get_args(sys.argv)) except exception.SpockException as e: - print(e.message) + print(e) print('Starting normal IPython console') except KeyboardInterrupt: print("\nUser pressed Ctrl+C. Exiting...") diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index 973554be89..a4aed9c83f 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -1245,7 +1245,7 @@ def start(user_ns=None): try: user_ns.update(get_args(sys.argv)) except exception.SpockException as e: - print(e.message) + print(e) print('Starting normal IPython console') except KeyboardInterrupt: print("\nUser pressed Ctrl+C. Exiting...") diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index e24d15c39d..30b698ef48 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -1248,7 +1248,7 @@ def terminate(self): try: log_messages.extend(prepare_server(args, tango_args)) except AbortException as e: - print(e.message) + print(e) return except KeyboardInterrupt: print("\nInterrupted by keyboard") diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 2ac6932ccc..b763064d48 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -1287,8 +1287,8 @@ def fromPlainText(self, plainTextMacros, macroInfos): try: macroParams = ParamParser(paramsDef).parse(plainTextParams) except ParseError as e: - msg = "{0} can not be parsed ({1})".format(plainTextMacro, - e.message) + msg = "{0} can not be parsed ({1})".format(plainTextMacro, e) + # TODO: think of using `raise from` syntax raise ValueError(msg) macro.fromList(macroParams) diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py index 948df83b27..ace214903e 100644 --- a/src/sardana/taurus/core/tango/sardana/motion.py +++ b/src/sardana/taurus/core/tango/sardana/motion.py @@ -566,17 +566,17 @@ def test(): m = Motion(["m1", "m2"], [ms1, ms2, ms3], read_only=True) m.startMove([0.5, 20.4]) except Exception as e: - assert(e.message == "Trying to move read only motion") + assert(str(e) == "Trying to move read only motion") try: m = Motion(["m1", "m1"], [ms1, ms2, ms3]) except Exception as e: - assert(e.message == "Moveable item m1 appears more than once") + assert(str(e) == "Moveable item m1 appears more than once") try: m = Motion(["m1", "m999"], [ms1, ms2, ms3]) except Exception as e: - assert(e.message == "Moveable item m999 not found") + assert(str(e) == "Moveable item m999 not found") if __name__ == "__main__": test() From c6b3644d125f2a1b00eefaed925677e9c2fe5f2f Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jul 2019 10:01:11 +0200 Subject: [PATCH 108/830] Move recur_map to sardanautils (avoid duplication of code) The same implementation of recur_map is placed in two different modules. Avoid duplication of code. --- src/sardana/macroserver/msmacromanager.py | 18 +----------------- src/sardana/sardanautils.py | 18 +++++++++++++++++- .../taurus/core/tango/sardana/macroserver.py | 15 ++------------- 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index b34ec2cf31..6033769c0b 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -58,7 +58,7 @@ from sardana.sardanadefs import ElementType from sardana.sardanamodulemanager import ModuleManager from sardana.sardanaexception import format_exception_only_str -from sardana.sardanautils import is_pure_str, is_non_str_seq +from sardana.sardanautils import is_pure_str, is_non_str_seq, recur_map from sardana.macroserver.msmanager import MacroServerManager from sardana.macroserver.msmetamacro import MACRO_TEMPLATE, MacroLibrary, \ @@ -141,22 +141,6 @@ def is_macro(macro, abs_file=None, logger=None): return True -def recur_map(fun, data, keep_none=False): - """Recursive map. Similar to map, but maintains the list objects structure - - :param fun: the same purpose as in map function - :param data: the same purpose as in map function - :param keep_none: keep None elements without applying fun - """ - if hasattr(data, "__iter__") and not isinstance(data, str): - return [recur_map(fun, elem, keep_none) for elem in data] - else: - if keep_none is True and data is None: - return data - else: - return fun(data) - - def is_flat_list(obj): """Check if a given object is a flat list.""" if not isinstance(obj, list): diff --git a/src/sardana/sardanautils.py b/src/sardana/sardanautils.py index a0115e74f2..2be31f671e 100644 --- a/src/sardana/sardanautils.py +++ b/src/sardana/sardanautils.py @@ -31,7 +31,7 @@ __all__ = ["is_pure_str", "is_non_str_seq", "is_integer", "is_number", "is_bool", "check_type", "assert_type", "str_to_value", "is_callable", "translate_version_str2int", - "translate_version_str2list", "py2_round"] + "translate_version_str2list", "py2_round", "recur_map"] __docformat__ = 'restructuredtext' @@ -196,3 +196,19 @@ def py2_round(x, d=0): return float(math.floor((x * p) + 0.5)) / p else: return float(math.ceil((x * p) - 0.5)) / p + + +def recur_map(fun, data, keep_none=False): + """Recursive map. Similar to map, but maintains the list objects structure + + :param fun: the same purpose as in map function + :param data: the same purpose as in map function + :param keep_none: keep None elements without applying fun + """ + if hasattr(data, "__iter__") and not isinstance(data, str): + return [recur_map(fun, elem, keep_none) for elem in data] + else: + if keep_none is True and data is None: + return data + else: + return fun(data) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 72c1d5bfdf..6d0e6642b9 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -53,6 +53,8 @@ from taurus.core.util.codecs import CodecFactory from taurus.core.util.event import EventGenerator, AttributeEventWait from taurus.core.tango import TangoDevice + +from sardana.sardanautils import recur_map from .macro import MacroInfo, Macro, MacroNode, ParamFactory, \ SingleParamNode, ParamNode, createMacroNode from .sardana import BaseSardanaElementContainer, BaseSardanaElement @@ -62,20 +64,7 @@ CHANGE_EVT_TYPES = TaurusEventType.Change, TaurusEventType.Periodic -def recur_map(fun, data, keep_none=False): - """Recursive map. Similar to map, but maintains the list objects structure - :param fun: the same purpose as in map function - :param data: the same purpose as in map function - :param keep_none: keep None elements without applying fun - """ - if hasattr(data, "__iter__"): - return [recur_map(fun, elem, keep_none) for elem in data] - else: - if keep_none is True and data is None: - return data - else: - return fun(data) def _get_console_width(): From 55c958abf4c2fceb8e8dcd7dcb5ce4701f92d7d6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jul 2019 10:06:43 +0200 Subject: [PATCH 109/830] Fix bug in RecorderManager ModuleManager.reloadModule expects reload boolean argument. Current implementation passes reload built-in function. Fix it and don't pass anything using the default value True (inspired on MacroManager). The behavior should not change cause reload built-in should be at the end evaluated to True. --- src/sardana/macroserver/msrecordermanager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sardana/macroserver/msrecordermanager.py b/src/sardana/macroserver/msrecordermanager.py index d00f6a3672..4e2f53cba7 100644 --- a/src/sardana/macroserver/msrecordermanager.py +++ b/src/sardana/macroserver/msrecordermanager.py @@ -271,8 +271,7 @@ def reloadRecorderLib(self, module_name, path=None): mod_manager = ModuleManager() m, exc_info = None, None try: - m = mod_manager.reloadModule( - module_name, path, reload=reload) + m = mod_manager.reloadModule(module_name, path) except: exc_info = sys.exc_info() From 2c57de640adcf570618ef935ba31f47283eee93a Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jul 2019 10:24:14 +0200 Subject: [PATCH 110/830] py3: Implement __lt__ to allow sorting of macros Implement __lt__ to allow sorting of macros (MacroClass and MacroFunction) --- src/sardana/macroserver/msmetamacro.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sardana/macroserver/msmetamacro.py b/src/sardana/macroserver/msmetamacro.py index 9128358823..5d8c6e913a 100644 --- a/src/sardana/macroserver/msmetamacro.py +++ b/src/sardana/macroserver/msmetamacro.py @@ -244,6 +244,9 @@ def __init__(self, **kwargs): SardanaClass.__init__(self, **kwargs) Parameterizable.__init__(self) + def __lt__(self, o): + return self.name < o.name + def serialize(self, *args, **kwargs): kwargs = SardanaClass.serialize(self, *args, **kwargs) kwargs = Parameterizable.serialize(self, *args, **kwargs) @@ -271,6 +274,9 @@ def __init__(self, **kwargs): SardanaFunction.__init__(self, **kwargs) Parameterizable.__init__(self) + def __lt__(self, o): + return self.name < o.name + def serialize(self, *args, **kwargs): kwargs = SardanaFunction.serialize(self, *args, **kwargs) kwargs = Parameterizable.serialize(self, *args, **kwargs) From 06f23e55048b6bd8058cc2921c65077c3a45c8c9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jul 2019 10:54:04 +0200 Subject: [PATCH 111/830] py3: adapt to string module removals Several functions have existed both in the string module and as methods on the str type and instances. These have now been removed from the string module. Use them either on string instances or from the str type itself. --- src/sardana/macroserver/recorders/output.py | 6 +++--- src/sardana/sardanameta.py | 3 +-- src/sardana/tango/core/util.py | 2 +- src/sardana/tools/config/pexpect23.py | 3 +-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/sardana/macroserver/recorders/output.py b/src/sardana/macroserver/recorders/output.py index c981d2505a..1ca652ab71 100644 --- a/src/sardana/macroserver/recorders/output.py +++ b/src/sardana/macroserver/recorders/output.py @@ -32,7 +32,6 @@ import numpy import datetime import operator -import string import weakref from taurus.core.util.codecs import CodecFactory @@ -208,7 +207,7 @@ def _startRecordList(self, recordlist): for row in range(empty_row_nb): header[row].append(col_size * " ") for i, l in enumerate(label): - header[i + empty_row_nb].append(string.center(l, col_size)) + header[i + empty_row_nb].append(l.center(col_size)) head = [] for header_row in header: head.append(col_sep.join(header_row)) @@ -266,7 +265,8 @@ def _writeRecord(self, record): cell = cell_data else: cell %= record.data - cell = string.center(cell.strip(), self._col_sizes[i]) + cell = cell.strip() + cell = cell.center(self._col_sizes[i]) cells.append(cell) scan_line = self._col_sep.join(cells) diff --git a/src/sardana/sardanameta.py b/src/sardana/sardanameta.py index 46d793d5a1..fe9d951942 100644 --- a/src/sardana/sardanameta.py +++ b/src/sardana/sardanameta.py @@ -34,7 +34,6 @@ import os import inspect -import string import weakref import linecache import traceback @@ -81,7 +80,7 @@ def getsource(object): or code object. The source code is returned as a single string. An IOError is raised if the source code cannot be retrieved.""" lines, lnum = getsourcelines(object) - return string.join(lines, '') + return str.join('', lines) # End patch around inspect issue http://bugs.python.org/issue993580 # ---------------------------------------------------------------------------- diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 30b698ef48..4bd71ad42d 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -716,7 +716,7 @@ def prepare_server(args, tango_args): inst_name = input( "Please indicate %s instance name: " % server_name) # should be a instance name validator. - valid_set = string.letters + string.digits + '_' + '-' + valid_set = string.ascii_letters + string.digits + '_' + '-' out = ''.join([c for c in inst_name if c not in valid_set]) valid = len(inst_name) > 0 and len(out) == 0 if not valid: diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 20cad4026e..e93e78ad96 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -68,7 +68,6 @@ import sys import time import select - import string import re import struct import resource @@ -1778,7 +1777,7 @@ def which(filename): # Oddly enough this was the one line that made Pexpect # incompatible with Python 1.5.2. #pathlist = p.split (os.pathsep) - pathlist = string.split(p, os.pathsep) + pathlist = str.split(p, os.pathsep) for path in pathlist: f = os.path.join(path, filename) From a17fed0f788c9aa5066e16577468efd898e20d8e Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jul 2019 11:10:25 +0200 Subject: [PATCH 112/830] py3: don't use ensure_ascii kwarg of JSONCodec ensure_ascii kwarg is not documented neither used in Taurus. Seems like this kwarg when used JSONCodec.decode additionally encodes to bytes using utf-8. This breaks the usage in Sardana. The normal use of JSONCodec seems to be enough with encoding and decoding (it already encodes and decodes with utf-8). Remove usage of ensure_ascii. --- src/sardana/tango/pool/MeasurementGroup.py | 5 ++--- src/sardana/taurus/core/tango/sardana/macroserver.py | 5 ++--- src/sardana/taurus/core/tango/sardana/pool.py | 4 ++-- src/sardana/tools/config/sardana.py | 3 +-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 28f90a9cd7..447e1b4003 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -219,7 +219,7 @@ def read_Configuration(self, attr): def write_Configuration(self, attr): data = attr.get_write_value() - cfg = CodecFactory().decode(('json', data), ensure_ascii=True) + cfg = CodecFactory().decode(('json', data)) self.measurement_group.set_configuration_from_user(cfg) def read_NbStarts(self, attr): @@ -251,8 +251,7 @@ def read_Synchronization(self, attr): def write_Synchronization(self, attr): data = attr.get_write_value() - synchronization = CodecFactory().decode(('json', data), - ensure_ascii=True) + synchronization = CodecFactory().decode(('json', data)) # translate dictionary keys synchronization = self._synchronization_str2enum(synchronization) self.measurement_group.synchronization = synchronization diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 6d0e6642b9..fedeeca2dc 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -214,8 +214,7 @@ def get(self, cache=False): for mnt_grp, reply in zip(mnt_grps_names, replies): try: mnt_grp_configs[mnt_grp] = \ - codec.decode(('json', reply.get_data().value), - ensure_ascii=True)[1] + codec.decode(('json', reply.get_data().value))[1] except Exception as e: from taurus.core.util.log import warning warning('Cannot load Measurement group "%s": %s', @@ -923,7 +922,7 @@ def _on_elements_changed(self, evt_src, evt_type, evt_value): if evt_type not in CHANGE_EVT_TYPES: return ret try: - elems = CodecFactory().decode(evt_value.value, ensure_ascii=True) + elems = CodecFactory().decode(evt_value.value) except: self.error("Could not decode element info format=%s len=%s", evt_value.value[0], len(evt_value.value[1])) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 0717c70882..95998899b6 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1361,7 +1361,7 @@ class MGConfiguration(object): def __init__(self, mg, data): self._mg = weakref.ref(mg) if isinstance(data, str): - data = CodecFactory().decode(('json', data), ensure_ascii=True) + data = CodecFactory().decode(('json', data)) self.raw_data = data self.__dict__.update(data) @@ -2252,7 +2252,7 @@ def on_elements_changed(self, evt_src, evt_type, evt_value): elif evt_type not in CHANGE_EVT_TYPES: return try: - elems = CodecFactory().decode(evt_value.value, ensure_ascii=True) + elems = CodecFactory().decode(evt_value.value) except: self.error("Could not decode element info") self.info("value: '%s'", evt_value.value) diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index 92f52f44a9..8bd006c467 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -783,8 +783,7 @@ def loadPool(self): pool_dp = PyTango.DeviceProxy(pool_dev_name) factory = CodecFactory() - elements = factory.decode( - pool_dp.elements, ensure_ascii='True')['new'] + elements = factory.decode(pool_dp.elements) ctrl_classes_info = {} for elem in elements: From 49e43831806d2c89ed4f78dcfa438a68267e9a15 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jul 2019 11:16:47 +0200 Subject: [PATCH 113/830] py3: sys.stdout.write expects str and not bytes In Python 2 it was indifferent to use string or bytes for sys.stdout.write. In Python 3 this can not be bytes. Remove the unnecessary encoding. --- src/sardana/taurus/core/tango/sardana/macroserver.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index fedeeca2dc..64cc86f384 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -734,7 +734,6 @@ def logReceived(self, log_name, output): def write(self, msg, stream=None): if self.isSilent(): return - msg = msg.encode('utf-8') self._output_stream = sys.stdout out = self._output_stream if stream is not None: From 7af21a56b6ef5dc860b360c92219d962cdfad0df Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jul 2019 11:22:39 +0200 Subject: [PATCH 114/830] Adapt travis configuration to use python 3 Use sardana-test:py3 docker image. Remove sardana-test:taurus-support-3.x image (drop support to Taurus 3). Use pip3 and python3. --- .travis.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4d178fc483..5877d7d411 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ services: - docker python: - - "3.6" + - "3.5" env: global: @@ -17,17 +17,16 @@ env: matrix: - TEST="flake8" - - TEST="testsuite" DOCKER_IMG=reszelaz/sardana-test - - TEST="testsuite" DOCKER_IMG=reszelaz/sardana-test:taurus-support-3.x - - TEST="doc" DOCKER_IMG=reszelaz/sardana-test + - TEST="testsuite" DOCKER_IMG=reszelaz/sardana-test:py3 + - TEST="doc" DOCKER_IMG=reszelaz/sardana-test:py3 before_install: # install flake8 to perform python code style check in the script part # install it using pip in order to get the newest version - if [ $TEST == "flake8" ]; then sudo apt-get update -qq ; fi - - if [ $TEST == "flake8" ]; then sudo apt-get install -qq python-pip; fi - - if [ $TEST == "flake8" ]; then sudo pip install flake8; fi + - if [ $TEST == "flake8" ]; then sudo apt-get install -qq python3-pip; fi + - if [ $TEST == "flake8" ]; then sudo pip3 install flake8; fi install: # run reszelaz/sardana-test docker container (Debian8 with sardana-deps) @@ -38,7 +37,7 @@ install: - if [ $TEST == "testsuite" ]; then sleep 10; fi # install sardana in order to create the launcher scripts for servers - - if [ $TEST == "testsuite" ]; then docker exec sardana-test bash -c "cd /sardana && python setup.py install"; fi + - if [ $TEST == "testsuite" ]; then docker exec sardana-test bash -c "cd /sardana && python3 setup.py install"; fi # start Pool and MacroServer necessary for macro tests - if [ $TEST == "testsuite" ]; then docker exec sardana-test supervisorctl start Pool; fi From 7a9ff0a3546363124047dda359f5cd36b96f2965 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jul 2019 11:25:39 +0200 Subject: [PATCH 115/830] Properly use infinite timeout for threading.Event None seems to be the proper way of setting an infinite timeout for the threading.Event objects. float("inf") apparently worked on Python 2 but does not work on Python 3 and raises: OverflowError: timestamp too large to convert to C _PyTime_t Change all timeouts from float("inf") to None. --- src/sardana/macroserver/macros/test/base.py | 14 ++++++++------ .../macroserver/macros/test/macroexecutor.py | 14 ++++++++------ src/sardana/macroserver/macros/test/test_scanct.py | 12 ++++++------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/sardana/macroserver/macros/test/base.py b/src/sardana/macroserver/macros/test/base.py index 660cec9c8c..0b0ea45db0 100644 --- a/src/sardana/macroserver/macros/test/base.py +++ b/src/sardana/macroserver/macros/test/base.py @@ -195,7 +195,7 @@ def setUp(self): self.macro_executor.registerAll() def macro_runs(self, macro_name=None, macro_params=None, - wait_timeout=float("inf"), data=_NOT_PASSED): + wait_timeout=None, data=_NOT_PASSED): """A helper method to create tests that check if the macro can be successfully executed for the given input parameters. It may also optionally perform checks on the outputs from the execution. @@ -206,7 +206,8 @@ class member) If passed, they must be given as a sequence of their string representations. :param wait_timeout: (float) maximum allowed time (in s) for the macro - to finish. By default infinite timeout is used. + to finish. By default infinite timeout is + used (None). :param data: (obj) Optional. If passed, the macro data after the execution is tested to be equal to this. """ @@ -227,14 +228,14 @@ class member) # in a similar way to what is done for macro data def macro_fails(self, macro_name=None, macro_params=None, - wait_timeout=float("inf"), exception=None): + wait_timeout=None, exception=None): """Check that the macro fails to run for the given input parameters :param macro_name: (str) macro name (takes precedence over macro_name class member) :param macro_params: (seq) input parameters for the macro :param wait_timeout: maximum allowed time for the macro to fail. By - default infinite timeout is used. + default infinite timeout (None) is used. :param exception: (str or Exception) if given, an additional check of the type of the exception is done. (IMPORTANT: this is just a comparison of str @@ -274,7 +275,7 @@ def assertStopped(self, msg): self.assertIn(state, stoppedStates, msg) def macro_stops(self, macro_name=None, macro_params=None, stop_delay=0.1, - wait_timeout=float("inf")): + wait_timeout=None): """A helper method to create tests that check if the macro can be successfully stoped (a.k.a. aborted) after it has been launched. @@ -286,7 +287,8 @@ class member) :param stop_delay: (float) Time (in s) to wait between launching the macro and sending the stop command. default=0.1 :param wait_timeout: (float) maximum allowed time (in s) for the macro - to finish. By default infinite timeout is used. + to finish. By default infinite timeout (None) is + used. """ self.macro_executor.run(macro_name=macro_name or self.macro_name, macro_params=macro_params, diff --git a/src/sardana/macroserver/macros/test/macroexecutor.py b/src/sardana/macroserver/macros/test/macroexecutor.py index f52b98d3dc..7f62a53f13 100644 --- a/src/sardana/macroserver/macros/test/macroexecutor.py +++ b/src/sardana/macroserver/macros/test/macroexecutor.py @@ -65,7 +65,7 @@ def _clean(self): self._common.__init__() def run(self, macro_name, macro_params=None, sync=True, - timeout=float("inf")): + timeout=None): """Execute macro. :param macro_name: (string) name of macro to be executed @@ -75,7 +75,8 @@ def run(self, macro_name, macro_params=None, sync=True, :param sync: (bool) whether synchronous or asynchronous call (default is sync=True) :param timeout: (float) timeout (in s) that will be passed to the wait - method, in case of synchronous execution + method, in case of synchronous execution; None means + wait infinitely In asyncrhonous execution method :meth:`~wait` has to be explicitly called. @@ -101,14 +102,15 @@ def _run(self, macro_name, macro_params): raise NotImplementedError('Method _run not implemented in class %s' % self.__class__.__name__) - def wait(self, timeout=float("inf")): + def wait(self, timeout=None): """ Wait until macro is done. Use it in asynchronous executions. - :param timeout: (float) waiting timeout (in s) + :param timeout: (float) waiting timeout (in s); None means wait + infinitely """ - if timeout <= 0: - timeout = float("inf") + if timeout is not None and timeout <= 0: + timeout = None self._wait(timeout) # TODO: workaround: this sleep is necessary to perform multiple tests. diff --git a/src/sardana/macroserver/macros/test/test_scanct.py b/src/sardana/macroserver/macros/test/test_scanct.py index c200937755..46b1b06a0e 100644 --- a/src/sardana/macroserver/macros/test/test_scanct.py +++ b/src/sardana/macroserver/macros/test/test_scanct.py @@ -249,7 +249,7 @@ def setUp(self): unittest.TestCase.setUp(self) ScanctTest.setUp(self) - def macro_runs(self, meas_config, macro_params, wait_timeout=float("inf")): + def macro_runs(self, meas_config, macro_params, wait_timeout=None): motors = [macro_params[0]] ScanctTest.configure_motors(self, motors) ScanctTest.configure_mntgrp(self, meas_config) @@ -263,7 +263,7 @@ def macro_runs(self, meas_config, macro_params, wait_timeout=float("inf")): ScanctTest.check_using_output(self, expected_nb_points) ScanctTest.check_using_data(self, expected_nb_points) - def macro_stops(self, meas_config, macro_params, wait_timeout=float("inf"), + def macro_stops(self, meas_config, macro_params, wait_timeout=None, stop_delay=0.1): motors = [macro_params[0]] ScanctTest.configure_motors(self, motors) @@ -303,7 +303,7 @@ def setUp(self): unittest.TestCase.setUp(self) ScanctTest.setUp(self) - def macro_runs(self, meas_config, macro_params, wait_timeout=float("inf")): + def macro_runs(self, meas_config, macro_params, wait_timeout=None): motors = [macro_params[self.MOT1], macro_params[self.MOT2]] ScanctTest.configure_motors(self, motors) ScanctTest.configure_mntgrp(self, meas_config) @@ -317,7 +317,7 @@ def macro_runs(self, meas_config, macro_params, wait_timeout=float("inf")): ScanctTest.check_using_output(self, expected_nb_points) ScanctTest.check_using_data(self, expected_nb_points) - def macro_stops(self, meas_config, macro_params, wait_timeout=float("inf"), + def macro_stops(self, meas_config, macro_params, wait_timeout=None, stop_delay=0.1): motors = [macro_params[self.MOT1], macro_params[self.MOT2]] ScanctTest.configure_motors(self, motors) @@ -359,7 +359,7 @@ def setUp(self): unittest.TestCase.setUp(self) ScanctTest.setUp(self) - def macro_runs(self, meas_config, macro_params, wait_timeout=float("inf")): + def macro_runs(self, meas_config, macro_params, wait_timeout=None): motors = [macro_params[self.MOT1], macro_params[self.MOT2]] ScanctTest.configure_motors(self, motors) ScanctTest.configure_mntgrp(self, meas_config) @@ -373,7 +373,7 @@ def macro_runs(self, meas_config, macro_params, wait_timeout=float("inf")): ScanctTest.check_using_output(self, expected_nb_points) ScanctTest.check_using_data(self, expected_nb_points) - def macro_stops(self, meas_config, macro_params, wait_timeout=float("inf"), + def macro_stops(self, meas_config, macro_params, wait_timeout=None, stop_delay=0.1): motors = [macro_params[self.MOT1], macro_params[self.MOT2]] ScanctTest.configure_motors(self, motors) From 212596ad6d3f6e3dbaff5c58beaa855a4591c8f8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jul 2019 14:04:28 +0200 Subject: [PATCH 116/830] py3: fix ordering of None with integer In Python 3 None can not be ordered against integer. In Python 2 this was possible. Fix and consider min = None as aspecial case. --- src/sardana/taurus/core/tango/sardana/macro.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index b763064d48..2b3556795c 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -1310,7 +1310,8 @@ def ParamFactory(paramInfo): """ if isinstance(paramInfo.get('type'), list): param = RepeatParamNode(param=paramInfo) - if param.min() > 0: + param_min = param.min() + if param_min is not None and param_min > 0: param.addRepeat() else: param = SingleParamNode(param=paramInfo) From 29bd761076cfff11166838f62c5910a2b32fab00 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jul 2019 17:01:21 +0200 Subject: [PATCH 117/830] py3: fix synch enums interpreation from string In Python2 IntEnum was serialized to "." e.g. "SynchDomain.Time" and we were using a class method `fromStr` to interpret the enumeration objects. In Python3 IntEnum is serialiazed to "" e.g. "0". Use cast to int and synch enum constructor to interpret it as object. --- src/sardana/tango/pool/MeasurementGroup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 447e1b4003..723888700b 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -161,14 +161,14 @@ def _synchronization_str2enum(self, synchronization): for group in synchronization: for param, conf in group.items(): group.pop(param) - param = SynchParam.fromStr(param) + param = SynchParam(int(param)) group[param] = conf # skip repeats cause its value is just a long number if param == SynchParam.Repeats: continue for domain, value in conf.items(): conf.pop(domain) - domain = SynchDomain.fromStr(domain) + domain = SynchDomain(int(domain)) conf[domain] = value return synchronization From 5d6842377440b5e29ec88f7edd671ac06e572a4e Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jul 2019 17:04:08 +0200 Subject: [PATCH 118/830] py3: avoid re-raising exceptions in finally The re-raise of exceptions in finally worked in Python 2 but does not work in Python 3 anymore. Apparently it is not documented anywhere as a good practice to do. Simply re-raise the exception in the except. --- src/sardana/macroserver/scan/gscan.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 8c0660f191..1ccb745d5b 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1004,10 +1004,13 @@ def step_scan(self): endstatus = ScanEndStatus.Normal except StopException: endstatus = ScanEndStatus.Stop + raise except AbortException: endstatus = ScanEndStatus.Abort + raise except Exception: endstatus = ScanEndStatus.Exception + raise finally: self._env["endstatus"] = endstatus self.end() @@ -1016,8 +1019,6 @@ def step_scan(self): if hasattr(macro, 'getHooks'): for hook in macro.getHooks('post-scan'): hook() - else: - raise def scan_loop(self): raise NotImplementedError('Scan method cannot be called by ' From 748a027a1a9f7a9575911b1528c2d150a005cb8b Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jul 2019 17:07:16 +0200 Subject: [PATCH 119/830] py3: remove all macroserver environment files In Python 2 there was only one macroserver environment file (shelve). Now these are three files. Remove all three. --- src/sardana/tango/macroserver/test/base.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/sardana/tango/macroserver/test/base.py b/src/sardana/tango/macroserver/test/base.py index f4d987a02b..e6b35425de 100644 --- a/src/sardana/tango/macroserver/test/base.py +++ b/src/sardana/tango/macroserver/test/base.py @@ -110,12 +110,17 @@ def tearDown(self): "ds_exec_name": "MacroServer", "ds_inst_name": ds_inst_name} ms_properties = os.path.normpath(ms_properties) - try: - os.remove(ms_properties) - except Exception as e: - msg = "Not possible to remove macroserver environment file" - print(msg) - print(("Details: %s" % e)) + extensions = [".bak", ".dat", ".dir"] + for ext in extensions: + name = ms_properties + ext + if not os.path.exists(name): + continue + try: + os.remove(name) + except Exception as e: + msg = "Not possible to remove macroserver environment file" + print(msg) + print(("Details: %s" % e)) if __name__ == '__main__': From 3b29d9483bc34cd317f86d76094261e68cf2cdb0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jul 2019 17:37:50 +0200 Subject: [PATCH 120/830] Correct order of tearDowns in macro tests to avoid unsubscribe problems First unsubscribe from Tango events (`RunStopMacroTestCase.tearDown`) and then delete the MacroServer (`BaseMacroServerTestCase.tearDown`). --- src/sardana/macroserver/macros/test/test_macro.py | 2 +- src/sardana/macroserver/macros/test/test_scanct.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/test/test_macro.py b/src/sardana/macroserver/macros/test/test_macro.py index cca43cd648..6b70d5ea28 100644 --- a/src/sardana/macroserver/macros/test/test_macro.py +++ b/src/sardana/macroserver/macros/test/test_macro.py @@ -46,6 +46,6 @@ def setUp(self): RunMacroTestCase.setUp(self) def tearDown(self): - BaseMacroServerTestCase.tearDown(self) RunMacroTestCase.tearDown(self) + BaseMacroServerTestCase.tearDown(self) unittest.TestCase.tearDown(self) diff --git a/src/sardana/macroserver/macros/test/test_scanct.py b/src/sardana/macroserver/macros/test/test_scanct.py index 46b1b06a0e..e680f2c42b 100644 --- a/src/sardana/macroserver/macros/test/test_scanct.py +++ b/src/sardana/macroserver/macros/test/test_scanct.py @@ -160,9 +160,9 @@ def check_stopped(self): self.assertEqual(state, desired_state, msg) def tearDown(self): + RunStopMacroTestCase.tearDown(self) BaseMacroServerTestCase.tearDown(self) MeasSarTestTestCase.tearDown(self) - RunStopMacroTestCase.tearDown(self) mg_config1 = { From 25dce83fd4174fbdb7fb636c00f07a904f40744c Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jul 2019 17:42:54 +0200 Subject: [PATCH 121/830] py3: workaround for JSONCodec additional encoding/decoding utf-8 This commit will probably get reverted --- src/sardana/taurus/core/tango/sardana/pool.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 95998899b6..76c5170eb8 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1699,6 +1699,9 @@ def getConfigurationAttrEG(self): def setConfiguration(self, configuration): codec = CodecFactory().getCodec('json') f, data = codec.encode(('', configuration)) + # workaround until it is clarified if the JSONCodec correctly encodes + # to utf-8 (taurus-org/taurus#945) + data = data.decode("utf-8") self.write_attribute('configuration', data) def _setConfiguration(self, data): @@ -1828,6 +1831,9 @@ def getSynchronization(self): def setSynchronization(self, synchronization): codec = CodecFactory().getCodec('json') _, data = codec.encode(('', synchronization)) + # workaround until it is clarified if the JSONCodec correctly encodes + # to utf-8 (taurus-org/taurus#945) + data = data.decode("utf-8") self.getSynchronizationObj().write(data) self._last_integ_time = None From 8feb4fb0b934654a975d770de38aad83dd4e2576 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jul 2019 22:12:22 +0200 Subject: [PATCH 122/830] py3: don't import when defining class Sardana import was delayed until class definition, most probably this still, comes from the times when the sardana-taurus widgets were in the Taurus project. Then it had sense cause in Taurus we did not want to have sardana dependency. Since these widgets are now part of Sardana project the import can be advanced. The reason of changing this now is that this way of importing has some problems on Python 3. --- src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index ef0163f992..0bfe330659 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -38,6 +38,7 @@ from taurus.qt.qtgui import resource from sardana.taurus.qt.qtcore.tango.sardana.model import SardanaBaseProxyModel, SardanaTypeTreeItem +from sardana.sardanadefs import ElementType, TYPE_ACQUIRABLE_ELEMENTS from taurus.qt.qtgui.util.ui import UILoadable # Using a plain model and filtering and checking 'Acquirable' in item.itemData().interfaces is more elegant, but things don't get properly sorted... @@ -87,7 +88,6 @@ class SardanaAcquirableProxyModel(SardanaBaseProxyModel): # 'TwoDExpChannel', 'ComChannel', 'IORegister', 'PseudoMotor', # 'PseudoCounter'] - from sardana.sardanadefs import ElementType, TYPE_ACQUIRABLE_ELEMENTS ALLOWED_TYPES = [ElementType[t] for t in TYPE_ACQUIRABLE_ELEMENTS] def filterAcceptsRow(self, sourceRow, sourceParent): From bb368d7eb4bb466a0a41571842628fbb5cddca95 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 15 Jul 2019 18:34:50 +0200 Subject: [PATCH 123/830] Taurus4: use TangoAttrInfo instead of TaurusAttrInfo Taurus4 removed TaurusAttrInfo and moved its implementation to Tango specific part. Use TangoAttrInfo instead. --- .../macroparameterseditor/customeditors/senv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py index 0ad5dfa871..5b95eba76f 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py @@ -26,7 +26,7 @@ from taurus.external.qt import Qt from taurus import Database from taurus.core.taurusbasetypes import TaurusElementType -from taurus.core.taurusdatabase import TaurusAttrInfo +from taurus.core.tango.tangodatabase import TangoAttrInfo from taurus.qt.qtgui.input import TaurusAttrListComboBox from taurus.qt.qtgui.tree import TaurusDbTreeWidget from taurus.qt.qtgui.resource import getThemeIcon @@ -244,7 +244,7 @@ def setModelData(self, editor, model, index): return taurusTreeAttributeItem = selectedItems[0] itemData = taurusTreeAttributeItem.itemData() - if isinstance(itemData, TaurusAttrInfo): + if isinstance(itemData, TangoAttrInfo): model.setData(index, itemData.fullName()) elif column == 2: model.setData(index, editor.currentText()) From b24eeb9f1ee2752402ac9b1578389bcb720fde40 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 15 Jul 2019 18:39:54 +0200 Subject: [PATCH 124/830] Keep reference to Device objects so gc does not destroy them Subscribe and unsubscribe events of taurus.core.util.event.EventGenerator is based on callback and data objects uniqueness and match. ValueBuffer and ValueReferenceBuffer objects use taurus.Device class objects as data. If we don't keep references to them, these can be garbage collected. Keep them in a list between subscribe and unsubscribe of the MeasurementGroup. --- src/sardana/taurus/core/tango/sardana/pool.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 76c5170eb8..e15ef7d92c 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1677,10 +1677,12 @@ def __init__(self, name, **kw): self.__cfg_attr.addListener(self.on_configuration_changed) self._value_buffer_cb = None + self._value_buffer_channels = None codec_name = getattr(sardanacustomsettings, "VALUE_BUFFER_CODEC") self._value_buffer_codec = CodecFactory().getCodec(codec_name) self._value_ref_buffer_cb = None + self._value_ref_buffer_channels = None codec_name = getattr(sardanacustomsettings, "VALUE_REF_BUFFER_CODEC") self._value_ref_buffer_codec = CodecFactory().getCodec(codec_name) @@ -1892,6 +1894,7 @@ def subscribeValueBuffer(self, cb=None): channel's callback :type cb: callable """ + self._value_buffer_channels = [] for channel_info in self.getChannels(): full_name = channel_info["full_name"] value_ref_enabled = channel_info.get("value_ref_enabled", False) @@ -1906,6 +1909,7 @@ def subscribeValueBuffer(self, cb=None): else: value_buffer_obj.subscribeEvent(channel.valueBufferChanged, with_first_event=False) + self._value_buffer_channels.append(channel) def unsubscribeValueBuffer(self, cb=None): """Unsubscribe from channels' value buffer events. If no callback is @@ -1928,6 +1932,7 @@ def unsubscribeValueBuffer(self, cb=None): self._value_buffer_cb = None else: value_buffer_obj.unsubscribeEvent(channel.valueBufferChanged) + self._value_buffer_channels = None def valueRefBufferChanged(self, channel, value_ref_buffer): """Receive value ref buffer updates, pre-process them, and call @@ -1954,6 +1959,7 @@ def subscribeValueRefBuffer(self, cb=None): channel's callback :type cb: callable """ + self._value_ref_buffer_channels = [] for channel_info in self.getChannels(): full_name = channel_info["full_name"] value_ref_enabled = channel_info.get("value_ref_enabled", False) @@ -1970,6 +1976,7 @@ def subscribeValueRefBuffer(self, cb=None): else: value_ref_buffer_obj.subscribeEvent( channel.valueRefBufferChanged, with_first_event=False) + self._value_ref_buffer_channels.append(channel) def unsubscribeValueRefBuffer(self, cb=None): """Unsubscribe from channels' value ref buffer events. If no @@ -1995,6 +2002,7 @@ def unsubscribeValueRefBuffer(self, cb=None): else: value_ref_buffer_obj.unsubscribeEvent( channel.valueRefBufferChanged) + self._value_ref_buffer_channels = None def enableChannels(self, channels): '''Enable acquisition of the indicated channels. From 52b40c1e7d4e02e071ee344c2f80d3395f248ef8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 16 Jul 2019 15:16:33 +0200 Subject: [PATCH 125/830] Move to CANDIDATE * change register -> catalogue * choose categories in plugins catalogue --- doc/source/sep/SEP16.md | 142 +++++++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 47 deletions(-) diff --git a/doc/source/sep/SEP16.md b/doc/source/sep/SEP16.md index ff6047ef1d..88b1534c8f 100644 --- a/doc/source/sep/SEP16.md +++ b/doc/source/sep/SEP16.md @@ -1,4 +1,4 @@ - Title: Plugins (controllers, macros, etc.) register + Title: Plugins (controllers, macros, etc.) catalogue SEP: 16 State: DRAFT Reason: @@ -59,24 +59,96 @@ Design * Sardana organization will no more manage the plugins repositories. These will be managed directly by their developers. * Sardana organization will advice on how to organize the plugin projects. -* Sardana organization will maintain a register of the third party plugins. +* Sardana organization will maintain a catalogue of the third party plugins. -### Register +### Old third-party repositories + +The old third-party repositories([controllers](https://sourceforge.net/p/sardana/controllers.git/ci/master/tree/) +and [macros](https://sourceforge.net/p/sardana/macros.git/ci/master/tree/)) +will stay opened until we move the actively maintained plugins. This process +may continue after this SEP gets ACCEPTED. + +### Advices on how to organize plugins projects + +This SEP leaves to the plugin developer the decision on how to organize the +project. However based on years of experience of developing Sardana plugins +we have observed some common patterns and questions that emerge to the +developer on how to organize the project. The Community will maintain a +document with a set of advices, analysis of possible scenarios and lessons +learnt on how to organize plugins projects but won't give neither clear +answers nor rules. This document is out of the scope of this SEP is +we anticipate it to evolve in the future and be very dynamic. + +### Catalogue A new repository, called sardana-plugins, will be added to the sardana-org GitHub organization. This repository will not contain any of the plugins -itself but will serve as a register of all the plugins. The only role of this -register will be to list all the plugins together with the information +itself but will serve as a catalogue of all the plugins. The only role of this +catalogue will be to list all the plugins together with the information on where to find more information about them e.g. links to the project pages or artifacts. Github searches over this repository will be useful when looking for a plugin. -**Option 1** +The catalogue will group plugins in categories. The categories may change in + the future and new categories may be added. Also plugins may change between + categories in order to facilitate the users the process of finding them. + +Initially the following categories will be created: + +* Hardware - lists plugins for specific hardware like, motor controllers, + counting cards, etc. Example: IcePAP, Pmac, NI6602. +* Instrument - lists plugins for a specific instrument like, tables, + monochromators, attenuators. Example: three-legged table, DCM. +* System - lists plugins for the complete systems e.g. beamlines, laboratories. +* Software/Interfaces - lists plugins for interacting with other control +systems, frameworks e.g. Lima, Tango, Taurus. +* Other - lists plugins that does not meet any other criteria. + +See Appendix 1 for alternative options that were evaluated. + +The sardana-plugins repository will be managed exactly the same +as the sardana repository (administrators, push permissions, etc.). In order +to add a new plugin to the catalogue, one would need to open a PR with the +necessary changes in the catalogue. Anyone interested in receiving updates +on new plugins in the catalogue will just need to subscribe to this GitHub +repository. + +Implementation +-------------- + +### Old third-party repositories + +* Whenever a plugin module is moved away from the old repository it is +necessary to delete this module from the old location. +* A README file gets added to the old repository with information about this +SEP and location of the plugins catalogue. + +### Advices on how to organize plugins projects + +Currently the advices on how to organize plugins projects are written in +this [wiki page](https://github.com/sardana-org/sardana/wiki/How-to-organize-your-plugin-project). +This location may change in the future without affecting this SEP + +### Catalogue + +1. Implement sardana-plugins catalogue using the markdown format with one file + per category. The plugin projects are listed alphabetically within the + category. This format and organization of files may change in the future + not affecting this SEP. +2. Start accepting PR to the sardana-plugins catalogue whenever this SEP gets + into the CANDIDATE state. + +Appendix 1 +---------- +**Alternative Option 1** + +(This option was discarded when discussing the SEP but is kept here for +reference) This is a conservative option. It simply reflects the organization of the current repositories and adds two more categories: Recorders and GUIs. -Register will be divided in the following categories: +Catalogue will be divided in the following categories: * Motor controllers * Pseudomotor controllers * Counter/timer controllers @@ -90,49 +162,25 @@ Register will be divided in the following categories: * Recorders * GUIs -**Option 2** +**Alternative Option 2** -This option is more revolutionary. Register will be divided into the following -categories: -* Hardware - lists plugins for specific hardware like, motor controllers, - counting cards, etc. Example: IcePAP, Pmac, NI6602. -* Instrument - lists plugins for a specific instrument like, tables, - monochromators, attenuators. Example: three-legged table, DCM. -* System - lists plugins for the complete systems e.g. beamlines, laboratories. -* Software - lists plugins for interacting with other control systems, - frameworks e.g. Lima, Tango, Taurus. -* Other - lists plugins that does not meet any other criteria. +(This option was discarded when discussing the SEP but is kept here for +reference) -**Option 3** +Mix of selected categories and alternative option 1. The catalogue +will have all the categories from the alternative option 1 +and the System category from the selected categories. The extra System +category is because maintaining an up-to-date catalogue of plugins from a +system like a beamline is not realistic. -Mix of options 1 and 2. The register will have all the categories from option 1 -and the System category from option 2. This is because maintaining an -up-to-date register of plugins from a system like a beamline is not realistic. -This sardana-plugins repository will be managed exactly the same as the sardana -repository (administrators, push permissions, etc.). In order to add a new -plugin to the register, one would need to open a PR. -Implementation --------------- +Changes +------- + +- 2017-04-03 [reszelaz](https://github.com/reszelaz) Create SEP16 draft +- 2019-07-16 [reszelaz](https://github.com/reszelaz) Rename register to +catalogue +- 2019-07-16 [reszelaz](https://github.com/reszelaz) Move to CANDIDATE after +meeting the DESY, MAXIV and SOLARIS. -1. Implement sardana-plugins register: use the markdown format with one file - per category. -2. Start accepting PR to the sardana-plugins register whenever this SEP gets - into the CANDIDATE state. -3. Remove write permissions to the current third-party repositories - in SourceForge with the Jul18 release. - -Advices on how to manage plugins projects ---------------------------------------------------- - -(Since I anticipate this section to evolve over time it will not be part of -this SEP but will be added to the documentation) - -1. Description of the plugins e.g. the purpose, dependencies, installation - instructions must be documented e.g. README file, project’s wiki pages - or documentation. -2. Related controllers, macros, recorders and GUIs should coexist in the same - repository e.g. IcePAP controller (motor and trigger/gate) and IcePAP - macros. In this case a top level directories in the repository - e.g. controllers and macros could be useful to group them. From 3ca968c43d1193fb301346c69eb73c830d7f5a05 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 18 Jul 2019 10:30:15 +0200 Subject: [PATCH 126/830] Fix synchronization transformation from strings to enums Don't modify dict while iterating over it. Otherwise the information in the dict was randomly not coherent. --- src/sardana/tango/pool/MeasurementGroup.py | 32 ++++++++++++---------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 723888700b..43d08ebda7 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -154,22 +154,24 @@ def _on_measurement_group_changed(self, event_source, event_type, quality=quality, priority=priority, error=error, synch=False) - def _synchronization_str2enum(self, synchronization): - '''Translates synchronization data structure so it uses SynchDomain - enums as keys instead of strings. - ''' - for group in synchronization: - for param, conf in group.items(): - group.pop(param) - param = SynchParam(int(param)) + def _synchronization_str2enum(self, synchronization_str): + """Translates synchronization data structure so it uses + SynchParam and SynchDomain enums as keys instead of strings. + """ + synchronization = [] + for group_str in synchronization_str: + group = {} + for param_str, conf_str in group_str.items(): + param = SynchParam(int(param_str)) + if isinstance(conf_str, dict): + conf = {} + for domain_str, value in conf_str.items(): + domain = SynchDomain(int(domain_str)) + conf[domain] = value + else: + conf = conf_str group[param] = conf - # skip repeats cause its value is just a long number - if param == SynchParam.Repeats: - continue - for domain, value in conf.items(): - conf.pop(domain) - domain = SynchDomain(int(domain)) - conf[domain] = value + synchronization.append(group) return synchronization def always_executed_hook(self): From 2f0a36aa0adb18c261ebcb2bf8451bad8706c2fa Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 20 Jul 2019 16:47:56 +0200 Subject: [PATCH 127/830] Use DeviceProxy instead of taurus in MacroServer Use DeviceProxy instead of taurus to avoid crashes in Py3 See: tango-controls/pytango#292 To be reverted when the above issue gets clarified --- src/sardana/macroserver/scan/gscan.py | 10 ++++- src/sardana/taurus/core/tango/sardana/pool.py | 44 ++++++++++++++----- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 1ccb745d5b..f91bed9a42 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -666,7 +666,10 @@ def _setupEnvironment(self, additional_env): for ci in channels_info: full_name = ci.full_name try: - channel = taurus.Device(full_name) + # Use DeviceProxy instead of taurus to avoid crashes in Py3 + # See: tango-controls/pytango#292 + # channel = taurus.Device(full_name) + channel = PyTango.DeviceProxy(full_name) instrument = channel.instrument except Exception: # full_name of external channels is the name of the attribute @@ -2036,7 +2039,10 @@ def is_measurement_group_compatible(measurement_group): full_name = channel_info["full_name"] name = channel_info["name"] try: - taurus.Device(full_name) + # Use DeviceProxy instead of taurus to avoid crashes in Py3 + # See: tango-controls/pytango#292 + # taurus.Device(full_name) + PyTango.DeviceProxy(full_name) except Exception: # external channels are attributes so Device constructor fails non_compatible_channels.append(name) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index e15ef7d92c..a99072d08d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -92,6 +92,15 @@ } +def _is_referable(channel): + # Equivalent to ExpChannel.isReferable. + # Use DeviceProxy instead of taurus to avoid crashes in Py3 + # See: tango-controls/pytango#292 + if isinstance(channel, str): + channel = DeviceProxy(channel) + return "valueref" in list(map(str.lower, channel.get_attribute_list())) + + class InterruptException(Exception): pass @@ -1447,12 +1456,15 @@ def _build(self): dev_data = tg_dev_chs.get(dev_name) # technical debt: read Value or ValueRef attribute # ideally the source configuration should include this info - channel = Device(dev_name) - if (isinstance(channel, ExpChannel) - and channel.isReferable() + # Use DeviceProxy instead of taurus to avoid crashes in Py3 + # See: tango-controls/pytango#292 + # channel = Device(dev_name) + # if (isinstance(channel, ExpChannel) + # and channel.isReferable() + # and channel_data.get("value_ref_enabled", False)): + if (_is_referable(dev_name) and channel_data.get("value_ref_enabled", False)): attr_name += "Ref" - if dev_data is None: # Build tango device dev = None @@ -1898,9 +1910,11 @@ def subscribeValueBuffer(self, cb=None): for channel_info in self.getChannels(): full_name = channel_info["full_name"] value_ref_enabled = channel_info.get("value_ref_enabled", False) - channel = Device(full_name) - if channel.isReferable() and value_ref_enabled: + # Use DeviceProxy instead of taurus to avoid crashes in Py3 + # See: tango-controls/pytango#292 + if _is_referable(full_name) and value_ref_enabled: continue + channel = Device(full_name) value_buffer_obj = channel.getValueBufferObj() if cb is not None: self._value_buffer_cb = cb @@ -1922,9 +1936,11 @@ def unsubscribeValueBuffer(self, cb=None): for channel_info in self.getChannels(): full_name = channel_info["full_name"] value_ref_enabled = channel_info.get("value_ref_enabled", False) - channel = Device(full_name) - if channel.isReferable() and value_ref_enabled: + # Use DeviceProxy instead of taurus to avoid crashes in Py3 + # See: tango-controls/pytango#292 + if _is_referable(full_name) and value_ref_enabled: continue + channel = Device(full_name) value_buffer_obj = channel.getValueBufferObj() if cb is not None: value_buffer_obj.unsubscribeEvent(self.valueBufferChanged, @@ -1963,11 +1979,13 @@ def subscribeValueRefBuffer(self, cb=None): for channel_info in self.getChannels(): full_name = channel_info["full_name"] value_ref_enabled = channel_info.get("value_ref_enabled", False) - channel = Device(full_name) - if not channel.isReferable(): + # Use DeviceProxy instead of taurus to avoid crashes in Py3 + # See: tango-controls/pytango#292 + if not _is_referable(full_name): continue if not value_ref_enabled: continue + channel = Device(full_name) value_ref_buffer_obj = channel.getValueRefBufferObj() if cb is not None: self._value_ref_buffer_cb = cb @@ -1989,11 +2007,13 @@ def unsubscribeValueRefBuffer(self, cb=None): for channel_info in self.getChannels(): full_name = channel_info["full_name"] value_ref_enabled = channel_info.get("value_ref_enabled", False) - channel = Device(full_name) - if not channel.isReferable(): + # Use DeviceProxy instead of taurus to avoid crashes in Py3 + # See: tango-controls/pytango#292 + if not _is_referable(full_name): continue if not value_ref_enabled: continue + channel = Device(full_name) value_ref_buffer_obj = channel.getValueRefBufferObj() if cb is not None: value_ref_buffer_obj.unsubscribeEvent( From 256b9c52a2f46c742a5e3be9a63cb471a57ecbcf Mon Sep 17 00:00:00 2001 From: cfalcon Date: Tue, 16 Jul 2019 17:12:23 +0200 Subject: [PATCH 128/830] py3: Set etree.tostring encoding Sardana treats the xml as string. This behaviour changes in Py3, since etree.tostring method returns bytes. Set encode to `unicode` in order to fix the problems. --- src/sardana/tango/macroserver/Door.py | 3 ++- src/sardana/taurus/core/tango/sardana/macroserver.py | 8 ++++++-- src/sardana/taurus/core/tango/sardana/test/test_macro.py | 5 +++-- .../qtgui/extra_macroexecutor/favouriteseditor/model.py | 3 ++- .../extra_macroexecutor/macroparameterseditor/model.py | 2 +- .../qt/qtgui/extra_macroexecutor/sequenceeditor/model.py | 3 ++- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py index ccb4e2dd57..c0a0ae0122 100644 --- a/src/sardana/tango/macroserver/Door.py +++ b/src/sardana/tango/macroserver/Door.py @@ -407,7 +407,8 @@ def RunMacro(self, par_str_list): return [] xml_seq = self.door.run_macro(par_str_list, asynch=True) - return [etree.tostring(xml_seq, pretty_print=False)] + return [etree.tostring(xml_seq, encoding='unicode', + pretty_print=False)] def is_RunMacro_allowed(self): return self.get_state() in [Macro.Finished, Macro.Abort, diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 64cc86f384..4bdbc6f5aa 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -566,7 +566,9 @@ def runMacro(self, obj, parameters=[], synch=False): def _runMacro(self, xml, synch=False): if not synch: - return self.command_inout("RunMacro", [etree.tostring(xml)]) + return self.command_inout("RunMacro", + [etree.tostring(xml, + encoding='unicode')]) timeout = self.InteractiveTimeout evt_wait = self._getEventWait() evt_wait.connect(self.getAttribute("state")) @@ -579,7 +581,9 @@ def _runMacro(self, xml, synch=False): # the time stamp resolution is not better than 1 ms. evt_wait.clearEventSet() ts = time.time() - result = self.command_inout("RunMacro", [etree.tostring(xml)]) + result = self.command_inout("RunMacro", + [etree.tostring(xml, + encoding='unicode')]) evt_wait.waitEvent(self.Running, after=ts, timeout=timeout) if synch: evt_wait.waitEvent(self.Running, equal=False, after=ts, diff --git a/src/sardana/taurus/core/tango/sardana/test/test_macro.py b/src/sardana/taurus/core/tango/sardana/test/test_macro.py index 69c1a3df42..99e9c685f8 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_macro.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_macro.py @@ -100,8 +100,9 @@ def _validateXML(self, macronode_xml, expected_xml): :param macronode_xml: macronode lxml.etree :param expected_xml: expected lxml.etree ''' - expected_str = etree.tostring(expected_xml) - macronode_str = etree.tostring(macronode_xml, pretty_print=True) + expected_str = etree.tostring(expected_xml, encoding='unicode') + macronode_str = etree.tostring(macronode_xml, encoding='unicode', + pretty_print=True) msg = "XML encodings are not equal" # TODO: check why macronode_str has an extra whitespace charactger # at the end. strips should not be necessary diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py index 449cb30681..2852137ac2 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py @@ -92,7 +92,8 @@ def toXmlString(self, pretty=False): for macroNode in self.list: listElement.append(macroNode.toXml(withId=False)) xmlTree = etree.ElementTree(listElement) - xmlString = etree.tostring(xmlTree, pretty_print=pretty) + xmlString = etree.tostring(xmlTree, encoding='unicode', + pretty_print=pretty) return xmlString def fromXmlString(self, xmlString): diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py index 24abaa608a..9cfb3d588a 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py @@ -252,4 +252,4 @@ def toXmlString(self): """ xmlElement = self.root().toXml() - return etree.tostring(xmlElement) + return etree.tostring(xmlElement, encoding='unicode') \ No newline at end of file diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py index 52e733ea6f..7055fce8d8 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py @@ -239,7 +239,8 @@ def nodeFromIndex(self, index): def toXmlString(self, pretty=False, withId=True): xmlSequence = self.root().toXml(withId=withId) xmlTree = etree.ElementTree(xmlSequence) - xmlString = etree.tostring(xmlTree, pretty_print=pretty) + xmlString = etree.tostring(xmlTree, encoding='unicode', + pretty_print=pretty) return xmlString def fromXmlString(self, xmlString): From c77c2e875a8a76b397c879e9f32bf0bf2541b143 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 20 Jul 2019 18:28:15 +0200 Subject: [PATCH 129/830] py3: fix MS environment by not forcing pickle protocol = 0 Use default Python pickle protocol (in Pytnon 3 it is version 3) instead of forcing it to 0. Prior to using MS environment created with Python 2 and previous versions of Sardana one needs to upgrade it with the upgrade_env.py script. --- scripts/upgrade/upgrade_env.py | 68 +++++++++++++++++++++++++ src/sardana/macroserver/msenvmanager.py | 3 +- 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 scripts/upgrade/upgrade_env.py diff --git a/scripts/upgrade/upgrade_env.py b/scripts/upgrade/upgrade_env.py new file mode 100644 index 0000000000..ff6cb94b7c --- /dev/null +++ b/scripts/upgrade/upgrade_env.py @@ -0,0 +1,68 @@ +# This serves to upgrade MacroServer environment from Python 2 to Python 3: + +# IMPORTANT: IT HAS TO BE USED WITH PYTHON 2!!! + +# Usage: python upgrade_env.py + +# From: https://stackoverflow.com/questions/27493733/use-python-2-shelf-in-python-3 +# Thanks to Eric Myers + +import os +import sys +import shelve +import dumbdbm + +import PyTango + + +DefaultEnvBaseDir = "/tmp/tango" +DefaultEnvRelDir = "%(ds_exec_name)s/%(ds_inst_name)s/macroserver.properties" + + +def get_ms_properties(ms_name, ms_ds_name): + db = PyTango.Database() + prop = db.get_device_property(ms_name, "EnvironmentDb") + ms_properties = prop["EnvironmentDb"] + if not ms_properties: + dft_ms_properties = os.path.join( + DefaultEnvBaseDir, + DefaultEnvRelDir) + ds_inst_name = ms_ds_name.split("/")[1] + ms_properties = dft_ms_properties % { + "ds_exec_name": "MacroServer", + "ds_inst_name": ds_inst_name} + ms_properties = os.path.normpath(ms_properties) + return ms_properties + + +def dumbdbm_shelve(filename, flag="c"): + return shelve.Shelf(dumbdbm.open(filename, flag)) + + +def upgrade_env(ms_name): + db = PyTango.Database() + ms_info = db.get_device_info(ms_name) + ms_ds_name = ms_info.ds_full_name + + env_filename = get_ms_properties(ms_name, ms_ds_name) + env_filename_py2 = env_filename + ".py2" + + os.rename(env_filename, env_filename_py2) + + out_shelf = dumbdbm_shelve(env_filename) + in_shelf = shelve.open(env_filename_py2) + + key_list = in_shelf.keys() + for key in key_list: + out_shelf[key] = in_shelf[key] + + out_shelf.close() + in_shelf.close() + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print "python upgrade_env.py " + sys.exit(1) + ms_name = sys.argv[1] + upgrade_env(ms_name) diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index cccdc2e2ef..da8292daf0 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -118,8 +118,7 @@ def setEnvironmentDb(self, f_name): self.debug("Details:", exc_info=1) raise ose try: - self._env = shelve.open(f_name, flag='c', protocol=0, - writeback=False) + self._env = shelve.open(f_name, flag='c', writeback=False) except: self.error("Failed to create/access environment in %s", f_name) self.debug("Details:", exc_info=1) From 48ce3347f861ee4bd10b766bec8cf8b905f18a4a Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 20 Jul 2019 19:34:35 +0200 Subject: [PATCH 130/830] Bump version 2.8.1-alpha to 3.0.0-alpha --- .bumpversion.cfg | 2 +- src/sardana/release.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4631c0ffd9..ea20b04bbf 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,7 +3,7 @@ commit = True message = Bump version {current_version} to {new_version} tag = False tag_name = {new_version} -current_version = 2.8.1-alpha +current_version = 3.0.0-alpha parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? serialize = {major}.{minor}.{patch}-{release} diff --git a/src/sardana/release.py b/src/sardana/release.py index 1f2354cbfc..c2f71a45a8 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = '2.8.1-alpha' +version = '3.0.0-alpha' # generate version_info and revision (**deprecated** since v 2.1.2--alpha). if '-' in version: From f26579e678ac6f8af4dd03fb7b8fed4bc617e282 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 20 Jul 2019 19:42:08 +0200 Subject: [PATCH 131/830] Update requirements after migration to Py3 --- setup.py | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/setup.py b/setup.py index 43d54bc37c..e275d5e4a0 100644 --- a/setup.py +++ b/setup.py @@ -51,27 +51,17 @@ def get_release_info(): ] requires = [ - 'PyTango (>=7.2.3)', - # when using PyTango < 9 the dependency is >= 0.0.1 and < 0.1.0 - # when using PyTango >= 9 the dependency is >= 0.1.6 - 'itango (>=0.0.1)', - # for Taurus3 requires >= 3.12 (special version from - # taurus-org/taurus@3.x-sdn2.5.1 branch) - # for Taurus4 requires >= 4.5.0 - 'taurus (>= 3.12)', - 'lxml (>=2.1)', - # ordereddict is necessary for Python < 2.6 - 'ordereddict' + 'PyTango (>=9.2.5)', + 'itango (>=0.1.6)', + 'taurus (>= 4.5)', + 'lxml (>=2.3)', ] install_requires = [ - 'PyTango>=7.2.3', - 'itango>=0.0.1', - # for Taurus3 requires >= 3.10 (special version from - # taurus-org/taurus@3.x-sdn2.5.1 branch) - # for Taurus4 requires >= 4.5.0 - 'taurus>=3.12,!=4.0,!=4.1,!=4.3,!=4.4', - 'lxml>=2.1' + 'PyTango>=9.2.5', + 'itango>=0.1.6', + 'taurus>=4.5,' + 'lxml>=2.3' ] @@ -108,7 +98,7 @@ def get_release_info(): 'Operating System :: POSIX :: Linux', 'Operating System :: Unix', 'Operating System :: OS Independent', - 'Programming Language :: Python', + 'Programming Language :: Python :: 3.5', 'Topic :: Scientific/Engineering', 'Topic :: Software Development :: Libraries', ] From de113d2b84424f7c96240b376a9805fa8652cd5b Mon Sep 17 00:00:00 2001 From: reszelaz Date: Sat, 20 Jul 2019 22:30:58 +0200 Subject: [PATCH 132/830] Fix typo in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e275d5e4a0..13f7a4bbb1 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,7 @@ def get_release_info(): install_requires = [ 'PyTango>=9.2.5', 'itango>=0.1.6', - 'taurus>=4.5,' + 'taurus>=4.5', 'lxml>=2.3' ] From 663cedf7b2ee43cb4f03b70767735d8b2a191e26 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 20 Jul 2019 22:35:26 +0200 Subject: [PATCH 133/830] Bump requirements for check at runtime --- src/sardana/requirements.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/requirements.py b/src/sardana/requirements.py index 12b3e361b5..9372d8564a 100644 --- a/src/sardana/requirements.py +++ b/src/sardana/requirements.py @@ -35,9 +35,9 @@ __requires__ = { # module minimum - "Python": (2, 6, 0), - "PyTango": (7, 2, 3), - "taurus.core": (3, 10), + "Python": (3, 5, 0), + "PyTango": (9, 2, 5), + "taurus.core": (4, 5), } From 6e827ad8ea93a349ecfe2d45df4fb672a9c7a83a Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 21 Jul 2019 18:48:49 +0200 Subject: [PATCH 134/830] Fix flake8 --- scripts/upgrade/upgrade_env.py | 4 +- src/sardana/macroserver/macro.py | 1 - src/sardana/macroserver/macros/discrete.py | 4 +- .../macros/examples/specific_experiments.py | 3 +- src/sardana/macroserver/macros/standard.py | 3 +- .../macroserver/macros/test/__init__.py | 8 +- src/sardana/macroserver/msparameter.py | 6 +- .../macroserver/recorders/sharedmemory.py | 8 +- src/sardana/macroserver/recorders/storage.py | 3 +- src/sardana/macroserver/scan/__init__.py | 4 +- src/sardana/pool/poolbasegroup.py | 3 +- .../poolcontrollers/DummyMotorController.py | 23 +++-- .../pool/poolcontrollers/test/__init__.py | 2 +- src/sardana/pool/poolmotorgroup.py | 3 +- src/sardana/pool/poolpseudocounter.py | 3 +- src/sardana/pool/poolpseudomotor.py | 6 +- .../pool/test/test_measurementgroup.py | 3 +- src/sardana/requirements.py | 15 ++- src/sardana/sardanadefs.py | 32 ++++--- src/sardana/spock/__init__.py | 4 +- src/sardana/spock/genutils.py | 6 +- src/sardana/spock/ipython_00_10/genutils.py | 23 +++-- src/sardana/spock/ipython_00_11/genutils.py | 20 ++-- src/sardana/spock/ipython_01_00/genutils.py | 20 ++-- src/sardana/spock/magic.py | 4 +- src/sardana/spock/spockms.py | 19 ++-- src/sardana/tango/core/util.py | 2 +- .../tango/macroserver/test/__init__.py | 4 +- src/sardana/tango/pool/Pool.py | 3 +- src/sardana/tango/pool/PoolDevice.py | 3 +- src/sardana/tango/pool/test/__init__.py | 12 +-- src/sardana/tango/pool/test/base.py | 6 +- .../tango/pool/test/test_measurementgroup.py | 3 +- src/sardana/taurus/core/tango/sardana/pool.py | 20 ++-- .../taurus/core/tango/sardana/sardana.py | 18 ++-- .../taurus/qt/qtgui/extra_hkl/__init__.py | 6 +- .../taurus/qt/qtgui/extra_hkl/hklscan.py | 3 +- .../favouriteseditor/__init__.py | 4 +- .../macroparameterseditor/__init__.py | 5 +- .../customeditors/__init__.py | 2 +- .../macroparameterseditor/model.py | 2 +- .../sequenceeditor/__init__.py | 2 +- .../taurus/qt/qtgui/extra_pool/__init__.py | 14 +-- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 9 +- .../qt/qtgui/extra_sardana/expdescription.py | 21 +++-- .../qtgui/extra_sardana/measurementgroup.py | 3 +- .../qtgui/extra_sardana/sardanabasewizard.py | 2 +- src/sardana/tools/config/get_pool_config.py | 29 ++++-- src/sardana/tools/config/pexpect23.py | 35 ++++--- src/sardana/tools/config/sardana.py | 93 ++++++++++++------- src/sardana/util/motion/motion.py | 22 ++--- 51 files changed, 335 insertions(+), 218 deletions(-) diff --git a/scripts/upgrade/upgrade_env.py b/scripts/upgrade/upgrade_env.py index ff6cb94b7c..6743633c66 100644 --- a/scripts/upgrade/upgrade_env.py +++ b/scripts/upgrade/upgrade_env.py @@ -4,7 +4,7 @@ # Usage: python upgrade_env.py -# From: https://stackoverflow.com/questions/27493733/use-python-2-shelf-in-python-3 +# From: https://stackoverflow.com/questions/27493733/use-python-2-shelf-in-python-3 # noqa # Thanks to Eric Myers import os @@ -62,7 +62,7 @@ def upgrade_env(ms_name): if __name__ == "__main__": if len(sys.argv) != 2: - print "python upgrade_env.py " + print "python upgrade_env.py " # noqa sys.exit(1) ms_name = sys.argv[1] upgrade_env(ms_name) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 174f86b56f..a3884d0463 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -27,7 +27,6 @@ scan""" - import collections import numbers diff --git a/src/sardana/macroserver/macros/discrete.py b/src/sardana/macroserver/macros/discrete.py index f8544f9336..76ea8bf74c 100644 --- a/src/sardana/macroserver/macros/discrete.py +++ b/src/sardana/macroserver/macros/discrete.py @@ -65,7 +65,9 @@ def add_point(self, label, pos, setpos, dmin, dmax): else: point['set'] = float(setpos) # If point exists, we use current min, max values - if label in list(self.keys()) and math.isinf(dmin) and math.isinf(dmax): + if (label in list(self.keys()) + and math.isinf(dmin) + and math.isinf(dmax)): p = self[label] min_pos = point['set'] + p['set'] - p['min'] max_pos = point['set'] + p['set'] - p['max'] diff --git a/src/sardana/macroserver/macros/examples/specific_experiments.py b/src/sardana/macroserver/macros/examples/specific_experiments.py index 80fb4259b5..b997b935df 100644 --- a/src/sardana/macroserver/macros/examples/specific_experiments.py +++ b/src/sardana/macroserver/macros/examples/specific_experiments.py @@ -72,7 +72,8 @@ def prepare(self, start, final, nr_interv, integ_time, **opts): for n, e in self.getElementsWithInterface('Instrument').items(): inst = e.getObj() # ,inst.getElements() - print(n, e.name, inst.getFullName(), type(e), type(inst), type(inst.getPoolObj())) + print(n, e.name, inst.getFullName(), type(e), type(inst), + type(inst.getPoolObj())) # maybe I should use the instrument interface to obtain the right # counters diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index fc8433da18..7cc2ffba2d 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -374,7 +374,8 @@ def run(self, motor_list): if show_ctrlaxis: valctrl = str_fmt % (ctrl_name) valaxis = str_fmt % str(axis_nb) - upos = list(map(str, [valctrl, valaxis, ' ', val2, val1, val3])) + upos = list(map(str, [valctrl, valaxis, ' ', val2, val1, + val3])) else: upos = list(map(str, ['', val2, val1, val3])) pos_data = upos diff --git a/src/sardana/macroserver/macros/test/__init__.py b/src/sardana/macroserver/macros/test/__init__.py index c827bdc1ee..0ecb57b628 100644 --- a/src/sardana/macroserver/macros/test/__init__.py +++ b/src/sardana/macroserver/macros/test/__init__.py @@ -23,7 +23,7 @@ ## ############################################################################## -from .macroexecutor import BaseMacroExecutor, MacroExecutorFactory -from .base import (macroTest, BaseMacroTestCase, RunMacroTestCase, - RunStopMacroTestCase, testRun, testFail, testStop) -from .sardemoenv import * +from .macroexecutor import BaseMacroExecutor, MacroExecutorFactory # noqa +from .base import (macroTest, BaseMacroTestCase, RunMacroTestCase, # noqa + RunStopMacroTestCase, testRun, testFail, testStop) # noqa +from .sardemoenv import * # noqa diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py index f10c2bc968..c43edc298a 100644 --- a/src/sardana/macroserver/msparameter.py +++ b/src/sardana/macroserver/msparameter.py @@ -252,7 +252,8 @@ def getObjDict(self, pool=ParamType.All, cache=False): for elem_info in pool.getElements(): if self.accepts(elem_info): objs[elem_info.name] = elem_info - for macro_lib_name, macro_lib in list(macro_server.get_macros().items()): + macros = macro_server.get_macros() + for macro_lib_name, macro_lib in list(macros.items()): if self.accepts(macro_lib): objs[macro_lib_name] = macro_lib for macro_name, macro in list(macro_server.get_macros().items()): @@ -325,7 +326,8 @@ def getObjDict(self, pool=ParamType.All, cache=False): else: pools = macro_server.get_pool(pool), for pool in pools: - for elem_info in list(pool.getElementsWithInterface(self._name).values()): + elements = pool.getElementsWithInterface(self._name) + for elem_info in list(elements.values()): if self.accepts(elem_info): objs[elem_info.name] = elem_info return objs diff --git a/src/sardana/macroserver/recorders/sharedmemory.py b/src/sardana/macroserver/recorders/sharedmemory.py index e0eb9e31fc..e2622ea96a 100644 --- a/src/sardana/macroserver/recorders/sharedmemory.py +++ b/src/sardana/macroserver/recorders/sharedmemory.py @@ -144,9 +144,11 @@ def _writeRecord(self, record): for colname in self.labels: val = record.data.get(colname) - if (not val is None) and (isinstance(val, numbers.Number) and (type(val) in [int, float])): + if ((val is not None) + and (isinstance(val, numbers.Number) + and (type(val) in [int, float]))): vals.append(val) - elif (not val is None) and (isinstance(val, numbers.Number)): + elif (val is not None) and (isinstance(val, numbers.Number)): valsmca = [] for i in range(0, len(val)): valsmca.append(val[i]) @@ -272,7 +274,7 @@ def _writeRecord(self, record): for colname in self.labels: dim_list.append(0) val = record.data.get(colname) - if (not val is None) and (type(val) in [int, float]): + if (val is not None) and (type(val) in [int, float]): vals.append(val) myj = 0 diff --git a/src/sardana/macroserver/recorders/storage.py b/src/sardana/macroserver/recorders/storage.py index 89bbca0ae1..b54605385f 100644 --- a/src/sardana/macroserver/recorders/storage.py +++ b/src/sardana/macroserver/recorders/storage.py @@ -137,7 +137,8 @@ def _startRecordList(self, recordlist): self.fd.write("!\n! Parameter\n!\n%p\n") self.fd.flush() env = self.macro().getAllEnv() - if 'FlagFioWriteMotorPositions' in env and env['FlagFioWriteMotorPositions']: + if ('FlagFioWriteMotorPositions' in env + and env['FlagFioWriteMotorPositions']): all_motors = sorted( self.macro().findObjs('.*', type_class=Type.Motor)) for mot in all_motors: diff --git a/src/sardana/macroserver/scan/__init__.py b/src/sardana/macroserver/scan/__init__.py index 79e15c718d..8d00d3298f 100644 --- a/src/sardana/macroserver/scan/__init__.py +++ b/src/sardana/macroserver/scan/__init__.py @@ -27,5 +27,5 @@ __docformat__ = 'restructuredtext' -from .scandata import * -from .gscan import * +from .scandata import * # noqa +from .gscan import * # noqa diff --git a/src/sardana/pool/poolbasegroup.py b/src/sardana/pool/poolbasegroup.py index f878bea301..cf09ed1401 100644 --- a/src/sardana/pool/poolbasegroup.py +++ b/src/sardana/pool/poolbasegroup.py @@ -317,7 +317,8 @@ def _find_physical_elements(self, element, physical_elements=None, own_elements.add(element) physical_elements_set.add(element) else: - for ctrl, elements in list(element.get_physical_elements().items()): + elem_physical_elements = element.get_physical_elements() + for ctrl, elements in list(elem_physical_elements.items()): own_elements = physical_elements.get(ctrl) if own_elements is None: physical_elements[ctrl] = own_elements = set() diff --git a/src/sardana/pool/poolcontrollers/DummyMotorController.py b/src/sardana/pool/poolcontrollers/DummyMotorController.py index c5c6017efd..d95667a234 100644 --- a/src/sardana/pool/poolcontrollers/DummyMotorController.py +++ b/src/sardana/pool/poolcontrollers/DummyMotorController.py @@ -474,21 +474,30 @@ def setPower(self, power): def info(self): print("Small movement =", self.small_motion) print("length =", self.dsplmnt) - print("position where maximum velocity will be reached =", self.curr_max_vel_pos) - print("necessary displacement to reach maximum velocity =", self.curr_dsplmnt_reach_max_vel) - print("necessary displacement to stop from maximum velocity =", self.curr_dsplmnt_reach_min_vel) + print("position where maximum velocity will be reached =", + self.curr_max_vel_pos) + print("necessary displacement to reach maximum velocity =", + self.curr_dsplmnt_reach_max_vel) + print("necessary displacement to stop from maximum velocity =", + self.curr_dsplmnt_reach_min_vel) print("maximum velocity possible =", self.curr_max_vel) print("time at top velocity =", self.curr_at_max_vel_time) print("displacement at top velocity =", self.curr_at_max_vel_dsplmnt) print("time to reach maximum velocity =", self.curr_max_vel_time) print("time to reach minimum velocity =", self.curr_min_vel_time) print("time the motion will take =", self.duration) - print("instant when maximum velocity should be reached =", self.curr_max_vel_instant) - print("instant when should start decelerating =", self.curr_min_vel_instant) + print("instant when maximum velocity should be reached =", + self.curr_max_vel_instant) + print("instant when should start decelerating =", + self.curr_min_vel_instant) print("instant the motion will end", self.final_instant) print("") - print("For long movements (where top vel is possible), necessary displacement to reach maximum velocity =", self.dsplmnt_reach_max_vel) - print("For long movements (where top vel is possible), necessary displacement to stop from maximum velocity =", self.dsplmnt_reach_min_vel) + print("For long movements (where top vel is possible), " + "necessary displacement to reach maximum velocity =", + self.dsplmnt_reach_max_vel) + print("For long movements (where top vel is possible), " + "necessary displacement to stop from maximum velocity =", + self.dsplmnt_reach_min_vel) class BasicDummyMotorController(MotorController): diff --git a/src/sardana/pool/poolcontrollers/test/__init__.py b/src/sardana/pool/poolcontrollers/test/__init__.py index 9b5ed21c9e..4b40b38c84 100644 --- a/src/sardana/pool/poolcontrollers/test/__init__.py +++ b/src/sardana/pool/poolcontrollers/test/__init__.py @@ -1 +1 @@ -from .base import * +from .base import * # noqa diff --git a/src/sardana/pool/poolmotorgroup.py b/src/sardana/pool/poolmotorgroup.py index 297cba123f..3e86de5a11 100644 --- a/src/sardana/pool/poolmotorgroup.py +++ b/src/sardana/pool/poolmotorgroup.py @@ -112,7 +112,8 @@ def update(self, cache=True, propagate=1): if not cache: dial_position_values = self.obj.motion.read_dial_position( serial=True) - for motion_obj, position_value in list(dial_position_values.items()): + for motion_obj, position_value in \ + list(dial_position_values.items()): motion_obj.put_dial_position( position_value, propagate=propagate) diff --git a/src/sardana/pool/poolpseudocounter.py b/src/sardana/pool/poolpseudocounter.py index 39f7f1aad9..5aa7b4c123 100644 --- a/src/sardana/pool/poolpseudocounter.py +++ b/src/sardana/pool/poolpseudocounter.py @@ -274,7 +274,8 @@ def set_action_cache(self, action_cache): def get_siblings(self): if self._siblings is None: self._siblings = siblings = set() - for axis, sibling in list(self.controller.get_element_axis().items()): + for axis, sibling in \ + list(self.controller.get_element_axis().items()): if axis == self.axis: continue siblings.add(sibling) diff --git a/src/sardana/pool/poolpseudomotor.py b/src/sardana/pool/poolpseudomotor.py index a3282db844..15cd310521 100644 --- a/src/sardana/pool/poolpseudomotor.py +++ b/src/sardana/pool/poolpseudomotor.py @@ -227,7 +227,8 @@ def update(self, cache=True, propagate=1): serial=True) if not len(dial_position_values): self._local_timestamp = time.time() - for motion_obj, position_value in list(dial_position_values.items()): + for motion_obj, position_value in \ + list(dial_position_values.items()): motion_obj.put_dial_position( position_value, propagate=propagate) @@ -293,7 +294,8 @@ def set_action_cache(self, action_cache): def get_siblings(self): if self._siblings is None: self._siblings = siblings = set() - for axis, sibling in list(self.controller.get_element_axis().items()): + for axis, sibling in \ + list(self.controller.get_element_axis().items()): if axis == self.axis: continue siblings.add(sibling) diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index 5d589ed245..e8c359ae32 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -215,7 +215,8 @@ def meas_cont_stop_acquisition(self, config, synchronization, msg = "The number of busy workers is not zero; numBW = %s" % (numBW) self.assertEqual(numBW, 0, msg) # print the acquisition records - for i, record in enumerate(zip(*list(self.attr_listener.data.values()))): + for i, record in \ + enumerate(zip(*list(self.attr_listener.data.values()))): print(i, record) def meas_contpos_acquisition(self, config, synchronization, moveable, diff --git a/src/sardana/requirements.py b/src/sardana/requirements.py index 9372d8564a..b8ccccb904 100644 --- a/src/sardana/requirements.py +++ b/src/sardana/requirements.py @@ -58,7 +58,8 @@ def check_requirements(exec_name=None): pyver_str = ".".join(map(str, pyver)) if pyver < pyver_: - print("Sardana requires python %s. Installed version is %s" % (pyver_str_, pyver_str)) + print("Sardana requires python %s. Installed version is %s" % + (pyver_str_, pyver_str)) sys.exit(-1) pytangover = None @@ -71,11 +72,13 @@ def check_requirements(exec_name=None): pytangover = tuple(map(int, PyTango.__version__.split('.', 3))) if pytangover is None: - print("%s requires PyTango %s. No version installed" % (exec_name, pytangover_str_,)) + print("%s requires PyTango %s. No version installed" % + (exec_name, pytangover_str_,)) sys.exit(-1) if pytangover < pytangover_: pytangover_str = ".".join(map(str, pytangover)) - print("%s requires PyTango %s. Installed version is %s" % (exec_name, pytangover_str_, pytangover_str)) + print("%s requires PyTango %s. Installed version is %s" % + (exec_name, pytangover_str_, pytangover_str)) sys.exit(-1) # TODO: add itango as runtime dependency of spock @@ -92,11 +95,13 @@ def check_requirements(exec_name=None): taurusver = tuple(map(int, taurus.Release.version.split('.', 3))) if taurusver is None: - print("%s requires taurus %s. No version installed" % (exec_name, taurusver_str_,)) + print("%s requires taurus %s. No version installed" % + (exec_name, taurusver_str_,)) sys.exit(-1) if taurusver < taurusver_: taurusver_str = ".".join(map(str, taurusver)) - print("%s requires taurus %s. Installed version is %s" % (exec_name, taurusver_str_, taurusver_str)) + print("%s requires taurus %s. Installed version is %s" % + (exec_name, taurusver_str_, taurusver_str)) sys.exit(-1) try: diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index 385db4dcdd..08e5e3d328 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -309,9 +309,9 @@ def to_daccess(daccess): #: a set containning all "controllable" element types. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` TYPE_ELEMENTS = {ET.Motor, ET.CTExpChannel, ET.ZeroDExpChannel, - ET.OneDExpChannel, ET.TwoDExpChannel, ET.TriggerGate, - ET.ComChannel, ET.IORegister, ET.PseudoMotor, - ET.PseudoCounter, ET.Constraint} + ET.OneDExpChannel, ET.TwoDExpChannel, ET.TriggerGate, + ET.ComChannel, ET.IORegister, ET.PseudoMotor, + ET.PseudoCounter, ET.Constraint} #: a set containing all group element types. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` @@ -324,30 +324,31 @@ def to_daccess(daccess): #: a set containing the possible types of physical elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` TYPE_PHYSICAL_ELEMENTS = {ET.Motor, ET.CTExpChannel, ET.ZeroDExpChannel, - ET.OneDExpChannel, ET.TwoDExpChannel, ET.TriggerGate, - ET.ComChannel, ET.IORegister} + ET.OneDExpChannel, ET.TwoDExpChannel, ET.TriggerGate, + ET.ComChannel, ET.IORegister} #: a set containing the possible types of acquirable elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` TYPE_ACQUIRABLE_ELEMENTS = {ET.Motor, ET.CTExpChannel, ET.ZeroDExpChannel, - ET.OneDExpChannel, ET.TwoDExpChannel, - ET.ComChannel, ET.IORegister, ET.PseudoMotor, - ET.PseudoCounter} + ET.OneDExpChannel, ET.TwoDExpChannel, + ET.ComChannel, ET.IORegister, ET.PseudoMotor, + ET.PseudoCounter} #: a set containing the possible measure-able elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` TYPE_COUNTABLE_ELEMENTS = {ET.CTExpChannel, ET.OneDExpChannel, - ET.TwoDExpChannel, ET.MeasurementGroup} + ET.TwoDExpChannel, ET.MeasurementGroup} #: a set containing the possible types of experimental channel elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` TYPE_EXP_CHANNEL_ELEMENTS = {ET.CTExpChannel, ET.ZeroDExpChannel, - ET.OneDExpChannel, ET.TwoDExpChannel, ET.PseudoCounter} + ET.OneDExpChannel, ET.TwoDExpChannel, + ET.PseudoCounter} #: a set containing the possible timer-able elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` TYPE_TIMERABLE_ELEMENTS = {ET.CTExpChannel, ET.OneDExpChannel, - ET.TwoDExpChannel} + ET.TwoDExpChannel} #: a set containing the possible types of pseudo elements. #: Constant values belong to :class:`~sardana.sardanadefs.ElementType` @@ -430,13 +431,16 @@ def to_daccess(daccess): "External": ({"Object"}, "An external object"), "MacroServerObject": ({"Object"}, "A generic macro server object"), - "MacroServerElement": ({"Element", "MacroServerObject"}, "A generic macro server element"), + "MacroServerElement": ({"Element", "MacroServerObject"}, + "A generic macro server element"), "MacroServer": ({"MacroServerElement"}, "A MacroServer"), "Door": ({"MacroServerElement"}, "A macro server door"), - "MacroLibrary": ({"Library", "MacroServerObject"}, "A macro server library"), + "MacroLibrary": ({"Library", "MacroServerObject"}, + "A macro server library"), "MacroCode": ({"MacroServerObject"}, "A macro server macro code"), "MacroClass": ({"Class", "MacroCode"}, "A macro server macro class"), - "MacroFunction": ({"Function", "MacroCode"}, "A macro server macro function"), + "MacroFunction": ({"Function", "MacroCode"}, + "A macro server macro function"), "Macro": ({"MacroClass", "MacroFunction"}, "A macro server macro"), "ParameterType": ({"Meta"}, "A generic macro server parameter type"), diff --git a/src/sardana/spock/__init__.py b/src/sardana/spock/__init__.py index dbeef274e7..12d6d6f5f6 100644 --- a/src/sardana/spock/__init__.py +++ b/src/sardana/spock/__init__.py @@ -26,8 +26,8 @@ """This package provides spock""" -from .genutils import load_ipython_extension, unload_ipython_extension, \ - load_config, run +from .genutils import (load_ipython_extension, unload_ipython_extension, # noqa + load_config, run) # noqa def main(): diff --git a/src/sardana/spock/genutils.py b/src/sardana/spock/genutils.py index b41840c247..e1c8dcb60b 100644 --- a/src/sardana/spock/genutils.py +++ b/src/sardana/spock/genutils.py @@ -66,8 +66,8 @@ def get_ipython_version_list(): ipv = get_ipython_version_list() if ipv >= [0, 10] and ipv < [0, 11]: - from .ipython_00_10.genutils import * + from .ipython_00_10.genutils import * # noqa elif ipv >= [0, 11] and ipv < [1, 0]: - from .ipython_00_11.genutils import * + from .ipython_00_11.genutils import * # noqa else: - from .ipython_01_00.genutils import * + from .ipython_01_00.genutils import * # noqa diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py index 71a989c4d1..586e41d1d4 100644 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ b/src/sardana/spock/ipython_00_10/genutils.py @@ -373,7 +373,8 @@ def get_device_from_user(expected_class, dft=None): cl_name = db.get_class_for_device(name) class_correct = cl_name == expected_class if not class_correct: - print("Warning: the given name is not a %s (it is a %s)" % (expected_class, cl_name)) + print("Warning: the given name is not a %s (it is a %s)" % + (expected_class, cl_name)) except Exception as e: print("Warning: unable to confirm if '%s' is valid" % name) print(str(e)) @@ -817,20 +818,24 @@ def check_for_upgrade(ipy_profile_file, ipythondir, session, profile): alpha_in_spock_profile == alpha_in_spock_lib: return if spocklib_ver < spock_profile_ver: - print('%sYour spock profile (%s) is newer than your spock version ' \ - '(%s)!' % (TermColors.Brown, spock_profile_ver_str, spock_lib_ver_str)) - print('Please upgrade spock or delete the current profile %s' % TermColors.Normal) + print('%sYour spock profile (%s) is newer than your spock version ' + '(%s)!' % (TermColors.Brown, spock_profile_ver_str, + spock_lib_ver_str)) + print('Please upgrade spock or delete the current profile %s' % + TermColors.Normal) sys.exit(1) # there was no version track of spock profiles since spock 0.2.0 so change # the message if spock_profile_ver_str == '0.0.0': spock_profile_ver_str = '<= 0.2.0' - msg = 'Your current spock door extension profile has been created with spock %s.\n' \ - 'Your current spock door extension version is %s, therefore a profile upgrade is needed.\n' \ - % (spock_profile_ver_str, spock_lib_ver_str) - print(msg) - prompt = 'Do you wish to upgrade now (warn: this will shutdown the current spock session) ([y]/n)? ' + print('Your current spock door extension profile has been created with ' + 'spock %s.\n' + 'Your current spock door extension version is %s, therefore a ' + 'profile upgrade is needed.\n' + % (spock_profile_ver_str, spock_lib_ver_str)) + prompt = ('Do you wish to upgrade now (warn: this will shutdown the ' + 'current spock session) ([y]/n)? ') r = input(prompt) or 'y' if r.lower() == 'y': create_spock_profile(ipythondir, session, profile, door_name) diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py index 9a168472c6..ad1931bb86 100644 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ b/src/sardana/spock/ipython_00_11/genutils.py @@ -323,7 +323,8 @@ def get_device_from_user(expected_class, dft=None): cl_name = db.get_class_for_device(name) class_correct = cl_name == expected_class if not class_correct: - print("Warning: the given name is not a %s (it is a %s)" % (expected_class, cl_name)) + print("Warning: the given name is not a %s (it is a %s)" % + (expected_class, cl_name)) except Exception as e: print("Warning: unable to confirm if '%s' is valid" % name) print(str(e)) @@ -805,21 +806,24 @@ def check_for_upgrade(ipy_profile_dir): alpha_in_spock_profile == alpha_in_spock_lib: return if spocklib_ver < spock_profile_ver: - print('%sYour spock profile (%s) is newer than your spock version ' \ + print('%sYour spock profile (%s) is newer than your spock version ' '(%s)!' % (SpockTermColors.Brown, spock_profile_ver_str, spock_lib_ver_str)) - print('Please upgrade spock or delete the current profile %s' % SpockTermColors.Normal) + print('Please upgrade spock or delete the current profile %s' % + SpockTermColors.Normal) sys.exit(1) # there was no version track of spock profiles since spock 0.2.0 so change # the message if spock_profile_ver_str == '0.0.0': spock_profile_ver_str = '<= 0.2.0' - msg = 'Your current spock door extension profile has been created with spock %s.\n' \ - 'Your current spock door extension version is %s, therefore a profile upgrade is needed.\n' \ - % (spock_profile_ver_str, spock_lib_ver_str) - print(msg) - prompt = 'Do you wish to upgrade now (warn: this will shutdown the current spock session) ([y]/n)? ' + print('Your current spock door extension profile has been created with ' + 'spock %s.\n' + 'Your current spock door extension version is %s, therefore a ' + 'profile upgrade is needed.\n' % (spock_profile_ver_str, + spock_lib_ver_str)) + prompt = ('Do you wish to upgrade now (warn: this will shutdown the ' + 'current spock session) ([y]/n)? ') r = input(prompt) or 'y' if r.lower() == 'y': upgrade_spock_profile(ipy_profile_dir, door_name) diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index a4aed9c83f..d3f81f34dc 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -331,7 +331,8 @@ def get_device_from_user(expected_class, dft=None): cl_name = db.get_class_for_device(name) class_correct = cl_name == expected_class if not class_correct: - print("Warning: the given name is not a %s (it is a %s)" % (expected_class, cl_name)) + print("Warning: the given name is not a %s (it is a %s)" % + (expected_class, cl_name)) except Exception as e: print("Warning: unable to confirm if '%s' is valid" % name) print(str(e)) @@ -850,21 +851,24 @@ def check_for_upgrade(ipy_profile_dir): alpha_in_spock_profile == alpha_in_spock_lib: return if spocklib_ver < spock_profile_ver: - print('%sYour spock profile (%s) is newer than your spock version ' \ + print('%sYour spock profile (%s) is newer than your spock version ' '(%s)!' % (SpockTermColors.Brown, spock_profile_ver_str, spock_lib_ver_str)) - print('Please upgrade spock or delete the current profile %s' % SpockTermColors.Normal) + print('Please upgrade spock or delete the current profile %s' % + SpockTermColors.Normal) sys.exit(1) # there was no version track of spock profiles since spock 0.2.0 so change # the message if spock_profile_ver_str == '0.0.0': spock_profile_ver_str = '<= 0.2.0' - msg = 'Your current spock door extension profile has been created with spock %s.\n' \ - 'Your current spock door extension version is %s, therefore a profile upgrade is needed.\n' \ - % (spock_profile_ver_str, spock_lib_ver_str) - print(msg) - prompt = 'Do you wish to upgrade now (warn: this will shutdown the current spock session) ([y]/n)? ' + print('Your current spock door extension profile has been created with ' + 'spock %s.\n' + 'Your current spock door extension version is %s, therefore a ' + 'profile upgrade is needed.\n' + % (spock_profile_ver_str, spock_lib_ver_str)) + prompt = ('Do you wish to upgrade now (warn: this will shutdown the ' + 'current spock session) ([y]/n)? ') r = input(prompt) or 'y' if r.lower() == 'y': upgrade_spock_profile(ipy_profile_dir, door_name) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index edffb1cb18..352271365b 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -42,7 +42,7 @@ def expconf(self, parameter_s=''): try: from sardana.taurus.qt.qtgui.extra_sardana import ExpDescriptionEditor except: - print("Error importing ExpDescriptionEditor " \ + print("Error importing ExpDescriptionEditor " "(hint: is taurus extra_sardana installed?)") return try: @@ -293,7 +293,7 @@ def edmac(self, parameter_s=''): print('Reason:', str(e)) f.close() except: - print('Could not open file \'%s\' for safe transfer to the ' \ + print('Could not open file \'%s\' for safe transfer to the ' 'server' % local_fname) print('Did you forget to save?') else: diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 2cdd00daa9..fcb85e2ef2 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -160,7 +160,8 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): break if local_file is None: print("Cannot plot scan:") - print("Could not find %s in any of the following locations:" % (scan_file,)) + print("Could not find %s in any of the following locations:" % + (scan_file,)) print("\n".join(locations)) return @@ -198,14 +199,15 @@ def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): def plot(self): try: import sps - except: + except Exception: print('sps module not available. No plotting') return try: import pylab - except: - print("pylab not available (try running 'spock -pylab'). No plotting") + except Exception: + print("pylab not available (try running 'spock -pylab'). " + "No plotting") return door = genutils.get_door() @@ -350,7 +352,8 @@ def runMacro(self, obj, parameters=[], synch=False): def _runMacro(self, xml, **kwargs): # kwargs like 'synch' are ignored in this re-implementation if self._spock_state != RUNNING_STATE: - print("Unable to run macro: No connection to door '%s'" % self.getSimpleName()) + print("Unable to run macro: No connection to door '%s'" % + self.getSimpleName()) raise Exception("Unable to run macro: No connection") if xml is None: xml = self.getRunningXML() @@ -377,10 +380,12 @@ def _runMacro(self, xml, **kwargs): print("Unknown parameter:", desc) elif reason == 'MissingEnv': print("Missing environment:", desc) - elif reason in ('API_CantConnectToDevice', 'API_DeviceNotExported'): + elif reason in ('API_CantConnectToDevice', + 'API_DeviceNotExported'): self._updateState(self._old_sw_door_state, TaurusSWDevState.Shutdown, silent=True) - print("Unable to run macro: No connection to door '%s'" % self.getSimpleName()) + print("Unable to run macro: No connection to door '%s'" % + self.getSimpleName()) else: print("Unable to run macro:", reason, desc) diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 4bd71ad42d..2d09e61152 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -706,7 +706,7 @@ def prepare_server(args, tango_args): nodb = "-nodb" in tango_args if nodb and not hasattr(DeviceClass, "device_name_factory"): - print("In order to start %s with 'nodb' you need PyTango >= 7.2.3" %\ + print("In order to start %s with 'nodb' you need PyTango >= 7.2.3" % server_name) sys.exit(1) diff --git a/src/sardana/tango/macroserver/test/__init__.py b/src/sardana/tango/macroserver/test/__init__.py index de2a4fc1b9..23f927f75e 100644 --- a/src/sardana/tango/macroserver/test/__init__.py +++ b/src/sardana/tango/macroserver/test/__init__.py @@ -23,5 +23,5 @@ ## ############################################################################## -from .macroexecutor import TangoMacroExecutor -from .base import * +from .macroexecutor import TangoMacroExecutor # noqa +from .base import * # noqa diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index 17bb051e61..f46e804803 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -622,7 +622,8 @@ def create_element_cb(device_name): # them to the newly created element ctrl_class_info = ctrl.get_ctrl_info() attrs = [] - for attr_name, attr_info in list(ctrl_class_info.getAxisAttributes().items()): + for attr_name, attr_info in \ + list(ctrl_class_info.getAxisAttributes().items()): default_value = attr_info.default_value if default_value is None: continue diff --git a/src/sardana/tango/pool/PoolDevice.py b/src/sardana/tango/pool/PoolDevice.py index 19a5a23d1f..e3247e8191 100644 --- a/src/sardana/tango/pool/PoolDevice.py +++ b/src/sardana/tango/pool/PoolDevice.py @@ -219,7 +219,8 @@ def remove_unwanted_dynamic_attributes(self, new_std_attrs, new_dyn_attrs): dev_class = self.get_device_class() multi_attr = self.get_device_attr() multi_class_attr = dev_class.get_class_attr() - static_attr_names = list(map(str.lower, list(dev_class.attr_list.keys()))) + static_attr_names = \ + list(map(str.lower, list(dev_class.attr_list.keys()))) static_attr_names.extend(('state', 'status')) new_attrs = CaselessDict(new_std_attrs) diff --git a/src/sardana/tango/pool/test/__init__.py b/src/sardana/tango/pool/test/__init__.py index ad1cd75194..a8b698a6c7 100644 --- a/src/sardana/tango/pool/test/__init__.py +++ b/src/sardana/tango/pool/test/__init__.py @@ -23,9 +23,9 @@ ## ############################################################################## -from .base import (BasePoolTestCase, ControllerLoadsTestCase, - ControllerCreationTestCase, ElementCreationTestCase) -from .base_sartest import SarTestTestCase -from .test_measurementgroup import * -from .test_Motor import * -from .test_persistence import * +from .base import (BasePoolTestCase, ControllerLoadsTestCase, # noqa + ControllerCreationTestCase, ElementCreationTestCase) # noqa +from .base_sartest import SarTestTestCase # noqa +from .test_measurementgroup import * # noqa +from .test_Motor import * # noqa +from .test_persistence import * # noqa diff --git a/src/sardana/tango/pool/test/base.py b/src/sardana/tango/pool/test/base.py index fb87a954cc..b9c024ed6e 100644 --- a/src/sardana/tango/pool/test/base.py +++ b/src/sardana/tango/pool/test/base.py @@ -89,9 +89,11 @@ class ControllerLoadsTestCase(BasePoolTestCase): def test_controller_loads(self): """Test that the controller library and class can be loaded. """ - libraries = list(self.pool.getElementsOfType('ControllerLibrary').values()) + libraries = \ + list(self.pool.getElementsOfType('ControllerLibrary').values()) libraries_names = [lib.getName() for lib in libraries] - classes = list(self.pool.getElementsOfType('ControllerClass').values()) + classes = \ + list(self.pool.getElementsOfType('ControllerClass').values()) classes_names = [cls.getName() for cls in classes] for test_lib, test_classes in list(self.controller_classes.items()): diff --git a/src/sardana/tango/pool/test/test_measurementgroup.py b/src/sardana/tango/pool/test/test_measurementgroup.py index 3adce2524b..ed6d7be083 100644 --- a/src/sardana/tango/pool/test/test_measurementgroup.py +++ b/src/sardana/tango/pool/test/test_measurementgroup.py @@ -263,7 +263,8 @@ def tearDown(self): # Delete the meas self.pool.DeleteElement(self.mg_name) except Exception as e: - print(('Impossible to delete MeasurementGroup: %s' % (self.mg_name))) + print('Impossible to delete MeasurementGroup: %s' % + self.mg_name) print(e) SarTestTestCase.tearDown(self) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index a99072d08d..05f7e75f01 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -622,7 +622,8 @@ def removeElement(self, elem): def getElementByAxis(self, axis): pool = self.getPoolObj() - for _, elem in list(pool.getElementsOfType(self.getMainType()).items()): + for _, elem in \ + list(pool.getElementsOfType(self.getMainType()).items()): if (elem.controller != self.getFullName() or elem.getAxis() != axis): continue @@ -630,7 +631,8 @@ def getElementByAxis(self, axis): def getElementByName(self, name): pool = self.getPoolObj() - for _, elem in list(pool.getElementsOfType(self.getMainType()).items()): + for _, elem in \ + list(pool.getElementsOfType(self.getMainType()).items()): if (elem.controller != self.getFullName() or elem.getName() != name): continue @@ -645,7 +647,8 @@ def getUsedAxes(self): pool = self.getPoolObj() axes = [] - for _, elem in list(pool.getElementsOfType(self.getMainType()).items()): + for _, elem in \ + list(pool.getElementsOfType(self.getMainType()).items()): if elem.controller != self.getFullName(): continue axes.append(elem.getAxis()) @@ -1380,13 +1383,14 @@ def __init__(self, mg, data): self.channels = channels = CaselessDict() for _, ctrl_data in list(self.controllers.items()): - for channel_name, channel_data in list(ctrl_data['channels'].items()): + for channel_name, channel_data in \ + list(ctrl_data['channels'].items()): 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 each element is the channel data in form of a dict as @@ -1633,7 +1637,7 @@ def _read_parallel(self): try: dev_replies[dev] = dev.read_attributes_asynch( list(attrs.keys())), attrs - except: + except Exception: dev_replies[dev] = None, attrs # gather all replies @@ -1648,7 +1652,7 @@ def _read_parallel(self): else: value = data_item.value ret[channel_data['full_name']] = value - except: + except Exception: for _, channel_data in list(attrs.items()): ret[channel_data['full_name']] = None diff --git a/src/sardana/taurus/core/tango/sardana/sardana.py b/src/sardana/taurus/core/tango/sardana/sardana.py index 33c74bbf24..2e9ccbec73 100644 --- a/src/sardana/taurus/core/tango/sardana/sardana.py +++ b/src/sardana/taurus/core/tango/sardana/sardana.py @@ -196,7 +196,8 @@ def getElementsWithInterfaces(self, interfaces): return ret def getElementNamesWithInterface(self, interface): - return [e.name for e in list(self.getElementsWithInterface(interface).values())] + return [e.name for e in + list(self.getElementsWithInterface(interface).values())] def hasElementName(self, elem_name): return self.getElement(elem_name) is not None @@ -537,19 +538,22 @@ def _init(self): ms_props = db.get_device_property(ms_dev_name, ms_prop_list) ms_name = dev_info.server().serverInstance() ms_alias = dev_info.alias() - ms = MacroServer(self, ms_name, ms_props.get("macropath"), ms_props.get("poolnames"), + ms = MacroServer(self, ms_name, ms_props.get("macropath"), + ms_props.get("poolnames"), ms_props.get("version"), ms_alias, ms_dev_name) self._macroservers.append(ms) for pool_dev_name in ms_props.get("poolnames", ()): - pool_prop_list = list(map( - str.lower, db.get_device_property_list(pool_dev_name, "*"))) + pool_prop_list = \ + list(map(str.lower, + db.get_device_property_list(pool_dev_name, "*"))) pool_props = db.get_device_property( pool_dev_name, pool_prop_list) pool_dev_info = cache.devices()[pool_dev_name] pool_name = pool_dev_info.server().serverInstance() pool_alias = pool_dev_info.alias() - pool = Pool(self, pool_name, pool_props.get( - "poolpath"), pool_props.get("version"), pool_alias, pool_dev_name) + 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): @@ -652,7 +656,7 @@ def refresh(self): try: sardanas[service_instance] = Sardana( self, service_instance, dev) - except: + except Exception: pass def create_sardana(self, name, device_name): diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py b/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py index 11085946b3..9521c3a919 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py @@ -27,6 +27,6 @@ __init__.py: """ -from .hklscan import HKLScan -from .ubmatrix import UBMatrixBase -from .diffractometeralignment import DiffractometerAlignment +from .hklscan import HKLScan # noqa +from .ubmatrix import UBMatrixBase # noqa +from .diffractometeralignment import DiffractometerAlignment # noqa diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py index bb03f30a5b..d07f8b2f6c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py @@ -378,7 +378,8 @@ def main(): if len(args) > 1: w.onDoorChanged(args[1]) else: - print("WARNING: Not door name supplied. Connection to MacroServer/Door not automatically done") + print("WARNING: Not door name supplied. Connection to " + "MacroServer/Door not automatically done") w.show() sys.exit(app.exec_()) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/__init__.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/__init__.py index d8bf71f660..98d39adcdb 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/__init__.py @@ -23,5 +23,5 @@ ## ############################################################################## -from .favouriteseditor import FavouritesMacrosEditor -from .historyviewer import HistoryMacrosViewer +from .favouriteseditor import FavouritesMacrosEditor # noqa +from .historyviewer import HistoryMacrosViewer # noqa diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/__init__.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/__init__.py index 254043ad22..5aa02a0610 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/__init__.py @@ -26,5 +26,6 @@ """ __init__.py: """ -from .macroparameterseditor import ParamEditorManager, StandardMacroParametersEditor -from .model import ParamEditorModel +from .macroparameterseditor import (ParamEditorManager, # noqa + StandardMacroParametersEditor) # noqa +from .model import ParamEditorModel # noqa diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/__init__.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/__init__.py index cde35af141..b9b532224b 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/__init__.py @@ -23,4 +23,4 @@ ## ############################################################################## -from .senv import SenvEditor +from .senv import SenvEditor # noqa diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py index 9cfb3d588a..a31b2a3abe 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py @@ -252,4 +252,4 @@ def toXmlString(self): """ xmlElement = self.root().toXml() - return etree.tostring(xmlElement, encoding='unicode') \ No newline at end of file + return etree.tostring(xmlElement, encoding='unicode') diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/__init__.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/__init__.py index 623433cbe7..20d6a8932a 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/__init__.py @@ -27,4 +27,4 @@ __init__.py: """ -from .sequenceeditor import TaurusSequencer, TaurusSequencerWidget, main +from .sequenceeditor import TaurusSequencer, TaurusSequencerWidget, main # noqa diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py b/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py index 4839ee5ae2..8b7bc0d0ed 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py @@ -27,9 +27,11 @@ __init__.py: """ -from .motor import TaurusMotorH, TaurusMotorH2, TaurusMotorV, TaurusMotorV2 -from .poolmotor import PoolMotorSlim, LabelWidgetDragsDeviceAndAttribute -from .poolmotor import PoolMotorTV, PoolMotorTVLabelWidget, PoolMotorTVReadWidget, PoolMotorTVWriteWidget, PoolMotorTVUnitsWidget -from .poolmotor import PoolMotor -from .poolchannel import PoolChannel, PoolChannelTV -from .poolioregister import PoolIORegisterTV, PoolIORegisterReadWidget, PoolIORegisterWriteWidget, PoolIORegister, PoolIORegisterButtons +from .motor import TaurusMotorH, TaurusMotorH2, TaurusMotorV, TaurusMotorV2 # noqa +from .poolmotor import PoolMotorSlim, LabelWidgetDragsDeviceAndAttribute # noqa +from .poolmotor import (PoolMotorTV, PoolMotorTVLabelWidget, # noqa + PoolMotorTVReadWidget, PoolMotorTVWriteWidget, PoolMotorTVUnitsWidget, # noqa + PoolMotor) # noqa +from .poolchannel import PoolChannel, PoolChannelTV # noqa +from .poolioregister import (PoolIORegisterTV, PoolIORegisterReadWidget, # noqa + PoolIORegisterWriteWidget, PoolIORegister, PoolIORegisterButtons) # noqa diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index f7ce85404c..561c8f0968 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -859,7 +859,8 @@ def __init__(self, parent=None, designMode=False): # I don't like this approach, there should be something like # self.lbl_alias.addAction(...) - self.lbl_alias.contextMenuEvent = lambda event: self.contextMenuEvent(event) + self.lbl_alias.contextMenuEvent = \ + lambda event: self.contextMenuEvent(event) # I' don't like this approach, there should be something like # self.lbl_alias.addToolTipCallback(self.calculate_extra_tooltip) @@ -872,11 +873,13 @@ def __init__(self, parent=None, designMode=False): self.lbl_alias.mouseMoveEvent = self.mouseMoveEvent def setExpertView(self, expertView): - btn_poweron_visible = expertView and self.taurusValueBuddy().hasPowerOn() + btn_poweron_visible = expertView \ + and self.taurusValueBuddy().hasPowerOn() self.btn_poweron.setVisible(btn_poweron_visible) @Qt.pyqtSlot() - @ProtectTaurusMessageBox(msg='An error occurred trying to write PowerOn Attribute.') + @ProtectTaurusMessageBox( + msg='An error occurred trying to write PowerOn Attribute.') def setPowerOn(self): motor_dev = self.taurusValueBuddy().motor_dev if motor_dev is not None: diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index 0bfe330659..40612169fb 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -456,7 +456,8 @@ def _reloadConf(self, force=False): self._dirtyMntGrps = set() # set a list of available channels avail_channels = {} - for ch_info in list(door.macro_server.getExpChannelElements().values()): + for ch_info in \ + list(door.macro_server.getExpChannelElements().values()): avail_channels[ch_info.full_name] = ch_info.getData() self.ui.channelEditor.getQModel().setAvailableChannels(avail_channels) # set a list of available triggers @@ -610,13 +611,17 @@ def createMntGrp(self): # check that the given name is not an existing pool element ms = self.getModelObj().macro_server poolElementNames = [ - v.name for v in list(ms.getElementsWithInterface("PoolElement").values())] + v.name for v in + list(ms.getElementsWithInterface("PoolElement").values())] while mntGrpName in poolElementNames: + msg = ("The name '%s' already is used for another pool element. " + "Please Choose a different one." % mntGrpName) 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", + msg, Qt.QMessageBox.Ok) + msg = "Enter a name for the new measurement Group" + mntGrpName, ok = Qt.QInputDialog.getText(self, + "New Measurement Group", + msg, Qt.QLineEdit.Normal, mntGrpName) if not ok: @@ -624,9 +629,11 @@ def createMntGrp(self): mntGrpName = str(mntGrpName) # check that the measurement group is not already in the localConfig + msg = ('A measurement group named "%s" already exists. A new one ' + 'will not be created' % mntGrpName) 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) + msg) return # add an empty configuration dictionary to the local config diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index 1b767fb3af..d7e657b9f8 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -1047,7 +1047,8 @@ def addChannel(self, channel=None): if channel is None: shown = [n for n, d in getChannelConfigs(dataSource)] avail_channels = qmodel.getAvailableChannels() - clist = [ch_info['name'] for ch_name, ch_info in list(avail_channels.items()) + clist = [ch_info['name'] for ch_name, ch_info + in list(avail_channels.items()) if ch_name not in shown] clist = sorted(clist) + ['(Other...)'] chname, ok = Qt.QInputDialog.getItem( diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanabasewizard.py b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanabasewizard.py index 751c397e70..000627a303 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanabasewizard.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanabasewizard.py @@ -64,7 +64,7 @@ def __getitem__(self, name): if isinstance(p, SardanaBasePage): try: return p[name]() - except Exception as e: + except Exception: pass return self._item_funcs[name]() return None diff --git a/src/sardana/tools/config/get_pool_config.py b/src/sardana/tools/config/get_pool_config.py index fad35df2f6..72865d27c4 100644 --- a/src/sardana/tools/config/get_pool_config.py +++ b/src/sardana/tools/config/get_pool_config.py @@ -37,8 +37,8 @@ def checkPoolElements(pool): try: ctrl_library = pool_ctrl_classes[ctrl_class][0] except KeyError: - print(("#WARNING: There is no controller class %s for controller %s" % - (ctrl_class, ctrl_name))) + print(("#WARNING: There is no controller class %s for " + "controller %s" % (ctrl_class, ctrl_name))) continue ctrl_type = str(info['main_type']) # sardana script is not compatible with the new type CTExpChannel @@ -139,7 +139,9 @@ def checkPoolElements(pool): pool_elements_detail[alias]['attr_dicts'][attr] = attr_dict else: if attr.lower() in ['position', 'value']: - print('***', specific_element_type, alias, attr, 'NO MEMORIZED ATTRIBUTES OR ATTRIBUTE CONFIGURATIONS ***') + print('***', specific_element_type, alias, attr, + 'NO MEMORIZED ATTRIBUTES OR ATTRIBUTE ' + 'CONFIGURATIONS ***') # print '\n' # print '----------------------------------------------------------------' @@ -356,31 +358,38 @@ def checkPoolElements(pool): acquisition_sheet += row + '\n' print('\n' * 2) - print('################################ CONTROLLERS ################################\n' * 4) + print('################################ CONTROLLERS ' + '################################\n' * 4) print('\n' * 2) print(controllers_sheet) print('\n' * 2) - print('################################ INSTRUMENTS ################################\n' * 4) + print('################################ INSTRUMENTS ' + '################################\n' * 4) print('\n' * 2) print(instruments_sheet) print('\n' * 2) - print('################################ MOTORS ################################\n' * 4) + print('################################ MOTORS ' + '################################\n' * 4) print('\n' * 2) print(motors_sheet) print('\n' * 2) - print('################################ IOREGS ################################\n' * 4) + print('################################ IOREGS ' + '################################\n' * 4) print('\n' * 2) print(ioregs_sheet) print('\n' * 2) - print('################################ CHANNELS ################################\n' * 4) + print('################################ CHANNELS ' + '################################\n' * 4) print('\n' * 2) print(channels_sheet) print('\n' * 2) - print('################################ ACQUISITION ################################\n' * 4) + print('################################ ACQUISITION ' + '################################\n' * 4) print('\n' * 2) print(acquisition_sheet) print('\n' * 2) - print('################################ PARAMETERS ################################\n' * 4) + print('################################ PARAMETERS ' + '################################\n' * 4) print('\n' * 2) print(parameters_sheet) diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index e93e78ad96..a27d3b27a9 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -251,10 +251,10 @@ def print_ticks(d): raise TypeError( 'The callback must be a string or function type.') event_count = event_count + 1 - except TIMEOUT as e: + except TIMEOUT: child_result_list.append(child.before) break - except EOF as e: + except EOF: child_result_list.append(child.before) break child_result = ''.join(child_result_list) @@ -594,7 +594,8 @@ def __fork_pty(self): parent_fd, child_fd = os.openpty() if parent_fd < 0 or child_fd < 0: - raise ExceptionPexpect("Error! Could not open pty with os.openpty().") + raise ExceptionPexpect( + "Error! Could not open pty with os.openpty().") pid = os.fork() if pid < 0: @@ -635,7 +636,8 @@ def __pty_make_controlling_tty(self, tty_fd): fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) if fd >= 0: os.close(fd) - raise ExceptionPexpect("Error! We are not disconnected from a controlling tty.") + raise ExceptionPexpect( + "Error! We are not disconnected from a controlling tty.") except: # Good! We are disconnected from a controlling tty. pass @@ -643,14 +645,16 @@ def __pty_make_controlling_tty(self, tty_fd): # Verify we can open child pty. fd = os.open(child_name, os.O_RDWR) if fd < 0: - raise ExceptionPexpect("Error! Could not open child pty, " + child_name) + raise ExceptionPexpect( + "Error! Could not open child pty, " + child_name) else: os.close(fd) # Verify we now have a controlling tty. fd = os.open("/dev/tty", os.O_WRONLY) if fd < 0: - raise ExceptionPexpect("Error! Could not open controlling tty, /dev/tty") + raise ExceptionPexpect( + "Error! Could not open controlling tty, /dev/tty") else: os.close(fd) @@ -828,12 +832,14 @@ def read_nonblocking(self, size=1, timeout=-1): if not r: if not self.isalive(): - # Some platforms, such as Irix, will claim that their processes are alive; + # Some platforms, such as Irix, will claim that their + # processes are alive; # then timeout on the select; and then finally admit that they # are not alive. self.flag_eof = True raise EOF( - 'End of File (EOF) in read_nonblocking(). Very pokey platform.') + 'End of File (EOF) in read_nonblocking(). ' + 'Very pokey platform.') else: raise TIMEOUT('Timeout exceeded in read_nonblocking().') @@ -843,11 +849,13 @@ def read_nonblocking(self, size=1, timeout=-1): except OSError as e: # Linux does this self.flag_eof = True raise EOF( - 'End Of File (EOF) in read_nonblocking(). Exception style platform.') + 'End Of File (EOF) in read_nonblocking(). ' + 'Exception style platform.') if s == '': # BSD style self.flag_eof = True raise EOF( - 'End Of File (EOF) in read_nonblocking(). Empty string style platform.') + 'End Of File (EOF) in read_nonblocking(). ' + 'Empty string style platform.') if self.logfile is not None: self.logfile.write(s) @@ -1124,9 +1132,10 @@ def isalive(self): return False if self.flag_eof: - # This is for Linux, which requires the blocking form of waitpid to get - # status of a defunct process. This is super-lame. The flag_eof would have - # been set in read_nonblocking(), so this should be safe. + # This is for Linux, which requires the blocking form of waitpid + # to get status of a defunct process. This is super-lame. The + # flag_eof would have been set in read_nonblocking(), so this + # should be safe. waitpid_options = 0 else: waitpid_options = os.WNOHANG diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py index 8bd006c467..7c33fe8487 100644 --- a/src/sardana/tools/config/sardana.py +++ b/src/sardana/tools/config/sardana.py @@ -79,7 +79,8 @@ print("[WARNING]: pexpect module not found. Using local pexpect 2.3") except Exception as e: print(e) - print("The Sardana requires pexpect python module which was not found.") + print("The Sardana requires pexpect python module which was" + "not found.") print("This module can be found at http://www.noah.org/wiki/Pexpect") sys.exit(2) @@ -94,7 +95,8 @@ class Process: MaxStartupTime = 30 MaxShutdownTime = 30 - def __init__(self, executable, args, name="Process", instance=None, logfile=None, env=None): + def __init__(self, executable, args, name="Process", instance=None, + logfile=None, env=None): self._start_time = -1 self._stop_time = -1 self._process = None @@ -202,7 +204,8 @@ def stop(self, max_shutdown_time=None): try: res = self.terminate() - idx = self._process.expect(['Exiting', 'Exited', pexpect.EOF, pexpect.TIMEOUT], + idx = self._process.expect(['Exiting', 'Exited', + pexpect.EOF, pexpect.TIMEOUT], timeout=max_shutdown_time) except Exception as e: self.on("[FAILED]") @@ -222,7 +225,8 @@ def stop(self, max_shutdown_time=None): return try: - idx = self._process.expect(['Exited', pexpect.EOF, pexpect.TIMEOUT], + idx = self._process.expect(['Exited', pexpect.EOF, + pexpect.TIMEOUT], timeout=5) except Exception as e: self.on("[FAILED]") @@ -253,7 +257,7 @@ def stop(self, max_shutdown_time=None): self.o(" (shutdown time exceeded). Forcing... ") self.kill() - except KeyboardInterrupt as ki: + except KeyboardInterrupt: self.o("(Ctrl-C during stop). Forcing... ") self.kill() @@ -315,7 +319,7 @@ def __init__(self, instname, db=None, logfile=None): f, path, desc = imp.find_module('SimuMotor') if f: f.close() - except exceptions.ImportError as e: + except exceptions.ImportError: msg = "Could not find %s executable.\n" \ "Make sure PYTHONPATH points to the directory(ies) where " \ "SimuMotorCtrl.py and SimuMotor.py files are installed" % name @@ -336,7 +340,7 @@ def __init__(self, instname, db=None, logfile=None): f, fname, desc = imp.find_module('SimuCoTiCtrl') if f: f.close() - except exceptions.ImportError as e: + except exceptions.ImportError: msg = "Could not find %s executable.\n" \ "Make sure PYTHONPATH points to the directory(ies) where " \ "SimuCoTiCtrl.py file is installed" % name @@ -357,7 +361,7 @@ def __init__(self, instname, db=None, logfile=None): f, fname, desc = imp.find_module('PySignalSimulator') if f: f.close() - except exceptions.ImportError as e: + except exceptions.ImportError: msg = "Could not find %s executable.\n" \ "Make sure PYTHONPATH points to the directory where " \ "PySignalSimulator.py is installed" % name @@ -448,30 +452,37 @@ def __init__(self, servNode, bl, createProc=False, log=False): import socket server_host_ip = socket.gethostbyname_ex(server_host)[2][0] pytango_host_ip = socket.gethostbyname_ex(pytango_host)[2][0] - if (server_host_ip != pytango_host_ip) or (server_port != pytango_port): - print('\t!!! WARNING !!! %s TANGO_HOST is not the PyTango default. You may erase the WRONG sardana definition.' % self._complete_name) - print('\tServer: %s PyTango: %s' % (server_tango_host, pytango_tango_host)) + if (server_host_ip != pytango_host_ip) \ + or (server_port != pytango_port): + print('\t!!! WARNING !!! %s TANGO_HOST is not the PyTango ' + 'default. You may erase the WRONG sardana definition.' % + self._complete_name) + print('\tServer: %s PyTango: %s' % + (server_tango_host, pytango_tango_host)) ans = input('\tDo you _really_ want to continue? [y|N] ') if ans.lower() not in ['y', 'yes']: raise Exception( - 'User cancelled the creation of %s server' % self._complete_name) - ####################################################################### - - ####################################################################### - # Before erasing the content in the database, we will also create a backup - # of all the tango devices's properties and memorized attributes "a-la jive". - # There's an script called jive-save-config that given the parameters - # and it saves the config into the specified file the - # same way you can right-click an instance within jive and select the - # option 'Save server data'. + 'User cancelled the creation of %s server' % + self._complete_name) + ###################################################################### + + ###################################################################### + # Before erasing the content in the database, we will also create a + # backup of all the tango devices's properties and memorized + # attributes "a-la jive". There's an script called jive-save-config + # that given the parameters and it saves + # the config into the specified file the same way you can + # right-click an instance within jive and select the option 'Save + # server data'. try: config_file_name = self._klass_name + '-' + self._inst_name + \ '-' + time.strftime('%Y%m%d_%H%M%S') + '.jive' cmd = 'TANGO_HOST=%s jive-save-config %s %s &>/dev/null' % ( server_tango_host, self._complete_name, config_file_name) os.system(cmd) - print('There is a backup of the deleted server config in: %s' % config_file_name) - except: + print('There is a backup of the deleted server config in: %s' % + config_file_name) + except Exception: pass ####################################################################### @@ -691,7 +702,9 @@ def handle_attributes(self, dev_name, node): try: dev.write_attribute(name, v) except Exception as ex: - print('SOME PROBLEMS SETTING ATTRIBUTE VALUE FOR DEVICE', dev_name, 'ATTRIBUTE', tango_attr.name, 'VALUE', str(v)) + print('SOME PROBLEMS SETTING ATTRIBUTE VALUE FOR ' + 'DEVICE', dev_name, 'ATTRIBUTE', + tango_attr.name, 'VALUE', str(v)) print('EXCEPTION:', ex) c_node = attr.find("Configuration") @@ -739,7 +752,7 @@ def handle_attributes(self, dev_name, node): attr_info.events.ch_event.rel_change = rel p_node = attr.find("Polling") - if not p_node is None: + if p_node is not None: polled = p_node.get("polled") or 'False' polled = not (polled.lower() in ( 'false', 'no', 'n', '0')) @@ -753,7 +766,8 @@ def handle_attributes(self, dev_name, node): try: dev.set_attribute_config(attr_info) except Exception as e: - print('COULD NOT SET THE FOLLOWING CONFIG FOR DEVICE', dev_name, 'ATTR', tango_attr.name) + print('COULD NOT SET THE FOLLOWING CONFIG FOR DEVICE', + dev_name, 'ATTR', tango_attr.name) print('ATTRIBUTE INFO:', attr_info) print('EXCEPTION:', e) @@ -765,7 +779,8 @@ def handle_attributes(self, dev_name, node): value = value.strip() dev.write_attribute('Instrument', value) except Exception as ex: - print('SOME PROBLEMS SETTING INSTRUMENT VALUE FOR DEVICE', dev_name, 'VALUE', value) + print('SOME PROBLEMS SETTING INSTRUMENT VALUE FOR DEVICE', + dev_name, 'VALUE', value) print('EXCEPTION:', ex) def loadPool(self): @@ -1552,11 +1567,13 @@ def _preprocess(self): simu0DCtrl, "Property") pSimAttributes.set("name", "DynamicAttributes") pSimAttributes.set("type", "DevVarStringArray") - simAttrTempl = "zerod%03d=float(100.0+10.0*random())" + simAttrTempl = \ + "zerod%03d=float(100.0+10.0*random())" for i in range(len(zerods)): pSimAttributeItem = etree.SubElement( pSimAttributes, "Item") - pSimAttributeItem.text = simAttrTempl % (i + 1) + pSimAttributeItem.text = \ + simAttrTempl % (i + 1) # change the pool XML nodes to refer to simulator lib # instead of real lib @@ -1601,19 +1618,22 @@ def _preprocess(self): sarName, pySigSimNb) simu1DCtrl.set("deviceName", simu1DCtrlName) - onedds = ctrl.findall("OneDExpChannel") + oneds = ctrl.findall("OneDExpChannel") - if len(onedds) > 0: + if len(oneds) > 0: pSimAttributes = etree.SubElement( simu1DCtrl, "Property") pSimAttributes.set("name", "DynamicAttributes") pSimAttributes.set("type", "DevVarStringArray") - simAttrTempl = "oned%03d=DevVarLongArray([10*sin(0.01*x) for x in xrange(100)])" + simAttrTempl = \ + ("oned%03d=DevVarLongArray([10*sin(0.01*x) " + "for x in xrange(100)])") for i in range(len(oneds)): pSimAttributeItem = etree.SubElement( pSimAttributes, "Item") - pSimAttributeItem.text = simAttrTempl % (i + 1) + pSimAttributeItem.text =\ + simAttrTempl % (i + 1) # change the pool XML nodes to refer to simulator lib # instead of real lib @@ -1881,7 +1901,8 @@ def getRoot(self): import to_sar sar_doc = to_sar.transform(filename) except Exception as e: - print('Sorry, but some problems found when trying to convert to SARDANA xml:') + print('Sorry, but some problems found when trying to convert to ' + 'SARDANA xml:') print(str(e)) sardana = Sardana(sar_doc, simulation=simulation, @@ -1896,9 +1917,9 @@ def getRoot(self): sardana.setUp() print("Ready!") sardana.run() - except KeyboardInterrupt as e: + except KeyboardInterrupt: print("User pressed Ctrl+C...") - except Exception as e: + except Exception: traceback.print_exc() print("Shutting down!") diff --git a/src/sardana/util/motion/motion.py b/src/sardana/util/motion/motion.py index dca5ab323c..4fb0e93294 100644 --- a/src/sardana/util/motion/motion.py +++ b/src/sardana/util/motion/motion.py @@ -253,12 +253,12 @@ def _calculateMotionPath(self): def info(self): print("Small movement =", self.small_motion) print("length =", self.displacement) - print("position where maximum velocity will be reached =", \ - self.max_vel_pos) - print("necessary displacement to reach maximum velocity =", \ - self.displacement_reach_max_vel) - print("necessary displacement to stop from maximum velocity =", \ - self.displacement_reach_min_vel) + print("position where maximum velocity will be reached =", + self.max_vel_pos) + print("necessary displacement to reach maximum velocity =", + self.displacement_reach_max_vel) + print("necessary displacement to stop from maximum velocity =", + self.displacement_reach_min_vel) print("maximum velocity possible =", self.max_vel) print("time at top velocity =", self.at_max_vel_time) print("displacement at top velocity =", self.at_max_vel_displacement) @@ -266,12 +266,12 @@ def info(self): print("time to reach minimum velocity =", self.min_vel_time) print("time the motion will take =", self.duration) print("") - print("For long movements (where top vel is possible), necessary " \ + print("For long movements (where top vel is possible), necessary " "displacement to reach maximum velocity =", \ - self.displacement_reach_max_vel) - print("For long movements (where top vel is possible), necessary " \ - "displacement to stop from maximum velocity =", \ - self.displacement_reach_min_vel) + self.displacement_reach_max_vel) + print("For long movements (where top vel is possible), necessary " + "displacement to stop from maximum velocity =", + self.displacement_reach_min_vel) class Motion(object): From c59b2570bbb3230baa2c11e5c75aa6a6bd857119 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 21 Jul 2019 21:43:34 +0200 Subject: [PATCH 135/830] More flake8 corrections --- src/sardana/spock/ipython_01_00/genutils.py | 2 +- src/sardana/taurus/core/tango/sardana/pool.py | 7 ++++--- src/sardana/tools/config/pexpect23.py | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index d3f81f34dc..5ed43e4030 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -866,7 +866,7 @@ def check_for_upgrade(ipy_profile_dir): 'spock %s.\n' 'Your current spock door extension version is %s, therefore a ' 'profile upgrade is needed.\n' - % (spock_profile_ver_str, spock_lib_ver_str)) + % (spock_profile_ver_str, spock_lib_ver_str)) prompt = ('Do you wish to upgrade now (warn: this will shutdown the ' 'current spock session) ([y]/n)? ') r = input(prompt) or 'y' diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 05f7e75f01..7fcbce06c2 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1539,9 +1539,10 @@ def getChannels(self): def getChannelInfo(self, channel_name): try: return self.tango_channels_info[channel_name] - except: + except Exception: channel_name = channel_name.lower() - for d_name, a_name, ch_info in list(self.tango_channels_info.values()): + for d_name, a_name, ch_info in \ + list(self.tango_channels_info.values()): if ch_info.name.lower() == channel_name: return d_name, a_name, ch_info @@ -1673,7 +1674,7 @@ def _read(self): else: value = data_item.value ret[channel_data['full_name']] = value - except: + except Exception: for _, channel_data in list(attrs.items()): ret[channel_data['full_name']] = None return ret diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index a27d3b27a9..0093b6360d 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -846,7 +846,7 @@ def read_nonblocking(self, size=1, timeout=-1): if self.child_fd in r: try: s = os.read(self.child_fd, size) - except OSError as e: # Linux does this + except OSError: # Linux does this self.flag_eof = True raise EOF( 'End Of File (EOF) in read_nonblocking(). ' @@ -1083,7 +1083,7 @@ def terminate(self, force=False): else: return False return False - except OSError as e: + except OSError: # I think there are kernel timing issues that sometimes cause # this to happen. I think isalive() reports True, but the # process is dead to the kernel. From 3e1fd8ea4fc743b27f0793e792ec2f8a7aac1731 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 21 Jul 2019 22:31:45 +0200 Subject: [PATCH 136/830] 2to3: migrate documentation --- doc/source/conf.py | 10 ++-- .../devel/howto_controllers/springfieldlib.py | 46 +++++++++---------- doc/source/sphinxext/sardanaextension.py | 21 +++++---- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index b2aea89fb3..1e92dd0904 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -99,9 +99,9 @@ def type_getattr(self, name): master_doc = 'index' # General information about the project. -project = u'sardana' -copyright = u'2012, ALBA - CELLS, Creative Commons Attribution-Share Alike 3.0' -copyright = u"""Except where otherwise noted, content on this site is +project = 'sardana' +copyright = '2012, ALBA - CELLS, Creative Commons Attribution-Share Alike 3.0' +copyright = """Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution 3.0 License""" # Ideally we would like to put the following html code for copyright... @@ -243,8 +243,8 @@ def type_getattr(self, name): # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'sardana.tex', u'Sardana Documentation', - u'Sardana team', 'manual'), + ('index', 'sardana.tex', 'Sardana Documentation', + 'Sardana team', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/doc/source/devel/howto_controllers/springfieldlib.py b/doc/source/devel/howto_controllers/springfieldlib.py index 71dc8ff26e..62daa16cb2 100644 --- a/doc/source/devel/howto_controllers/springfieldlib.py +++ b/doc/source/devel/howto_controllers/springfieldlib.py @@ -125,7 +125,7 @@ def setMinVelocity(self, vi): """ Sets the minimum velocity in ms^-1. A.k.a. base rate""" vi = float(vi) if vi < 0: - raise "Minimum velocity must be >= 0" + raise Exception("Minimum velocity must be >= 0") self.min_vel = vi @@ -145,7 +145,7 @@ def setMaxVelocity(self, vf): """ Sets the maximum velocity in ms^-1.""" vf = float(vf) if vf <= 0: - raise "Maximum velocity must be > 0" + raise Exception("Maximum velocity must be > 0") self.max_vel = vf @@ -165,7 +165,7 @@ def setAccelerationTime(self, at): """Sets the time to go from minimum velocity to maximum velocity in seconds""" at = float(at) if at <= 0: - raise "Acceleration time must be > 0" + raise Exception("Acceleration time must be > 0") self.accel_time = at self.accel = (self.max_vel - self.min_vel) / at @@ -179,7 +179,7 @@ def setDecelerationTime(self, dt): """Sets the time to go from maximum velocity to minimum velocity in seconds""" dt = float(dt) if dt <= 0: - raise "Deceleration time must be > 0" + raise Exception("Deceleration time must be > 0") self.decel_time = dt self.decel = (self.min_vel - self.max_vel) / dt @@ -193,7 +193,7 @@ def setAcceleration(self, a): """Sets the acceleration in ms^-2""" a = float(a) if a < 0: - raise "Acceleration must be >= 0" + raise Exception("Acceleration must be >= 0") self.accel = float(a) @@ -208,7 +208,7 @@ def setDeceleration(self, d): """Sets the deceleration in ms^-2""" d = float(d) if d > 0: - raise "Deceleration must be <= 0" + raise Exception("Deceleration must be <= 0") self.decel = d @@ -485,23 +485,23 @@ def setPower(self, power): self.power = power def info(self): - print "Small movement =", self.small_motion - print "length =", self.dsplmnt - print "position where maximum velocity will be reached =", self.curr_max_vel_pos - print "necessary displacement to reach maximum velocity =", self.curr_dsplmnt_reach_max_vel - print "necessary displacement to stop from maximum velocity =", self.curr_dsplmnt_reach_min_vel - print "maximum velocity possible =", self.curr_max_vel - print "time at top velocity =", self.curr_at_max_vel_time - print "displacement at top velocity =", self.curr_at_max_vel_dsplmnt - print "time to reach maximum velocity =", self.curr_max_vel_time - print "time to reach minimum velocity =", self.curr_min_vel_time - print "time the motion will take =", self.duration - print "instant when maximum velocity should be reached =", self.curr_max_vel_instant - print "instant when should start decelerating =", self.curr_min_vel_instant - print "instant the motion will end", self.final_instant - print "" - print "For long movements (where top vel is possible), necessary displacement to reach maximum velocity =", self.dsplmnt_reach_max_vel - print "For long movements (where top vel is possible), necessary displacement to stop from maximum velocity =", self.dsplmnt_reach_min_vel + print("Small movement =", self.small_motion) + print("length =", self.dsplmnt) + print("position where maximum velocity will be reached =", self.curr_max_vel_pos) + print("necessary displacement to reach maximum velocity =", self.curr_dsplmnt_reach_max_vel) + print("necessary displacement to stop from maximum velocity =", self.curr_dsplmnt_reach_min_vel) + print("maximum velocity possible =", self.curr_max_vel) + print("time at top velocity =", self.curr_at_max_vel_time) + print("displacement at top velocity =", self.curr_at_max_vel_dsplmnt) + print("time to reach maximum velocity =", self.curr_max_vel_time) + print("time to reach minimum velocity =", self.curr_min_vel_time) + print("time the motion will take =", self.duration) + print("instant when maximum velocity should be reached =", self.curr_max_vel_instant) + print("instant when should start decelerating =", self.curr_min_vel_instant) + print("instant the motion will end", self.final_instant) + print("") + print("For long movements (where top vel is possible), necessary displacement to reach maximum velocity =", self.dsplmnt_reach_max_vel) + print("For long movements (where top vel is possible), necessary displacement to stop from maximum velocity =", self.dsplmnt_reach_min_vel) class SpringfieldMotorHW(object): diff --git a/doc/source/sphinxext/sardanaextension.py b/doc/source/sphinxext/sardanaextension.py index 5cc2837190..e33fee9178 100644 --- a/doc/source/sphinxext/sardanaextension.py +++ b/doc/source/sphinxext/sardanaextension.py @@ -65,9 +65,9 @@ def process_param(line): new_lines.append('%s:type %s: %s' % (prefix, param_name, klass)) new_lines.append('%s:param %s: %s' % (prefix, param_name, desc)) - except Exception, e: - print "Sardana sphinx extension: Not able to process param: '%s'" % line - print " Reason:", str(e) + except Exception as e: + print("Sardana sphinx extension: Not able to process param: '%s'" % line) + print(" Reason:", str(e)) new_lines.append(line) return new_lines @@ -85,9 +85,9 @@ def process_return(line): desc = desc[pos + 1:] new_lines.append('%s:rtype: %s' % (prefix, klass)) new_lines.append('%s:return: %s' % (prefix, desc)) - except Exception, e: - print "Sardana sphinx extension: Not able to process 'return': '%s'" % line - print " Reason:", str(e) + except Exception as e: + print("Sardana sphinx extension: Not able to process 'return': '%s'" % line) + print(" Reason:", str(e)) new_lines.append(line) return new_lines @@ -105,9 +105,9 @@ def process_raise(line): klass = "(" + process_type(elem_type, obj_type='exc') + ")" desc = desc[pos + 1:] new_lines.append('%s:raise: %s %s' % (prefix, klass, desc)) - except Exception, e: - print "Sardana sphinx extension: Not able to process 'raise': '%s'" % line - print " Reason:", str(e) + except Exception as e: + print("Sardana sphinx extension: Not able to process 'raise': '%s'" % line) + print(" Reason:", str(e)) new_lines.append(line) return new_lines @@ -192,7 +192,8 @@ def process_signature(app, what, name, obj, options, signature, return_annotatio if hasattr(obj, "__wrapped__"): if what == "method": from taurus.core.util.wrap import wrapped - obj = wrapped(obj) + # import pdb; pdb.set_trace() + # obj = wrapped(obj) signature = _format_method_args(obj) return signature, return_annotation From af93f9187561574a0bc50a33b9bf0f70d11e17f9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 22 Jul 2019 14:49:56 +0200 Subject: [PATCH 137/830] More flake8 corrections --- .../devel/howto_controllers/springfieldlib.py | 23 +++++++++++++------ doc/source/sphinxext/sardanaextension.py | 9 +++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/doc/source/devel/howto_controllers/springfieldlib.py b/doc/source/devel/howto_controllers/springfieldlib.py index 62daa16cb2..d42cf7c5cf 100644 --- a/doc/source/devel/howto_controllers/springfieldlib.py +++ b/doc/source/devel/howto_controllers/springfieldlib.py @@ -487,21 +487,30 @@ def setPower(self, power): def info(self): print("Small movement =", self.small_motion) print("length =", self.dsplmnt) - print("position where maximum velocity will be reached =", self.curr_max_vel_pos) - print("necessary displacement to reach maximum velocity =", self.curr_dsplmnt_reach_max_vel) - print("necessary displacement to stop from maximum velocity =", self.curr_dsplmnt_reach_min_vel) + print("position where maximum velocity will be reached =", + self.curr_max_vel_pos) + print("necessary displacement to reach maximum velocity =", + self.curr_dsplmnt_reach_max_vel) + print("necessary displacement to stop from maximum velocity =", + self.curr_dsplmnt_reach_min_vel) print("maximum velocity possible =", self.curr_max_vel) print("time at top velocity =", self.curr_at_max_vel_time) print("displacement at top velocity =", self.curr_at_max_vel_dsplmnt) print("time to reach maximum velocity =", self.curr_max_vel_time) print("time to reach minimum velocity =", self.curr_min_vel_time) print("time the motion will take =", self.duration) - print("instant when maximum velocity should be reached =", self.curr_max_vel_instant) - print("instant when should start decelerating =", self.curr_min_vel_instant) + print("instant when maximum velocity should be reached =", + self.curr_max_vel_instant) + print("instant when should start decelerating =", + self.curr_min_vel_instant) print("instant the motion will end", self.final_instant) print("") - print("For long movements (where top vel is possible), necessary displacement to reach maximum velocity =", self.dsplmnt_reach_max_vel) - print("For long movements (where top vel is possible), necessary displacement to stop from maximum velocity =", self.dsplmnt_reach_min_vel) + print("For long movements (where top vel is possible), " + "necessary displacement to reach maximum velocity =", + self.dsplmnt_reach_max_vel) + print("For long movements (where top vel is possible), " + "necessary displacement to stop from maximum velocity =", + self.dsplmnt_reach_min_vel) class SpringfieldMotorHW(object): diff --git a/doc/source/sphinxext/sardanaextension.py b/doc/source/sphinxext/sardanaextension.py index e33fee9178..e767935c32 100644 --- a/doc/source/sphinxext/sardanaextension.py +++ b/doc/source/sphinxext/sardanaextension.py @@ -66,7 +66,8 @@ def process_param(line): (prefix, param_name, klass)) new_lines.append('%s:param %s: %s' % (prefix, param_name, desc)) except Exception as e: - print("Sardana sphinx extension: Not able to process param: '%s'" % line) + print("Sardana sphinx extension: Not able to process param: '%s'" + % line) print(" Reason:", str(e)) new_lines.append(line) return new_lines @@ -86,7 +87,8 @@ def process_return(line): new_lines.append('%s:rtype: %s' % (prefix, klass)) new_lines.append('%s:return: %s' % (prefix, desc)) except Exception as e: - print("Sardana sphinx extension: Not able to process 'return': '%s'" % line) + print("Sardana sphinx extension: Not able to process 'return': '%s'" + % line) print(" Reason:", str(e)) new_lines.append(line) return new_lines @@ -106,7 +108,8 @@ def process_raise(line): desc = desc[pos + 1:] new_lines.append('%s:raise: %s %s' % (prefix, klass, desc)) except Exception as e: - print("Sardana sphinx extension: Not able to process 'raise': '%s'" % line) + print("Sardana sphinx extension: Not able to process 'raise': '%s'" + % line) print(" Reason:", str(e)) new_lines.append(line) return new_lines From d2f6c896256500d89d41e6be08f62c48cb7c68a1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 23 Jul 2019 09:56:09 +0200 Subject: [PATCH 138/830] Bump version 2.8.1 to 2.8.2-alpha --- .bumpversion.cfg | 2 +- src/sardana/release.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 302ec4e657..64511593a2 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,7 +3,7 @@ commit = True message = Bump version {current_version} to {new_version} tag = False tag_name = {new_version} -current_version = 2.8.1 +current_version = 2.8.2-alpha parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? serialize = {major}.{minor}.{patch}-{release} diff --git a/src/sardana/release.py b/src/sardana/release.py index 5ff470b16c..82ee80d29b 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = '2.8.1' +version = '2.8.2-alpha' # generate version_info and revision (**deprecated** since v 2.1.2--alpha). if '-' in version: From 472426af9afa0e25640be0ffa5480b0023016ef1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 23 Jul 2019 16:13:40 +0200 Subject: [PATCH 139/830] py3: add back. comp. for Synchronization memorized in Py2 --- src/sardana/tango/pool/MeasurementGroup.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 43d08ebda7..4eb7916378 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -157,16 +157,27 @@ def _on_measurement_group_changed(self, event_source, event_type, def _synchronization_str2enum(self, synchronization_str): """Translates synchronization data structure so it uses SynchParam and SynchDomain enums as keys instead of strings. + + .. todo:: At some point remove the backwards compatibility + for memorized values created with Python 2. In Python 2 IntEnum was + serialized to "." e.g. "SynchDomain.Time" and we were + using a class method `fromStr` to interpret the enumeration objects. """ synchronization = [] for group_str in synchronization_str: group = {} for param_str, conf_str in group_str.items(): - param = SynchParam(int(param_str)) + try: + param = SynchParam(int(param_str)) + except ValueError: + param = SynchParam.fromStr(param_str) if isinstance(conf_str, dict): conf = {} for domain_str, value in conf_str.items(): - domain = SynchDomain(int(domain_str)) + try: + domain = SynchDomain(int(domain_str)) + except ValueError: + domain = SynchDomain.fromStr(domain_str) conf[domain] = value else: conf = conf_str @@ -335,6 +346,7 @@ class MeasurementGroupClass(PoolGroupDeviceClass): 'Moveable': [[DevString, SCALAR, READ_WRITE], {'Memorized': "true", 'Display level': DispLevel.EXPERT}], + # TODO: Does it have sense to memorize Synchronization? 'Synchronization': [[DevString, SCALAR, READ_WRITE], {'Memorized': "true", 'Display level': DispLevel.EXPERT}], From a74869d5660bf12a0f87ce3be299e9bd8daf55f7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 23 Jul 2019 17:20:24 +0200 Subject: [PATCH 140/830] Revert "py3: workaround for JSONCodec additional encoding/decoding utf-8" This reverts commit 25dce83fd4174fbdb7fb636c00f07a904f40744c. --- src/sardana/taurus/core/tango/sardana/pool.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 7fcbce06c2..db792b4736 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1718,9 +1718,6 @@ def getConfigurationAttrEG(self): def setConfiguration(self, configuration): codec = CodecFactory().getCodec('json') f, data = codec.encode(('', configuration)) - # workaround until it is clarified if the JSONCodec correctly encodes - # to utf-8 (taurus-org/taurus#945) - data = data.decode("utf-8") self.write_attribute('configuration', data) def _setConfiguration(self, data): @@ -1850,9 +1847,6 @@ def getSynchronization(self): def setSynchronization(self, synchronization): codec = CodecFactory().getCodec('json') _, data = codec.encode(('', synchronization)) - # workaround until it is clarified if the JSONCodec correctly encodes - # to utf-8 (taurus-org/taurus#945) - data = data.decode("utf-8") self.getSynchronizationObj().write(data) self._last_integ_time = None From 16d7109215e146bd9a12d692529a8b649a898550 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 23 Jul 2019 17:58:49 +0200 Subject: [PATCH 141/830] py3: Use Utf8Codec when JSON serialization needs to provide bytes After changes in taurus-org/taurus#930 one needs to explicitly encode and decode with utf-8. For this need we use the Utf8Codec in a pipeline. --- src/sardana/macroserver/recorders/output.py | 6 +----- src/sardana/tango/macroserver/Door.py | 2 +- src/sardana/tango/macroserver/MacroServer.py | 6 +++--- src/sardana/tango/pool/Pool.py | 6 +++--- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/sardana/macroserver/recorders/output.py b/src/sardana/macroserver/recorders/output.py index 1ca652ab71..c7224bc28b 100644 --- a/src/sardana/macroserver/recorders/output.py +++ b/src/sardana/macroserver/recorders/output.py @@ -34,7 +34,6 @@ import operator import weakref -from taurus.core.util.codecs import CodecFactory from taurus.core.util.containers import CaselessList from sardana.macroserver.scan.recorder.datarecorder import DataRecorder @@ -48,7 +47,6 @@ class JsonRecorder(DataRecorder): def __init__(self, stream, cols=None, **pars): DataRecorder.__init__(self, **pars) self._stream = weakref.ref(stream) - self._codec = CodecFactory().getCodec('json') def _startRecordList(self, recordlist): macro_id = recordlist.getEnvironValue('macro_id') @@ -103,9 +101,7 @@ def _writeRecord(self, record): def _sendPacket(self, **kwargs): '''creates a JSON packet using the keyword arguments passed and then sends it''' - #data = self._codec.encode(('', kwargs)) - # self._stream().sendRecordData(*data) - self._stream()._sendRecordData(kwargs, codec='json') + self._stream()._sendRecordData(kwargs, codec='utf8_json') def _addCustomData(self, value, name, **kwargs): ''' diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py index c0a0ae0122..fb1ca618cb 100644 --- a/src/sardana/tango/macroserver/Door.py +++ b/src/sardana/tango/macroserver/Door.py @@ -281,7 +281,7 @@ def on_door_changed(self, event_source, event_type, event_value): event_value = event_value.value if attr.get_data_type() == ArgType.DevEncoded: - codec = CodecFactory().getCodec('json') + codec = CodecFactory().getCodec('utf8_json') event_value = codec.encode(('', event_value)) self.set_attribute(attr, value=event_value, timestamp=timestamp) diff --git a/src/sardana/tango/macroserver/MacroServer.py b/src/sardana/tango/macroserver/MacroServer.py index b55926cacb..23363c3a2c 100644 --- a/src/sardana/tango/macroserver/MacroServer.py +++ b/src/sardana/tango/macroserver/MacroServer.py @@ -169,7 +169,7 @@ def on_macro_server_changed(self, evt_src, evt_type, evt_value): key = 'del' json_elem = elem.serialize(pool=self.pool.full_name) value[key] = json_elem, - value = CodecFactory().getCodec('json').encode(('', value)) + value = CodecFactory().getCodec('utf8_json').encode(('', value)) self.set_attribute(elems_attr, value=value) #self.push_change_event('Elements', *value) elif evt_name == "elementschanged": @@ -190,7 +190,7 @@ def on_macro_server_changed(self, evt_src, evt_type, evt_value): deleted_values.append(json_elem) value = {"new": new_values, "change": changed_values, "del": deleted_values} - value = CodecFactory().getCodec('json').encode(('', value)) + value = CodecFactory().getCodec('utf8_json').encode(('', value)) self.set_attribute(elems_attr, value=value) #self.push_change_event('Elements', *value) elif evt_name == "environmentchanged": @@ -229,7 +229,7 @@ def getElements(self, cache=True): return value elements = self.macro_server.get_elements_info() value = dict(new=elements) - value = CodecFactory().getCodec('json').encode(('', value)) + value = CodecFactory().getCodec('utf8_json').encode(('', value)) self.ElementsCache = value return value diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index f46e804803..e073abf4f4 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -250,7 +250,7 @@ def getElements(self, cache=True): if cache and value is not None: return value value = dict(new=self.pool.get_elements_info()) - value = CodecFactory().encode('json', ('', value)) + value = CodecFactory().encode('utf8_json', ('', value)) self.ElementsCache = value return value @@ -764,7 +764,7 @@ def on_pool_changed(self, evt_src, evt_type, evt_value): key = 'change' json_elem = elem.serialize(pool=self.pool.full_name) value[key] = json_elem, - value = CodecFactory().getCodec('json').encode(('', value)) + value = CodecFactory().getCodec('utf8_json').encode(('', value)) self.push_change_event('Elements', *value) elif evt_name == "elementschanged": # force the element list cache to be rebuild next time someone reads @@ -783,7 +783,7 @@ def on_pool_changed(self, evt_src, evt_type, evt_value): deleted_values.append(json_elem) value = {"new": new_values, "change": changed_values, "del": deleted_values} - value = CodecFactory().getCodec('json').encode(('', value)) + value = CodecFactory().getCodec('utf8_json').encode(('', value)) self.push_change_event('Elements', *value) def _format_create_json_arguments(self, argin): From 872df62926cd36910a768edcf797d3df8a81294b Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 25 Jul 2019 23:38:33 +0200 Subject: [PATCH 142/830] py3: use python3 in subprocess (spock's magics) --- src/sardana/spock/magic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index 352271365b..ab6bf8963b 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -64,7 +64,7 @@ def expconf(self, parameter_s=''): import subprocess import sys fname = sys.modules[ExpDescriptionEditor.__module__].__file__ - args = ['python', fname, doorname] + args = ['python3', fname, doorname] if parameter_s == '--auto-update': args.insert(2, parameter_s) subprocess.Popen(args) @@ -115,7 +115,7 @@ def showscan(self, parameter_s=''): import subprocess import sys fname = sys.modules[ShowScanOnline.__module__].__file__ - args = ['python', fname, doorname] + args = ['python3', fname, doorname] subprocess.Popen(args) # show the scan plot, ignoring the plot configuration From b48e0290db0e306be7938618ffc6eadd5a1593d2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 25 Jul 2019 23:39:02 +0200 Subject: [PATCH 143/830] Disable showscan online magic (no Qwt5 for python3) --- src/sardana/spock/magic.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index ab6bf8963b..ef69358abc 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -88,6 +88,9 @@ def showscan(self, parameter_s=''): online, scan_nb = False, None if len(params) > 0: if params[0].lower() == 'online': + print("'showscan online' does not work (requires migration to " + "taurus_pyqtgraph)") + return try: from sardana.taurus.qt.qtgui.extra_sardana import \ ShowScanOnline From 1eb511a0c7df2187913c8659072b2e333898ddae Mon Sep 17 00:00:00 2001 From: cfalcon Date: Fri, 26 Jul 2019 15:00:41 +0200 Subject: [PATCH 144/830] Avoid to set min and max in Parameterizable class All parameters have been initializing their min and max value. This is an unimplemented feature and nowadays only make sense in paramrepeats. Fix it. --- src/sardana/macroserver/msmetamacro.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/msmetamacro.py b/src/sardana/macroserver/msmetamacro.py index 5d8c6e913a..0a2ea3ca20 100644 --- a/src/sardana/macroserver/msmetamacro.py +++ b/src/sardana/macroserver/msmetamacro.py @@ -157,9 +157,10 @@ def _build_parameter(self, param_def): param_def = param_def or () for p in param_def: t = p[1] - ret_p = {'min': 1, 'max': None} + ret_p = {'min': None, 'max': None} # take care of old ParamRepeat if isinstance(t, ParamRepeat): + ret_p = {'min': 1, 'max': None} t = t.obj() if isinstance(t, collections.Sequence) and not isinstance(t, str): From 33cd4dcfc3aae4fbfa6de3c13f8cc323f40738cb Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 26 Jul 2019 15:40:36 +0200 Subject: [PATCH 145/830] Avoid to set min and max in Parameterizable class Basically do the same as in 1eb511a0 but also for new API of repeat parameters. All parameters have been initializing their min and max value. This is an unimplemented feature and nowadays only make sense in paramrepeats. --- src/sardana/macroserver/msmetamacro.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sardana/macroserver/msmetamacro.py b/src/sardana/macroserver/msmetamacro.py index 0a2ea3ca20..9d8b583cd2 100644 --- a/src/sardana/macroserver/msmetamacro.py +++ b/src/sardana/macroserver/msmetamacro.py @@ -164,6 +164,7 @@ def _build_parameter(self, param_def): t = t.obj() if isinstance(t, collections.Sequence) and not isinstance(t, str): + ret_p = {'min': 1, 'max': None} if isinstance(t[-1], collections.Mapping): ret_p.update(t[-1]) t = self._build_parameter(t[:-1]) From d45481a0d5a10e8db6f6857b0ff3c09ca071467d Mon Sep 17 00:00:00 2001 From: cfalcon Date: Fri, 26 Jul 2019 16:04:59 +0200 Subject: [PATCH 146/830] py3: Fix mimedata mimeData method works with bytes in Py3. Encode/Decode the sting data. --- .../taurus/qt/qtcore/tango/sardana/model.py | 16 ++++++++-------- .../qt/qtgui/extra_sardana/controllertree.py | 8 ++++---- .../taurus/qt/qtgui/extra_sardana/macrotree.py | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/model.py b/src/sardana/taurus/qt/qtcore/tango/sardana/model.py index 97865798d8..00cbf23bd6 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/model.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/model.py @@ -231,11 +231,11 @@ def mimeData(self, indexes): mime_data_item = tree_item.mimeData(index) if mime_data_item is None: continue - data.append(mime_data_item) - ret.setData(TAURUS_MODEL_LIST_MIME_TYPE, "\r\n".join(data)) - ret.setText(", ".join(data)) + data.append(bytes(mime_data_item, encoding='utf8')) + ret.setData(TAURUS_MODEL_LIST_MIME_TYPE, b"\r\n".join(data)) + ret.setText(", ".join(map(str, data))) if len(data) == 1: - ret.setData(TAURUS_MODEL_MIME_TYPE, str(data[0])) + ret.setData(TAURUS_MODEL_MIME_TYPE, data[0]) return ret def accept(self, element): @@ -442,11 +442,11 @@ def mimeData(self, indexes): mime_data_item = tree_item.mimeData(index) if mime_data_item is None: continue - data.append(mime_data_item) - ret.setData(TAURUS_MODEL_LIST_MIME_TYPE, "\r\n".join(data)) - ret.setText(", ".join(data)) + data.append(bytes(mime_data_item, encoding='utf8')) + ret.setData(TAURUS_MODEL_LIST_MIME_TYPE, b"\r\n".join(data)) + ret.setText(", ".join(map(str, data))) if len(data) == 1: - ret.setData(TAURUS_MODEL_MIME_TYPE, str(data[0])) + ret.setData(TAURUS_MODEL_MIME_TYPE, data[0]) return ret def accept(self, environment): diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py b/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py index 7aefded30e..2824f77b1f 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py @@ -170,11 +170,11 @@ def mimeData(self, indexes): mime_data_item = tree_item.mimeData(index) if mime_data_item is None: continue - data.append(mime_data_item) - ret.setData(TAURUS_MODEL_LIST_MIME_TYPE, "\r\n".join(data)) - ret.setText(", ".join(data)) + data.append(bytes(mime_data_item, encoding='utf8')) + ret.setData(TAURUS_MODEL_LIST_MIME_TYPE, b"\r\n".join(data)) + ret.setText(", ".join(map(str, data))) if len(data) == 1: - ret.setData(TAURUS_MODEL_MIME_TYPE, str(data[0])) + ret.setData(TAURUS_MODEL_MIME_TYPE, data[0]) return ret def pyData(self, index, role): diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py b/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py index 07c0a6a537..00f53add63 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py @@ -172,11 +172,11 @@ def mimeData(self, indexes): mime_data_item = tree_item.mimeData(index) if mime_data_item is None: continue - data.append(mime_data_item) - ret.setData(TAURUS_MODEL_LIST_MIME_TYPE, "\r\n".join(data)) - ret.setText(", ".join(data)) + data.append(bytes(mime_data_item, encoding='utf8')) + ret.setData(TAURUS_MODEL_LIST_MIME_TYPE, b"\r\n".join(data)) + ret.setText(", ".join(map(str, data))) if len(data) == 1: - ret.setData(TAURUS_MODEL_MIME_TYPE, str(data[0])) + ret.setData(TAURUS_MODEL_MIME_TYPE, data[0]) return ret def setupModelData(self, data): From 8dbb5b5db324368234d8f090e838bc7bbe92e298 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 26 Jul 2019 16:11:05 +0200 Subject: [PATCH 147/830] py3: Use Exception.args for indexing PEP352 removed backwards compatibility for indexing of BaseException. Now one needs to use args member get item based on index. --- .../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 4 ++-- src/sardana/tools/config/pexpect23.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index fc32ff9bfa..26cd8588db 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -251,7 +251,7 @@ def validateAllExpresion(self, secValidation=False): if self.disableEditMode: self.updateMacroEditor(mlist[0]) raise Exception(e) - message = e[0] + message = e.args[0] #raise Exception(e) problems.append(message) @@ -274,7 +274,7 @@ def validateAllExpresion(self, secValidation=False): except Exception as e: self.model().setData(self.currentIndex, 'None') txt = str(ix.sibling(ix.row(), 0).data()) - message = "" + txt + " " + e[0] + message = "" + txt + " " + e.args[0] problems.append(message) except IndexError: txt = str(ix.sibling(ix.row(), 0).data()) diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 0093b6360d..f2862a0a07 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -1143,7 +1143,7 @@ def isalive(self): try: pid, status = os.waitpid(self.pid, waitpid_options) except OSError as e: # No child processes - if e[0] == errno.ECHILD: + if e.args[0] == errno.ECHILD: raise ExceptionPexpect( 'isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?') else: @@ -1157,7 +1157,7 @@ def isalive(self): # os.WNOHANG) # Solaris! pid, status = os.waitpid(self.pid, waitpid_options) except OSError as e: # This should never happen... - if e[0] == errno.ECHILD: + if e.args[0] == errno.ECHILD: raise ExceptionPexpect( 'isalive() encountered condition that should never happen. There was no child process. Did someone else call waitpid() on our process?') else: @@ -1555,7 +1555,7 @@ def __select(self, iwtd, owtd, ewtd, timeout=None): try: return select.select(iwtd, owtd, ewtd, timeout) except select.error as e: - if e[0] == errno.EINTR: + if e.args[0] == errno.EINTR: # if we loop back we have to subtract the amount of time we # already waited. if timeout is not None: From 793e4f29aa9d76b7e94811d8ff9ca010a263a24e Mon Sep 17 00:00:00 2001 From: cfalcon Date: Fri, 26 Jul 2019 16:24:32 +0200 Subject: [PATCH 148/830] py3: Fix uses of os.write os.write method requires bytes instead of str. Fix it. --- src/sardana/spock/magic.py | 2 +- src/sardana/tools/config/pexpect23.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index ef69358abc..421ef7f439 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -276,7 +276,7 @@ def edmac(self, parameter_s=''): fd, local_fname = tempfile.mkstemp(prefix='spock_%s_' % pars[0], suffix='.py', text=True) - os.write(fd, code) + os.write(fd, code.encode('utf8')) os.close(fd) cmd = 'edit -x -n %s %s' % (line_nb, local_fname) diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 0093b6360d..5d4bd6efdc 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -1529,6 +1529,7 @@ def __interact_copy(self, escape_character=None, input_filter=None, output_filte if self.logfile is not None: self.logfile.write(data) self.logfile.flush() + # TODO: This could be broken!!! Decide what to do... os.write(self.STDOUT_FILENO, data) if self.STDIN_FILENO in r: data = self.__interact_read(self.STDIN_FILENO) From 2297e93902256a71fb56f38e94f7a5d440b022a0 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Fri, 26 Jul 2019 17:21:22 +0200 Subject: [PATCH 149/830] Fix formatResult method for `File` type The current implementation of formatResult method for file type is broken. Add TODO to formalize api and documentation Fix it. --- src/sardana/taurus/core/tango/sardana/macro.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 2b3556795c..d77feb4f91 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -270,13 +270,13 @@ def formatResult(self, result): raise Exception('Macro %s does not return any result' % self.name) result_info = self.getResult() rtype = result_info['type'] + # TODO: formalize and document way of passing files with macro result if rtype == 'File': fd, filename = tempfile.mkstemp(prefix='spock_', text=True) - os.write(fd, result[1]) - os.close(fd) - # put the local filename in the result - result.insert(0, filename) - return result + with os.fdopen(fd, 'w') as fp: + result = result[0] + fp.write(result) + return filename, result res = [] for idx, item in enumerate(result): result_info = self.getResult(idx) From c0cb9122985ff5911257b659f062af6df0f6a532 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Wed, 24 Jul 2019 10:57:51 +0200 Subject: [PATCH 150/830] upgrade_env: improve script - ensure script is run in python 2 - use dbm instead of dumbdb --- scripts/upgrade/upgrade_env.py | 52 ++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/scripts/upgrade/upgrade_env.py b/scripts/upgrade/upgrade_env.py index 6743633c66..1b2518c317 100644 --- a/scripts/upgrade/upgrade_env.py +++ b/scripts/upgrade/upgrade_env.py @@ -1,20 +1,29 @@ # This serves to upgrade MacroServer environment from Python 2 to Python 3: -# IMPORTANT: IT HAS TO BE USED WITH PYTHON 2!!! +# IMPORTANT: +# 1. IT HAS TO BE USED WITH PYTHON 2.7!!! +# 2. CONDA python 2 package is missing the standard dbm package so this +# script will fail inside a conda python 2 environment +# 3. a new env file with an additional .db extension will be created. You +# should **NOT** change the macroserver EnvironmentDb property. The dbm +# will figure out automatically the file extension +# 4. a backup will of the original environment will be available with th +# extension .py2 # Usage: python upgrade_env.py -# From: https://stackoverflow.com/questions/27493733/use-python-2-shelf-in-python-3 # noqa -# Thanks to Eric Myers +import sys +assert sys.version_info[:2] == (2, 7), "Must run with python 2.7" import os -import sys import shelve -import dumbdbm +import dbm +import contextlib import PyTango + DefaultEnvBaseDir = "/tmp/tango" DefaultEnvRelDir = "%(ds_exec_name)s/%(ds_inst_name)s/macroserver.properties" @@ -31,12 +40,25 @@ def get_ms_properties(ms_name, ms_ds_name): ms_properties = dft_ms_properties % { "ds_exec_name": "MacroServer", "ds_inst_name": ds_inst_name} + else: + ms_properties = ms_properties[0] ms_properties = os.path.normpath(ms_properties) return ms_properties -def dumbdbm_shelve(filename, flag="c"): - return shelve.Shelf(dumbdbm.open(filename, flag)) +def dbm_shelve(filename, flag="c"): + # NOTE: dbm appends '.db' to the end of the filename + return shelve.Shelf(dbm.open(filename, flag)) + + +def migrate_file(filename): + assert not filename.endswith('.db'), \ + "Cannot migrate '.db' (It would be overwritten)" + with contextlib.closing(shelve.open(filename)) as src_db: + data = dict(src_db) + with contextlib.closing(dbm_shelve(filename)) as dst_db: + dst_db.update(data) + os.rename(filename, filename + '.py2') def upgrade_env(ms_name): @@ -45,24 +67,12 @@ def upgrade_env(ms_name): ms_ds_name = ms_info.ds_full_name env_filename = get_ms_properties(ms_name, ms_ds_name) - env_filename_py2 = env_filename + ".py2" - - os.rename(env_filename, env_filename_py2) - - out_shelf = dumbdbm_shelve(env_filename) - in_shelf = shelve.open(env_filename_py2) - - key_list = in_shelf.keys() - for key in key_list: - out_shelf[key] = in_shelf[key] - - out_shelf.close() - in_shelf.close() + migrate_file(env_filename) if __name__ == "__main__": if len(sys.argv) != 2: - print "python upgrade_env.py " # noqa + print("python upgrade_env.py ") # noqa sys.exit(1) ms_name = sys.argv[1] upgrade_env(ms_name) From a3ac6b168b5a3a546f99caad14b0d06fa63c19a5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 29 Jul 2019 11:16:09 +0200 Subject: [PATCH 151/830] py3: Use Exception.args for indexing PEP352 removed backwards compatibility for indexing of BaseException. Now one needs to use args member get item based on index.py3: user Exception --- src/sardana/pool/poolcontrollers/TangoController.py | 2 +- src/sardana/taurus/core/tango/sardana/pool.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/pool/poolcontrollers/TangoController.py b/src/sardana/pool/poolcontrollers/TangoController.py index d9bb2975a9..b85cbe1b66 100644 --- a/src/sardana/pool/poolcontrollers/TangoController.py +++ b/src/sardana/pool/poolcontrollers/TangoController.py @@ -119,7 +119,7 @@ def set_extra_attribute_par(self, axis, name, value): try: proxy = PyTango.DeviceProxy(dev_name) except PyTango.DevFailed as df: - if len(df): + if len(df.args): self._pending[axis] = df.args[0].reason + ": " + \ df.args[0].desc else: diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index db792b4736..575963b7fe 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -997,7 +997,7 @@ def _start(self, *args, **kwargs): try: self.write_attribute('position', new_pos) except DevFailed as df: - for err in df: + for err in df.args: if err.reason == 'API_AttrNotAllowed': raise RuntimeError('%s is already moving' % self) else: From 8b793e33ce9b97709696d0f21a342057a8d8b0d9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 29 Jul 2019 11:40:49 +0200 Subject: [PATCH 152/830] Cast default values to string in macrobutton editors Macrobutton editors are text editors (QLineEdit) and requires strings. Macro parameter default values can be however of different types e.g. int, float. Cast them all to strings. --- .../taurus/qt/qtgui/extra_macroexecutor/macrobutton.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 069ab0d4af..9542fbdbc9 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -467,7 +467,9 @@ def create_layout(self, macro_name): _argEditors.append(self.argEdit) for e, v in zip(_argEditors, d_values): - e.setText(v) + if v is None: + continue + e.setText(str(v)) # Create bottom layout self.mb = MacroButton() From f8c32aaa4e17e649507272547869fe1558568ad3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 29 Jul 2019 12:09:28 +0200 Subject: [PATCH 153/830] Fix flake8 --- scripts/upgrade/upgrade_env.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/scripts/upgrade/upgrade_env.py b/scripts/upgrade/upgrade_env.py index 1b2518c317..c5be9b175d 100644 --- a/scripts/upgrade/upgrade_env.py +++ b/scripts/upgrade/upgrade_env.py @@ -1,20 +1,18 @@ -# This serves to upgrade MacroServer environment from Python 2 to Python 3: - -# IMPORTANT: -# 1. IT HAS TO BE USED WITH PYTHON 2.7!!! -# 2. CONDA python 2 package is missing the standard dbm package so this -# script will fail inside a conda python 2 environment -# 3. a new env file with an additional .db extension will be created. You -# should **NOT** change the macroserver EnvironmentDb property. The dbm -# will figure out automatically the file extension -# 4. a backup will of the original environment will be available with th -# extension .py2 - -# Usage: python upgrade_env.py - +""" This serves to upgrade MacroServer environment from Python 2 to Python 3: + +IMPORTANT: +1. IT HAS TO BE USED WITH PYTHON 2.7!!! +2. CONDA python 2 package is missing the standard dbm package so this + script will fail inside a conda python 2 environment +3. a new env file with an additional .db extension will be created. You + should **NOT** change the macroserver EnvironmentDb property. The dbm + will figure out automatically the file extension +4. a backup will of the original environment will be available with th + extension .py2 + +Usage: python upgrade_env.py +""" import sys -assert sys.version_info[:2] == (2, 7), "Must run with python 2.7" - import os import shelve import dbm @@ -22,7 +20,7 @@ import PyTango - +assert sys.version_info[:2] == (2, 7), "Must run with python 2.7" DefaultEnvBaseDir = "/tmp/tango" DefaultEnvRelDir = "%(ds_exec_name)s/%(ds_inst_name)s/macroserver.properties" From 282ddf87590b3818c48dd737d82a78c2c2ddf5d0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 30 Jul 2019 10:00:52 +0200 Subject: [PATCH 154/830] Add a possibility to select backend in upgrade_env script ndbm backend does not work correctly in the way Sardana uses shelve. This backedn does not store data on disk when process receives SIGKILL or SIGABRT. gnu and dumb however works much better. --- scripts/upgrade/upgrade_env.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/scripts/upgrade/upgrade_env.py b/scripts/upgrade/upgrade_env.py index c5be9b175d..a296fa1935 100644 --- a/scripts/upgrade/upgrade_env.py +++ b/scripts/upgrade/upgrade_env.py @@ -7,7 +7,7 @@ 3. a new env file with an additional .db extension will be created. You should **NOT** change the macroserver EnvironmentDb property. The dbm will figure out automatically the file extension -4. a backup will of the original environment will be available with th +4. a backup will of the original environment will be available with the extension .py2 Usage: python upgrade_env.py @@ -15,7 +15,6 @@ import sys import os import shelve -import dbm import contextlib import PyTango @@ -44,33 +43,42 @@ def get_ms_properties(ms_name, ms_ds_name): return ms_properties -def dbm_shelve(filename, flag="c"): +def dbm_shelve(filename, backend, flag="c"): # NOTE: dbm appends '.db' to the end of the filename - return shelve.Shelf(dbm.open(filename, flag)) + if backend == "gnu": + import gdbm + backend_dict = gdbm.open(filename, flag) + elif backend == "dumb": + import dumbdbm + backend_dict = dumbdbm.open(filename, flag) + return shelve.Shelf(backend_dict) -def migrate_file(filename): +def migrate_file(filename, backend): assert not filename.endswith('.db'), \ "Cannot migrate '.db' (It would be overwritten)" - with contextlib.closing(shelve.open(filename)) as src_db: + os.rename(filename, filename + '.py2') + with contextlib.closing(shelve.open(filename + '.py2')) as src_db: data = dict(src_db) - with contextlib.closing(dbm_shelve(filename)) as dst_db: + with contextlib.closing(dbm_shelve(filename, backend)) as dst_db: dst_db.update(data) - os.rename(filename, filename + '.py2') -def upgrade_env(ms_name): +def upgrade_env(ms_name, backend): db = PyTango.Database() ms_info = db.get_device_info(ms_name) ms_ds_name = ms_info.ds_full_name env_filename = get_ms_properties(ms_name, ms_ds_name) - migrate_file(env_filename) + migrate_file(env_filename, backend) if __name__ == "__main__": - if len(sys.argv) != 2: - print("python upgrade_env.py ") # noqa + if len(sys.argv) != 3: + print("python upgrade_env.py ") # noqa sys.exit(1) ms_name = sys.argv[1] - upgrade_env(ms_name) + backend = sys.argv[2] + if backend not in ("gnu", "dumb"): + raise ValueError("{0} backend is not supported") + upgrade_env(ms_name, backend) From 2fac82b8c78344667393da46bf31acc09f8fd6a9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 30 Jul 2019 10:23:11 +0200 Subject: [PATCH 155/830] Add mechanism to select shelve backend for MS environment ndbm backend does not work correctly in the way Sardana uses shelve. This backend does not store data on disk when process receives SIGKILL or SIGABRT. gnu and dumb however works much better. Add MS_ENV_SHELVE_BACKEND to sardanacustomseetings in order to select between gnu and dumb backends. In case it is not specified, first try to use gnu and if not available use dumb. --- src/sardana/macroserver/msenvmanager.py | 47 +++++++++++++++++++++---- src/sardana/sardanacustomsettings.py | 10 ++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index da8292daf0..75bae20ee0 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -38,9 +38,34 @@ from sardana.macroserver.msmanager import MacroServerManager from sardana.macroserver.msexception import UnknownEnv +from sardana import sardanacustomsettings import collections +def _dbm_gnu(filename): + import dbm.gnu + return dbm.gnu.open(filename, "c") + + +def _dbm_dumb(filename): + import dbm.dumb + return dbm.dumb.open(filename, "c") + + +def _dbm_shelve(filename, backend): + if backend is None: + try: + return _dbm_gnu(filename) + except ImportError: + return _dbm_dumb(filename) + elif backend == "gnu": + return _dbm_gnu(filename) + elif backend == "dumb": + return _dbm_dumb(filename) + else: + raise ValueError("'{}' is not a supported backend".format(backend)) + + class EnvironmentManager(MacroServerManager): """The MacroServer environment manager class. It is designed to be a singleton for the entire application. @@ -117,12 +142,22 @@ def setEnvironmentDb(self, f_name): self.error("Creating environment: %s" % ose.strerror) self.debug("Details:", exc_info=1) raise ose - try: - self._env = shelve.open(f_name, flag='c', writeback=False) - except: - self.error("Failed to create/access environment in %s", f_name) - self.debug("Details:", exc_info=1) - raise + if os.path.exists(f_name) or os.path.exists(f_name + ".dat"): + try: + self._env = shelve.open(f_name, flag='w', writeback=False) + except Exception: + self.error("Failed to access environment in %s", f_name) + self.debug("Details:", exc_info=1) + raise + else: + backend = getattr(sardanacustomsettings, "MS_ENV_SHELVE_BACKEND", + None) + try: + self._env = shelve.Shelf(_dbm_shelve(f_name, backend)) + except Exception: + self.error("Failed to create environment in %s", f_name) + self.debug("Details:", exc_info=1) + raise self.info("Environment is being stored in %s", f_name) diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py index ce3bc15a08..8714c976c0 100644 --- a/src/sardana/sardanacustomsettings.py +++ b/src/sardana/sardanacustomsettings.py @@ -76,3 +76,13 @@ #: Type of encoding for ValueRefBuffer Tango attribute of experimental #: channels VALUE_REF_BUFFER_CODEC = "pickle" + +#: Database backend for MacroServer environment implemented using shelve. +#: Available options: +#: +#: - None (default) - first try "gnu" and if not available fallback to "dumb" +#: - "gnu" - better performance than dumb, but requires installation of +#: additional package e.g. python3-gdbm on Debian. At the time of writing of +#: this documentation it is not available for conda. +#: - "dumb" - worst performance but directly available with Python 3. +MS_ENV_SHELVE_BACKEND = None From f87fcdc4fe04c0b1f89b014e2a93b1509ad87900 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 30 Jul 2019 16:01:43 +0200 Subject: [PATCH 156/830] Bump taurus requirement to > 4.5.4 Sardana requires taurus-org/taurus#960 (JSON and UTF-8 coding). Bump taurus requirement to > 4.5.4. --- setup.py | 4 ++-- src/sardana/requirements.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 13f7a4bbb1..d69916f532 100644 --- a/setup.py +++ b/setup.py @@ -53,14 +53,14 @@ def get_release_info(): requires = [ 'PyTango (>=9.2.5)', 'itango (>=0.1.6)', - 'taurus (>= 4.5)', + 'taurus (> 4.5.5)', 'lxml (>=2.3)', ] install_requires = [ 'PyTango>=9.2.5', 'itango>=0.1.6', - 'taurus>=4.5', + 'taurus>4.5.4', 'lxml>=2.3' ] diff --git a/src/sardana/requirements.py b/src/sardana/requirements.py index b8ccccb904..63dcb96ab6 100644 --- a/src/sardana/requirements.py +++ b/src/sardana/requirements.py @@ -37,7 +37,7 @@ # module minimum "Python": (3, 5, 0), "PyTango": (9, 2, 5), - "taurus.core": (4, 5), + "taurus.core": (4, 5, 5), } From e27ce959610bbd4c0e3b30a174eb6cb428d57f1f Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 31 Jul 2019 11:13:43 +0200 Subject: [PATCH 157/830] Make upgrade_env script work with PyTango7 and Python 2.6 --- scripts/upgrade/upgrade_env.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts/upgrade/upgrade_env.py b/scripts/upgrade/upgrade_env.py index a296fa1935..4baa49b5b2 100644 --- a/scripts/upgrade/upgrade_env.py +++ b/scripts/upgrade/upgrade_env.py @@ -19,12 +19,19 @@ import PyTango -assert sys.version_info[:2] == (2, 7), "Must run with python 2.7" +assert sys.version_info[:2] in ((2, 6), (2, 7)),\ + "Must run with python 2.6 or 2.7" DefaultEnvBaseDir = "/tmp/tango" DefaultEnvRelDir = "%(ds_exec_name)s/%(ds_inst_name)s/macroserver.properties" +def get_ds_full_name(dev_name): + db = PyTango.Database() + db_dev = PyTango.DeviceProxy(db.dev_name()) + return db_dev.DbGetDeviceInfo(dev_name)[1][3] + + def get_ms_properties(ms_name, ms_ds_name): db = PyTango.Database() prop = db.get_device_property(ms_name, "EnvironmentDb") @@ -66,9 +73,11 @@ def migrate_file(filename, backend): def upgrade_env(ms_name, backend): db = PyTango.Database() - ms_info = db.get_device_info(ms_name) - ms_ds_name = ms_info.ds_full_name - + try: + ms_info = db.get_device_info(ms_name) + ms_ds_name = ms_info.ds_full_name + except AttributeError: + ms_ds_name = get_ds_full_name(ms_name) env_filename = get_ms_properties(ms_name, ms_ds_name) migrate_file(env_filename, backend) From c971710cbc8daa570085eb6d810871900f6fcb1f Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Tue, 30 Jul 2019 17:20:39 +0200 Subject: [PATCH 158/830] Fix sardana executable --- src/sardana/tango/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/tango/__init__.py b/src/sardana/tango/__init__.py index 770d4bf471..4e609cc46c 100644 --- a/src/sardana/tango/__init__.py +++ b/src/sardana/tango/__init__.py @@ -38,10 +38,10 @@ def prepare_sardana(util): def main_sardana(args=None, start_time=None, mode=None): - from . import core + from .core import util # pass server name so the scripts generated with setuptools work on Windows - return core.util.run(prepare_sardana, args=args, start_time=start_time, - mode=mode, name=SERVER_NAME) + return util.run(prepare_sardana, args=args, start_time=start_time, + mode=mode, name=SERVER_NAME) run = main_sardana From 94112bd8b1e938ad80c3bcc1c3af3591918234eb Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 1 Aug 2019 10:57:01 +0200 Subject: [PATCH 159/830] Make online scan work with pyqtgraph --- src/sardana/spock/magic.py | 5 +- .../qt/qtgui/extra_sardana/showscanonline.py | 7 +- .../qt/qtgui/macrolistener/macrolistener.py | 384 ++++++++---------- 3 files changed, 185 insertions(+), 211 deletions(-) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index 421ef7f439..8a39b55d5c 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -88,9 +88,6 @@ def showscan(self, parameter_s=''): online, scan_nb = False, None if len(params) > 0: if params[0].lower() == 'online': - print("'showscan online' does not work (requires migration to " - "taurus_pyqtgraph)") - return try: from sardana.taurus.qt.qtgui.extra_sardana import \ ShowScanOnline @@ -118,7 +115,7 @@ def showscan(self, parameter_s=''): import subprocess import sys fname = sys.modules[ShowScanOnline.__module__].__file__ - args = ['python3', fname, doorname] + args = ['python3', fname, doorname, '--taurus-log-level=error'] subprocess.Popen(args) # show the scan plot, ignoring the plot configuration diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index 4c2e3dcd1c..e154e645b8 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -27,9 +27,10 @@ __all__ = ["ShowScanOnline"] -from sardana.taurus.qt.qtgui.macrolistener import \ - DynamicPlotManager +from taurus.external.qt import Qt from taurus.qt.qtgui.taurusgui import TaurusGui +from sardana.taurus.qt.qtgui.macrolistener import (DynamicPlotManager, + assertPlotAvailability) class ShowScanOnline(DynamicPlotManager): @@ -96,6 +97,8 @@ def main(): org_name="Tango communinity", cmd_line_parser=parser) + assertPlotAvailability() + gui = TaurusGuiLite() args = app.get_command_line_args() diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index b9eec33f33..897e43e8b3 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -40,23 +40,102 @@ from builtins import object import datetime +import collections + +import numpy + +try: + import pyqtgraph +except ImportError: + pyqtgraph = None -from taurus.core.util.containers import CaselessList from taurus.external.qt import Qt from taurus.qt.qtgui.base import TaurusBaseComponent +from taurus.core.util.containers import ArrayBuffer, LoopList + +from sardana.taurus.core.tango.sardana import PlotType -__all__ = ['MacroBroker', 'DynamicPlotManager'] +__all__ = ['MacroBroker', 'DynamicPlotManager', 'assertPlotAvailability'] + __docformat__ = 'restructuredtext' +COLORS = [Qt.QColor(Qt.Qt.red), + Qt.QColor(Qt.Qt.blue), + Qt.QColor(Qt.Qt.green), + Qt.QColor(Qt.Qt.magenta), + Qt.QColor(Qt.Qt.cyan), + Qt.QColor(Qt.Qt.yellow), + Qt.QColor(Qt.Qt.white)] + +SYMBOLS = ['o', 't', 't1', 't2', 't3', 's', 'p'] + +CURVE_STYLES = [(color, symbol) for symbol in SYMBOLS for color in COLORS] + +NO_PLOT_MESSAGE = 'Plots cannot be displayed (pyqtgraph not installed)' + + +def assertPlotAvailability(exit_on_error=True): + if pyqtgraph is None: + Qt.QMessageBox.critical(None, 'Plot error', NO_PLOT_MESSAGE) + if exit_on_error: + exit(1) + -class ChannelFilter(object): +class ScanPlot(Qt.QWidget): - def __init__(self, chlist): - self.chlist = tuple(chlist) + def __init__(self, x_axis, parent=None): + super().__init__(parent) + layout = Qt.QVBoxLayout(self) +# layout.setContentsMargins(0, 0, 0, 0) + self.plot_widget = self._buildPlotWidget(x_axis) + layout.addWidget(self.plot_widget) + self.x_axis = dict(x_axis, data=[]) + self.channels = [] - def __call__(self, x): - return x in self.chlist + def _buildPlotWidget(self, x_axis): + available = pyqtgraph is not None + if available: + widget = pyqtgraph.PlotWidget(labels=dict(bottom=x_axis['label'])) + widget.showGrid(x=True, y=True) + widget.scan_legend = widget.addLegend() + else: + widget = Qt.QLabel(NO_PLOT_MESSAGE) + widget.plot_available = available + return widget + + def prepare(self, channels, nb_points=None): + widget = self.plot_widget + if not widget.plot_available: + return + widget.clear() + # legend is not properly updated when we clear the plot + widget.scan_legend.scene().removeItem(widget.scan_legend) + widget.scan_legend = widget.addLegend() + + nb_points = 2**16 if nb_points is None else nb_points + self.x_axis['data'] = ArrayBuffer(numpy.full(nb_points, numpy.nan)) + self.channels = [] + styles = LoopList(CURVE_STYLES) + for channel in channels: + # don't use symbol: slows down plotting + pen, _ = styles.next() + item = widget.plot(name=channel['label'], pen=pen) + channel = dict(channel, plot_item=item, + data=ArrayBuffer(numpy.full(nb_points, numpy.nan))) + self.channels.append(channel) + + def onNewPoint(self, data): + if not self.plot_widget.plot_available: + return + x_data = self.x_axis['data'] + x_data.append(data[self.x_axis['name']]) + for channel in self.channels: + name = channel['name'] + y_data = channel['data'] + plot_item = channel['plot_item'] + y_data.append(data[name]) + plot_item.setData(x_data.contents(), y_data.contents()) class DynamicPlotManager(Qt.QObject, TaurusBaseComponent): @@ -71,6 +150,8 @@ class DynamicPlotManager(Qt.QObject, TaurusBaseComponent): used. ''' + plots_available = pyqtgraph is not None + newShortMessage = Qt.pyqtSignal('QString') def __init__(self, parent=None): @@ -81,10 +162,7 @@ def __init__(self, parent=None): self._trends1d = {} self._trends2d = {} - - self._parent_can_notify_changes = False - if hasattr(parent, 'experimentConfigurationChanged'): - self._parent_can_notify_changes = True + Qt.qApp.SDM.connectWriter("shortMessage", self, 'newShortMessage') def setModel(self, doorname): '''reimplemented from :meth:`TaurusBaseComponent` @@ -105,18 +183,9 @@ def setModel(self, doorname): self._checkJsonRecorder() - # read the expconf - expconf = self.door.getExperimentConfiguration() - self.onExpConfChanged(expconf) - - # Connect experimentConfigurationChanged signal, - # The event can be emitted by parents like expconf or emitted by QDoor - if self._parent_can_notify_changes: - self.parent().experimentConfigurationChanged.connect( - self.onExpConfChanged) - else: - self.door.recordDataUpdated.connect(self.onRecordDataUpdated) - self.old_arg = None + self.door.recordDataUpdated.connect(self.onRecordDataUpdated) + self.old_arg = None + self.message_template = 'Ready!' def _checkJsonRecorder(self): '''Checks if JsonRecorder env var is set and offers to set it''' @@ -154,191 +223,98 @@ def onRecordDataUpdated(self, arg): data = arg[1] if 'type' in data: - if data['type'] == 'data_desc': - expconf = self.door.getExperimentConfiguration() - self.onExpConfChanged(expconf) - self.door.recordDataUpdated.emit(arg) - - def onExpConfChanged(self, expconf): - ''' - Slot to be called when experimental configuration changes. It should - remove the temporary panels and create the new ones needed. - - :param expconf: (dict) An Experiment Description dictionary. See - :meth:`sardana.taurus.qt.qtcore.tango.sardana. - QDoor.getExperimentDescription` - for more details - ''' - - from sardana.taurus.core.tango.sardana import PlotType - from sardana.taurus.core.tango.sardana.pool import getChannelConfigs - activeMntGrp = expconf['ActiveMntGrp'] - if activeMntGrp is None: - return - if activeMntGrp not in expconf['MntGrpConfigs']: - self.warning( - "ActiveMntGrp '%s' is not defined" % - activeMntGrp) - return - mgconfig = expconf['MntGrpConfigs'][activeMntGrp] - channels = dict(getChannelConfigs(mgconfig, sort=False)) - - # classify by type of plot: - trends1d = {} - trends2d = {} - - for chname, chdata in list(channels.items()): - ptype = chdata['plot_type'] + event_type = data['type'] + if event_type == 'data_desc': + self.prepare(data) + elif event_type == 'record_data': + self.newPoint(data) + elif event_type == 'record_end': + self.end(data) + + def prepare(self, data_desc): + """ + Prepare UI for a new scan. Rebuilds plots as necessary to adapt to the + new scan channels and moveables + """ + data = data_desc['data'] + # dict< axis: list > + trends1d = collections.defaultdict(list) + column_map = {col['name']:col for col in data['column_desc']} + + # build a map of axis and corresponding channels + for column in data['column_desc']: + ptype = column.get('plot_type', PlotType.No) if ptype == PlotType.No: continue - elif ptype == PlotType.Spectrum: - axes = tuple(chdata['plot_axes']) - # TODO: get default value from the channel. - ndim = chdata.get('ndim', 0) or 0 + ch_name = column['name'] + axes = [] + for axis in column.get('plot_axes', ()): + if axis == '': + axis = 'point_nb' + axes.append(axis) + if ptype == PlotType.Spectrum: + ndim = column.get('ndim', 0) or 0 if ndim == 0: # this is a trend - if axes in trends1d: - trends1d[axes].append(chname) - else: - trends1d[axes] = CaselessList([chname]) - elif ndim == 1: # a 1D plot (e.g. a spectrum) - pass # TODO: implement + for axis in axes: + trends1d[axis].append(column) else: - self.warning('Cannot create plot for %s', chname) - + self.warning('Cannot create spectrum plot for %d dims channel %r', + ndim, ch_name) elif ptype == PlotType.Image: - axes = tuple(chdata['plot_axes']) - # TODO: get default value from the channel. - ndim = chdata.get('ndim', 1) - if ndim == 0: # a mesh-like plot? - pass # TODO implement - elif ndim == 1: # a 2D trend - if axes in trends2d: - trends2d[axes].append(chname) - else: - trends2d[axes] = CaselessList([chname]) - elif ndim == 2: # a 2D plot (e.g. an image) - pass # TODO: implement - else: - self.warning('Cannot create plot for %s', chname) - try: - # TODO: adapt _updateTemporaryTrends1D to use tpg - new1d, removed1d = self._updateTemporaryTrends1D(trends1d) - self.newShortMessage.emit("Changed panels (%i new, %i removed)" - % (len(new1d), len(removed1d))) - except Exception as e: - self.warning( - 'Plots cannot be updated. Only qwt5 is supported for now' - ) - self.error(e) - - # try: - # # TODO: adapt _updateTemporaryTrends2D to use tpg - # print(trends2d) - # new2d, removed2d = self._updateTemporaryTrends2D(trends2d) - # self.newShortMessage.emit("Changed panels (%i new, %i removed)" - # % (len(new2d), len(removed2d))) - # except Exception as e: - # self.warning( - # 'Plots 2d cannot be updated. Only qwt5 is supported for now' - # ) - # self.error(e) - - def _updateTemporaryTrends1D(self, trends1d): - '''adds necessary trend1D panels and removes no longer needed ones - - :param trends1d: (dict) A dict whose keys are tuples of axes and - whose values are list of model names to plot - - :returns: (tuple) two lists new,rm:new contains the names of the new - panels and rm contains the names of the removed panels - ''' - from taurus.qt.qtgui.plot import TaurusTrend # TODO: use tpg instead! - newpanels = [] - for axes, plotables in list(trends1d.items()): - if not axes: - continue - if axes not in self._trends1d: - w = TaurusTrend() - w.setXIsTime(False) - w.setScanDoor(self.getModelObj().getFullName()) - # TODO: use a standard key for and - w.setScansXDataKey(axes[0]) - pname = 'Trend1D - %s' % ":".join(axes) - panel = self.createPanel(w, pname, registerconfig=False, - permanent=False) - try: # if the panel is a dockwidget, raise it - panel.raise_() - except Exception: - pass - self._trends1d[axes] = pname - newpanels.append(pname) - - widget = self.getPanelWidget(self._trends1d[axes]) - flt = ChannelFilter(plotables) - widget.onScanPlotablesFilterChanged(flt) - - # remove trends that are no longer configured - removedpanels = [] - olditems = list(self._trends1d.items()) - for axes, name in olditems: - if axes not in trends1d: - removedpanels.append(name) - self.removePanel(name) - self._trends1d.pop(axes) - - return newpanels, removedpanels - - def _updateTemporaryTrends2D(self, trends2d): - '''adds necessary trend2D panels and removes no longer needed ones - - :param trends2d: (dict) A dict whose keys are tuples of axes and - whose values are list of model names to plot - - :returns: (tuple) two lists new,rm:new contains the names of the new - panels and rm contains the names of the removed panels - - ..note:: Not fully implemented yet - ''' - try: - from taurus.qt.qtgui.extra_guiqwt.taurustrend2d import \ - TaurusTrend2DDialog - from taurus.qt.qtgui.extra_guiqwt.image import ( - TaurusTrend2DScanItem) - except Exception: - self.info('guiqwt extension cannot be loaded. ' - + '2D Trends will not be created') - raise - return - - newpanels = [] - for axes, plotables in list(trends2d.items()): - for chname in plotables: - pname = 'Trend2D - %s' % chname - if pname in self._trends2d: - self._trends2d[pname].widget().trendItem.clearTrend() - else: - axis = axes[0] - w = TaurusTrend2DDialog(stackMode='event') - plot = w.get_plot() - name = self.getModelObj().getFullName() - t2d = TaurusTrend2DScanItem(chname, axis, - name) - plot.add_item(t2d) - self.createPanel(w, pname, registerconfig=False, - permanent=False) - self._trends2d[(axes, chname)] = pname - newpanels.append(pname) - - # remove trends that are no longer configured - removedpanels = [] - olditems = list(self._trends2d.items()) - for axes, name in olditems: - if axes not in trends2d: - removedpanels.append(name) - self.removePanel(name) - self._trends2d.pop(axes) - - return newpanels, removedpanels + self.warning('Unsupported image plot for %s', ch_name) + + # build list of widgets: one plot for each axis. Widgets are recycled + # from the previous scans if possible to avoid rearranging the GUI + for axis in trends1d: + if axis not in self._trends1d: + x_axis = column_map[axis] + w = ScanPlot(x_axis) + title = 'Trend1D - ' + x_axis['label'] + self.createPanel(w, title, registerconfig=False, permanent=False) + self._trends1d[axis] = title + + # remove widgets from previous scans which are not used in current scan + for axis in tuple(self._trends1d): + if axis not in trends1d: + self.removePanel(self._trends1d[axis]) + del self._trends1d[axis] + + # prepare each plot widget with list of channels + nb_points = data.get('total_scan_intervals', 2**16) + 1 + for axis, panel_name in self._trends1d.items(): + widget = self.getPanelWidget(panel_name) + widget.prepare(trends1d[axis], nb_points) + + # build status message + serialno = 'Scan #{}'.format(data.get('serialno', '?')) + title = data.get('title', 'unnamed operation') + if data.get('scandir') and data.get('scanfile'): + scan_file = data['scanfile'] + if isinstance(scan_file, (list, tuple)): + scan_file = '&'.join(data['scanfile']) + saving = data['scandir'] + '/' + scan_file + else: + saving = 'no saving!' + started = 'Started ' + data.get('starttime', '?') + progress = '{progress}' + self.message_template = ' | '.join((serialno, title, started, + progress, saving)) + self.newShortMessage.emit(self.message_template.format(progress='Preparing...')) + + def newPoint(self, point): + data = point['data'] + for _, panel_name in self._trends1d.items(): + widget = self.getPanelWidget(panel_name) + widget.onNewPoint(data) + point_nb = 'Point #{}'.format(data['point_nb']) + msg = self.message_template.format(progress=point_nb) + self.newShortMessage.emit(msg) + + def end(self, end_data): + data = end_data['data'] + progress = 'Ended {}'.format(data['endtime']) + msg = self.message_template.format(progress=progress) + self.newShortMessage.emit(msg) def createPanel(self, widget, name, **kwargs): '''Creates a "panel" from a widget. In this basic implementation this @@ -411,8 +387,6 @@ def __init__(self, parent): # connect the broker to shared data Qt.qApp.SDM.connectReader("doorName", self.setModel) - Qt.qApp.SDM.connectReader("expConfChanged", self.onExpConfChanged) - Qt.qApp.SDM.connectWriter("shortMessage", self, 'newShortMessage') def setModel(self, doorname): ''' Reimplemented from :class:`DynamicPlotManager`.''' From 37c3d19a60e36e5ddbedefe578a1cfccfe1ea3e3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 5 Aug 2019 19:14:23 +0200 Subject: [PATCH 160/830] Fix flake8 --- .../taurus/qt/qtgui/extra_sardana/showscanonline.py | 1 - .../taurus/qt/qtgui/macrolistener/macrolistener.py | 12 +++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index e154e645b8..3a39c1e606 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -27,7 +27,6 @@ __all__ = ["ShowScanOnline"] -from taurus.external.qt import Qt from taurus.qt.qtgui.taurusgui import TaurusGui from sardana.taurus.qt.qtgui.macrolistener import (DynamicPlotManager, assertPlotAvailability) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 897e43e8b3..05905bcfc9 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -239,7 +239,7 @@ def prepare(self, data_desc): data = data_desc['data'] # dict< axis: list > trends1d = collections.defaultdict(list) - column_map = {col['name']:col for col in data['column_desc']} + column_map = {col['name']: col for col in data['column_desc']} # build a map of axis and corresponding channels for column in data['column_desc']: @@ -258,8 +258,8 @@ def prepare(self, data_desc): for axis in axes: trends1d[axis].append(column) else: - self.warning('Cannot create spectrum plot for %d dims channel %r', - ndim, ch_name) + self.warning('Cannot create spectrum plot for %d dims ' + 'channel %r', ndim, ch_name) elif ptype == PlotType.Image: self.warning('Unsupported image plot for %s', ch_name) @@ -270,7 +270,8 @@ def prepare(self, data_desc): x_axis = column_map[axis] w = ScanPlot(x_axis) title = 'Trend1D - ' + x_axis['label'] - self.createPanel(w, title, registerconfig=False, permanent=False) + self.createPanel(w, title, registerconfig=False, + permanent=False) self._trends1d[axis] = title # remove widgets from previous scans which are not used in current scan @@ -299,7 +300,8 @@ def prepare(self, data_desc): progress = '{progress}' self.message_template = ' | '.join((serialno, title, started, progress, saving)) - self.newShortMessage.emit(self.message_template.format(progress='Preparing...')) + self.newShortMessage.emit( + self.message_template.format(progress='Preparing...')) def newPoint(self, point): data = point['data'] From 1d1823212f34b935006050b1d25f538a22fb34e8 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Tue, 6 Aug 2019 15:36:49 +0200 Subject: [PATCH 161/830] Accept str in record data Allow str record data in NXscanH5_FileRecorder. --- src/sardana/macroserver/recorders/h5storage.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/recorders/h5storage.py b/src/sardana/macroserver/recorders/h5storage.py index 2e51b6b6ad..2b34d5abe7 100644 --- a/src/sardana/macroserver/recorders/h5storage.py +++ b/src/sardana/macroserver/recorders/h5storage.py @@ -66,8 +66,8 @@ class NXscanH5_FileRecorder(BaseFileRecorder): """ formats = {'h5': '.h5'} # from http://docs.h5py.org/en/latest/strings.html - str_dt = h5py.special_dtype(vlen=str) # Variable-length UTF-8 (PY2) - byte_dt = h5py.special_dtype(vlen=bytes) # Variable-length UTF-8 (PY2) + str_dt = h5py.special_dtype(vlen=str) # Variable-length UTF-8 + byte_dt = h5py.special_dtype(vlen=bytes) # Variable-length UTF-8 supported_dtypes = ('float32', 'float64', 'int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', @@ -182,6 +182,8 @@ def _startRecordList(self, recordlist): if dd.dtype == 'bool': dd.dtype = 'int8' self.debug('%r will be stored with type=%r', dd.name, dd.dtype) + elif dd.dtype == 'str': + dd.dtype = NXscanH5_FileRecorder.str_dt if dd.value_ref_enabled: # substitute original data (image or spectrum) type and shape # since we will receive references instead @@ -293,6 +295,9 @@ def _createPreScanSnapshot(self, env): pre_scan_value = numpy.int8(dd.pre_scan_value) self.debug('Pre-scan snapshot of %s will be stored as type %s', dd.name, dtype) + elif dd.dtype == 'str': + dd.dtype = NXscanH5_FileRecorder.str_dt + if dtype in self.supported_dtypes: _ds = _snap.create_dataset( label, From 205162b85415501e4df580cbd266aa0d23a707b7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 7 Aug 2019 11:00:55 +0200 Subject: [PATCH 162/830] Doc: add info about writing IOR value It was decided that IOR won't change state to MOVING after Value attribute was written. Also this attribute won't change quality to CHANGING (#21). --- doc/source/devel/overview/overview_IOR.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/source/devel/overview/overview_IOR.rst b/doc/source/devel/overview/overview_IOR.rst index 19a2c263c0..3dab167056 100644 --- a/doc/source/devel/overview/overview_IOR.rst +++ b/doc/source/devel/overview/overview_IOR.rst @@ -11,6 +11,10 @@ register a value. This value type may be one of: :class:`int`, :class:`float`, :class:`bool` but the hardware usually expects a fixed type for a given register. +In contrary to the writing of the :ref:`motor's ` +position attribute the writing of the IOR's value attribute is an instant +operation. + The IOR has a very wide range of applications it can serve to control the :term:`PLC` registers, a discrete motor, etc. From 5c7a7ca91c90e24810f86f72f41e353ce2b2118d Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 7 Aug 2019 11:06:35 +0200 Subject: [PATCH 163/830] Doc: remove confusing info in IOR overview about discrete motors --- doc/source/devel/overview/overview_IOR.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/devel/overview/overview_IOR.rst b/doc/source/devel/overview/overview_IOR.rst index 3dab167056..d641636a46 100644 --- a/doc/source/devel/overview/overview_IOR.rst +++ b/doc/source/devel/overview/overview_IOR.rst @@ -15,8 +15,8 @@ In contrary to the writing of the :ref:`motor's ` position attribute the writing of the IOR's value attribute is an instant operation. -The IOR has a very wide range of applications it can serve to control the -:term:`PLC` registers, a discrete motor, etc. +The IOR has a very wide range of applications, for example it can serve to +control the :term:`PLC` registers. .. seealso:: From d309a80aa5f4e0ae6d69040f85c2db9f9a81c108 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 9 Aug 2019 10:25:15 +0200 Subject: [PATCH 164/830] Update SEP16.md --- doc/source/sep/SEP16.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/sep/SEP16.md b/doc/source/sep/SEP16.md index 88b1534c8f..429222915f 100644 --- a/doc/source/sep/SEP16.md +++ b/doc/source/sep/SEP16.md @@ -1,6 +1,6 @@ Title: Plugins (controllers, macros, etc.) catalogue SEP: 16 - State: DRAFT + State: CANDIDATE Reason: SEP15 migrated the Sardana repository from SourceForge to GitHub. The third-party repositories are still at SourceForge and a decision From 7e9c245ecf9584ec8d193ad9363e48e96ce2d8b0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 9 Aug 2019 10:44:15 +0200 Subject: [PATCH 165/830] Add linkt to Plugins Cataloque --- doc/source/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/index.rst b/doc/source/index.rst index 1ae979c0df..284e466994 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -40,6 +40,7 @@ Projects related to Sardana Project Page Download from PyPI Documentation + Plugins Catalogue Wiki .. _ALBA: http://www.albasynchrotron.es From fd9c89166c95d9112784068df3a040a80f6ac300 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 9 Aug 2019 16:30:17 +0200 Subject: [PATCH 166/830] Use directly sardana-test docker image (it is Py3 ready now) --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 42130492cc..2a6463b326 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,8 +17,8 @@ env: matrix: - TEST="flake8" - - TEST="testsuite" DOCKER_IMG=reszelaz/sardana-test:py3 - - TEST="doc" DOCKER_IMG=reszelaz/sardana-test:py3 + - TEST="testsuite" DOCKER_IMG=reszelaz/sardana-test + - TEST="doc" DOCKER_IMG=reszelaz/sardana-test before_install: From 7e14046172a23475bc354980a1c3747f9f00d08e Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Mon, 6 May 2019 16:08:19 +0200 Subject: [PATCH 167/830] Implement a basic spock Qt widget --- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py new file mode 100644 index 0000000000..a4bed79e1e --- /dev/null +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -0,0 +1,69 @@ +"""A RichJupyterWidget that loads a spock profile. +""" +from __future__ import (absolute_import, division, print_function, + unicode_literals) +from builtins import (bytes, str, open, super, range, # noqa + zip, round, input, int, pow, object) + +import sys + +from taurus.external.qt import Qt + +from qtconsole.rich_jupyter_widget import RichJupyterWidget +from qtconsole.manager import QtKernelManager + + +class QtSpockWidget(RichJupyterWidget): + """A RichJupyterWidget that starts a kernel with a spock profile. + + It is important to call `shutdown_kernel` to gracefully clean up the + started subprocesses. + + Useful methods of the base class include execute, interrupt_kernel, + restart_kernel, and clear. + + Parameters + ---------- + profile : string + The name of the spock profile to use. The default is 'spockdoor'. + kernel : string + The name of the kernel to use. The default is 'python2'. + **kwargs + All remaining keywords are passed to the RichJupyterWidget base class + + Examples + -------- + >>> from taurus.external.qt import Qt + >>> from sardana.taurus.qt.qtgui.extra_sardana.qtspock import QtSpockWidget + >>> app = Qt.QApplication([]) + ... widget = QtSpockWidget() + ... widget.show() + ... app.aboutToQuit.connect(widget.shutdown_kernel) + ... app.exec_() + """ + def __init__(self, profile='spockdoor', kernel='python2', **kw): + super().__init__(**kw) + + self.kernel_manager = QtKernelManager(kernel_name=kernel) + self.kernel_manager.start_kernel( + extra_arguments=["--profile", profile]) + + self.kernel_client = self.kernel_manager.client() + self.kernel_client.start_channels() + + def shutdown_kernel(self): + """Cleanly shut down the kernel and client subprocesses""" + self.kernel_client.stop_channels() + self.kernel_manager.shutdown_kernel() + + +def main(): + app = Qt.QApplication(sys.argv) + widget = QtSpockWidget() + widget.show() + app.aboutToQuit.connect(widget.shutdown_kernel) + sys.exit(app.exec_()) + + +if __name__ == "__main__": + main() From 350e82014764078cd117dc0eb94eed353c577bea Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Mon, 6 May 2019 16:13:47 +0200 Subject: [PATCH 168/830] Use spock banner --- src/sardana/spock/ipython_01_00/genutils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index 5ed43e4030..db1d967287 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -1225,7 +1225,8 @@ def out_prompt_tokens(self): # ------------------------------------ # ZMQInteractiveShell # ------------------------------------ - #zmq_i_shell = config.ZMQInteractiveShell + zmq_i_shell = config.ZMQInteractiveShell + zmq_i_shell.banner1 = banner # Tell console everything is ready. config.Spock.ready = True From 6bbbf0cff9f1708941491d4a21cc6c93eb6723f6 Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Wed, 8 May 2019 18:05:46 +0200 Subject: [PATCH 169/830] Check the spock profile before starting the kernel --- src/sardana/spock/ipython_01_00/genutils.py | 17 ++++-- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 52 ++++++++++++++++--- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index db1d967287..5ed1560454 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -812,11 +812,8 @@ def upgrade_spock_profile(ipy_profile_dir, door_name): _create_config_file(ipy_profile_dir, door_name) -def check_for_upgrade(ipy_profile_dir): - """Check if the current profile is up to date with the spock version - - :param ipy_profile_dir: directory with the spock profile - """ +def get_profile_metadata(ipy_profile_dir): + """Read the profile version string and the door name from the profile""" spock_profile_ver_str = '0.0.0' door_name = None @@ -840,6 +837,16 @@ def check_for_upgrade(ipy_profile_dir): if line.startswith('# door_name = '): door_name = line[line.index('=') + 1:].strip() + return spock_profile_ver_str, door_name + + +def check_for_upgrade(ipy_profile_dir): + """Check if the current profile is up to date with the spock version + + :param ipy_profile_dir: directory with the spock profile + """ + spock_profile_ver_str, door_name = get_profile_metadata(ipy_profile_dir) + # convert version from string to numbers spock_lib_ver_str = release.version spocklib_ver = translate_version_str2int(spock_lib_ver_str) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index a4bed79e1e..13ae6a2116 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -7,11 +7,22 @@ import sys +from IPython.core.profiledir import ProfileDirError, ProfileDir +try: + # IPython 4.x + from IPython.paths import get_ipython_dir +except: + # IPython <4.x + from IPython.utils.path import get_ipython_dir + from taurus.external.qt import Qt from qtconsole.rich_jupyter_widget import RichJupyterWidget from qtconsole.manager import QtKernelManager +from sardana import release +from sardana.spock.ipython_01_00.genutils import get_profile_metadata + class QtSpockWidget(RichJupyterWidget): """A RichJupyterWidget that starts a kernel with a spock profile. @@ -44,17 +55,42 @@ class QtSpockWidget(RichJupyterWidget): def __init__(self, profile='spockdoor', kernel='python2', **kw): super().__init__(**kw) - self.kernel_manager = QtKernelManager(kernel_name=kernel) - self.kernel_manager.start_kernel( - extra_arguments=["--profile", profile]) - - self.kernel_client = self.kernel_manager.client() - self.kernel_client.start_channels() + if self.check_spock_profile(profile): + self.kernel_manager = QtKernelManager(kernel_name=kernel) + self.kernel_manager.start_kernel( + extra_arguments=["--profile", profile]) + + self.kernel_client = self.kernel_manager.client() + self.kernel_client.start_channels() + else: + self.append_stream( + "Spock profile error: please close the application" + " and run spock in the terminal.") + + def get_spock_profile_dir(self, profile): + """Return the path to the profile with the given name.""" + try: + profile_dir = ProfileDir.find_profile_dir_by_name( + get_ipython_dir(), profile, self.config) + except ProfileDirError: + return None + return profile_dir.location + + def check_spock_profile(self, profile): + """Check if the profile exists and has the correct value""" + profile_dir = self.get_spock_profile_dir(profile) + if profile_dir: + profile_version_str, door_name = get_profile_metadata(profile_dir) + if profile_version_str == release.version: + return True + return False def shutdown_kernel(self): """Cleanly shut down the kernel and client subprocesses""" - self.kernel_client.stop_channels() - self.kernel_manager.shutdown_kernel() + if self.kernel_client: + self.kernel_client.stop_channels() + if self.kernel_manager and self.kernel_manager.kernel: + self.kernel_manager.shutdown_kernel() def main(): From 01e36f82737986e00008b2c0ceeee73cdd9e84ee Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Wed, 8 May 2019 18:24:38 +0200 Subject: [PATCH 170/830] Add method to retrieve values from the kernel --- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 13ae6a2116..18bbcfa071 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -6,6 +6,8 @@ zip, round, input, int, pow, object) import sys +import pickle +import ast from IPython.core.profiledir import ProfileDirError, ProfileDir try: @@ -85,6 +87,46 @@ def check_spock_profile(self, profile): return True return False + # Adapted from + # https://github.com/moble/remote_exec/blob/master/remote_exec.py#L61 + def get_value(self, var, timeout=None): + """Retrieve a value from the user namespace through a blocking call. + + The value must be able to be pickled on the kernel side and unpickled + on the frontend side. + + The command will import the pickle module in the user namespace. This + may overwrite a user defined variable with the same name. + + Parameters + ---------- + var : str + The name of the variable to be retrieved + timeout : int or None + Number of seconds to wait for reply. If no reply is recieved, a + `Queue.Empty` exception is thrown. The default is to wait + indefinitely + + Returns + ------- + The value of the variable from the user namespace + """ + pickle_dumps = 'pickle.dumps({})'.format(var) + msg_id = self.blocking_client.execute( + "import pickle", silent=True, + user_expressions={'output': pickle_dumps}) + reply = self.blocking_client.get_shell_msg(msg_id, timeout=timeout) + if reply['content']['status'] != "ok": + raise RuntimeError("{}: {}".format( + reply['content']['ename'], reply['content']['evalue'])) + output = reply['content']['user_expressions']['output'] + if output['status'] != "ok": + raise RuntimeError("{}: {}".format( + output['ename'], output['evalue'])) + output_bytes = output['data']['text/plain'] + output_bytes = ast.literal_eval(output_bytes) + return pickle.loads(output_bytes) + def shutdown_kernel(self): """Cleanly shut down the kernel and client subprocesses""" if self.kernel_client: From 4fb7c8bce8c5b885653ef9c55283aca556342da2 Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Wed, 8 May 2019 18:24:58 +0200 Subject: [PATCH 171/830] Set in and out prompts --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 18bbcfa071..f47c115911 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -64,6 +64,11 @@ def __init__(self, profile='spockdoor', kernel='python2', **kw): self.kernel_client = self.kernel_manager.client() self.kernel_client.start_channels() + self.in_prompt = self.get_value( + "get_ipython().config.Spock.door_alias") + self.in_prompt += ' [%i]:' + self.out_prompt = ('Result ' + '[%i]:') else: self.append_stream( "Spock profile error: please close the application" From 2c774399ab153d6a81b6cd90fcf75edf979103f4 Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Wed, 8 May 2019 18:25:09 +0200 Subject: [PATCH 172/830] Add some logging --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index f47c115911..d0d0f66dc5 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -18,6 +18,7 @@ from IPython.utils.path import get_ipython_dir from taurus.external.qt import Qt +from taurus import info from qtconsole.rich_jupyter_widget import RichJupyterWidget from qtconsole.manager import QtKernelManager @@ -59,6 +60,7 @@ def __init__(self, profile='spockdoor', kernel='python2', **kw): if self.check_spock_profile(profile): self.kernel_manager = QtKernelManager(kernel_name=kernel) + info('Starting kernel...') self.kernel_manager.start_kernel( extra_arguments=["--profile", profile]) @@ -134,6 +136,7 @@ def get_value(self, var, timeout=None): def shutdown_kernel(self): """Cleanly shut down the kernel and client subprocesses""" + info('Shutting down kernel...') if self.kernel_client: self.kernel_client.stop_channels() if self.kernel_manager and self.kernel_manager.kernel: From 50dec90dcc0a6e1edcee29bb2f3f758cc141b2a6 Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Wed, 8 May 2019 18:36:49 +0200 Subject: [PATCH 173/830] Add argument for loading additional extensions and overwrite edmac macro --- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 19 ++++++++++++++++--- .../qt/qtgui/extra_sardana/qtspock_ext.py | 6 ++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/sardana/taurus/qt/qtgui/extra_sardana/qtspock_ext.py diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index d0d0f66dc5..b945731d63 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -55,14 +55,27 @@ class QtSpockWidget(RichJupyterWidget): ... app.aboutToQuit.connect(widget.shutdown_kernel) ... app.exec_() """ - def __init__(self, profile='spockdoor', kernel='python2', **kw): + def __init__( + self, + profile='spockdoor', + extensions=None, + kernel='python2', + **kw): super().__init__(**kw) + if extensions is None: + extensions = [] + extensions.insert( + 0, "sardana.taurus.qt.qtgui.extra_sardana.qtspock_ext") + + extra_arguments = ["--profile", profile] + for ext in extensions: + extra_arguments.extend(["--ext", ext]) + if self.check_spock_profile(profile): self.kernel_manager = QtKernelManager(kernel_name=kernel) info('Starting kernel...') - self.kernel_manager.start_kernel( - extra_arguments=["--profile", profile]) + self.kernel_manager.start_kernel(extra_arguments=extra_arguments) self.kernel_client = self.kernel_manager.client() self.kernel_client.start_channels() diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock_ext.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock_ext.py new file mode 100644 index 0000000000..547a5dbe8d --- /dev/null +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock_ext.py @@ -0,0 +1,6 @@ +from IPython.core.magic import register_line_magic + + +@register_line_magic +def edmac(self, parameter_s=''): + return "edmac macro not implemented for qtspock" From e596d89e6b9a335621221fc5f9fbc93a156b9e5d Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Wed, 15 May 2019 14:34:43 +0200 Subject: [PATCH 174/830] Move profile check to custom kernel manager and update prompt on restart As the spock profile can change while the frontend is running, it is necessary to check the profile on each start of the kernel and update the banner and the prompt to always contain the correct spock version and door name, respectively. Using a custom kernel manager ensures that the spock profile is checked always before the kernel is started, e.g., by the auto-restarter or by calls to QtSpock.restart_kernel. From the check of the profile until it is actually loaded by the kernel, a small time window for unchecked changes remains. --- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 129 +++++++++++++----- 1 file changed, 96 insertions(+), 33 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index b945731d63..4406a2819d 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -18,7 +18,7 @@ from IPython.utils.path import get_ipython_dir from taurus.external.qt import Qt -from taurus import info +from taurus import info, error from qtconsole.rich_jupyter_widget import RichJupyterWidget from qtconsole.manager import QtKernelManager @@ -27,6 +27,55 @@ from sardana.spock.ipython_01_00.genutils import get_profile_metadata +def get_spock_profile_dir(profile): + """Return the path to the profile with the given name.""" + try: + profile_dir = ProfileDir.find_profile_dir_by_name( + get_ipython_dir(), profile) + except ProfileDirError: + return None + return profile_dir.location + + +def check_spock_profile(profile): + """Check if the profile exists and has the correct value""" + profile_dir = get_spock_profile_dir(profile) + if profile_dir: + profile_version_str, door_name = get_profile_metadata(profile_dir) + if profile_version_str == release.version: + return True + return False + + +class SpockKernelManager(QtKernelManager): + """ + A kernel manager that checks the spock profile before starting a kernel. + + If the check fails, i.e., the profile does not exist or has a different + version, an ipython kernel without spock functionality is started instead + and the attribute `valid_spock_profile` is set to `False`. + """ + kernel_about_to_launch = Qt.pyqtSignal() + + def _launch_kernel(self, kernel_cmd, **kw): + try: + profile = kernel_cmd[kernel_cmd.index("--profile") + 1] + except ValueError: + profile = "spockdoor" + kernel_cmd.append(["--profile", profile]) + if check_spock_profile(profile): + self.is_valid_spock_profile = True + else: + index = kernel_cmd.index("--profile") + del kernel_cmd[index] + del kernel_cmd[index] + self.is_valid_spock_profile = False + error("Checking spock profile failed.") + info('Starting kernel...') + self.kernel_about_to_launch.emit() + return super()._launch_kernel(kernel_cmd, **kw) + + class QtSpockWidget(RichJupyterWidget): """A RichJupyterWidget that starts a kernel with a spock profile. @@ -72,40 +121,39 @@ def __init__( for ext in extensions: extra_arguments.extend(["--ext", ext]) - if self.check_spock_profile(profile): - self.kernel_manager = QtKernelManager(kernel_name=kernel) - info('Starting kernel...') - self.kernel_manager.start_kernel(extra_arguments=extra_arguments) - - self.kernel_client = self.kernel_manager.client() - self.kernel_client.start_channels() - self.in_prompt = self.get_value( - "get_ipython().config.Spock.door_alias") - self.in_prompt += ' [%i]:' - self.out_prompt = ('Result ' - '[%i]:') - else: + self.kernel_manager = SpockKernelManager(kernel_name=kernel) + self.kernel_manager.kernel_about_to_launch.connect( + self._handle_kernel_lauched) + self.kernel_manager.start_kernel(extra_arguments=extra_arguments) + + self.kernel_client = self.kernel_manager.client() + self.kernel_client.start_channels() + + def _set_prompts(self): + var = "get_ipython().config.Spock.door_alias" + self._silent_exec_callback( + var, self._set_prompts_callback) + + def _set_prompts_callback(self, msg): + in_prefix = 'In' + if msg['status'] == 'ok': + output_bytes = msg['data']['text/plain'] + try: + in_prefix = ast.literal_eval(output_bytes) + except SyntaxError: + pass + + if not self.kernel_manager.is_valid_spock_profile: self.append_stream( - "Spock profile error: please close the application" - " and run spock in the terminal.") + "\nSpock profile error: please run spock in the terminal and " + "restart the kernel.\n" + "\nThis is a normal ipython kernel. " + "Spock functionality is not available.\n") - def get_spock_profile_dir(self, profile): - """Return the path to the profile with the given name.""" - try: - profile_dir = ProfileDir.find_profile_dir_by_name( - get_ipython_dir(), profile, self.config) - except ProfileDirError: - return None - return profile_dir.location - - def check_spock_profile(self, profile): - """Check if the profile exists and has the correct value""" - profile_dir = self.get_spock_profile_dir(profile) - if profile_dir: - profile_version_str, door_name = get_profile_metadata(profile_dir) - if profile_version_str == release.version: - return True - return False + self.in_prompt = ( + in_prefix + ' [%i]:') + self.out_prompt = ( + 'Result [%i]:') # Adapted from # https://github.com/moble/remote_exec/blob/master/remote_exec.py#L61 @@ -155,6 +203,21 @@ def shutdown_kernel(self): if self.kernel_manager and self.kernel_manager.kernel: self.kernel_manager.shutdown_kernel() + def _handle_kernel_lauched(self): + if self.kernel_client: + self.kernel_client.kernel_info() + + def _handle_kernel_info_reply(self, rep): + self._set_prompts() + is_starting = self._starting + super()._handle_kernel_info_reply(rep) + if not is_starting: + # The base method did not print the banner and reset the prompt. + # As the profile might have changed, do it here. + self._append_plain_text("\n\n") + self._append_plain_text(self.kernel_banner) + self.reset() + def main(): app = Qt.QApplication(sys.argv) From f33be5b12f42f0bc07b5b7c14ad3c9866193800d Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Fri, 17 May 2019 14:31:35 +0200 Subject: [PATCH 175/830] Add some tests --- .../qt/qtgui/extra_sardana/test/__init__.py | 0 .../qtgui/extra_sardana/test/test_qtspock.py | 236 ++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 src/sardana/taurus/qt/qtgui/extra_sardana/test/__init__.py create mode 100644 src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/test/__init__.py b/src/sardana/taurus/qt/qtgui/extra_sardana/test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py new file mode 100644 index 0000000000..84720257fc --- /dev/null +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py @@ -0,0 +1,236 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) +from builtins import (bytes, str, open, super, range, # noqa + zip, round, input, int, pow, object) + +import re +import os +import tempfile +import numpy as np + +from taurus.external.unittest import TestCase, main +from taurus.external import qt +from taurus.external.qt import Qt +from sardana.spock.ipython_01_00.genutils import _create_config_file +from sardana.sardanacustomsettings import UNITTEST_DOOR_NAME +from sardana.taurus.qt.qtgui.extra_sardana.qtspock import QtSpockWidget, \ + get_spock_profile_dir + +if qt.PYQT4: + from PyQt4.QtTest import QTest +elif qt.PYQT5: + from PyQt5.QtTest import QTest +elif qt.PYSIDE: + from PySide.QtTest import QTest + + +def waitFor(predicate, timeout): + """Process events until predicate is true or timeout seconds passed + + This seems to not handle the destruction of widget correctly, use + waitForLoop in such cases. + """ + if predicate(): + return True + + timer = Qt.QElapsedTimer() + timer.start() + + while not timer.hasExpired(timeout): + QTest.qWait(min(100, timeout - timer.elapsed())) + if predicate(): + return True + + return predicate() + + +def waitForLoop(predicate, timeout): + """Run event loop and periodically check for predicate""" + if predicate(): + return True + + timer = Qt.QElapsedTimer() + timer.start() + + loop = Qt.QEventLoop() + + while not timer.hasExpired(timeout): + Qt.QTimer.singleShot(min(100, timeout - timer.elapsed()), loop.quit) + loop.exec_() + if predicate(): + return True + + return predicate() + + +class QtSpockTestCase(TestCase): + @classmethod + def setUpClass(cls): + # Use setUpClass instead of setUp because starting QtSpock + # takes a relatively long time. + cls.test_ipython_dir = tempfile.mkdtemp() + cls._create_profile() + os.environ["IPYTHONDIR"] = cls.test_ipython_dir + cls.widget = QtSpockWidget() + cls.widget.show() + cls._isDestroyed = False + + @classmethod + def _create_profile(cls): + cls.test_spockdoor_dir = os.path.join( + cls.test_ipython_dir, "profile_spockdoor") + os.mkdir(cls.test_spockdoor_dir) + _create_config_file(cls.test_spockdoor_dir, UNITTEST_DOOR_NAME) + + @classmethod + def _handle_destroyed(cls): + cls._isDestroyed = True + + @classmethod + def tearDownClass(cls): + cls.widget.shutdown_kernel() + cls.widget.setAttribute(Qt.Qt.WA_DeleteOnClose) + cls.widget.destroyed.connect(cls._handle_destroyed) + cls.widget.close() + cls.widget = None + waitForLoop(lambda: cls._isDestroyed, 5000) + + +class CorrectProfileOutputMixin(object): + def test_spock_banner(self): + def predicate(): + text = self.widget._control.toPlainText() + matches = re.findall(r"^Spock \d\.\d", text, re.MULTILINE) + return len(matches) == 1 + self.assertTrue(waitFor(predicate, 5000)) + + def test_spock_prompt(self): + def predicate(): + text = self.widget._control.toPlainText() + matches = re.findall( + r"^{}.*\[(\d)\]".format(UNITTEST_DOOR_NAME), + text, re.MULTILINE) + return len(matches) == 1 and matches[0] == "1" + self.assertTrue(waitFor(predicate, 5000)) + + +class CorrectProfileTestCase(QtSpockTestCase, CorrectProfileOutputMixin): + def test_get_value(self): + msg_id = self.widget.blocking_client.execute( + "a = arange(3)", silent=True) + self.widget.blocking_client.get_shell_msg(msg_id) + self.assertTrue( + np.array_equal(self.widget.get_value("a"), np.arange(3))) + + def test_find_spock_profile(self): + self.assertEqual( + get_spock_profile_dir("spockdoor"), self.test_spockdoor_dir) + + def test_is_valid_spock_profile(self): + self.assertTrue(self.widget.kernel_manager.is_valid_spock_profile) + + +class ProfileErrorOutputMixin(object): + def test_ipython_banner(self): + def predicate(): + text = self.widget._control.toPlainText() + matches = re.findall(r"^IPython \d\.\d", text, re.MULTILINE) + return len(matches) == 1 + self.assertTrue(waitFor(predicate, 5000)) + + def test_ipython_prompt(self): + def predicate(): + text = self.widget._control.toPlainText() + matches = re.findall(r"^In.*\[(\d)\]", text, re.MULTILINE) + return len(matches) == 1 and matches[0] == "1" + self.assertTrue(waitFor(predicate, 5000)) + + def test_profile_error_info(self): + def predicate(): + text = self.widget._control.toPlainText() + matches = re.findall(r"^Spock profile error", text, re.MULTILINE) + return len(matches) == 1 + self.assertTrue(waitFor(predicate, 5000)) + + +class MissingProfileTestCase(QtSpockTestCase, ProfileErrorOutputMixin): + @classmethod + def setUpClass(cls): + cls.test_ipython_dir = tempfile.mkdtemp() + os.environ["IPYTHONDIR"] = cls.test_ipython_dir + cls.widget = QtSpockWidget() + cls.widget.show() + cls._isDestroyed = False + + def test_is_valid_spock_profile(self): + self.assertTrue(not self.widget.kernel_manager.is_valid_spock_profile) + + +class CorrectProfileAfterRestartTestCase( + MissingProfileTestCase, CorrectProfileOutputMixin): + @classmethod + def setUpClass(cls): + super(CorrectProfileAfterRestartTestCase, cls).setUpClass() + + # Wait until startup finished + def predicate(): + text = cls.widget._control.toPlainText() + matches = re.findall(r"\[1\]: $", text, re.MULTILINE) + return len(matches) == 1 + assert waitFor(predicate, 5000) + + cls._create_profile() + + # Restart kernel + def acceptDialog(): + topLevelWidgets = Qt.QApplication.topLevelWidgets() + dialog = None + for widget in topLevelWidgets: + if isinstance(widget, Qt.QMessageBox): + dialog = widget + dialog.button(Qt.QMessageBox.Yes).click() + + Qt.QTimer.singleShot(10, acceptDialog) + cls.widget.restart_kernel("Restart?") + + def test_is_valid_spock_profile(self): + self.assertTrue(self.widget.kernel_manager.is_valid_spock_profile) + + +class ProfileErrorAfterRestartTestCase( + QtSpockTestCase, ProfileErrorOutputMixin): + @classmethod + def setUpClass(cls): + super(ProfileErrorAfterRestartTestCase, cls).setUpClass() + + # Wait until startup finished + def predicate(): + text = cls.widget._control.toPlainText() + matches = re.findall(r"\[1\]: $", text, re.MULTILINE) + return len(matches) == 1 + assert waitFor(predicate, 5000) + + # "Update" profile + config_file = os.path.join( + cls.test_spockdoor_dir, "ipython_config.py") + with open(config_file) as f: + config = f.read() + config = re.sub( + r"^# spock_creation_version = \d\.\d.*", + "# spock_creation_version = 9.9.9-gamma", + config, flags=re.MULTILINE) + with open(config_file, 'w') as f: + f.write(config) + + # Restart kernel + cls.widget.kernel_manager.restart_kernel() + + def test_is_valid_spock_profile(self): + self.assertTrue(not self.widget.kernel_manager.is_valid_spock_profile) + + +if __name__ == '__main__': + app = Qt.QApplication.instance() + if not app: + app = Qt.QApplication([]) + main() From a899d327c5264e7406fa1b7686115e207a171ed0 Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Fri, 14 Jun 2019 17:36:50 +0200 Subject: [PATCH 176/830] Do not support old, untested IPython versions This fixes a flake8 error for using bare except --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 4406a2819d..ce3c36e9b7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -10,12 +10,6 @@ import ast from IPython.core.profiledir import ProfileDirError, ProfileDir -try: - # IPython 4.x - from IPython.paths import get_ipython_dir -except: - # IPython <4.x - from IPython.utils.path import get_ipython_dir from taurus.external.qt import Qt from taurus import info, error From ba692bc9c1c2273da6c2d56b08bb2c9890f870d9 Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Thu, 20 Jun 2019 14:02:07 +0200 Subject: [PATCH 177/830] Set the door as a Taurus model instead of using the Spock profile config As a side effect, the kernel will not be started automatically in the constructor. Call either setModel or start_kernel to start the kernel. --- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 144 +++++++++++++++--- .../qtgui/extra_sardana/test/test_qtspock.py | 103 ++++++++++++- 2 files changed, 220 insertions(+), 27 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index ce3c36e9b7..a91f201e45 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -17,8 +17,11 @@ from qtconsole.rich_jupyter_widget import RichJupyterWidget from qtconsole.manager import QtKernelManager +from taurus.qt.qtgui.base import TaurusBaseWidget + from sardana import release -from sardana.spock.ipython_01_00.genutils import get_profile_metadata +from sardana.spock.ipython_01_00.genutils import get_profile_metadata, \ + get_ipython_dir, from_name_to_tango, get_macroserver_for_door def get_spock_profile_dir(profile): @@ -55,22 +58,25 @@ def _launch_kernel(self, kernel_cmd, **kw): try: profile = kernel_cmd[kernel_cmd.index("--profile") + 1] except ValueError: - profile = "spockdoor" - kernel_cmd.append(["--profile", profile]) - if check_spock_profile(profile): - self.is_valid_spock_profile = True - else: - index = kernel_cmd.index("--profile") - del kernel_cmd[index] - del kernel_cmd[index] self.is_valid_spock_profile = False - error("Checking spock profile failed.") + else: + if check_spock_profile(profile): + self.is_valid_spock_profile = True + else: + index = kernel_cmd.index("--profile") + del kernel_cmd[index] + del kernel_cmd[index] + for arg in kernel_cmd[:]: + if arg.startswith("--Spock"): + kernel_cmd.remove(arg) + self.is_valid_spock_profile = False + error("Checking spock profile failed.") info('Starting kernel...') self.kernel_about_to_launch.emit() return super()._launch_kernel(kernel_cmd, **kw) -class QtSpockWidget(RichJupyterWidget): +class QtSpockWidget(RichJupyterWidget, TaurusBaseWidget): """A RichJupyterWidget that starts a kernel with a spock profile. It is important to call `shutdown_kernel` to gracefully clean up the @@ -85,6 +91,9 @@ class QtSpockWidget(RichJupyterWidget): The name of the spock profile to use. The default is 'spockdoor'. kernel : string The name of the kernel to use. The default is 'python2'. + use_model_from_profile : bool + If true, the door name is taken from the spock profile, otherwise it + has to be set via setModel. **kwargs All remaining keywords are passed to the RichJupyterWidget base class @@ -100,28 +109,109 @@ class QtSpockWidget(RichJupyterWidget): """ def __init__( self, + parent=None, profile='spockdoor', + use_model_from_profile=False, extensions=None, kernel='python2', **kw): - super().__init__(**kw) + RichJupyterWidget.__init__(self, parent=parent, **kw) + TaurusBaseWidget.__init__(self) + + self._profile = profile + self.use_model_from_profile = use_model_from_profile if extensions is None: extensions = [] extensions.insert( 0, "sardana.taurus.qt.qtgui.extra_sardana.qtspock_ext") - extra_arguments = ["--profile", profile] - for ext in extensions: - extra_arguments.extend(["--ext", ext]) + self._extensions = extensions + self._kernel_name = kernel + + self._macro_server_name = None + self._macro_server_alias = None + self._door_name = None + self._door_alias = None self.kernel_manager = SpockKernelManager(kernel_name=kernel) self.kernel_manager.kernel_about_to_launch.connect( self._handle_kernel_lauched) - self.kernel_manager.start_kernel(extra_arguments=extra_arguments) - self.kernel_client = self.kernel_manager.client() - self.kernel_client.start_channels() + def start_kernel(self): + """Start the kernel + + A normal IPython kernel is started if no model is set via `setModel` or + `use_model_from_profile`. + """ + if not self.kernel_manager.has_kernel: + self.kernel_manager.start_kernel( + extra_arguments=self._extra_arguments()) + self.kernel_client = self.kernel_manager.client() + self.kernel_client.start_channels() + + def _extra_arguments(self): + extra_arguments = ["--profile", self._profile] + for ext in self._extensions: + extra_arguments.extend(["--ext", ext]) + + if not self.use_model_from_profile: + if self._macro_server_name and self._door_name: + extra_arguments.append("--Spock.macro_server={}".format( + self._macro_server_name)) + extra_arguments.append("--Spock.macro_server_alias={}".format( + self._macro_server_alias)) + extra_arguments.append("--Spock.door_name={}".format( + self._door_name)) + extra_arguments.append("--Spock.door_alias={}".format( + self._door_alias)) + else: + # Loading the spock profile would use the macro server and door + # configured there. Instead, use no extra arguments for an + # ipython kernel without Spock functionality + extra_arguments = [] + + return extra_arguments + + def setModel(self, door): + """Set a door as the model + + An empty string or None will start a normal IPython kernel without + spock functionality. + """ + self._set_door_name(door) + self._set_macro_server_name(door) + + if self.kernel_manager.has_kernel: + # RichJupyterWidget.restart_kernel does not support extra arguments + self.kernel_manager.restart_kernel( + extra_arguments=self._extra_arguments()) + self._kernel_restarted_message(died=False) + else: + self.start_kernel() + + def _set_door_name(self, door): + if door: + full_door_tg_name, door_tg_name, door_tg_alias = ( + from_name_to_tango(door)) + door_alias = door_tg_alias or door_tg_name + self._door_name = full_door_tg_name + self._door_alias = door_alias + else: + self._door_name = None + self._door_alias = None + + def _set_macro_server_name(self, door): + if door: + macro_server = get_macroserver_for_door(door) + full_ms_tg_name, ms_tg_name, ms_tg_alias = ( + from_name_to_tango(macro_server)) + ms_alias = ms_tg_alias or ms_tg_name + self._macro_server_name = full_ms_tg_name + self._macro_server_alias = ms_alias + else: + self._macro_server_name = None + self._macro_server_alias = None def _set_prompts(self): var = "get_ipython().config.Spock.door_alias" @@ -138,17 +228,25 @@ def _set_prompts_callback(self, msg): pass if not self.kernel_manager.is_valid_spock_profile: - self.append_stream( - "\nSpock profile error: please run spock in the terminal and " - "restart the kernel.\n" - "\nThis is a normal ipython kernel. " - "Spock functionality is not available.\n") + self._print_ipython_warning() self.in_prompt = ( in_prefix + ' [%i]:') self.out_prompt = ( 'Result [%i]:') + def _print_ipython_warning(self): + if self.use_model_from_profile or self._extra_arguments(): + self.append_stream( + "\nSpock profile error: please run spock in the terminal" + " and restart the kernel.\n") + else: + self.append_stream( + "\nNo door selected. Please select a valid door.\n") + self.append_stream( + "\nThis is a normal ipython kernel. " + "Spock functionality is not available.\n") + # Adapted from # https://github.com/moble/remote_exec/blob/master/remote_exec.py#L61 def get_value(self, var, timeout=None): diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py index 84720257fc..47b5bf6eb2 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py @@ -63,7 +63,7 @@ def waitForLoop(predicate, timeout): return predicate() -class QtSpockTestCase(TestCase): +class QtSpockBaseTestCase(TestCase): @classmethod def setUpClass(cls): # Use setUpClass instead of setUp because starting QtSpock @@ -71,8 +71,6 @@ def setUpClass(cls): cls.test_ipython_dir = tempfile.mkdtemp() cls._create_profile() os.environ["IPYTHONDIR"] = cls.test_ipython_dir - cls.widget = QtSpockWidget() - cls.widget.show() cls._isDestroyed = False @classmethod @@ -96,6 +94,15 @@ def tearDownClass(cls): waitForLoop(lambda: cls._isDestroyed, 5000) +class QtSpockTestCase(QtSpockBaseTestCase): + @classmethod + def setUpClass(cls): + super(QtSpockTestCase, cls).setUpClass() + cls.widget = QtSpockWidget(use_model_from_profile=True) + cls.widget.start_kernel() + cls.widget.show() + + class CorrectProfileOutputMixin(object): def test_spock_banner(self): def predicate(): @@ -158,7 +165,8 @@ class MissingProfileTestCase(QtSpockTestCase, ProfileErrorOutputMixin): def setUpClass(cls): cls.test_ipython_dir = tempfile.mkdtemp() os.environ["IPYTHONDIR"] = cls.test_ipython_dir - cls.widget = QtSpockWidget() + cls.widget = QtSpockWidget(use_model_from_profile=True) + cls.widget.start_kernel() cls.widget.show() cls._isDestroyed = False @@ -229,6 +237,93 @@ def test_is_valid_spock_profile(self): self.assertTrue(not self.widget.kernel_manager.is_valid_spock_profile) +class QtSpockNoModelTestCase(QtSpockBaseTestCase, ProfileErrorOutputMixin): + @classmethod + def setUpClass(cls): + super(QtSpockNoModelTestCase, cls).setUpClass() + cls.widget = QtSpockWidget() + cls.widget.start_kernel() + cls.widget.show() + + def test_is_valid_spock_profile(self): + self.assertTrue(not self.widget.kernel_manager.is_valid_spock_profile) + + def test_profile_error_info(self): + def predicate(): + text = self.widget._control.toPlainText() + matches = re.findall(r"^No door selected", text, re.MULTILINE) + return len(matches) == 1 + self.assertTrue(waitFor(predicate, 5000)) + + +class QtSpockModelTestCase(QtSpockBaseTestCase, CorrectProfileOutputMixin): + @classmethod + def setUpClass(cls): + super(QtSpockModelTestCase, cls).setUpClass() + cls.widget = QtSpockWidget() + cls.widget.setModel(UNITTEST_DOOR_NAME) + cls.widget.show() + + def test_is_valid_spock_profile(self): + self.assertTrue(self.widget.kernel_manager.is_valid_spock_profile) + + +class QtSpockModelAfterRestartTestCase( + QtSpockBaseTestCase, CorrectProfileOutputMixin): + @classmethod + def setUpClass(cls): + super(QtSpockModelAfterRestartTestCase, cls).setUpClass() + cls.widget = QtSpockWidget() + cls.widget.start_kernel() + cls.widget.show() + + # Wait until startup finished + def predicate(): + text = cls.widget._control.toPlainText() + matches = re.findall(r"\[1\]: $", text, re.MULTILINE) + return len(matches) == 1 + assert waitFor(predicate, 5000) + + cls.widget.setModel(UNITTEST_DOOR_NAME) + + def test_is_valid_spock_profile(self): + self.assertTrue(self.widget.kernel_manager.is_valid_spock_profile) + + +class QtSpockNoModelAfterRestartTestCase(QtSpockNoModelTestCase): + @classmethod + def setUpClass(cls): + # Do only call the original base class constructor + super(QtSpockNoModelTestCase, cls).setUpClass() + cls.widget = QtSpockWidget() + cls.widget.setModel(UNITTEST_DOOR_NAME) + cls.widget.show() + + # Wait until startup finished + def predicate(): + text = cls.widget._control.toPlainText() + matches = re.findall(r"\[1\]: $", text, re.MULTILINE) + return len(matches) == 1 + assert waitFor(predicate, 5000) + + cls.widget.setModel("") + + +class QtSpockModelMissingProfileTestCase( + QtSpockTestCase, ProfileErrorOutputMixin): + @classmethod + def setUpClass(cls): + cls.test_ipython_dir = tempfile.mkdtemp() + os.environ["IPYTHONDIR"] = cls.test_ipython_dir + cls.widget = QtSpockWidget() + cls.widget.setModel(UNITTEST_DOOR_NAME) + cls.widget.show() + cls._isDestroyed = False + + def test_is_valid_spock_profile(self): + self.assertTrue(not self.widget.kernel_manager.is_valid_spock_profile) + + if __name__ == '__main__': app = Qt.QApplication.instance() if not app: From d82122cb8a6b2c5ca1da5bdcac54e975a64a1f51 Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Thu, 20 Jun 2019 14:41:59 +0200 Subject: [PATCH 178/830] Add standalone QtSpock Taurus application --- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index a91f201e45..4ebce6eac7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -18,10 +18,14 @@ from qtconsole.manager import QtKernelManager from taurus.qt.qtgui.base import TaurusBaseWidget +from taurus.qt.qtgui.container import TaurusMainWindow +from taurus.qt.qtgui.resource import getThemeIcon from sardana import release from sardana.spock.ipython_01_00.genutils import get_profile_metadata, \ get_ipython_dir, from_name_to_tango, get_macroserver_for_door +from sardana.taurus.qt.qtgui.extra_macroexecutor import \ + TaurusMacroConfigurationDialog def get_spock_profile_dir(profile): @@ -311,11 +315,46 @@ def _handle_kernel_info_reply(self, rep): self.reset() +class QtSpock(TaurusMainWindow): + """A standalone QtSpock window""" + def __init__(self, parent=None, designMode=False): + super().__init__(parent, designMode) + self.spockWidget = QtSpockWidget(parent=self) + self.registerConfigDelegate(self.spockWidget) + self.spockWidget.setModelInConfig(True) + self.setCentralWidget(self.spockWidget) + self.configureAction = self.createConfigureAction() + self.taurusMenu.addAction(self.configureAction) + self.statusBar().showMessage("MacroExecutor ready") + self.loadSettings() + + def createConfigureAction(self): + configureAction = Qt.QAction(getThemeIcon( + "preferences-system-session"), "Change configuration", self) + configureAction.triggered.connect(self.changeConfiguration) + configureAction.setToolTip("Configuring MacroServer and Door") + configureAction.setShortcut("F10") + return configureAction + + def changeConfiguration(self): + """This method is used to change macroserver as a model of application. + It shows dialog with list of all macroservers on tango host, if the + user Cancel dialog it doesn't do anything.""" + dialog = TaurusMacroConfigurationDialog( + self, self.spockWidget._macro_server_name, + self.modelName) + if dialog.exec_(): + self.spockWidget.setModel(str(dialog.doorComboBox.currentText())) + else: + return + + def main(): - app = Qt.QApplication(sys.argv) - widget = QtSpockWidget() - widget.show() - app.aboutToQuit.connect(widget.shutdown_kernel) + from taurus.qt.qtgui.application import TaurusApplication + app = TaurusApplication() + window = QtSpock() + window.show() + app.aboutToQuit.connect(window.spockWidget.shutdown_kernel) sys.exit(app.exec_()) From 1e32bc28c0100ca406673eb1969f200492abea6b Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Tue, 25 Jun 2019 17:32:54 +0200 Subject: [PATCH 179/830] Only restart kernel when new model differs from old model --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 6 ++++++ .../taurus/qt/qtgui/extra_sardana/test/test_qtspock.py | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 4ebce6eac7..768f14ca48 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -183,9 +183,15 @@ def setModel(self, door): An empty string or None will start a normal IPython kernel without spock functionality. """ + old_door_name = self._door_name + old_macroserver_name = self._macro_server_name self._set_door_name(door) self._set_macro_server_name(door) + if (self._door_name == old_door_name + and self._macro_server_name == old_macroserver_name): + return + if self.kernel_manager.has_kernel: # RichJupyterWidget.restart_kernel does not support extra arguments self.kernel_manager.restart_kernel( diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py index 47b5bf6eb2..612fe35f14 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py @@ -7,6 +7,10 @@ import os import tempfile import numpy as np +try: + from unittest.mock import patch +except ImportError: + from mock import patch from taurus.external.unittest import TestCase, main from taurus.external import qt @@ -267,6 +271,12 @@ def setUpClass(cls): def test_is_valid_spock_profile(self): self.assertTrue(self.widget.kernel_manager.is_valid_spock_profile) + def test_setModel_twice_no_restart(self): + with patch.object( + self.widget.kernel_manager, "restart_kernel", spec=True) as m: + self.widget.setModel(UNITTEST_DOOR_NAME) + self.assertTrue(not m.called) + class QtSpockModelAfterRestartTestCase( QtSpockBaseTestCase, CorrectProfileOutputMixin): From 7b2142f0233817af46b3e1dd2c8519912127072e Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Tue, 25 Jun 2019 17:48:10 +0200 Subject: [PATCH 180/830] Add QtSpock requirements --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index d69916f532..3b7d36847e 100644 --- a/setup.py +++ b/setup.py @@ -64,6 +64,9 @@ def get_release_info(): 'lxml>=2.3' ] +test_require = [ + 'mock; python_version<"3.3"' +] console_scripts = [ "MacroServer = sardana.tango.macroserver:main", @@ -123,5 +126,6 @@ def get_release_info(): provides=provides, requires=requires, install_requires=install_requires, + test_require=test_require, test_suite='sardana.test.testsuite.get_sardana_unitsuite', ) From e5590909c93684c65341d592563ddb3eecc4589b Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Wed, 26 Jun 2019 13:46:18 +0200 Subject: [PATCH 181/830] Fix QtSpock GUI tests --- .travis.yml | 2 +- .../qtgui/extra_sardana/test/test_qtspock.py | 20 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2a6463b326..9a8d50f64b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,7 +49,7 @@ script: # run flake8 check on all python files in the project - if [ $TEST == "flake8" ]; then ci/flake8_diff.sh; fi # run the full testsuite - - if [ $TEST == "testsuite" ]; then docker exec sardana-test sardanatestsuite; fi + - if [ $TEST == "testsuite" ]; then docker exec sardana-test xvfb-run -s '-screen 0 1920x1080x24' sardanatestsuite; fi # build docs - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py index 612fe35f14..858b1ef147 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py @@ -15,7 +15,8 @@ from taurus.external.unittest import TestCase, main from taurus.external import qt from taurus.external.qt import Qt -from sardana.spock.ipython_01_00.genutils import _create_config_file +from sardana.spock.ipython_01_00.genutils import _create_config_file, \ + from_name_to_tango from sardana.sardanacustomsettings import UNITTEST_DOOR_NAME from sardana.taurus.qt.qtgui.extra_sardana.qtspock import QtSpockWidget, \ get_spock_profile_dir @@ -28,6 +29,11 @@ from PySide.QtTest import QTest +app = Qt.QApplication.instance() +if not app: + app = Qt.QApplication([]) + + def waitFor(predicate, timeout): """Process events until predicate is true or timeout seconds passed @@ -113,16 +119,19 @@ def predicate(): text = self.widget._control.toPlainText() matches = re.findall(r"^Spock \d\.\d", text, re.MULTILINE) return len(matches) == 1 - self.assertTrue(waitFor(predicate, 5000)) + self.assertTrue(waitFor(predicate, 10000)) def test_spock_prompt(self): + full_name, name, alias = from_name_to_tango(UNITTEST_DOOR_NAME) + alias = alias or name + def predicate(): text = self.widget._control.toPlainText() matches = re.findall( - r"^{}.*\[(\d)\]".format(UNITTEST_DOOR_NAME), + r"^{}.*\[(\d)\]".format(alias), text, re.MULTILINE) return len(matches) == 1 and matches[0] == "1" - self.assertTrue(waitFor(predicate, 5000)) + self.assertTrue(waitFor(predicate, 10000)) class CorrectProfileTestCase(QtSpockTestCase, CorrectProfileOutputMixin): @@ -335,7 +344,4 @@ def test_is_valid_spock_profile(self): if __name__ == '__main__': - app = Qt.QApplication.instance() - if not app: - app = Qt.QApplication([]) main() From 12e58ad64b57049e46bae6a6d745e72949e74b83 Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Tue, 25 Jun 2019 16:45:42 +0200 Subject: [PATCH 182/830] Add runMacro method to QtSpock for executing macro nodes --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 768f14ca48..7b2df755ab 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -257,6 +257,9 @@ def _print_ipython_warning(self): "\nThis is a normal ipython kernel. " "Spock functionality is not available.\n") + def runMacro(self, macro_node): + self.execute(macro_node.toSpockCommand()) + # Adapted from # https://github.com/moble/remote_exec/blob/master/remote_exec.py#L61 def get_value(self, var, timeout=None): From 4b22a59924e18d94f54e5656a3cc9cca1e3cd2af Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Mon, 15 Jul 2019 14:00:43 +0200 Subject: [PATCH 183/830] Fix bug introduced in juypter-client 5.2.3 --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 7b2df755ab..9aaa4dab06 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -151,8 +151,9 @@ def start_kernel(self): if not self.kernel_manager.has_kernel: self.kernel_manager.start_kernel( extra_arguments=self._extra_arguments()) - self.kernel_client = self.kernel_manager.client() - self.kernel_client.start_channels() + kernel_client = self.kernel_manager.client() + kernel_client.start_channels() + self.kernel_client = kernel_client def _extra_arguments(self): extra_arguments = ["--profile", self._profile] From 08b564af3fa14a7360fde692ec300cc6e411325c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Jul 2019 17:11:19 +0200 Subject: [PATCH 184/830] Fix storing of model name in Taurus settings --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 9aaa4dab06..820c396e6c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -121,6 +121,7 @@ def __init__( **kw): RichJupyterWidget.__init__(self, parent=parent, **kw) TaurusBaseWidget.__init__(self) + self.setModelInConfig(True) self._profile = profile self.use_model_from_profile = use_model_from_profile @@ -201,6 +202,9 @@ def setModel(self, door): else: self.start_kernel() + def getModel(self): + return self._door_name + def _set_door_name(self, door): if door: full_door_tg_name, door_tg_name, door_tg_alias = ( From 2f8d16991537e8e97aba53dbcc720a13e0df8987 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Jul 2019 17:19:19 +0200 Subject: [PATCH 185/830] Improve storing of Taurus settings --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 820c396e6c..75dd6c11c2 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -121,6 +121,7 @@ def __init__( **kw): RichJupyterWidget.__init__(self, parent=parent, **kw) TaurusBaseWidget.__init__(self) + self.setObjectName(self.__class__.__name__) self.setModelInConfig(True) self._profile = profile @@ -366,6 +367,8 @@ def changeConfiguration(self): def main(): from taurus.qt.qtgui.application import TaurusApplication app = TaurusApplication() + app.setOrganizationName("Taurus") + app.setApplicationName("QtSpock") window = QtSpock() window.show() app.aboutToQuit.connect(window.spockWidget.shutdown_kernel) From 3844c73f3211075ee29021026afc5daac22f44d6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Jul 2019 17:19:43 +0200 Subject: [PATCH 186/830] Fix message on status bar --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 75dd6c11c2..fde05f6c15 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -340,7 +340,7 @@ def __init__(self, parent=None, designMode=False): self.setCentralWidget(self.spockWidget) self.configureAction = self.createConfigureAction() self.taurusMenu.addAction(self.configureAction) - self.statusBar().showMessage("MacroExecutor ready") + self.statusBar().showMessage("QtSpock ready") self.loadSettings() def createConfigureAction(self): From 2e5b369d627b9347e4882db20d5f86e9a81022cf Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Mon, 12 Aug 2019 10:33:28 +0200 Subject: [PATCH 187/830] Remove compatibility with Python 3.2 or earlier --- setup.py | 4 ---- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 9 ++------- .../taurus/qt/qtgui/extra_sardana/test/test_qtspock.py | 5 ----- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/setup.py b/setup.py index 3b7d36847e..d69916f532 100644 --- a/setup.py +++ b/setup.py @@ -64,9 +64,6 @@ def get_release_info(): 'lxml>=2.3' ] -test_require = [ - 'mock; python_version<"3.3"' -] console_scripts = [ "MacroServer = sardana.tango.macroserver:main", @@ -126,6 +123,5 @@ def get_release_info(): provides=provides, requires=requires, install_requires=install_requires, - test_require=test_require, test_suite='sardana.test.testsuite.get_sardana_unitsuite', ) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index fde05f6c15..cf44e10cab 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -1,10 +1,5 @@ """A RichJupyterWidget that loads a spock profile. """ -from __future__ import (absolute_import, division, print_function, - unicode_literals) -from builtins import (bytes, str, open, super, range, # noqa - zip, round, input, int, pow, object) - import sys import pickle import ast @@ -94,7 +89,7 @@ class QtSpockWidget(RichJupyterWidget, TaurusBaseWidget): profile : string The name of the spock profile to use. The default is 'spockdoor'. kernel : string - The name of the kernel to use. The default is 'python2'. + The name of the kernel to use. The default is 'python3'. use_model_from_profile : bool If true, the door name is taken from the spock profile, otherwise it has to be set via setModel. @@ -117,7 +112,7 @@ def __init__( profile='spockdoor', use_model_from_profile=False, extensions=None, - kernel='python2', + kernel='python3', **kw): RichJupyterWidget.__init__(self, parent=parent, **kw) TaurusBaseWidget.__init__(self) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py index 858b1ef147..126bb30a4c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/test/test_qtspock.py @@ -1,8 +1,3 @@ -from __future__ import (absolute_import, division, print_function, - unicode_literals) -from builtins import (bytes, str, open, super, range, # noqa - zip, round, input, int, pow, object) - import re import os import tempfile From 0d1633c5c1dd6904f6155a26ea076e6c1329872c Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Wed, 21 Aug 2019 18:07:43 +0200 Subject: [PATCH 188/830] Remove the sardana.requirements module and usage The sardana.requirements module provides check_requirements() which is (only) used in the __init__.py of sardana. The current implementation and usage of check_requirements() presents some disadvantages: - checking requirements at import time is too strict and causes trouble in some scenarios where one may want to legitimately relax or ignore the requirements (e.g. when preparing a deb package while some deps are not yet available). The check is better done on installation. - the implementation of check_requirements() overlaps the more standard functionality provided by the pkg_resources module. - some requirement specifications are duplicated (in setup.py and in sardana.requirements) and must be kept in sync manually Fix all this by completely removing the module and its usage, and relying on the requirement checks done at installation time according to the specifications from the setup.py --- src/sardana/__init__.py | 3 - src/sardana/requirements.py | 111 ------------------------------------ 2 files changed, 114 deletions(-) delete mode 100644 src/sardana/requirements.py diff --git a/src/sardana/__init__.py b/src/sardana/__init__.py index a355e5fa36..3e01575808 100644 --- a/src/sardana/__init__.py +++ b/src/sardana/__init__.py @@ -27,9 +27,6 @@ """This package provides the sardana library""" from . import release as __release -from . import requirements as __requirements - -__requirements.check_requirements() class Release: diff --git a/src/sardana/requirements.py b/src/sardana/requirements.py deleted file mode 100644 index 63dcb96ab6..0000000000 --- a/src/sardana/requirements.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env python - -############################################################################## -## -# This file is part of Sardana -## -# http://www.sardana-controls.org/ -## -# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -# Sardana 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. -## -# Sardana 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 Sardana. If not, see . -## -############################################################################## - -""" """ - - - -__docformat__ = 'restructuredtext' - -__all__ = ["check_requirements"] - -import sys - -__requires__ = { - # module minimum - "Python": (3, 5, 0), - "PyTango": (9, 2, 5), - "taurus.core": (4, 5, 5), -} - - -def check_requirements(exec_name=None): - - if exec_name is None: - exec_name = sys.argv[0] - - pyver_ = __requires__['Python'] - pytangover_ = __requires__['PyTango'] - taurusver_ = __requires__['taurus.core'] - - pyver_str_ = ".".join(map(str, pyver_)) - pytangover_str_ = ".".join(map(str, pytangover_)) - taurusver_str_ = ".".join(map(str, taurusver_)) - - pyver = sys.version_info[:3] - pyver_str = ".".join(map(str, pyver)) - - if pyver < pyver_: - print("Sardana requires python %s. Installed version is %s" % - (pyver_str_, pyver_str)) - sys.exit(-1) - - pytangover = None - try: - import PyTango - pytangover = PyTango.Release.version_info[:3] - except ImportError: - pass - except: - pytangover = tuple(map(int, PyTango.__version__.split('.', 3))) - - if pytangover is None: - print("%s requires PyTango %s. No version installed" % - (exec_name, pytangover_str_,)) - sys.exit(-1) - if pytangover < pytangover_: - pytangover_str = ".".join(map(str, pytangover)) - print("%s requires PyTango %s. Installed version is %s" % - (exec_name, pytangover_str_, pytangover_str)) - sys.exit(-1) - - # TODO: add itango as runtime dependency of spock - # now it is not possible because itango does not provide info about its - # version - - taurusver = None - try: - import taurus - taurusver = taurus.Release.version_info[:3] - except ImportError: - pass - except: - taurusver = tuple(map(int, taurus.Release.version.split('.', 3))) - - if taurusver is None: - print("%s requires taurus %s. No version installed" % - (exec_name, taurusver_str_,)) - sys.exit(-1) - if taurusver < taurusver_: - taurusver_str = ".".join(map(str, taurusver)) - print("%s requires taurus %s. Installed version is %s" % - (exec_name, taurusver_str_, taurusver_str)) - sys.exit(-1) - - try: - from lxml import etree - except: - print("Could not find any suitable XML library") - sys.exit(-1) From 28d69762bca7d626d97534f4cfa47aa92e4614a5 Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Wed, 21 Aug 2019 18:22:57 +0200 Subject: [PATCH 189/830] Update CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a645953759..9c4208bd6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,14 @@ This file follows the formats and conventions from [keepachangelog.com] * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) +### Changed + +* requirements are no longer checked when importing sardana (#1185) + +### Removed + +* `sardana.requirements` (#1185) + ## [2.8.1] 2019-07-22 ### Fixed From 6c0f55a3f6004e7439bfe28e6ff8cf151bdace1c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 2 Sep 2019 17:22:19 +0200 Subject: [PATCH 190/830] Adapt docs to python3 After #1089, sardana only works with python3. The examples in the docs use "python", "pip", print statement, etc. Change them to use python3 equivalents. Fixes #1184. --- CONTRIBUTING.md | 3 +- .../devel/howto_macros/macros_general.rst | 18 ++++----- doc/source/glossary.rst | 4 +- .../sphinxext/ipython_console_highlighting.py | 2 +- .../sphinxext/spock_console_highlighting.py | 2 +- .../users/getting_started/installing.rst | 38 +++++++++---------- 6 files changed, 32 insertions(+), 35 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6b4ec3de88..35f86abd5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,7 +47,7 @@ In general, the contributions to Sardana should consider following: - The code must comply with the sardana coding conventions: - We try to follow the standard Python style conventions as described in [Style Guide for Python Code](http://www.python.org/peps/pep-0008.html) - - Code **must** be python 2.6 compatible + - Code **must** be python 3.5 compatible (python 2 is not supported) - Use 4 spaces for indentation - use ``lowercase`` for module names. If possible prefix module names with the word ``sardana`` (like :file:`sardanautil.py`) to avoid import mistakes. @@ -79,7 +79,6 @@ The following code can be used as a template for writing new python modules to Sardana: ```python - #!/usr/bin/env python # -*- coding: utf-8 -*- ############################################################################## diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst index 1be8dc0eec..03758cd313 100644 --- a/doc/source/devel/howto_macros/macros_general.rst +++ b/doc/source/devel/howto_macros/macros_general.rst @@ -212,7 +212,7 @@ the macro function version of *Hello, World!*:: .. note:: If you already know a little about Python_ your are probably wondering why - not use ``print "Hello, World!"``? + not use ``print("Hello, World!")``? Remember that your macro will be executed by a Sardana server which may be running in a different computer than the computer you are working on. @@ -225,9 +225,6 @@ the macro function version of *Hello, World!*:: function (it is a bit more powerful than :meth:`~sardana.macroserver.macro.Macro.output`\, and has a slightly different syntax) :: - - # mandatory first line in your code if you use Python < 3.0 - from __future__ import print_function from sardana.macroserver.macro import macro @@ -582,7 +579,7 @@ messages with different levels: * :meth:`~Macro.output` As you've seen, the special :meth:`~Macro.output` function has the same effect -as a print statement (with slightly different arguments). +as a print function (with slightly different arguments). Log messages may have several destinations depending on how your sardana server is configured. At least, one destination of each log message is the client(s) @@ -739,7 +736,7 @@ using :meth:`~Macro.data`: # and the result of the Macro.prepare method my_scan, _ = ret self.runMacro(my_scan) - print len(my_scan.data) + self.print(len(my_scan.data)) A set of macro call examples can be found :ref:`here `. @@ -769,7 +766,7 @@ macro. So, without further delay, here is the *Hello, World!* example:: """Hello, World! macro""" def run(self): - print "Hello, World!" + self.print("Hello, World!") .. _sardana-macro-add-parameters: @@ -818,7 +815,7 @@ prepare HelloWorld to run only after year 1989: raise Exception("HelloWorld can only run after year 1989") def run(self): - print "Hello, World!" + self.print("Hello, World!") .. _sardana-macro-handling-macro-stop-and-abort: @@ -1537,10 +1534,11 @@ The second method is to use standard Python threading_ library. .. rubric:: Footnotes .. [#f1] To find the absolute path for sardana's source code type on the - command line ``python -c "import sys, sardana; sys.stdout.write(str(sardana.__path__))"`` + command line ``python3 -c "import sys, sardana; sys.stdout.write + (str(sardana.__path__))"`` .. [#f2] To check which version of Python_ you are using type on the command - line ``python -c "import sys; sys.stdout.write(sys.version)"`` + line ``python3 -c "import sys; sys.stdout.write(sys.version)"`` .. |input_integer| image:: ../../_static/macro_input_integer.png :align: middle diff --git a/doc/source/glossary.rst b/doc/source/glossary.rst index 485e463517..c0d9e37289 100644 --- a/doc/source/glossary.rst +++ b/doc/source/glossary.rst @@ -247,12 +247,12 @@ Glossary people unfamiliar with Python sometimes use a numerical counter instead:: for i in range(len(food)): - print food[i] + print(food[i]) As opposed to the cleaner, Pythonic method:: for piece in food: - print piece + print(piece) sequence An :term:`iterable` which supports efficient element access using integer diff --git a/doc/source/sphinxext/ipython_console_highlighting.py b/doc/source/sphinxext/ipython_console_highlighting.py index 97bb6e0ed9..29ac0127b8 100644 --- a/doc/source/sphinxext/ipython_console_highlighting.py +++ b/doc/source/sphinxext/ipython_console_highlighting.py @@ -61,7 +61,7 @@ class IPythonConsoleLexer(Lexer): In [2]: a Out[2]: 'foo' - In [3]: print a + In [3]: print(a) foo In [4]: 1 / 0 diff --git a/doc/source/sphinxext/spock_console_highlighting.py b/doc/source/sphinxext/spock_console_highlighting.py index 0ccfaaa898..d77adfaffd 100644 --- a/doc/source/sphinxext/spock_console_highlighting.py +++ b/doc/source/sphinxext/spock_console_highlighting.py @@ -67,7 +67,7 @@ class SpockConsoleLexer(Lexer): LAB-01 [2]: a Result [2]: 'foo' - LAB-01 [3]: print a + LAB-01 [3]: print(a) foo LAB-01 [4]: 1 / 0 diff --git a/doc/source/users/getting_started/installing.rst b/doc/source/users/getting_started/installing.rst index 76baeb6cb5..6e2108a799 100644 --- a/doc/source/users/getting_started/installing.rst +++ b/doc/source/users/getting_started/installing.rst @@ -10,13 +10,13 @@ Installing with pip [1]_ (platform-independent) Sardana can be installed using pip. The following command will automatically download and install the latest release of Sardana (see -pip --help for options):: +pip3 --help for options):: - pip install sardana + pip3 install sardana You can test the installation by running:: - python -c "import sardana; print sardana.Release.version" + python3 -c "import sardana; print(sardana.Release.version)" Installing from PyPI manually [2]_ (platform-independent) @@ -28,11 +28,11 @@ You may alternatively install from a downloaded release package: #. Extract the downloaded source into a temporary directory and change to it #. run:: - python setup.py install + python3 setup.py install You can test the installation by running:: - python -c "import sardana; print sardana.Release.version" + python3 -c "import sardana; print(sardana.Release.version)" Linux (Debian-based) -------------------- @@ -45,7 +45,7 @@ doing (as root):: You can test the installation by running:: - python -c "import sardana; print sardana.Release.version" + python3 -c "import sardana; print(sardana.Release.version)" (see more detailed instructions in `this step-by-step howto `__) @@ -58,7 +58,7 @@ Windows #. Run the installation executable #. test the installation:: - C:\Python27\python -c "import sardana; print sardana.Release.version" + C:\Python35\python3 -c "import sardana; print(sardana.Release.version)" Windows installation shortcut ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -90,7 +90,7 @@ You can clone sardana from the main git repository:: Then, to work in editable mode, just do:: - pip install -e ./sardana + pip3 install -e ./sardana Note that you can also fork the git repository in github to get your own github-hosted clone of the sardana repository to which you will have full @@ -107,39 +107,39 @@ Dependencies Sardana has dependencies on some python libraries: -- Sardana uses Tango as the middleware so you need PyTango_ 7 or later +- Sardana uses Tango as the middleware so you need PyTango_ 9.2.5 or later installed. You can check it by doing:: - python -c 'import PyTango; print PyTango.Release.version' + python3 -c 'import tango; print(tango.__version__)' -- Sardana clients are developed with Taurus so you need Taurus_ 3.6.0 or later +- Sardana clients are developed with Taurus so you need Taurus_ 4.5.4 or later installed. You can check it by doing:: - python -c 'import taurus; print taurus.Release.version' + python3 -c 'import taurus; print(taurus.Release.version)' -- Sardana operate some data in the XML format and requires lxml_ library 2.1 or +- Sardana operate some data in the XML format and requires lxml_ library 2.3 or later. You can check it by doing:: - python -c 'import lxml.etree; print lxml.etree.LXML_VERSION' + python3 -c 'import lxml.etree; print(lxml.etree.LXML_VERSION)' -- spock (Sardana CLI) requires itango 0.0.1 or later [3]_. +- spock (Sardana CLI) requires itango 0.1.6 or later [3]_. .. rubric:: Footnotes .. [1] This command requires super user previledges on linux systems. If your user has them you can usually prefix the command with *sudo*: - ``sudo pip -U sardana``. Alternatively, if you don't have + ``sudo pip3 -U sardana``. Alternatively, if you don't have administrator previledges, you can install locally in your user - directory with: ``pip --user sardana`` + directory with: ``pip3 --user sardana`` In this case the executables are located at /.local/bin. Make sure the PATH is pointing there or you execute from there. .. [2] *setup.py install* requires user previledges on linux systems. If your user has them you can usually prefix the command with *sudo*: - ``sudo python setup.py install``. Alternatively, if you don't have + ``sudo python3 setup.py install``. Alternatively, if you don't have administrator previledges, you can install locally in your user directory - with: ``python setup.py install --user`` + with: ``python3 setup.py install --user`` In this case the executables are located at /.local/bin. Make sure the PATH is pointing there or you execute from there. From 88909afef36467f59f05fb86255ed1914be1a058 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 4 Sep 2019 12:42:35 +0200 Subject: [PATCH 191/830] Convert zip result (iterable) to list 2to3 (commit eb56b4) was supposed to do it but it did not. This affects setting of macro hooks programmatically. Cast to list the iterable returned by zip when we expect it to be subscriptable. --- src/sardana/macroserver/macro.py | 2 +- src/sardana/tools/config/pexpect23.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index a3884d0463..696ddc6817 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -287,7 +287,7 @@ def hooks(self, hooks): if hasattr(self, '_hookHintsDict'): del self._hookHintsDict # create _hookHintsDict - self._getHookHintsDict()['_ALL_'] = zip(*self._hooks)[0] + self._getHookHintsDict()['_ALL_'] = list(zip(*self._hooks))[0] nohints = self._hookHintsDict['_NOHINTS_'] for hook, hints in self._hooks: if len(hints) == 0: diff --git a/src/sardana/tools/config/pexpect23.py b/src/sardana/tools/config/pexpect23.py index 8f4598875b..49e363835c 100644 --- a/src/sardana/tools/config/pexpect23.py +++ b/src/sardana/tools/config/pexpect23.py @@ -1633,7 +1633,7 @@ def __str__(self): ss.append((self.timeout_index, ' %d: TIMEOUT' % self.timeout_index)) ss.sort() - ss = zip(*ss)[1] + ss = list(zip(*ss))[1] return '\n'.join(ss) def search(self, buffer, freshlen, searchwindowsize=None): @@ -1731,7 +1731,7 @@ def __str__(self): ss.append((self.timeout_index, ' %d: TIMEOUT' % self.timeout_index)) ss.sort() - ss = zip(*ss)[1] + ss = list(zip(*ss))[1] return '\n'.join(ss) def search(self, buffer, freshlen, searchwindowsize=None): From 840fbe0862ce276dff46aba9cdd58659fe6693eb Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 6 Sep 2019 14:39:57 +0200 Subject: [PATCH 192/830] Better macro exception message --- src/sardana/macroserver/msmacromanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 6033769c0b..7f746079eb 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1618,8 +1618,8 @@ def runMacro(self, macro_obj): if isinstance(macro_exp, MacroServerException): if macro_obj.parent_macro is None: door.debug(macro_exp.traceback) - door.error("An error occurred while running %s:\n%s" % - (macro_obj.description, macro_exp.msg)) + door.error("An error occurred while running %s:\n%r" % + (macro_obj.description, macro_exp)) self._popMacro() raise macro_exp self.debug("[ END ] runMacro %s" % desc) From 70d823471dae8a0906bd4cd704959185b00fc99b Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 10 Sep 2019 11:46:50 +0200 Subject: [PATCH 193/830] Update SEP16 status on the SEP's index --- doc/source/sep/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/sep/index.md b/doc/source/sep/index.md index 249c630033..666ab75ece 100644 --- a/doc/source/sep/index.md +++ b/doc/source/sep/index.md @@ -26,7 +26,7 @@ Proposals list [SEP13][] | REJECTED (moved to [TEP13][]) | Unified plugins support in Taurus & Sardana [SEP14][] | DRAFT | MSENV taurus schema [SEP15][] | ACCEPTED | Moving Sardana to Github - [SEP16][] | DRAFT | Plugins (controllers, macros, etc.) register + [SEP16][] | CANDIDATE | Plugins (controllers, macros, etc.) catalogue [SEP17][] | DRAFT | Ongoing acquisition formalization and implementation [SEP18][] | ACCEPTED | Extend acquisition and synchronization concepts for SEP2 needs From 50a037b30244256bc6a5f772bc2369fc2be2a45f Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 13 Sep 2019 10:28:07 +0200 Subject: [PATCH 194/830] Fix setting of env. vars. in py37 Construction of dict from a generator changed in py37 making the _pairwise method failing with "RuntimeError: generator raised StopIteration". Use grouper solution from "Python Itertools Recipies" instead which is py35 and higher compatible. --- src/sardana/macroserver/msenvmanager.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index 75bae20ee0..ee2921d73f 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -32,6 +32,7 @@ import os import shelve +from itertools import zip_longest import operator from taurus.core.util.containers import CaselessDict @@ -407,13 +408,13 @@ def getDoorMacroEnv(self, door_name, macro_name, keys=None): return ret - def _pairwise(self, iterable): - itnext = iter(iterable).__next__ - while True: - yield itnext(), itnext() + def _grouper(self, iterable): + # https://docs.python.org/3/library/itertools.html#itertools-recipes + args = [iter(iterable)] * 2 + return zip_longest(*args) def _dictFromSequence(self, seq): - return dict(self._pairwise(seq)) + return dict(self._grouper(seq)) def _encode(self, d): ret = {} From fc6ef1fbd6b6cbee3be85cc26c2987e97c61e12c Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 13 Sep 2019 11:03:13 +0200 Subject: [PATCH 195/830] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c4208bd6c..67195d43cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ This file follows the formats and conventions from [keepachangelog.com] ### Fixed -* Fix default macro parameter values in macroexecutor (#1153) +* Default macro parameter values in macroexecutor (#1153) +* Setting of environment variables in Python 3.7 (#1195) * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) From c0bec8dfd3ee34a240c281e5eb0ab16c3dc785c1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 13 Sep 2019 14:46:46 +0200 Subject: [PATCH 196/830] Bump version 3.0.0-alpha to 3.0.1-alpha --- .bumpversion.cfg | 2 +- src/sardana/release.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index ea20b04bbf..ca03403dc0 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,7 +3,7 @@ commit = True message = Bump version {current_version} to {new_version} tag = False tag_name = {new_version} -current_version = 3.0.0-alpha +current_version = 3.0.1-alpha parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? serialize = {major}.{minor}.{patch}-{release} diff --git a/src/sardana/release.py b/src/sardana/release.py index c2f71a45a8..14821d65ad 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = '3.0.0-alpha' +version = '3.0.1-alpha' # generate version_info and revision (**deprecated** since v 2.1.2--alpha). if '-' in version: From 1a9ae12c521d61765eec893eebc863b82eeb4bea Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 16 Sep 2019 15:53:44 +0200 Subject: [PATCH 197/830] doc: Update link to Sardan Plugins Catalogue --- doc/source/devel/howto_controllers/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/devel/howto_controllers/index.rst b/doc/source/devel/howto_controllers/index.rst index 34e093ebfb..8bf86fc5ea 100644 --- a/doc/source/devel/howto_controllers/index.rst +++ b/doc/source/devel/howto_controllers/index.rst @@ -9,8 +9,8 @@ Writing controllers This chapter provides the necessary information to write controllers in sardana. -Before writing a new controller you should check the `controller plugin -register `_. +Before writing a new controller you should check the `Sardana plugins +catalogue `_. There's a high chance that somebody already wrote the plugin for your hardware. An overview of the pool controller concept can be found From ee367266f12ce8f6a3cd81df9c235a7148b728ec Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 16 Sep 2019 16:24:00 +0200 Subject: [PATCH 198/830] Bump version 3.0.1-alpha to 3.0.2-alpha --- .bumpversion.cfg | 2 +- src/sardana/release.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index ca03403dc0..4a26a4a2a0 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,7 +3,7 @@ commit = True message = Bump version {current_version} to {new_version} tag = False tag_name = {new_version} -current_version = 3.0.1-alpha +current_version = 3.0.2-alpha parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? serialize = {major}.{minor}.{patch}-{release} diff --git a/src/sardana/release.py b/src/sardana/release.py index 14821d65ad..2434494909 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = '3.0.1-alpha' +version = '3.0.2-alpha' # generate version_info and revision (**deprecated** since v 2.1.2--alpha). if '-' in version: From f39ea6f0c8b52727572d08faa37c5feb4a56202a Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 17 Sep 2019 11:26:36 +0200 Subject: [PATCH 199/830] Add createInstrument method to Pool taurus extension --- src/sardana/taurus/core/tango/sardana/pool.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 575963b7fe..2dc78bbbea 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2505,6 +2505,11 @@ def createController(self, class_name, name, *props): def deleteController(self, name): return self.deleteElement(name) + def createInstrument(self, full_name, class_name): + self.command_inout("CreateInstrument", [full_name, class_name]) + elements_info = self.getElementsInfo() + return self._wait_for_element_in_container(elements_info, full_name) + def registerExtensions(): factory = Factory() From b572b170272ebcad4a5f9066671dbda46442d573 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 17 Sep 2019 11:27:03 +0200 Subject: [PATCH 200/830] Add setInstrumentName method to Pool element taurus extension --- src/sardana/taurus/core/tango/sardana/pool.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 2dc78bbbea..508f7a7422 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -454,6 +454,9 @@ def getInstrumentName(self, force=False): # instr_name = instr_name[:instr_name.index('(')] return instr_name + def setInstrumentName(self, instr_name): + self.getInstrumentObj().write(instr_name) + def getInstrument(self): instr_name = self.getInstrumentName() if not instr_name: From dc03cb6c583adca8b417d0a9d0c0f24e0c67ab80 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 17 Sep 2019 11:27:48 +0200 Subject: [PATCH 201/830] Create and Remove instruments in sar_demo and clear_sar_demo --- src/sardana/macroserver/macros/demo.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/demo.py b/src/sardana/macroserver/macros/demo.py index f58f101159..afc591891f 100644 --- a/src/sardana/macroserver/macros/demo.py +++ b/src/sardana/macroserver/macros/demo.py @@ -85,6 +85,11 @@ def clear_sar_demo(self): for ctrl in SAR_DEMO.get("controllers", ()): self.udefctrl(ctrl) + self.print("Removing instruments...") + pool = self.getPools()[0] + for instrument in SAR_DEMO.get("instruments", ()): + pool.DeleteElement(instrument) + self.unsetEnv(_ENV) self.print("DONE!") @@ -192,14 +197,29 @@ def sar_demo(self): self.print("Setting %s as ActiveMntGrp" % mg_name) self.setEnv("ActiveMntGrp", mg_name) + self.print("Creating instruments: /slit, /mirror and /monitor ...") + pool.createInstrument('/slit', 'NXcollimator') + pool.createInstrument('/mirror', 'NXmirror') + pool.createInstrument('/monitor', 'NXmonitor') + + self.print("Assigning elements to instruments...") + self.getMotor(motor_names[0]).setInstrumentName('/slit') + self.getMotor(motor_names[1]).setInstrumentName('/slit') + self.getPseudoMotor(gap).setInstrumentName('/slit') + self.getPseudoMotor(offset).setInstrumentName('/slit') + self.getMotor(motor_names[2]).setInstrumentName('/mirror') + self.getMotor(motor_names[3]).setInstrumentName('/mirror') + self.getCounterTimer(ct_names[0]).setInstrumentName('/monitor') + controllers = pm_ctrl_name, mot_ctrl_name, ct_ctrl_name, \ zerod_ctrl_name, oned_ctrl_name, twod_ctrl_name, \ tg_ctrl_name, ior_ctrl_name elements = [gap, offset] + motor_names + ct_names + \ zerod_names + oned_names + twod_names + tg_names + \ ior_names + instruments = ["/slit", "/mirror", "/monitor"] d = dict(controllers=controllers, elements=elements, - measurement_groups=[mg_name]) + measurement_groups=[mg_name], instruments=instruments) self.setEnv(_ENV, d) From 69825dbbc05f0d4c27d672af7e41fdeaba311435 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 18 Sep 2019 14:04:11 +0200 Subject: [PATCH 202/830] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcec004892..433f70f464 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This file follows the formats and conventions from [keepachangelog.com] ## [Unreleased] +### Added + +* Instruments creation and configuration in sar_demo (#1198) + ### Fixed * Default macro parameter values in macroexecutor (#1153) From 1ec5abb6330c9f9c0c7d93d894eec9b7d6d2d773 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 19 Sep 2019 16:23:11 +0200 Subject: [PATCH 203/830] Add lssnap and addsnap macros --- src/sardana/macroserver/macros/env.py | 69 +++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 8905859998..8e40f1f2cd 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -29,6 +29,7 @@ __docformat__ = 'restructuredtext' +import taurus from taurus.console.list import List from sardana.macroserver.macro import Macro, Type, ParamRepeat from sardana.macroserver.msexception import UnknownEnv @@ -435,3 +436,71 @@ def run(self, macro_name, hook_pos): self.info("Hook %s is undefineed" % macro_name) self.setEnv("_GeneralHooks", macros_list) + + +class lssnap(Macro): + """List pre-scan snapshot group. + + .. todo:: print in form of a table + + .. note:: + The `lssnap` macro has been included in Sardana + on a provisional basis. Backwards incompatible changes + (up to and including its removal) may occur if + deemed necessary by the core developers. + """ + + def run(self): + snapshot_items = self.getEnv("PreScanSnapshot") + for full_name, label in snapshot_items: + self.print("{} ({})".format(label, full_name)) + + +class addsnap(Macro): + """Add item(s) to snapshot group. Accepts: + - Pool moveables: motor, pseudo motor + - Pool experimental channels: counter/timer, 0D, 1D, 2D, pseudo counter + - Taurus attributes + + .. note:: + The `addsnap` macro has been included in Sardana + on a provisional basis. Backwards incompatible changes + (up to and including its removal) may occur if + deemed necessary by the core developers. + """ + + param_def = [ + ["snap_names", [[ + "name", Type.String, None, "Name of an item to be added to the " + "pre-scan snapshot group"]], + None, + "Items to be added to the pre-scan snapshot group"], + ] + + def run(self, snap_names): + + def get_item_info(item): + if isinstance(item, taurus.core.TaurusAttribute): + return item.fullname, item.label + else: + return item.full_name, item.name + + snap_items = self.getEnv("PreScanSnapshot") + snap_full_names = [item[0] for item in snap_items] + new_snap_items = [] + for name in snap_names: + obj = self.getObj(name) + if obj is None: + try: + obj = taurus.Attribute(name) + except taurus.TaurusException: + raise ValueError("item is neither Pool element not " + "Taurus attribute") + elif obj.type == "MotorGroup": + raise ValueError("MotorGroup item type is not accepted") + new_full_name, new_label = get_item_info(obj) + if new_full_name in snap_full_names: + msg = "{} already in pre-scan snapshot".format(name) + raise ValueError(msg) + new_snap_items.append((new_full_name, new_label)) + self.setEnv("PreScanSnapshot", snap_items + new_snap_items) From f21b48b65b03f94fa0226db9bb4460b498e8a201 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 20 Sep 2019 18:40:04 +0200 Subject: [PATCH 204/830] Fix WrongParam message showed in spock WrongParam message, for example due to object not found, are showed in spock as unknown sardana exception instead of showing a verbose message. This is due to the fact that spock shows just the high level exception message. Since Py3 we raise exception from lower level exceptions (see 77a7aa91) then we must also pass the exception message to the high level exception. Do it. --- src/sardana/macroserver/msparameter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py index c43edc298a..c2c5ee92eb 100644 --- a/src/sardana/macroserver/msparameter.py +++ b/src/sardana/macroserver/msparameter.py @@ -436,9 +436,9 @@ def decodeNormal(self, raw_param, param_def): param = param_type.getObj(str(value)) except ValueError as e: - raise WrongParamType from e + raise WrongParamType(str(e)) from e except UnknownParamObj as e: - raise WrongParam from e + raise WrongParam(str(e)) from e if param is None and not optional_param: msg = 'Could not create %s parameter "%s" for "%s"' % \ (param_type.getName(), name, raw_param) @@ -588,9 +588,9 @@ def decodeNormal(self, raw_params, params_def): try: val = par_type.getObj(par_str) except ValueError as e: - raise WrongParamType from e + raise WrongParamType(str(e)) from e except UnknownParamObj as e: - raise WrongParam from e + raise WrongParam(str(e)) from e if val is None: msg = 'Could not create %s parameter "%s" for "%s"' % \ (par_type.getName(), name, par_str) From 9d57919b81d206b9f486ce43c15068001a1a46f8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 22 Sep 2019 16:14:29 +0200 Subject: [PATCH 205/830] Remove device panel button The idea is that the attributes will be showed from the context menu of the label. Remove the device button. --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 4c2cee4d21..070943fa9f 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -36,25 +36,6 @@ from .poolmotor import LabelWidgetDragsDeviceAndAttribute -class _ParentDevButton(TaurusDevButton): - '''A TaurusDevButton that receives an attribute name but sets - the corresponding device as model. **For internal use only** ''' - - def __init__(self, **kwargs): - TaurusDevButton.__init__(self, **kwargs) - self.setText('') - self.setSizePolicy(Qt.QSizePolicy.Preferred, Qt.QSizePolicy.Maximum) - - def setModel(self, model): - try: - attr = taurus.Attribute(model) - except: - return - dev = attr.getParentObj() - devname = dev.getFullName() - TaurusDevButton.setModel(self, devname) - - class PoolChannelTV(TaurusValue): ''' A widget that displays and controls a pool channel device. It differs from :class:`PoolChannel` in that it behaves as a TaurusValue @@ -66,9 +47,6 @@ def __init__(self, parent=None, designMode=False): self.setLabelWidgetClass(LabelWidgetDragsDeviceAndAttribute) self.setLabelConfig('') - def getDefaultExtraWidgetClass(self): - return _ParentDevButton - def setModel(self, model): if model is not None: # @todo: change this (it assumes tango naming!) From 32b19147056789b06177db4999201fcbbfbbbf9a Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 22 Sep 2019 16:40:23 +0200 Subject: [PATCH 206/830] Change label to show TangoAttributes Copy and adapt solutions from PMTV widget. This breaks the value read widget. --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 134 +++++++++++++++++- 1 file changed, 128 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 070943fa9f..05c45b893c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -31,11 +31,123 @@ import taurus from taurus.external.qt import Qt -from taurus.qt.qtgui.panel import TaurusValue, TaurusDevButton +from taurus.qt.qtcore.mimetypes import (TAURUS_DEV_MIME_TYPE, + TAURUS_ATTR_MIME_TYPE) +from taurus.qt.qtgui.panel import (TaurusValue, TaurusDevButton, + DefaultLabelWidget, TaurusAttrForm) from taurus.qt.qtgui.container import TaurusWidget +from taurus.qt.qtgui.resource import getIcon from .poolmotor import LabelWidgetDragsDeviceAndAttribute +class PoolChannelTVLabelWidget(TaurusWidget): + """ + @TODO tooltip should be extended with status info + @TODO context menu should be the lbl_alias extended + @TODO default tooltip extended with the complete (multiline) status + @TODO rightclick popup menu with actions: (1) switch user/expert view, + (2) Config -all attributes-, (3) change channel + For the (3), a drop event should accept if it is a device, + and add it to the "change-channel" list and select + """ + + layoutAlignment = Qt.Qt.AlignTop + + def __init__(self, parent=None, designMode=False): + TaurusWidget.__init__(self, parent, designMode) + self.setLayout(Qt.QGridLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.layout().setSpacing(0) + + self.lbl_alias = DefaultLabelWidget(parent, designMode) + self.lbl_alias.setBgRole("none") + self.layout().addWidget(self.lbl_alias) + + # Align everything on top + self.layout().addItem(Qt.QSpacerItem( + 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding)) + + # I don't like this approach, there should be something like + # self.lbl_alias.addAction(...) + self.lbl_alias.contextMenuEvent = \ + lambda event: self.contextMenuEvent(event) + + # I' don't like this approach, there should be something like + # self.lbl_alias.addToolTipCallback(self.calculate_extra_tooltip) + self.lbl_alias.getFormatedToolTip = self.calculateExtendedTooltip + + # I' don't like this approach, there should be something like + # self.lbl_alias.disableDrag() or self.lbl_alias.setDragEnabled(False) + # or better, define if Attribute or Device or Both have to be included + # in the mimeData + self.lbl_alias.mouseMoveEvent = self.mouseMoveEvent + + def setModel(self, model): + if model in (None, ""): + self.lbl_alias.setModel(model) + TaurusWidget.setModel(self, model) + return + self.lbl_alias.taurusValueBuddy = self.taurusValueBuddy + self.lbl_alias.setModel(model) + TaurusWidget.setModel(self, model + "/Status") + self.taurusValueBuddy().expertViewChanged.connect( + self.setExpertView) + + def calculateExtendedTooltip(self, cache=False): + default_label_widget_tooltip = DefaultLabelWidget.getFormatedToolTip( + self.lbl_alias, cache) + status_info = "" + channel_dev = self.taurusValueBuddy().channel_dev + if channel_dev is not None: + status = channel_dev.getAttribute("Status").read().value + # MAKE IT LOOK LIKE THE STANDARD TABLE FOR TAURUS TOOLTIPS + status_lines = status.split("\n") + status_info = ("") + for status_extra_line in status_lines[1:]: + status_info += ("") + status_info += "
Status:" + + status_lines[0] + + "
" + + status_extra_line + + "
" + return default_label_widget_tooltip + status_info + + def contextMenuEvent(self, event): + # Overwrite the default taurus label behaviour + menu = Qt.QMenu(self) + action_tango_attributes = Qt.QAction(self) + action_tango_attributes.setIcon( + getIcon(":/categories/preferences-system.svg")) + action_tango_attributes.setText("Tango Attributes") + menu.addAction(action_tango_attributes) + action_tango_attributes.triggered.connect( + self.taurusValueBuddy().showTangoAttributes) + + cm_action = menu.addAction("Compact") + cm_action.setCheckable(True) + cm_action.setChecked(self.taurusValueBuddy().isCompact()) + cm_action.toggled.connect(self.taurusValueBuddy().setCompact) + + menu.exec_(event.globalPos()) + event.accept() + + def mouseMoveEvent(self, event): + model = self.taurusValueBuddy().getModelObj() + mimeData = Qt.QMimeData() + mimeData.setText(self.lbl_alias.text()) + dev_name = model.getFullName().encode("utf-8") + attr_name = dev_name + b"/Value" + mimeData.setData(TAURUS_DEV_MIME_TYPE, dev_name) + mimeData.setData(TAURUS_ATTR_MIME_TYPE, attr_name) + + drag = Qt.QDrag(self) + drag.setMimeData(mimeData) + drag.setHotSpot(event.pos() - self.rect().topLeft()) + drag.exec_(Qt.Qt.CopyAction, Qt.Qt.CopyAction) + + class PoolChannelTV(TaurusValue): ''' A widget that displays and controls a pool channel device. It differs from :class:`PoolChannel` in that it behaves as a TaurusValue @@ -44,14 +156,15 @@ class PoolChannelTV(TaurusValue): def __init__(self, parent=None, designMode=False): TaurusValue.__init__(self, parent=parent, designMode=designMode) - self.setLabelWidgetClass(LabelWidgetDragsDeviceAndAttribute) - self.setLabelConfig('') + self.setLabelWidgetClass(PoolChannelTVLabelWidget) + self.channel_dev = None + # self.setLabelConfig('') def setModel(self, model): - if model is not None: - # @todo: change this (it assumes tango naming!) - model = "%s/value" % model TaurusValue.setModel(self, model) + if model == "" or model is None: + return + self.channel_dev = taurus.Device(model) def showEvent(self, event): TaurusValue.showEvent(self, event) @@ -67,6 +180,15 @@ def hideEvent(self, event): except: pass + def showTangoAttributes(self): + model = self.getModel() + taurus_attr_form = TaurusAttrForm() + taurus_attr_form.setMinimumSize(Qt.QSize(555, 800)) + taurus_attr_form.setModel(model) + taurus_attr_form.setWindowTitle( + '%s Tango Attributes' % self.getModelObj().getSimpleName()) + taurus_attr_form.show() + class PoolChannel(TaurusWidget): ''' A widget that displays and controls a pool channel device From ce8b63bf8c089b49d2e622a2204fd148500b0c23 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 22 Sep 2019 17:23:59 +0200 Subject: [PATCH 207/830] Add read widget Copy and adapt solutions from PMTV. --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 85 ++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 05c45b893c..43e70d53f0 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -35,6 +35,9 @@ TAURUS_ATTR_MIME_TYPE) from taurus.qt.qtgui.panel import (TaurusValue, TaurusDevButton, DefaultLabelWidget, TaurusAttrForm) +from taurus.qt.qtgui.display import TaurusLabel +from taurus.qt.qtgui.dialog import ProtectTaurusMessageBox +from taurus.qt.qtgui.compact import TaurusReadWriteSwitcher from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtgui.resource import getIcon from .poolmotor import LabelWidgetDragsDeviceAndAttribute @@ -90,8 +93,6 @@ def setModel(self, model): self.lbl_alias.taurusValueBuddy = self.taurusValueBuddy self.lbl_alias.setModel(model) TaurusWidget.setModel(self, model + "/Status") - self.taurusValueBuddy().expertViewChanged.connect( - self.setExpertView) def calculateExtendedTooltip(self, cache=False): default_label_widget_tooltip = DefaultLabelWidget.getFormatedToolTip( @@ -148,6 +149,85 @@ def mouseMoveEvent(self, event): drag.exec_(Qt.Qt.CopyAction, Qt.Qt.CopyAction) +class PoolChannelTVReadWidget(TaurusWidget): + """ + """ + + layoutAlignment = Qt.Qt.AlignTop + + def __init__(self, parent=None, designMode=False): + TaurusWidget.__init__(self, parent, designMode) + + self.setLayout(Qt.QGridLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.layout().setSpacing(0) + + limits_layout = Qt.QHBoxLayout() + limits_layout.setContentsMargins(0, 0, 0, 0) + limits_layout.setSpacing(0) + + self.lbl_read = TaurusLabel() + self.lbl_read.setBgRole("quality") + self.lbl_read.setSizePolicy(Qt.QSizePolicy( + Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) + self.layout().addWidget(self.lbl_read, 0, 0) + + # WITH A COMPACT VIEW, BETTER TO BE ABLE TO STOP! + self.btn_stop = Qt.QPushButton() + self.btn_stop.setToolTip("Stops the channel") + self.prepare_button(self.btn_stop) + self.btn_stop.setIcon(getIcon(":/actions/media_playback_stop.svg")) + self.layout().addWidget(self.btn_stop, 0, 1) + + self.btn_stop.clicked.connect(self.abort) + + # WITH COMPACT VIEW, WE NEED TO FORWARD DOUBLE CLICK EVENT + self.lbl_read.installEventFilter(self) + + # Align everything on top + self.layout().addItem(Qt.QSpacerItem( + 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 2) + + def eventFilter(self, obj, event): + if event.type() == Qt.QEvent.MouseButtonDblClick: + if isinstance(self.parent(), TaurusReadWriteSwitcher): + self.parent().enterEdit() + return True + try: + if obj is self.lbl_read: + return self.lbl_read.eventFilter(obj, event) + except AttributeError: + # self.lbl_read may not exist now + pass + return True + + @Qt.pyqtSlot() + @ProtectTaurusMessageBox( + msg="An error occurred trying to abort the acquisition.") + def abort(self): + channel_dev = self.taurusValueBuddy().channel_dev + if channel_dev is not None: + channel_dev.abort() + + def prepare_button(self, btn): + btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, + Qt.QSizePolicy.Fixed) + btn_policy.setHorizontalStretch(0) + btn_policy.setVerticalStretch(0) + btn.setSizePolicy(btn_policy) + btn.setMinimumSize(25, 25) + btn.setMaximumSize(25, 25) + btn.setText("") + + def setModel(self, model): + if model in (None, ""): + TaurusWidget.setModel(self, model) + self.lbl_read.setModel(model) + return + TaurusWidget.setModel(self, model + "/Value") + self.lbl_read.setModel(model + "/Value") + + class PoolChannelTV(TaurusValue): ''' A widget that displays and controls a pool channel device. It differs from :class:`PoolChannel` in that it behaves as a TaurusValue @@ -157,6 +237,7 @@ class PoolChannelTV(TaurusValue): def __init__(self, parent=None, designMode=False): TaurusValue.__init__(self, parent=parent, designMode=designMode) self.setLabelWidgetClass(PoolChannelTVLabelWidget) + self.setReadWidgetClass(PoolChannelTVReadWidget) self.channel_dev = None # self.setLabelConfig('') From 54f5b76c3509bdf644d427324540656a1b7f4ae2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 22 Sep 2019 22:46:41 +0200 Subject: [PATCH 208/830] Add write widget Copy and adapt solution from PMTV. Write widget sets integ time and executes acq. --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 83 ++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 43e70d53f0..0e5d2476d7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -34,13 +34,16 @@ from taurus.qt.qtcore.mimetypes import (TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE) from taurus.qt.qtgui.panel import (TaurusValue, TaurusDevButton, - DefaultLabelWidget, TaurusAttrForm) + DefaultLabelWidget, TaurusAttrForm, + TaurusForm) +from taurus.qt.qtgui.input import TaurusValueLineEdit from taurus.qt.qtgui.display import TaurusLabel from taurus.qt.qtgui.dialog import ProtectTaurusMessageBox from taurus.qt.qtgui.compact import TaurusReadWriteSwitcher from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtgui.resource import getIcon -from .poolmotor import LabelWidgetDragsDeviceAndAttribute +from sardana.taurus.qt.qtgui.extra_pool.poolmotor import \ + LabelWidgetDragsDeviceAndAttribute class PoolChannelTVLabelWidget(TaurusWidget): @@ -228,6 +231,81 @@ def setModel(self, model): self.lbl_read.setModel(model + "/Value") +class _IntegrationTimeStartWidget(TaurusValueLineEdit): + """Line edit widget for starting acquisition with the integration time""" + + def writeValue(self, forceApply=False): + """Writes the value to the attribute, either by applying pending + operations or (if the ForcedApply flag is True), it writes directly + when no operations are pending + + It emits the applied signal if apply is not aborted. + + :param forceApply: (bool) If True, it behaves as in forceApply mode + (even if the forceApply mode is disabled by + :meth:`setForceApply`) + """ + TaurusValueLineEdit.writeValue(self, forceApply=forceApply) + channel_dev = self.getModelObj().getParentObj() + if channel_dev is not None: + channel_dev.Start() + + +class PoolChannelTVWriteWidget(TaurusWidget): + + layoutAlignment = Qt.Qt.AlignTop + + applied = Qt.pyqtSignal() + + def __init__(self, parent=None, designMode=False): + TaurusWidget.__init__(self, parent, designMode) + + self.setLayout(Qt.QGridLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.layout().setSpacing(0) + + self.le_write_absolute = _IntegrationTimeStartWidget() + self.layout().addWidget(self.le_write_absolute, 0, 0) + + # Align everything on top + self.layout().addItem(Qt.QSpacerItem( + 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 3) + + # list of widgets used for edition + editingWidgets = (self.le_write_absolute,) + + for w in editingWidgets: + w.installEventFilter(self) + + def eventFilter(self, obj, event): + """reimplemented to intercept events from the subwidgets""" + # emit editingFinished when focus out to a non-editing widget + if event.type() == Qt.QEvent.FocusOut: + focused = Qt.qApp.focusWidget() + focusInChild = focused in self.findChildren(focused.__class__) + if not focusInChild: + self.emitEditingFinished() + return False + + def setModel(self, model): + if model in (None, ""): + TaurusWidget.setModel(self, model) + self.le_write_absolute.setModel(model) + return + TaurusWidget.setModel(self, model + "/IntegrationTime") + self.le_write_absolute.setModel(model + "/IntegrationTime") + + def keyPressEvent(self, key_event): + if key_event.key() == Qt.Qt.Key_Escape: + self.abort() + key_event.accept() + TaurusWidget.keyPressEvent(self, key_event) + + @Qt.pyqtSlot() + def emitEditingFinished(self): + self.applied.emit() + + class PoolChannelTV(TaurusValue): ''' A widget that displays and controls a pool channel device. It differs from :class:`PoolChannel` in that it behaves as a TaurusValue @@ -238,6 +316,7 @@ def __init__(self, parent=None, designMode=False): TaurusValue.__init__(self, parent=parent, designMode=designMode) self.setLabelWidgetClass(PoolChannelTVLabelWidget) self.setReadWidgetClass(PoolChannelTVReadWidget) + self.setWriteWidgetClass(PoolChannelTVWriteWidget) self.channel_dev = None # self.setLabelConfig('') From 2a491dd047ca49e713840e62486a4dd90f676113 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 22 Sep 2019 22:46:56 +0200 Subject: [PATCH 209/830] Add main section to poolchanel module --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 0e5d2476d7..a6f358b479 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -383,18 +383,23 @@ def _updateTaurusValue(self): 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' -# model = 'ct_cp1_1' -# if len(sys.argv)>1: -# model = sys.argv[1] -# form.setModel(model) -# -# -# form.show() -# sys.exit(app.exec_()) +if __name__ == '__main__': + import sys + argv = sys.argv + if len(argv) > 0: + models = argv[1:] + app = Qt.QApplication(sys.argv) + + form_tv = TaurusForm() + form_tv.setModifiableByUser(True) + tv_widget_class = 'sardana.taurus.qt.qtgui.extra_pool.PoolChannelTV' + tv_class_map = {'CTExpChannel': (tv_widget_class, (), {})} + form_tv.setCustomWidgetMap(tv_class_map) + form_tv.setModel(models) + + w = Qt.QWidget() + w.setLayout(Qt.QVBoxLayout()) + w.layout().addWidget(form_tv) + + w.show() + sys.exit(app.exec_()) From c311fee3a7f70d07e90493199fd081eb7f1ee036 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 23 Sep 2019 09:47:52 +0200 Subject: [PATCH 210/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 433f70f464..842077d7c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This file follows the formats and conventions from [keepachangelog.com] ### Added +* Support to Python >= 3.5 (#1089, #1173, 1201) * Instruments creation and configuration in sar_demo (#1198) ### Fixed @@ -22,6 +23,7 @@ This file follows the formats and conventions from [keepachangelog.com] ### Removed +* Support to Python < 3.5 (#1089, #1173, 1201) * `sardana.requirements` (#1185) ## [2.8.3] 2019-09-16 From a67b2c7e11f24649a1f096ad1e29940d1c2460ea Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 22 Sep 2019 23:17:28 +0200 Subject: [PATCH 211/830] Improve alignment vertical alignment of sub-widgets * Remove spacer items copied from PMTV * Adjust size policy of read widget * Don't align sub-widgetst to top of TV --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index a6f358b479..f90330d709 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -57,8 +57,6 @@ class PoolChannelTVLabelWidget(TaurusWidget): and add it to the "change-channel" list and select """ - layoutAlignment = Qt.Qt.AlignTop - def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) self.setLayout(Qt.QGridLayout()) @@ -69,10 +67,6 @@ def __init__(self, parent=None, designMode=False): self.lbl_alias.setBgRole("none") self.layout().addWidget(self.lbl_alias) - # Align everything on top - self.layout().addItem(Qt.QSpacerItem( - 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding)) - # I don't like this approach, there should be something like # self.lbl_alias.addAction(...) self.lbl_alias.contextMenuEvent = \ @@ -156,8 +150,6 @@ class PoolChannelTVReadWidget(TaurusWidget): """ """ - layoutAlignment = Qt.Qt.AlignTop - def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) @@ -165,14 +157,10 @@ def __init__(self, parent=None, designMode=False): self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(0) - limits_layout = Qt.QHBoxLayout() - limits_layout.setContentsMargins(0, 0, 0, 0) - limits_layout.setSpacing(0) - self.lbl_read = TaurusLabel() self.lbl_read.setBgRole("quality") self.lbl_read.setSizePolicy(Qt.QSizePolicy( - Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) + Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Preferred)) self.layout().addWidget(self.lbl_read, 0, 0) # WITH A COMPACT VIEW, BETTER TO BE ABLE TO STOP! @@ -187,10 +175,6 @@ def __init__(self, parent=None, designMode=False): # WITH COMPACT VIEW, WE NEED TO FORWARD DOUBLE CLICK EVENT self.lbl_read.installEventFilter(self) - # Align everything on top - self.layout().addItem(Qt.QSpacerItem( - 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 2) - def eventFilter(self, obj, event): if event.type() == Qt.QEvent.MouseButtonDblClick: if isinstance(self.parent(), TaurusReadWriteSwitcher): @@ -253,8 +237,6 @@ def writeValue(self, forceApply=False): class PoolChannelTVWriteWidget(TaurusWidget): - layoutAlignment = Qt.Qt.AlignTop - applied = Qt.pyqtSignal() def __init__(self, parent=None, designMode=False): @@ -267,10 +249,6 @@ def __init__(self, parent=None, designMode=False): self.le_write_absolute = _IntegrationTimeStartWidget() self.layout().addWidget(self.le_write_absolute, 0, 0) - # Align everything on top - self.layout().addItem(Qt.QSpacerItem( - 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 3) - # list of widgets used for edition editingWidgets = (self.le_write_absolute,) From 4f7607d21b5c144c38312a5494900980ceb7cf24 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 11:49:45 +0200 Subject: [PATCH 212/830] Add extra widget for aborting. Abort button is still in the read widget - to be removed. --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index f90330d709..4c458b8ab7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -215,6 +215,33 @@ def setModel(self, model): self.lbl_read.setModel(model + "/Value") +class PoolChannelTVExtraWidget(TaurusWidget): + """ + """ + + def __init__(self, parent=None, designMode=False): + TaurusWidget.__init__(self, parent, designMode) + + self.setLayout(Qt.QVBoxLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.layout().setSpacing(0) + + # WITH A COMPACT VIEW, BETTER TO BE ABLE TO STOP! + self.btn_stop = Qt.QPushButton() + self.btn_stop.setToolTip("Stops the channel") + self.btn_stop.setIcon(getIcon(":/actions/media_playback_stop.svg")) + self.layout().addWidget(self.btn_stop) + self.btn_stop.clicked.connect(self.abort) + + @Qt.pyqtSlot() + @ProtectTaurusMessageBox( + msg="An error occurred trying to abort the acquisition.") + def abort(self): + channel_dev = self.taurusValueBuddy().channel_dev + if channel_dev is not None: + channel_dev.abort() + + class _IntegrationTimeStartWidget(TaurusValueLineEdit): """Line edit widget for starting acquisition with the integration time""" @@ -295,6 +322,7 @@ def __init__(self, parent=None, designMode=False): self.setLabelWidgetClass(PoolChannelTVLabelWidget) self.setReadWidgetClass(PoolChannelTVReadWidget) self.setWriteWidgetClass(PoolChannelTVWriteWidget) + self.setExtraWidgetClass(PoolChannelTVExtraWidget) self.channel_dev = None # self.setLabelConfig('') From 993cfacd3ef86dce414ff855a8e8e8fd57ee6f1c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 11:55:41 +0200 Subject: [PATCH 213/830] Add 1D and 2D TV map in main section --- src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 4c458b8ab7..269a54a4f8 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -389,7 +389,7 @@ def _updateTaurusValue(self): self._devButton.setModel(m) -if __name__ == '__main__': +if __name__ == "__main__": import sys argv = sys.argv if len(argv) > 0: @@ -398,8 +398,10 @@ def _updateTaurusValue(self): form_tv = TaurusForm() form_tv.setModifiableByUser(True) - tv_widget_class = 'sardana.taurus.qt.qtgui.extra_pool.PoolChannelTV' - tv_class_map = {'CTExpChannel': (tv_widget_class, (), {})} + tv_widget_class = "sardana.taurus.qt.qtgui.extra_pool.PoolChannelTV" + tv_class_map = {"CTExpChannel": (tv_widget_class, (), {}), + "OneDExpChannel": (tv_widget_class, (), {}), + "TwoDExpChannel": (tv_widget_class, (), {})} form_tv.setCustomWidgetMap(tv_class_map) form_tv.setModel(models) From 0540a172b38474fd48b42dd166114d5f5c7fefbc Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 15:38:39 +0200 Subject: [PATCH 214/830] Add support to 1D and 2D exp channels. Reuse (copy and adapt) code from TV widget regarding creation of read widget. --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 174 ++++++++++-------- 1 file changed, 101 insertions(+), 73 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 269a54a4f8..9955504644 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -29,13 +29,19 @@ __all__ = ["PoolChannel", "PoolChannelTV"] +import weakref + import taurus +from taurus.core import DataType, DataFormat from taurus.external.qt import Qt from taurus.qt.qtcore.mimetypes import (TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE) from taurus.qt.qtgui.panel import (TaurusValue, TaurusDevButton, - DefaultLabelWidget, TaurusAttrForm, - TaurusForm) + DefaultLabelWidget, DefaultUnitsWidget, + TaurusAttrForm, TaurusForm, + DefaultReadWidgetLabel, TaurusPlotButton, + TaurusImageButton, TaurusValuesTableButton, + ) from taurus.qt.qtgui.input import TaurusValueLineEdit from taurus.qt.qtgui.display import TaurusLabel from taurus.qt.qtgui.dialog import ProtectTaurusMessageBox @@ -146,82 +152,12 @@ def mouseMoveEvent(self, event): drag.exec_(Qt.Qt.CopyAction, Qt.Qt.CopyAction) -class PoolChannelTVReadWidget(TaurusWidget): - """ - """ - - def __init__(self, parent=None, designMode=False): - TaurusWidget.__init__(self, parent, designMode) - - self.setLayout(Qt.QGridLayout()) - self.layout().setContentsMargins(0, 0, 0, 0) - self.layout().setSpacing(0) - - self.lbl_read = TaurusLabel() - self.lbl_read.setBgRole("quality") - self.lbl_read.setSizePolicy(Qt.QSizePolicy( - Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Preferred)) - self.layout().addWidget(self.lbl_read, 0, 0) - - # WITH A COMPACT VIEW, BETTER TO BE ABLE TO STOP! - self.btn_stop = Qt.QPushButton() - self.btn_stop.setToolTip("Stops the channel") - self.prepare_button(self.btn_stop) - self.btn_stop.setIcon(getIcon(":/actions/media_playback_stop.svg")) - self.layout().addWidget(self.btn_stop, 0, 1) - - self.btn_stop.clicked.connect(self.abort) - - # WITH COMPACT VIEW, WE NEED TO FORWARD DOUBLE CLICK EVENT - self.lbl_read.installEventFilter(self) - - def eventFilter(self, obj, event): - if event.type() == Qt.QEvent.MouseButtonDblClick: - if isinstance(self.parent(), TaurusReadWriteSwitcher): - self.parent().enterEdit() - return True - try: - if obj is self.lbl_read: - return self.lbl_read.eventFilter(obj, event) - except AttributeError: - # self.lbl_read may not exist now - pass - return True - - @Qt.pyqtSlot() - @ProtectTaurusMessageBox( - msg="An error occurred trying to abort the acquisition.") - def abort(self): - channel_dev = self.taurusValueBuddy().channel_dev - if channel_dev is not None: - channel_dev.abort() - - def prepare_button(self, btn): - btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, - Qt.QSizePolicy.Fixed) - btn_policy.setHorizontalStretch(0) - btn_policy.setVerticalStretch(0) - btn.setSizePolicy(btn_policy) - btn.setMinimumSize(25, 25) - btn.setMaximumSize(25, 25) - btn.setText("") - - def setModel(self, model): - if model in (None, ""): - TaurusWidget.setModel(self, model) - self.lbl_read.setModel(model) - return - TaurusWidget.setModel(self, model + "/Value") - self.lbl_read.setModel(model + "/Value") - - class PoolChannelTVExtraWidget(TaurusWidget): """ """ def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) - self.setLayout(Qt.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(0) @@ -230,6 +166,10 @@ def __init__(self, parent=None, designMode=False): self.btn_stop = Qt.QPushButton() self.btn_stop.setToolTip("Stops the channel") self.btn_stop.setIcon(getIcon(":/actions/media_playback_stop.svg")) + btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, + Qt.QSizePolicy.Preferred) + btn_policy.setHorizontalStretch(0) + self.btn_stop.setSizePolicy(btn_policy) self.layout().addWidget(self.btn_stop) self.btn_stop.clicked.connect(self.abort) @@ -320,16 +260,104 @@ class PoolChannelTV(TaurusValue): def __init__(self, parent=None, designMode=False): TaurusValue.__init__(self, parent=parent, designMode=designMode) self.setLabelWidgetClass(PoolChannelTVLabelWidget) - self.setReadWidgetClass(PoolChannelTVReadWidget) self.setWriteWidgetClass(PoolChannelTVWriteWidget) self.setExtraWidgetClass(PoolChannelTVExtraWidget) self.channel_dev = None # self.setLabelConfig('') + def getDefaultReadWidgetClass(self, returnAll=False): + ''' + Returns the default class (or classes) to use as read widget for the + current model. + + :param returnAll: (bool) if True, the return value is a list of valid + classes instead of just one class + + :return: (class or list) the default class to use for the read + widget (or, if returnAll==True, a list of classes that can show + the attribute ). If a list is returned, it will be loosely + ordered by preference, being the first element always the + default one. + ''' + modelobj = self.getModelObj() + if modelobj is None: + if returnAll: + return [DefaultReadWidgetLabel] + else: + return DefaultReadWidgetLabel + + valueobj = modelobj.getAttribute("Value") + if valueobj.data_format == DataFormat._0D: + result = [DefaultReadWidgetLabel] + elif valueobj.data_format == DataFormat._1D: + if valueobj.type in (DataType.Float, DataType.Integer): + result = [TaurusPlotButton, + TaurusValuesTableButton, DefaultReadWidgetLabel] + else: + result = [TaurusValuesTableButton, DefaultReadWidgetLabel] + elif valueobj.data_format == DataFormat._2D: + if valueobj.type in (DataType.Float, DataType.Integer): + try: + # unused import but useful to determine if + # TaurusImageButton should be added + from taurus.qt.qtgui.extra_guiqwt import TaurusImageDialog + result = [TaurusImageButton, + TaurusValuesTableButton, DefaultReadWidgetLabel] + except ImportError: + result = [TaurusValuesTableButton, + DefaultReadWidgetLabel] + else: + result = [TaurusValuesTableButton, DefaultReadWidgetLabel] + else: + self.warning('Unsupported attribute type %s' % valueobj.type) + result = None + + if returnAll: + return result + else: + return result[0] + + def updateReadWidget(self): + """Update read widget by recreating it from scratch. + + Overrides TaurusValue.updateReadWidget. Simply do the same, just + don't call setModel on the read widget at the end. Model of the read + widget is set by our setModel when the read widget is already + recreated. + """ + # get the class for the widget and replace it if necessary + try: + klass = self.readWidgetClassFactory(self.readWidgetClassID) + self._readWidget = self._newSubwidget(self._readWidget, klass) + except Exception as e: + self._destroyWidget(self._readWidget) + self._readWidget = Qt.QLabel('[Error]') + msg = 'Error creating read widget:\n' + str(e) + self._readWidget.setToolTip(msg) + self.debug(msg) + # self.traceback(30) #warning level=30 + + # take care of the layout + self.addReadWidgetToLayout() + + if self._readWidget is not None: + # give the new widget a reference to its buddy TaurusValue object + self._readWidget.taurusValueBuddy = weakref.ref(self) + if isinstance(self._readWidget, TaurusReadWriteSwitcher): + self._readWidget.readWidget.taurusValueBuddy = weakref.ref( + self) + self._readWidget.writeWidget.taurusValueBuddy = weakref.ref( + self) + + # tweak the new widget + if self.minimumHeight() is not None: + self._readWidget.setMinimumHeight(self.minimumHeight()) + def setModel(self, model): TaurusValue.setModel(self, model) if model == "" or model is None: return + self.readWidget().setModel(model + "/Value") self.channel_dev = taurus.Device(model) def showEvent(self, event): From e27619cb6dfdab5769d63fe9f59a324a2768b510 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 15:39:53 +0200 Subject: [PATCH 215/830] Add units widget Copy and adapt solution from PMTV --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 9955504644..6c68e05343 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -251,6 +251,18 @@ def emitEditingFinished(self): self.applied.emit() +class PoolChannelTVUnitsWidget(DefaultUnitsWidget): + + def __init__(self, parent=None, designMode=False): + DefaultUnitsWidget.__init__(self, parent, designMode) + + def setModel(self, model): + if model in (None, ""): + DefaultUnitsWidget.setModel(self, model) + return + DefaultUnitsWidget.setModel(self, model + "/Value") + + class PoolChannelTV(TaurusValue): ''' A widget that displays and controls a pool channel device. It differs from :class:`PoolChannel` in that it behaves as a TaurusValue @@ -261,6 +273,7 @@ def __init__(self, parent=None, designMode=False): TaurusValue.__init__(self, parent=parent, designMode=designMode) self.setLabelWidgetClass(PoolChannelTVLabelWidget) self.setWriteWidgetClass(PoolChannelTVWriteWidget) + self.setUnitsWidgetClass(PoolChannelTVUnitsWidget) self.setExtraWidgetClass(PoolChannelTVExtraWidget) self.channel_dev = None # self.setLabelConfig('') From b32a58809c3b45d90e8c6566b541f109dd231176 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 18:24:37 +0200 Subject: [PATCH 216/830] Start acquisition from extra widget Don't use write widget at all. Add line edit to extra widget for setting the integration time. Make stop button be also the start button for starting the acquisition - the action change depending on the channel's state. --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 146 ++++++++---------- 1 file changed, 67 insertions(+), 79 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 6c68e05343..31796c04d5 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -31,6 +31,7 @@ import weakref +import tango import taurus from taurus.core import DataType, DataFormat from taurus.external.qt import Qt @@ -49,7 +50,7 @@ from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtgui.resource import getIcon from sardana.taurus.qt.qtgui.extra_pool.poolmotor import \ - LabelWidgetDragsDeviceAndAttribute + LabelWidgetDragsDeviceAndAttribute, TaurusAttributeListener class PoolChannelTVLabelWidget(TaurusWidget): @@ -152,103 +153,90 @@ def mouseMoveEvent(self, event): drag.exec_(Qt.Qt.CopyAction, Qt.Qt.CopyAction) +class _IntegTimeTaurusValueLineEdit(TaurusValueLineEdit): + + def sizeHint(self): + size = Qt.QSize() + size.setWidth(55) + return size + + class PoolChannelTVExtraWidget(TaurusWidget): """ """ def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) - self.setLayout(Qt.QVBoxLayout()) + self.setLayout(Qt.QHBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(0) - # WITH A COMPACT VIEW, BETTER TO BE ABLE TO STOP! - self.btn_stop = Qt.QPushButton() - self.btn_stop.setToolTip("Stops the channel") - self.btn_stop.setIcon(getIcon(":/actions/media_playback_stop.svg")) + self.le_integ_time = _IntegTimeTaurusValueLineEdit() + le_policy = Qt.QSizePolicy(Qt.QSizePolicy.Preferred, + Qt.QSizePolicy.Preferred) + self.le_integ_time.setSizePolicy(le_policy) + self.layout().addWidget(self.le_integ_time) + + # TODO: state listener should be part of *ExpChannel device Taurus + # Qt extension, it is necessary to change button's action, + # icon, etc. + self.state_listener = TaurusAttributeListener() + self.state_listener.eventReceivedSignal.connect(self.updateState) + self.btn_start_stop_clicked_slot = None + self.btn_start_stop = Qt.QPushButton() btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Preferred) btn_policy.setHorizontalStretch(0) - self.btn_stop.setSizePolicy(btn_policy) - self.layout().addWidget(self.btn_stop) - self.btn_stop.clicked.connect(self.abort) + self.btn_start_stop.setSizePolicy(btn_policy) + self.layout().addWidget(self.btn_start_stop) + + def updateState(self, state): + btn_start_stop = self.btn_start_stop + if self.btn_start_stop_clicked_slot is not None: + btn_start_stop.clicked.disconnect( + self.btn_start_stop_clicked_slot) + if state == tango.DevState.MOVING: + btn_start_stop.setToolTip("Stop the channel") + btn_start_stop.setIcon( + getIcon(":/actions/media_playback_stop.svg")) + self.btn_start_stop_clicked_slot = self.abort + else: + btn_start_stop.setToolTip("Start the channel") + btn_start_stop.setIcon( + getIcon(":/actions/media_playback_start.svg")) + self.btn_start_stop_clicked_slot = self.start + btn_start_stop.clicked.connect(self.btn_start_stop_clicked_slot) + + def setModel(self, model): + # first disconnect old model state listener + model_obj = self.getModelObj() + if model_obj is not None: + model_obj.getAttribute("State").removeListener( + self.state_listener) + TaurusWidget.setModel(self, model) + if model in (None, ""): + self.le_integ_time.setModel(model) + return + self.le_integ_time.setModel(model + "/IntegrationTime") + # connect new model state listener + self.getModelObj().getAttribute("State").addListener( + self.state_listener) @Qt.pyqtSlot() @ProtectTaurusMessageBox( - msg="An error occurred trying to abort the acquisition.") - def abort(self): + msg="An error occurred trying to start the acquisition.") + def start(self): channel_dev = self.taurusValueBuddy().channel_dev - if channel_dev is not None: - channel_dev.abort() - - -class _IntegrationTimeStartWidget(TaurusValueLineEdit): - """Line edit widget for starting acquisition with the integration time""" - - def writeValue(self, forceApply=False): - """Writes the value to the attribute, either by applying pending - operations or (if the ForcedApply flag is True), it writes directly - when no operations are pending - - It emits the applied signal if apply is not aborted. - - :param forceApply: (bool) If True, it behaves as in forceApply mode - (even if the forceApply mode is disabled by - :meth:`setForceApply`) - """ - TaurusValueLineEdit.writeValue(self, forceApply=forceApply) - channel_dev = self.getModelObj().getParentObj() if channel_dev is not None: channel_dev.Start() - -class PoolChannelTVWriteWidget(TaurusWidget): - - applied = Qt.pyqtSignal() - - def __init__(self, parent=None, designMode=False): - TaurusWidget.__init__(self, parent, designMode) - - self.setLayout(Qt.QGridLayout()) - self.layout().setContentsMargins(0, 0, 0, 0) - self.layout().setSpacing(0) - - self.le_write_absolute = _IntegrationTimeStartWidget() - self.layout().addWidget(self.le_write_absolute, 0, 0) - - # list of widgets used for edition - editingWidgets = (self.le_write_absolute,) - - for w in editingWidgets: - w.installEventFilter(self) - - def eventFilter(self, obj, event): - """reimplemented to intercept events from the subwidgets""" - # emit editingFinished when focus out to a non-editing widget - if event.type() == Qt.QEvent.FocusOut: - focused = Qt.qApp.focusWidget() - focusInChild = focused in self.findChildren(focused.__class__) - if not focusInChild: - self.emitEditingFinished() - return False - - def setModel(self, model): - if model in (None, ""): - TaurusWidget.setModel(self, model) - self.le_write_absolute.setModel(model) - return - TaurusWidget.setModel(self, model + "/IntegrationTime") - self.le_write_absolute.setModel(model + "/IntegrationTime") - - def keyPressEvent(self, key_event): - if key_event.key() == Qt.Qt.Key_Escape: - self.abort() - key_event.accept() - TaurusWidget.keyPressEvent(self, key_event) - @Qt.pyqtSlot() - def emitEditingFinished(self): - self.applied.emit() + @ProtectTaurusMessageBox( + msg="An error occurred trying to abort the acquisition.") + def abort(self): + channel_dev = self.taurusValueBuddy().channel_dev + if channel_dev is not None: + channel_dev.abort() class PoolChannelTVUnitsWidget(DefaultUnitsWidget): @@ -272,7 +260,7 @@ class PoolChannelTV(TaurusValue): def __init__(self, parent=None, designMode=False): TaurusValue.__init__(self, parent=parent, designMode=designMode) self.setLabelWidgetClass(PoolChannelTVLabelWidget) - self.setWriteWidgetClass(PoolChannelTVWriteWidget) + self.setWriteWidgetClass(None) self.setUnitsWidgetClass(PoolChannelTVUnitsWidget) self.setExtraWidgetClass(PoolChannelTVExtraWidget) self.channel_dev = None From 706c4441d963d7fd91c964080d1fc123ca12eb1e Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 18:26:44 +0200 Subject: [PATCH 217/830] Remove unused code (minor) --- src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 31796c04d5..25e08a6d3c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -44,7 +44,6 @@ TaurusImageButton, TaurusValuesTableButton, ) from taurus.qt.qtgui.input import TaurusValueLineEdit -from taurus.qt.qtgui.display import TaurusLabel from taurus.qt.qtgui.dialog import ProtectTaurusMessageBox from taurus.qt.qtgui.compact import TaurusReadWriteSwitcher from taurus.qt.qtgui.container import TaurusWidget @@ -264,7 +263,6 @@ def __init__(self, parent=None, designMode=False): self.setUnitsWidgetClass(PoolChannelTVUnitsWidget) self.setExtraWidgetClass(PoolChannelTVExtraWidget) self.channel_dev = None - # self.setLabelConfig('') def getDefaultReadWidgetClass(self, returnAll=False): ''' @@ -336,7 +334,6 @@ def updateReadWidget(self): msg = 'Error creating read widget:\n' + str(e) self._readWidget.setToolTip(msg) self.debug(msg) - # self.traceback(30) #warning level=30 # take care of the layout self.addReadWidgetToLayout() From 21bbda29353ad1b5b56ab5ce1d679fe5b6e881e2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 18:28:09 +0200 Subject: [PATCH 218/830] Remove code responsible for polling Value attribute This code was copied from PMTV and it does not have sense to poll value attribute. --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 25e08a6d3c..082701f7b8 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -358,20 +358,6 @@ def setModel(self, model): self.readWidget().setModel(model + "/Value") self.channel_dev = taurus.Device(model) - def showEvent(self, event): - TaurusValue.showEvent(self, event) - try: - self.getModelObj().getParentObj().getAttribute('Value').enablePolling(force=True) - except: - pass - - def hideEvent(self, event): - TaurusValue.hideEvent(self, event) - try: - self.getModelObj().getParentObj().getAttribute('Value').disablePolling() - except: - pass - def showTangoAttributes(self): model = self.getModel() taurus_attr_form = TaurusAttrForm() From 559822c9baf5e38150d777b9da683eda85f8cdd2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 19:39:04 +0200 Subject: [PATCH 219/830] Add docstring with todos --- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 082701f7b8..4ac269b9ba 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -251,10 +251,16 @@ def setModel(self, model): class PoolChannelTV(TaurusValue): - ''' A widget that displays and controls a pool channel device. + """A widget that displays and controls a pool channel device. It differs from :class:`PoolChannel` in that it behaves as a TaurusValue (i.e., it allows its subwidgets to be aligned in columns in a TaurusForm)` - ''' + + .. todo:: Ideally overriding of `getDefaultReadWidgetClass` and + `updateReadWidget` should not be necessary. Creation of a dedicated wiget + for displaying value should be delegated to a custom read widget. + .. todo:: draw state-based coloured frame around spectrum and image + buttons, when state is MOVING - blue, when ON - green, etc. + """ def __init__(self, parent=None, designMode=False): TaurusValue.__init__(self, parent=parent, designMode=designMode) @@ -265,19 +271,22 @@ def __init__(self, parent=None, designMode=False): self.channel_dev = None def getDefaultReadWidgetClass(self, returnAll=False): - ''' + """ Returns the default class (or classes) to use as read widget for the current model. + Override TaurusValue.getDefaultReadWidgetClass. Simply do the same + but based on the Value attribute while our model obj is a device. + :param returnAll: (bool) if True, the return value is a list of valid classes instead of just one class :return: (class or list) the default class to use for the read - widget (or, if returnAll==True, a list of classes that can show - the attribute ). If a list is returned, it will be loosely - ordered by preference, being the first element always the - default one. - ''' + widget (or, if returnAll==True, a list of classes that can + show the attribute ). If a list is returned, it will be + loosely ordered by preference, being the first element + always the default one. + """ modelobj = self.getModelObj() if modelobj is None: if returnAll: @@ -319,7 +328,7 @@ def getDefaultReadWidgetClass(self, returnAll=False): def updateReadWidget(self): """Update read widget by recreating it from scratch. - Overrides TaurusValue.updateReadWidget. Simply do the same, just + Override TaurusValue.updateReadWidget. Simply do the same, just don't call setModel on the read widget at the end. Model of the read widget is set by our setModel when the read widget is already recreated. From bd390c90c27d74be4a51e822e3e63cd91e4edb5f Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 19:45:44 +0200 Subject: [PATCH 220/830] Fix flake8 --- src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 4ac269b9ba..a9e82b8c9a 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -308,7 +308,7 @@ def getDefaultReadWidgetClass(self, returnAll=False): try: # unused import but useful to determine if # TaurusImageButton should be added - from taurus.qt.qtgui.extra_guiqwt import TaurusImageDialog + from taurus.qt.qtgui.extra_guiqwt import TaurusImageDialog # noqa result = [TaurusImageButton, TaurusValuesTableButton, DefaultReadWidgetLabel] except ImportError: From 30363814e8b8770688c32171d9b665a8a22bff8f Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 23 Sep 2019 19:56:46 +0200 Subject: [PATCH 221/830] Revert previous PoolChannelTV for 0D and PC --- .../taurus/qt/qtgui/extra_pool/__init__.py | 2 +- .../taurus/qt/qtgui/extra_pool/poolchannel.py | 57 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py b/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py index 8b7bc0d0ed..8950af34d6 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py @@ -32,6 +32,6 @@ from .poolmotor import (PoolMotorTV, PoolMotorTVLabelWidget, # noqa PoolMotorTVReadWidget, PoolMotorTVWriteWidget, PoolMotorTVUnitsWidget, # noqa PoolMotor) # noqa -from .poolchannel import PoolChannel, PoolChannelTV # noqa +from .poolchannel import PoolChannel, PoolChannelTV, _PoolChannelTV # noqa from .poolioregister import (PoolIORegisterTV, PoolIORegisterReadWidget, # noqa PoolIORegisterWriteWidget, PoolIORegister, PoolIORegisterButtons) # noqa diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index a9e82b8c9a..e7e881504d 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -27,7 +27,7 @@ channelWidgets.py: """ -__all__ = ["PoolChannel", "PoolChannelTV"] +__all__ = ["PoolChannel", "PoolChannelTV", "_PoolChannelTV"] import weakref @@ -377,6 +377,61 @@ def showTangoAttributes(self): taurus_attr_form.show() +class _ParentDevButton(TaurusDevButton): + '''A TaurusDevButton that receives an attribute name but sets + the corresponding device as model. **For internal use only** ''' + + def __init__(self, **kwargs): + TaurusDevButton.__init__(self, **kwargs) + self.setText('') + self.setSizePolicy(Qt.QSizePolicy.Preferred, Qt.QSizePolicy.Maximum) + + def setModel(self, model): + try: + attr = taurus.Attribute(model) + except: + return + dev = attr.getParentObj() + devname = dev.getFullName() + TaurusDevButton.setModel(self, devname) + + +class _PoolChannelTV(TaurusValue): + ''' A widget that displays and controls a pool channel device. + It differs from :class:`PoolChannel` in that it behaves as a TaurusValue + (i.e., it allows its subwidgets to be aligned in columns in a TaurusForm)` + ''' + + def __init__(self, parent=None, designMode=False): + TaurusValue.__init__(self, parent=parent, designMode=designMode) + self.setLabelWidgetClass(LabelWidgetDragsDeviceAndAttribute) + self.setLabelConfig('') + + def getDefaultExtraWidgetClass(self): + return _ParentDevButton + + def setModel(self, model): + if model is not None: + # @todo: change this (it assumes tango naming!) + model = "%s/value" % model + TaurusValue.setModel(self, model) + + def showEvent(self, event): + TaurusValue.showEvent(self, event) + try: + self.getModelObj().getParentObj().getAttribute('Value').enablePolling(force=True) + except: + pass + + def hideEvent(self, event): + TaurusValue.hideEvent(self, event) + try: + self.getModelObj().getParentObj().getAttribute('Value').disablePolling() + except: + pass + + + class PoolChannel(TaurusWidget): ''' A widget that displays and controls a pool channel device From 7bac4f3f7d717797c4bf6eb3250c1b825ed24c70 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Fri, 27 Sep 2019 17:00:28 +0200 Subject: [PATCH 222/830] Avoid taurus deprecation warnings in expconf Adapt sardana code to avoid taurus deprecation warnings in expconf widget --- src/sardana/pool/pooldefs.py | 2 +- .../taurus/core/tango/sardana/macroserver.py | 34 ++++++++----------- .../taurus/qt/qtcore/tango/sardana/model.py | 10 +++--- .../qt/qtgui/extra_sardana/expdescription.py | 6 ++-- .../qtgui/extra_sardana/measurementgroup.py | 27 +++++++-------- 5 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/sardana/pool/pooldefs.py b/src/sardana/pool/pooldefs.py index d4f5efe995..37e1a24935 100644 --- a/src/sardana/pool/pooldefs.py +++ b/src/sardana/pool/pooldefs.py @@ -31,7 +31,7 @@ __docformat__ = 'restructuredtext' from operator import __getitem__ -from taurus.external.enum import IntEnum +from enum import IntEnum from taurus.core.util.enumeration import Enumeration from sardana.taurus.core.tango.sardana import AcqTriggerType, AcqMode diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 4bdbc6f5aa..f8087c160c 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -48,12 +48,14 @@ from taurus.core.taurusbasetypes import TaurusEventType, TaurusSWDevState, \ TaurusSerializationMode +from taurus.core import TaurusDevState from taurus.core.util.log import Logger from taurus.core.util.containers import CaselessDict from taurus.core.util.codecs import CodecFactory from taurus.core.util.event import EventGenerator, AttributeEventWait from taurus.core.tango import TangoDevice + from sardana.sardanautils import recur_map from .macro import MacroInfo, Macro, MacroNode, ParamFactory, \ SingleParamNode, ParamNode, createMacroNode @@ -96,7 +98,7 @@ def eventReceived(self, src, type, evt_value): self.fireEvent(None) elif type != TaurusEventType.Config: if evt_value: - self.fireEvent(evt_value.value) + self.fireEvent(evt_value.rvalue) else: self.fireEvent(None) @@ -122,14 +124,14 @@ def clearLogBuffer(self): def eventReceived(self, src, type, evt_value): if type == TaurusEventType.Change: - if evt_value is None or evt_value.value is None: + if evt_value is None or evt_value.rvalue is None: self.fireEvent(None) else: - self._log_buffer.extend(evt_value.value) + self._log_buffer.extend(evt_value.rvalue) while len(self._log_buffer) > self._max_buff_size: self._log_buffer.pop(0) if evt_value: - self.fireEvent(evt_value.value) + self.fireEvent(evt_value.rvalue) class BaseInputHandler(object): @@ -340,14 +342,9 @@ def __init__(self, name, **kw): self.call__init__(MacroServerDevice, name, **kw) self._old_door_state = PyTango.DevState.UNKNOWN - try: - self._old_sw_door_state = TaurusSWDevState.Uninitialized - except RuntimeError: - # TODO: For Taurus 4 compatibility - from taurus.core import TaurusDevState - self._old_sw_door_state = TaurusDevState.Undefined + self._old_sw_door_state = TaurusDevState.Undefined - self.getStateObj().addListener(self.stateChanged) + self.stateObj.addListener(self.stateChanged) for log_name in self.log_streams: tg_attr = self.getAttribute(log_name) @@ -600,14 +597,11 @@ def stateChanged(self, s, t, v): # In this case provide the same behavior as Taurus3 - assign None to # the old state try: - self._old_door_state = self.getState() + self._old_door_state = self.stateObj.rvalue except PyTango.DevFailed: self._old_door_state = None - try: - self._old_sw_door_state = self.getSWState() - except: - # TODO: For Taurus 4 compatibility - self._old_sw_door_state = self.state + + self._old_sw_door_state = self.state def resultReceived(self, log_name, result): """Method invoked by the arrival of a change event on the Result @@ -847,7 +841,7 @@ def _on_environment_changed(self, evt_src, evt_type, evt_value): if evt_type not in CHANGE_EVT_TYPES: return ret - env = CodecFactory().decode(evt_value.value) + env = CodecFactory().decode(evt_value.rvalue) for key, value in list(env.get('new', {}).items()): self._addEnvironment(key, value) @@ -925,10 +919,10 @@ def _on_elements_changed(self, evt_src, evt_type, evt_value): if evt_type not in CHANGE_EVT_TYPES: return ret try: - elems = CodecFactory().decode(evt_value.value) + elems = CodecFactory().decode(evt_value.rvalue) except: self.error("Could not decode element info format=%s len=%s", - evt_value.value[0], len(evt_value.value[1])) + evt_value.rvalue[0], len(evt_value.rvalue[1])) return ret for element_data in elems.get('new', ()): diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/model.py b/src/sardana/taurus/qt/qtcore/tango/sardana/model.py index 00cbf23bd6..9cbdc9eb4a 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/model.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/model.py @@ -42,15 +42,16 @@ except: pygments = None -from taurus.core.taurusdevice import TaurusDevice from taurus.external.qt import Qt -from taurus.core.util.enumeration import Enumeration from taurus.qt.qtcore.model import TaurusBaseTreeItem, TaurusBaseModel, \ TaurusBaseProxyModel from taurus.qt.qtcore.mimetypes import TAURUS_MODEL_LIST_MIME_TYPE, \ TAURUS_MODEL_MIME_TYPE -_MOD, _CLS, _FNC, _TNG = ":/python-module.png", ":/class.png", ":/function.png", ":/tango.png" +_MOD = ":python-module.png" +_CLS = ":class.png" +_FNC = ":function.png" +_TNG = ":tango.png" TYPE_MAP = { "ControllerLibrary": ("Controller libraries", _MOD, "Controller library",), @@ -78,9 +79,8 @@ def getElementTypeLabel(t): def getElementTypeIcon(t): - import taurus.qt.qtgui.resource try: - return taurus.qt.qtgui.resource.getIcon(TYPE_MAP.get(t, (None, _TNG))[1]) + return Qt.QIcon(TYPE_MAP.get(t, (None, _TNG))[1]) except: return None diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index 40612169fb..33edb138d1 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -35,11 +35,11 @@ import taurus import taurus.core from taurus.qt.qtgui.base import TaurusBaseWidget -from taurus.qt.qtgui import resource from sardana.taurus.qt.qtcore.tango.sardana.model import SardanaBaseProxyModel, SardanaTypeTreeItem from sardana.sardanadefs import ElementType, TYPE_ACQUIRABLE_ELEMENTS from taurus.qt.qtgui.util.ui import UILoadable +from taurus.external.qt import Qt # Using a plain model and filtering and checking 'Acquirable' in item.itemData().interfaces is more elegant, but things don't get properly sorted... #from taurus.qt.qtcore.tango.sardana.model import SardanaElementPlainModel @@ -253,7 +253,7 @@ def __init__(self, parent=None, door=None, plotsButton=True, tooltip = "Show/Hide plots is not ready for %s" % API # -------------------------------------------------------------------- - icon = resource.getIcon(":/actions/view.svg") + icon = Qt.QIcon("actions:view.svg") measGrpTab = self.ui.tabWidget.widget(0) self.togglePlotsAction = Qt.QAction(icon, "Show/Hide plots", self) if tooltip is not None: @@ -432,7 +432,7 @@ def setModel(self, model): if door is None: return # @todo: get the tghost from the door model instead - tghost = taurus.Database().getNormalName() + tghost = taurus.Authority().getNormalName() msname = door.macro_server.getFullName() self.ui.taurusModelTree.setModel(tghost) self.ui.sardanaElementTree.setModel(msname) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index d7e657b9f8..1038d424cb 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -34,7 +34,6 @@ from taurus.external.qt import Qt from taurus.qt.qtcore.model import TaurusBaseTreeItem, TaurusBaseModel from taurus.qt.qtgui.model import EditorToolBar -from taurus.qt.qtgui.resource import getIcon, getThemeIcon from taurus.qt.qtgui.table import TaurusBaseTableWidget from taurus.qt.qtgui.panel import TaurusModelChooser from taurus.core.taurusbasetypes import TaurusElementType @@ -202,27 +201,27 @@ def createChannelDict(channel, index=None, **kwargs): def getElementTypeIcon(t): if t == ChannelView.Channel: - return getIcon(":/actions/system-shutdown.svg") + return Qt.QIcon("actions:system-shutdown.svg") elif t == ChannelView.Enabled: - return getIcon(":/status/true.svg") + return Qt.QIcon("status:true.svg") elif t == ChannelView.Output: - return getThemeIcon("utilities-terminal") + return Qt.QIcon.fromTheme("utilities-terminal") elif t == ChannelView.PlotType: - return getIcon(":/apps/utilities-system-monitor.svg") + return Qt.QIcon("apps:utilities-system-monitor.svg") elif t == ChannelView.PlotAxes: - return getIcon(":/apps/utilities-system-monitor.svg") + return Qt.QIcon("apps:utilities-system-monitor.svg") elif t == ChannelView.Timer: - return getIcon(":/status/flag-green-clock.svg") + return Qt.QIcon("status:flag-green-clock.svg") elif t == ChannelView.Monitor: - return getIcon(":/status/flag-green.svg") + return Qt.QIcon("status:flag-green.svg") elif t == ChannelView.Synchronization: - return getIcon(":/actions/system-shutdown.svg") + return Qt.QIcon("actions:system-shutdown.svg") elif t == ChannelView.NXPath: - return getThemeIcon("document-save-as") + return Qt.QIcon.fromTheme("document-save-as") elif t == ChannelView.Synchronizer: - return getIcon(":/actions/system-shutdown.svg") + return Qt.QIcon("actions:system-shutdown.svg") - return getIcon(":/tango.png") + return Qt.QIcon(":tango.png") def getElementTypeSize(t): @@ -387,7 +386,7 @@ def toolTip(self, index): def icon(self, index): taurus_role = index.model().role(index.column()) if taurus_role == ChannelView.Channel: - return getIcon(":/actions/system-shutdown.svg") + return Qt.QIcon("actions:system-shutdown.svg") class MntGrpUnitItem(TaurusBaseTreeItem): @@ -974,7 +973,7 @@ class MntGrpChannelEditor(TaurusBaseTableWidget): KnownPerspectives = { "Channel": { "label": "Channels", - "icon": ":/actions/system-shutdown.svg", + "icon": "actions:system-shutdown.svg", "tooltip": "View by channel", "model": [BaseMntGrpChannelModel, ], }, From 806697ed27ebf4029ea22a46eacd47881719f754 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Fri, 27 Sep 2019 17:50:47 +0200 Subject: [PATCH 223/830] Avoid taurus deprecation warnings in PMTV Adapt sardana code to avoid taurus deprecation warnings in PMTV widget --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 68 ++++++++----------- 1 file changed, 28 insertions(+), 40 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 561c8f0968..acacb9f234 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -23,37 +23,25 @@ ## ############################################################################## -import sys -import copy import PyTango -import numpy from taurus.external.qt import Qt, compat import taurus from taurus.core.util.colors import DEVICE_STATE_PALETTE from taurus.core.taurusbasetypes import TaurusEventType -try: - from taurus.core.taurusvalidator import DeviceNameValidator as \ - TangoDeviceNameValidator -except ImportError: - # TODO: For Taurus 4 adaptation - from taurus.core.tango.tangovalidator import TangoDeviceNameValidator +from taurus.core.tango.tangovalidator import TangoDeviceNameValidator import taurus.qt.qtcore.mimetypes -from taurus.qt.qtgui.base import TaurusBaseWritableWidget from taurus.qt.qtgui.compact import TaurusReadWriteSwitcher from taurus.qt.qtgui.dialog import ProtectTaurusMessageBox -from taurus.qt.qtgui.base import TaurusBaseWidget from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtgui.container import TaurusFrame from taurus.qt.qtgui.display import TaurusLabel from taurus.qt.qtgui.input import TaurusValueLineEdit -from taurus.qt.qtgui.input import TaurusValueSpinBox from taurus.qt.qtgui.panel import DefaultLabelWidget from taurus.qt.qtgui.panel import DefaultUnitsWidget from taurus.qt.qtgui.panel import TaurusValue, TaurusAttrForm from taurus.qt.qtcore.mimetypes import TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE -from taurus.qt.qtgui.resource import getIcon from taurus.qt.qtgui.util.ui import UILoadable @@ -429,33 +417,33 @@ def just_ctrl_status_line(evt_src, evt_type, evt_value): def __setTaurusIcons(self): self.ui.btnMin.setText('') - self.ui.btnMin.setIcon(getIcon(':/actions/list-remove.svg')) + self.ui.btnMin.setIcon(Qt.QIcon("actions:list-remove.svg")) self.ui.btnMax.setText('') - self.ui.btnMax.setIcon(getIcon(':/actions/list-add.svg')) + self.ui.btnMax.setIcon(Qt.QIcon("actions:list-add.svg")) self.ui.btnGoToNeg.setText('') self.ui.btnGoToNeg.setIcon( - getIcon(':/actions/media_skip_backward.svg')) + Qt.QIcon("actions:media_skip_backward.svg")) self.ui.btnGoToNegPress.setText('') self.ui.btnGoToNegPress.setIcon( - getIcon(':/actions/media_seek_backward.svg')) + Qt.QIcon("actions:media_seek_backward.svg")) self.ui.btnGoToNegInc.setText('') self.ui.btnGoToNegInc.setIcon( - getIcon(':/actions/media_playback_backward.svg')) + Qt.QIcon("actions:media_playback_backward.svg")) self.ui.btnGoToPos.setText('') - self.ui.btnGoToPos.setIcon(getIcon(':/actions/media_skip_forward.svg')) + self.ui.btnGoToPos.setIcon(Qt.QIcon("actions:media_skip_forward.svg")) self.ui.btnGoToPosPress.setText('') self.ui.btnGoToPosPress.setIcon( - getIcon(':/actions/media_seek_forward.svg')) + Qt.QIcon("actions:media_seek_forward.svg")) self.ui.btnGoToPosInc.setText('') self.ui.btnGoToPosInc.setIcon( - getIcon(':/actions/media_playback_start.svg')) + Qt.QIcon("actions:media_playback_start.svg")) self.ui.btnStop.setText('') - self.ui.btnStop.setIcon(getIcon(':/actions/media_playback_stop.svg')) + self.ui.btnStop.setIcon(Qt.QIcon("actions:media_playback_stop.svg")) self.ui.btnHome.setText('') - self.ui.btnHome.setIcon(getIcon(':/actions/go-home.svg')) + self.ui.btnHome.setIcon(Qt.QIcon("actions:go-home.svg")) self.ui.btnCfg.setText('') - self.ui.btnCfg.setIcon(getIcon(':/categories/preferences-system.svg')) + self.ui.btnCfg.setIcon(Qt.QIcon("categories:preferences-system.svg")) ####################################################################### #@Qt.pyqtSlot(list) @@ -744,7 +732,7 @@ def setModel(self, model): limits_attribute = self.motor_dev.getAttribute( 'Limit_switches') limits_attribute.addListener(self.limits_listener) - # self.updateLimits(limits_attribute.read().value) + # self.updateLimits(limits_attribute.read().rvalue) limits_visible = True self.ui.btnMin.setVisible(limits_visible) self.ui.btnMax.setVisible(limits_visible) @@ -820,7 +808,7 @@ def __init__(self): def eventReceived(self, evt_src, evt_type, evt_value): if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: return - value = evt_value.value + value = evt_value.rvalue self.eventReceivedSignal.emit(value) @@ -916,7 +904,7 @@ def calculateExtendedTooltip(self, cache=False): status_info = '' motor_dev = self.taurusValueBuddy().motor_dev if motor_dev is not None: - status = motor_dev.getAttribute('Status').read().value + status = motor_dev.getAttribute('Status').read().rvalue # MAKE IT LOOK LIKE THE STANDARD TABLE FOR TAURUS TOOLTIPS status_lines = status.split('\n') status_info = '
Status:' + \ @@ -939,7 +927,7 @@ def contextMenuEvent(self, event): action_tango_attributes = Qt.QAction(self) action_tango_attributes.setIcon( - getIcon(':/categories/preferences-system.svg')) + Qt.QIcon("categories:preferences-system.svg")) action_tango_attributes.setText('Tango Attributes') menu.addAction(action_tango_attributes) action_tango_attributes.triggered.connect( @@ -995,14 +983,14 @@ def __init__(self, parent=None, designMode=False): self.btn_lim_neg.setToolTip('Negative Limit') # self.btn_lim_neg.setEnabled(False) self.prepare_button(self.btn_lim_neg) - self.btn_lim_neg.setIcon(getIcon(':/actions/list-remove.svg')) + self.btn_lim_neg.setIcon(Qt.QIcon("actions:list-remove.svg")) limits_layout.addWidget(self.btn_lim_neg) self.btn_lim_pos = Qt.QPushButton() self.btn_lim_pos.setToolTip('Positive Limit') # self.btn_lim_pos.setEnabled(False) self.prepare_button(self.btn_lim_pos) - self.btn_lim_pos.setIcon(getIcon(':/actions/list-add.svg')) + self.btn_lim_pos.setIcon(Qt.QIcon("actions:list-add.svg")) limits_layout.addWidget(self.btn_lim_pos) self.layout().addLayout(limits_layout, 0, 0) @@ -1017,7 +1005,7 @@ def __init__(self, parent=None, designMode=False): self.btn_stop = Qt.QPushButton() self.btn_stop.setToolTip('Stops the motor') self.prepare_button(self.btn_stop) - self.btn_stop.setIcon(getIcon(':/actions/media_playback_stop.svg')) + self.btn_stop.setIcon(Qt.QIcon("actions:media_playback_stop.svg")) self.layout().addWidget(self.btn_stop, 0, 2) self.btn_stop.clicked.connect(self.abort) @@ -1158,13 +1146,13 @@ def __init__(self, parent=None, designMode=False): self.btn_step_down.setToolTip('Decrements motor position') self.prepare_button(self.btn_step_down) self.btn_step_down.setIcon( - getIcon(':/actions/media_playback_backward.svg')) + Qt.QIcon("actions:media_playback_backward.svg")) self.qw_write_relative.layout().addWidget(self.btn_step_down) self.btn_step_up = Qt.QPushButton() self.btn_step_up.setToolTip('Increments motor position') self.prepare_button(self.btn_step_up) - self.btn_step_up.setIcon(getIcon(':/actions/media_playback_start.svg')) + self.btn_step_up.setIcon(Qt.QIcon("actions:media_playback_start.svg")) self.qw_write_relative.layout().addWidget(self.btn_step_up) self.layout().addWidget(self.qw_write_relative, 0, 0) @@ -1194,7 +1182,7 @@ def __init__(self, parent=None, designMode=False): self.btn_to_neg.setToolTip( 'Moves the motor towards the Negative Software Limit') self.prepare_button(self.btn_to_neg) - self.btn_to_neg.setIcon(getIcon(':/actions/media_skip_backward.svg')) + self.btn_to_neg.setIcon(Qt.QIcon("actions:media_skip_backward.svg")) btns_layout.addWidget(self.btn_to_neg) self.btn_to_neg_press = Qt.QPushButton() @@ -1202,7 +1190,7 @@ def __init__(self, parent=None, designMode=False): 'Moves the motor (while pressed) towards the Negative Software Limit') self.prepare_button(self.btn_to_neg_press) self.btn_to_neg_press.setIcon( - getIcon(':/actions/media_seek_backward.svg')) + Qt.QIcon("actions:media_seek_backward.svg")) btns_layout.addWidget(self.btn_to_neg_press) self.btn_to_pos_press = Qt.QPushButton() @@ -1210,14 +1198,14 @@ def __init__(self, parent=None, designMode=False): self.btn_to_pos_press.setToolTip( 'Moves the motor (while pressed) towards the Positive Software Limit') self.btn_to_pos_press.setIcon( - getIcon(':/actions/media_seek_forward.svg')) + Qt.QIcon("actions:media_seek_forward.svg")) btns_layout.addWidget(self.btn_to_pos_press) self.btn_to_pos = Qt.QPushButton() self.btn_to_pos.setToolTip( 'Moves the motor towards the Positive Software Limit') self.prepare_button(self.btn_to_pos) - self.btn_to_pos.setIcon(getIcon(':/actions/media_skip_forward.svg')) + self.btn_to_pos.setIcon(Qt.QIcon("actions:media_skip_forward.svg")) btns_layout.addWidget(self.btn_to_pos) btns_layout.addItem(Qt.QSpacerItem( @@ -1303,7 +1291,7 @@ def goRelative(self, direction): motor_dev = self.taurusValueBuddy().motor_dev if motor_dev is not None: increment = direction * float(self.cb_step.currentText()) - position = float(motor_dev.getAttribute('Position').read().value) + position = float(motor_dev.getAttribute('Position').read().rvalue) target_position = position + increment motor_dev.getAttribute('Position').write(target_position) @@ -1554,7 +1542,7 @@ def updateLimits(self, limits, position=None): if self.motor_dev is not None: position_attribute = self.motor_dev.getAttribute('Position') if position is None: - position = position_attribute.read().value + position = position_attribute.read().rvalue max_value_str = position_attribute.max_value min_value_str = position_attribute.min_value try: @@ -1636,7 +1624,7 @@ def updatePosition(self, position='__no_argument__'): limit_switches = [False, False, False] if self.hasHwLimits(): limit_switches = self.motor_dev.getAttribute( - 'Limit_switches').read().value + 'Limit_switches').read().rvalue # print "update limits", limit_switches self.updateLimits(limit_switches, position=position) From 5b8e881ab8aa8fe24c08cc113d90cc0899494c58 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 27 Sep 2019 23:31:07 +0200 Subject: [PATCH 224/830] Change name from addsnap to defsnap --- src/sardana/macroserver/macros/env.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 8e40f1f2cd..59e67ab634 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -456,8 +456,8 @@ def run(self): self.print("{} ({})".format(label, full_name)) -class addsnap(Macro): - """Add item(s) to snapshot group. Accepts: +class defsnap(Macro): + """Define snapshot group item(s). Accepts: - Pool moveables: motor, pseudo motor - Pool experimental channels: counter/timer, 0D, 1D, 2D, pseudo counter - Taurus attributes From b51a3bdf913730a29df9116bda46eed37169bd30 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 27 Sep 2019 23:31:33 +0200 Subject: [PATCH 225/830] Handle cases when no pre-scan snapshot is defined --- src/sardana/macroserver/macros/env.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 59e67ab634..488ea1ead7 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -451,7 +451,11 @@ class lssnap(Macro): """ def run(self): - snapshot_items = self.getEnv("PreScanSnapshot") + try: + snapshot_items = self.getEnv("PreScanSnapshot") + except UnknownEnv: + self.output("No pre-scan snapshot") + return for full_name, label in snapshot_items: self.print("{} ({})".format(label, full_name)) @@ -484,8 +488,10 @@ def get_item_info(item): return item.fullname, item.label else: return item.full_name, item.name - - snap_items = self.getEnv("PreScanSnapshot") + try: + snap_items = self.getEnv("PreScanSnapshot") + except UnknownEnv: + snap_items = [] snap_full_names = [item[0] for item in snap_items] new_snap_items = [] for name in snap_names: From 503a8b5699b846965547542f44696fe153d3fb8a Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 27 Sep 2019 23:31:48 +0200 Subject: [PATCH 226/830] Print lssnap in the form of a table --- src/sardana/macroserver/macros/env.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 488ea1ead7..0330b81dd2 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -456,8 +456,11 @@ def run(self): except UnknownEnv: self.output("No pre-scan snapshot") return + out = List(['Snap item', 'Snap item full name']) for full_name, label in snapshot_items: - self.print("{} ({})".format(label, full_name)) + out.appendRow([label, full_name]) + for line in out.genOutput(): + self.output(line) class defsnap(Macro): From c9b4f01782985b0565a81001306dc00a0d98d4a0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 27 Sep 2019 23:31:57 +0200 Subject: [PATCH 227/830] Add udefsnap macro --- src/sardana/macroserver/macros/env.py | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 0330b81dd2..8f60281fea 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -513,3 +513,52 @@ def get_item_info(item): raise ValueError(msg) new_snap_items.append((new_full_name, new_label)) self.setEnv("PreScanSnapshot", snap_items + new_snap_items) + + +class udefsnap(Macro): + """Undefine snapshot group item(s). Without arguments undefine all. + + .. note:: + The `udefsnap` macro has been included in Sardana + on a provisional basis. Backwards incompatible changes + (up to and including its removal) may occur if + deemed necessary by the core developers. + """ + + param_def = [ + ["snap_names", [[ + "name", Type.String, None, "Name of an item to be removed " + "from the pre-scan snapshot group", + ], {"min": 0}], + None, + "Items to be remove from the pre-scan snapshot group"], + ] + + def run(self, snap_names): + if len(snap_names) == 0: + self.unsetEnv("PreScanSnapshot") + return + try: + snap_items = self.getEnv("PreScanSnapshot") + except UnknownEnv: + raise RuntimeError("no pre-scan snapshot defined") + snap_full_names = {} + for i, item in enumerate(snap_items): + snap_full_names[item[0]] = i + for name in snap_names: + obj = self.getObj(name) + if obj is None: + try: + obj = taurus.Attribute(name) + except taurus.TaurusException: + raise ValueError("item is neither Pool element not " + "Taurus attribute") + elif obj.type == "MotorGroup": + raise ValueError("MotorGroup item type is not accepted") + rm_full_name = obj.fullname + if rm_full_name not in snap_full_names.keys(): + msg = "{} not in pre-scan snapshot".format(name) + raise ValueError(msg) + i = snap_full_names[rm_full_name] + snap_items.pop(i) + self.setEnv("PreScanSnapshot", snap_items) From aba4d6c17126c99733651674130d688fbbef5c1b Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 27 Sep 2019 23:35:56 +0200 Subject: [PATCH 228/830] Add snap macros to catalogue --- doc/source/users/standard_macro_catalog.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/source/users/standard_macro_catalog.rst b/doc/source/users/standard_macro_catalog.rst index 1d5a7207f7..faf4d26543 100644 --- a/doc/source/users/standard_macro_catalog.rst +++ b/doc/source/users/standard_macro_catalog.rst @@ -117,6 +117,7 @@ list related macros * :class:`~sardana.macroserver.macros.lists.lsmac` * :class:`~sardana.macroserver.macros.lists.lsmaclib` * :class:`~sardana.macroserver.macros.env.lsgh` + * :class:`~sardana.macroserver.macros.env.lssnap` measurement configuration macros -------------------------------- @@ -126,6 +127,9 @@ measurement configuration macros * :class:`~sardana.macroserver.macros.expert.defmeas` * :class:`~sardana.macroserver.macros.expert.udefmeas` + * :class:`~sardana.macroserver.macros.expert.defsnap` + * :class:`~sardana.macroserver.macros.expert.udefsnap` + * :class:`~sardana.macroserver.macros.expert.lssnap` general hooks macros -------------------- From 837850f2b5382a029866eed18b30f524e4588592 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 27 Sep 2019 23:39:20 +0200 Subject: [PATCH 229/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 842077d7c7..8f28fa4b05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This file follows the formats and conventions from [keepachangelog.com] ### Added * Support to Python >= 3.5 (#1089, #1173, 1201) +* Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) * Instruments creation and configuration in sar_demo (#1198) ### Fixed From 97a9d786a8cc9adcdb39fd2aa955a7871d669f79 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Mon, 30 Sep 2019 10:45:32 +0200 Subject: [PATCH 230/830] Avoid taurus deprecation warnings in PCTV Adapt sardana code to avoid taurus deprecation warnings in PCTV widget --- src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py index 4c2cee4d21..142211edc7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolchannel.py @@ -64,7 +64,7 @@ class PoolChannelTV(TaurusValue): def __init__(self, parent=None, designMode=False): TaurusValue.__init__(self, parent=parent, designMode=designMode) self.setLabelWidgetClass(LabelWidgetDragsDeviceAndAttribute) - self.setLabelConfig('') + self.setLabelConfig('{dev.name}') def getDefaultExtraWidgetClass(self): return _ParentDevButton @@ -107,7 +107,7 @@ def __init__(self, parent=None, designMode=False): self._TaurusValue = TaurusValue(parent=w, designMode=designMode) self._TaurusValue.setLabelWidgetClass( LabelWidgetDragsDeviceAndAttribute) - self._TaurusValue.setLabelConfig('') + self._TaurusValue.setLabelConfig('{dev.name}') self.layout().addWidget(w) #...and a dev button next to the widget From 89e5c4891b5b172c99b573becc461beed0ea4d5c Mon Sep 17 00:00:00 2001 From: cfalcon Date: Mon, 30 Sep 2019 14:58:24 +0200 Subject: [PATCH 231/830] Avoid taurus deprecation warnings in macroexecutor and sequencer Adapt sardana code to avoid taurus deprecation warnings in macroexecutor and sequencer widgets --- .../qt/qtgui/extra_macroexecutor/common.py | 21 ++++++----- .../favouriteseditor/favouriteseditor.py | 9 +++-- .../favouriteseditor/historyviewer.py | 3 +- .../qtgui/extra_macroexecutor/macroeditor.py | 19 +++++----- .../extra_macroexecutor/macroexecutor.py | 35 +++++-------------- .../customeditors/senv.py | 6 ++-- .../macroparameterseditor.py | 13 ++++--- .../sequenceeditor/sequenceeditor.py | 34 ++++++++---------- 8 files changed, 57 insertions(+), 83 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py index 8feaa5c639..2aee0b78ea 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py @@ -31,7 +31,6 @@ from taurus.qt.qtgui.base import TaurusBaseWidget from taurus.qt.qtgui.input import TaurusAttrListComboBox from taurus.qt.qtgui.container import TaurusMainWindow -from taurus.qt.qtgui.resource import getThemeIcon, getIcon def str2bool(text): @@ -130,13 +129,13 @@ def __init__(self, parent=None, initMacroServer=None, initDoor=None): Qt.QDialog.__init__(self, parent) self.initMacroServer = initMacroServer self.initDoor = initDoor - configureAction = Qt.QAction(getThemeIcon( + configureAction = Qt.QAction(Qt.QIcon.fromTheme( "folder-open"), "Change custom macro editors paths", self) configureAction.triggered.connect(self.onReloadMacroServers) configureAction.setToolTip("Change custom macro editors paths") configureAction.setShortcut("F11") self.refreshMacroServersAction = Qt.QAction( - getThemeIcon("view-refresh"), "Reload macroservers", self) + Qt.QIcon.fromTheme("view-refresh"), "Reload macroservers", self) self.refreshMacroServersAction.triggered.connect( self.onReloadMacroServers) self.refreshMacroServersAction.setToolTip( @@ -188,8 +187,8 @@ def accept(self): def __retriveMacroServersFromDB(self): ms_stateIcons = [] - db = taurus.Database() - macroServerList = db.getValueObj().get_device_name('*', 'MacroServer') + db = taurus.Authority() + macroServerList = db.getTangoDB().get_device_name('*', 'MacroServer') for macroServer in macroServerList: #state = Device(macroServer).getState() state = None @@ -200,11 +199,11 @@ def __retriveMacroServersFromDB(self): pass icon = None if state == PyTango.DevState.ON: - icon = getIcon(":/leds/images24/ledgreen.png") + icon = Qt.QIcon("leds_images24:ledgreen.png") elif state == PyTango.DevState.FAULT: - icon = getIcon(":/leds/images24/ledred.png") + icon = Qt.QIcon("leds_images24:ledred.png") elif state is None: - icon = getIcon(":/leds/images24/ledredoff.png") + icon = Qt.QIcon("leds_images24:ledredoff.png") ms_stateIcons.append((macroServer, icon)) return ms_stateIcons @@ -263,7 +262,7 @@ def __init__(self, parent=None, designMode=False): self.registerConfigProperty( "customMacroEditorPaths", "setCustomMacroEditorPaths", "customMacroEditorPaths") self._qDoor = None - self.setWindowIcon(getIcon(":/apps/preferences-system-session.svg")) + self.setWindowIcon(Qt.QIcon("apps:preferences-system-session.svg")) toolBar = self.basicTaurusToolbar() toolBar.setIconSize(Qt.QSize(24, 24)) self.configureAction = self.createConfigureAction() @@ -315,7 +314,7 @@ def setModel(self, model): self.setWindowTitle(Qt.QApplication.applicationName() + ": " + model) def createConfigureAction(self): - configureAction = Qt.QAction(getThemeIcon( + configureAction = Qt.QAction(Qt.QIcon.fromTheme( "preferences-system-session"), "Change configuration", self) configureAction.triggered.connect(self.changeConfiguration) configureAction.setToolTip("Configuring MacroServer and Door") @@ -323,7 +322,7 @@ def createConfigureAction(self): return configureAction def createCustomMacroEditorPathsAction(self): - configureAction = Qt.QAction(getThemeIcon( + configureAction = Qt.QAction(Qt.QIcon.fromTheme( "folder-open"), "Change custom macro editors paths", self) configureAction.triggered.connect(self.onCustomMacroEditorPaths) configureAction.setToolTip("Change custom macro editors paths") diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py index 9787076695..68fe626764 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py @@ -29,7 +29,6 @@ import copy from taurus.external.qt import Qt, compat -from taurus.qt.qtgui.resource import getIcon from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtcore.configuration import BaseConfigurableClass from .model import MacrosListModel @@ -110,27 +109,27 @@ def __init__(self, parent=None): self.setSelectionMode(Qt.QListView.ExtendedSelection) - self.removeAction = Qt.QAction(getIcon(":/actions/list-remove.svg"), + self.removeAction = Qt.QAction(Qt.QIcon("actions:list-remove.svg"), "Remove from favourites", self) self.removeAction.triggered.connect(self.removeMacros) self.removeAction.setToolTip( "Clicking this button will remove selected macros " "from favourites.") - self.removeAllAction = Qt.QAction(getIcon(":/places/user-trash.svg"), + self.removeAllAction = Qt.QAction(Qt.QIcon("places:user-trash.svg"), "Remove all from favourites", self) self.removeAllAction.triggered.connect(self.removeAllMacros) self.removeAllAction.setToolTip( "Clicking this button will remove all macros from favourites.") - self.moveUpAction = Qt.QAction(getIcon(":/actions/go-up.svg"), + self.moveUpAction = Qt.QAction(Qt.QIcon("actions:go-up.svg"), "Move up", self) self.moveUpAction.triggered.connect(self.upMacro) self.moveUpAction.setToolTip( "Clicking this button will move the macro up " "in the favourites hierarchy.") - self.moveDownAction = Qt.QAction(getIcon(":/actions/go-down.svg"), + self.moveDownAction = Qt.QAction(Qt.QIcon("actions:go-down.svg"), "Move down", self) self.moveDownAction.triggered.connect(self.downMacro) self.moveDownAction.setToolTip( diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py index eb4026d7e2..6300fdaf38 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py @@ -29,7 +29,6 @@ import copy from taurus.external.qt import Qt, compat -from taurus.qt.qtgui.resource import getIcon from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtcore.configuration import BaseConfigurableClass from .model import MacrosListModel @@ -120,7 +119,7 @@ def __init__(self, parent=None): Qt.QListView.__init__(self, parent) self.setSelectionMode(Qt.QListView.SingleSelection) - self.removeAllAction = Qt.QAction(getIcon(":/places/user-trash.svg"), + self.removeAllAction = Qt.QAction(Qt.QIcon("places:user-trash.svg"), "Remove all from history", self) self.removeAllAction.triggered.connect(self.removeAllMacros) self.removeAllAction.setToolTip( diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroeditor.py index 042eed69e3..74d88efd7c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroeditor.py @@ -32,8 +32,6 @@ raise ImportError('MacroEditor requires Qsci (qscintilla): %r', e) -from taurus.qt.qtgui.resource import getThemeIcon - class MacroEditor(Qsci.QsciScintilla): __pyqtSignals__ = ("modelChanged(const QString &)",) @@ -54,36 +52,39 @@ def __init__(self, parent=None, designMode=False): self.pythonLexer.setAPIs(self.api) self.textEdit.setLexer(self.pythonLexer) - self.newAction = Qt.QAction(getThemeIcon("document-new"), "New", self) + self.newAction = Qt.QAction(Qt.QIcon.fromTheme("document-new"), "New", + self) self.newAction.triggered.connect(self.newFile) self.newAction.setToolTip("Create new file") self.newAction.setShortcut("Ctrl+N") self.openAction = Qt.QAction( - getThemeIcon("document-open"), "Open", self) + Qt.QIcon.fromTheme("document-open"), "Open", self) self.openAction.triggered.connect(self.openFile) self.openAction.setToolTip("Open existing file") self.openAction.setShortcut("Ctrl+O") self.saveAction = Qt.QAction( - getThemeIcon("document-save"), "Save", self) + Qt.QIcon.fromTheme("document-save"), "Save", self) self.saveAction.triggered.connect(self.saveFile) self.saveAction.setToolTip("Save document to disk") self.saveAction.setShortcut("Ctrl+S") - self.saveAsAction = Qt.QAction(getThemeIcon( + self.saveAsAction = Qt.QAction(Qt.QIcon.fromTheme( "document-save-as"), "Save as...", self) self.saveAction.triggered.connect(self.saveFile) self.saveAsAction.setToolTip("Save document under a new name") - self.cutAction = Qt.QAction(getThemeIcon("edit-cut"), "Cut", self) + self.cutAction = Qt.QAction(Qt.QIcon.fromTheme("edit-cut"), "Cut", + self) self.cutAction.triggered.connect(self.cut) self.cutAction.setToolTip( "Cut current selection's contents to the clipboard") self.cutAction.setShortcut("Ctrl+X") self.cutAction.setEnabled(False) - self.copyAction = Qt.QAction(getThemeIcon("edit-copy"), "Copy", self) + self.copyAction = Qt.QAction(Qt.QIcon.fromTheme("edit-copy"), "Copy", + self) self.copyAction.triggered.connect(self.copy) self.copyAction.setToolTip( "Copy current selection's contents to the clipboard") @@ -91,7 +92,7 @@ def __init__(self, parent=None, designMode=False): self.copyAction.setEnabled(False) self.pasteAction = Qt.QAction( - getThemeIcon("edit-paste"), "Paste", self) + Qt.QIcon.fromTheme("edit-paste"), "Paste", self) self.pasteAction.triggered.connect(self.paste) self.pasteAction.setToolTip( "Paste the clipboard's contents into the current selection") diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 26cd8588db..3b65d9b553 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -37,7 +37,6 @@ from taurus.qt.qtgui.container import TaurusWidget, TaurusMainWindow, TaurusBaseContainer from taurus.qt.qtgui.display import TaurusLed from taurus.qt.qtgui.dialog import TaurusMessageBox -from taurus.qt.qtgui.resource import getIcon, getThemeIcon import sardana from sardana.taurus.core.tango.sardana import macro @@ -625,20 +624,20 @@ def __init__(self, parent=None, designMode=False): self.setLayout(Qt.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) - self.addToFavouritesAction = Qt.QAction(getThemeIcon( + self.addToFavouritesAction = Qt.QAction(Qt.QIcon.fromTheme( "software-update-available"), "Add to favourites", self) self.addToFavouritesAction.triggered.connect(self.onAddToFavourites) self.addToFavouritesAction.setToolTip("Add to favourites") self.stopMacroAction = Qt.QAction( - getIcon(":/actions/media_playback_stop.svg"), "Stop macro", self) + Qt.QIcon("actions:media_playback_stop.svg"), "Stop macro", self) self.stopMacroAction.triggered.connect(self.onStopMacro) self.stopMacroAction.setToolTip("Stop macro") self.pauseMacroAction = Qt.QAction( - getIcon(":/actions/media_playback_pause.svg"), "Pause macro", self) + Qt.QIcon("actions:media_playback_pause.svg"), "Pause macro", self) self.pauseMacroAction.triggered.connect(self.onPauseMacro) self.pauseMacroAction.setToolTip("Pause macro") self.playMacroAction = Qt.QAction( - getIcon(":/actions/media_playback_start.svg"), "Start macro", self) + Qt.QIcon("actions:media_playback_start.svg"), "Start macro", self) self.playMacroAction.triggered.connect(self.onPlayMacro) self.playMacroAction.setToolTip("Start macro") actionsLayout = Qt.QHBoxLayout() @@ -738,7 +737,7 @@ def macroId(self): def contextMenuEvent(self, event): menu = Qt.QMenu() - action = menu.addAction(getThemeIcon( + action = menu.addAction(Qt.QIcon.fromTheme( "view-refresh"), "Check door state", self.checkDoorState) menu.exec_(event.globalPos()) @@ -879,11 +878,7 @@ def onDoorChanged(self, doorName): return self.doorStateLed.setModel(self.doorName() + "/State") door = Device(doorName) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.stateObj.rvalue if doorState == PyTango.DevState.ON: self.playMacroAction.setText("Start macro") self.playMacroAction.setToolTip("Start macro") @@ -893,11 +888,7 @@ def onDoorChanged(self, doorName): def onPlayMacro(self): door = Device(self.doorName()) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.getState() if doorState == PyTango.DevState.ON or doorState == PyTango.DevState.ALARM: self.setFocus() paramEditorModel = self.paramEditorModel() @@ -921,11 +912,7 @@ def onPlayMacro(self): def onStopMacro(self): door = Device(self.doorName()) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.getState() if doorState in (PyTango.DevState.RUNNING, PyTango.DevState.STANDBY): door.command_inout("StopMacro") @@ -935,11 +922,7 @@ def onStopMacro(self): def onPauseMacro(self): door = Device(self.doorName()) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.getState() if doorState == PyTango.DevState.RUNNING: door.command_inout("PauseMacro") diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py index 5b95eba76f..3af96a985f 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py @@ -27,9 +27,7 @@ from taurus import Database from taurus.core.taurusbasetypes import TaurusElementType from taurus.core.tango.tangodatabase import TangoAttrInfo -from taurus.qt.qtgui.input import TaurusAttrListComboBox from taurus.qt.qtgui.tree import TaurusDbTreeWidget -from taurus.qt.qtgui.resource import getThemeIcon from sardana.taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor.macroparameterseditor import MacroParametersEditor from sardana.taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor.parameditors import LineEditParam, ParamBase, ComboBoxParam, CheckBoxParam, DirPathParam, MSAttrListComboBoxParam from sardana.taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor.model import ParamEditorModel @@ -135,9 +133,9 @@ def __init__(self, parent=None, paramModel=None): self.layout().setContentsMargins(0, 0, 0, 0) addNewColumnButton = Qt.QPushButton( - getThemeIcon("list-add"), "Add new column...", self) + Qt.QIcon.fromTheme("list-add"), "Add new column...", self) removeSelectedColumnsButton = Qt.QPushButton( - getThemeIcon("list-remove"), "Remove selected...", self) + Qt.QIcon.fromTheme("list-remove"), "Remove selected...", self) buttonsLayout = Qt.QHBoxLayout() buttonsLayout.addWidget(addNewColumnButton) buttonsLayout.addWidget(removeSelectedColumnsButton) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/macroparameterseditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/macroparameterseditor.py index de58b3125c..1d62c27983 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/macroparameterseditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/macroparameterseditor.py @@ -27,12 +27,10 @@ macroparameterseditor.py: """ import sys -import inspect import glob from taurus.external.qt import Qt from taurus.core.util.singleton import Singleton -from taurus.qt.qtgui.resource import getThemeIcon from sardana.taurus.core.tango.sardana import macro from sardana.taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor.delegate import ParamEditorDelegate @@ -116,30 +114,31 @@ def __init__(self, parent=None, designMode=False): # self.setTabKeyNavigation(True) self.setEditTriggers(Qt.QAbstractItemView.AllEditTriggers) - self.addAction = Qt.QAction(getThemeIcon( + self.addAction = Qt.QAction(Qt.QIcon.fromTheme( "list-add"), "Add new repetition", self) self.addAction.triggered.connect(self.onAddRepeat) self.addAction.setToolTip( "Clicking this button will add new repetition to current parameter.") - self.deleteAction = Qt.QAction(getThemeIcon( + self.deleteAction = Qt.QAction(Qt.QIcon.fromTheme( "list-remove"), "Remove repetition", self) self.deleteAction.triggered.connect(self.onDelRepeat) self.deleteAction.setToolTip( "Clicking this button will remove current repetition.") - self.moveUpAction = Qt.QAction(getThemeIcon("go-up"), "Move up", self) + self.moveUpAction = Qt.QAction(Qt.QIcon.fromTheme("go-up"), "Move up", + self) self.moveUpAction.triggered.connect(self.onUpRepeat) self.moveUpAction.setToolTip( "Clicking this button will move current repetition up.") self.moveDownAction = Qt.QAction( - getThemeIcon("go-down"), "Move down", self) + Qt.QIcon.fromTheme("go-down"), "Move down", self) self.moveDownAction.triggered.connect(self.onDownRepeat) self.moveDownAction.setToolTip( "Clicking this button will move current repetition down.") - self.duplicateAction = Qt.QAction(getThemeIcon("edit-copy"), + self.duplicateAction = Qt.QAction(Qt.QIcon.fromTheme("edit-copy"), "Duplicate", self) self.duplicateAction.triggered.connect(self.onDuplicateRepeat) msg = "Clicking this button will duplicate the given node." diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 7b94592390..9d563c849a 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -39,7 +39,6 @@ from taurus.qt.qtcore.configuration import BaseConfigurableClass from taurus.qt.qtgui.display import TaurusLed from taurus.qt.qtgui.dialog import TaurusMessageBox -from taurus.qt.qtgui.resource import getIcon, getThemeIcon import sardana from sardana.taurus.qt.qtgui.extra_macroexecutor.common import \ @@ -104,30 +103,31 @@ def __init__(self, parent=None): self.setDropIndicatorShown(True) self.deleteAction = Qt.QAction( - getThemeIcon("list-remove"), "Remove macro", self) + Qt.QIcon.fromTheme("list-remove"), "Remove macro", self) self.deleteAction.triggered.connect(self.deleteMacro) self.deleteAction.setToolTip( "Clicking this button will remove current macro.") - self.moveUpAction = Qt.QAction(getThemeIcon("go-up"), "Move up", self) + self.moveUpAction = Qt.QAction(Qt.QIcon.fromTheme("go-up"), "Move up", + self) self.moveUpAction.triggered.connect(self.upMacro) self.moveUpAction.setToolTip( "Clicking this button will move current macro up.") self.moveDownAction = Qt.QAction( - getThemeIcon("go-down"), "Move down", self) + Qt.QIcon.fromTheme("go-down"), "Move down", self) self.moveDownAction.triggered.connect(self.downMacro) self.moveDownAction.setToolTip( "Clicking this button will move current macro down.") self.moveLeftAction = Qt.QAction( - getThemeIcon("go-previous"), "Move left", self) + Qt.QIcon.fromTheme("go-previous"), "Move left", self) self.moveLeftAction.triggered.connect(self.leftMacro) self.moveLeftAction.setToolTip( "Clicking this button will move current macro to the left.") self.moveRightAction = Qt.QAction( - getThemeIcon("go-next"), "Move right", self) + Qt.QIcon.fromTheme("go-next"), "Move right", self) self.moveRightAction.triggered.connect(self.rightMacro) self.moveRightAction.setToolTip( "Clicking this button will move current macro to the right.") @@ -365,7 +365,7 @@ def __init__(self, parent=None, designMode=False): actionsLayout = Qt.QHBoxLayout() actionsLayout.setContentsMargins(0, 0, 0, 0) self.newSequenceAction = Qt.QAction( - getThemeIcon("document-new"), "New", self) + Qt.QIcon.fromTheme("document-new"), "New", self) self.newSequenceAction.triggered.connect(self.onNewSequence) self.newSequenceAction.setToolTip("New sequence") self.newSequenceAction.setEnabled(False) @@ -374,7 +374,7 @@ def __init__(self, parent=None, designMode=False): actionsLayout.addWidget(newSequenceButton) self.openSequenceAction = Qt.QAction( - getThemeIcon("document-open"), "Open...", self) + Qt.QIcon.fromTheme("document-open"), "Open...", self) self.openSequenceAction.triggered.connect(self.onOpenSequence) self.openSequenceAction.setToolTip("Open sequence...") openSequenceButton = Qt.QToolButton() @@ -382,7 +382,7 @@ def __init__(self, parent=None, designMode=False): actionsLayout.addWidget(openSequenceButton) self.saveSequenceAction = Qt.QAction( - getThemeIcon("document-save"), "Save...", self) + Qt.QIcon.fromTheme("document-save"), "Save...", self) self.saveSequenceAction.triggered.connect(self.onSaveSequence) self.saveSequenceAction.setToolTip("Save sequence...") self.saveSequenceAction.setEnabled(False) @@ -391,7 +391,7 @@ def __init__(self, parent=None, designMode=False): actionsLayout.addWidget(saveSequenceButton) self.stopSequenceAction = Qt.QAction( - getIcon(":/actions/media_playback_stop.svg"), "Stop", self) + Qt.QIcon("actions:media_playback_stop.svg"), "Stop", self) self.stopSequenceAction.triggered.connect(self.onStopSequence) self.stopSequenceAction.setToolTip("Stop sequence") stopSequenceButton = Qt.QToolButton() @@ -399,7 +399,7 @@ def __init__(self, parent=None, designMode=False): actionsLayout.addWidget(stopSequenceButton) self.pauseSequenceAction = Qt.QAction( - getIcon(":/actions/media_playback_pause.svg"), "Pause", self) + Qt.QIcon("actions:media_playback_pause.svg"), "Pause", self) self.pauseSequenceAction.triggered.connect(self.onPauseSequence) self.pauseSequenceAction.setToolTip("Pause sequence") pauseSequenceButton = Qt.QToolButton() @@ -407,7 +407,7 @@ def __init__(self, parent=None, designMode=False): actionsLayout.addWidget(pauseSequenceButton) self.playSequenceAction = Qt.QAction( - getIcon(":/actions/media_playback_start.svg"), "Play", self) + Qt.QIcon("actions:media_playback_start.svg"), "Play", self) self.playSequenceAction.triggered.connect(self.onPlaySequence) self.playSequenceAction.setToolTip("Play sequence") playSequenceButton = Qt.QToolButton() @@ -443,7 +443,7 @@ def __init__(self, parent=None, designMode=False): macroLayout.addWidget(self.macroComboBox) self.addMacroAction = Qt.QAction( - getThemeIcon("list-add"), "Add macro...", self) + Qt.QIcon.fromTheme("list-add"), "Add macro...", self) self.addMacroAction.triggered.connect(self.onAdd) self.addMacroAction.setToolTip( "Clicking this button will add selected macro") @@ -503,7 +503,7 @@ def __init__(self, parent=None, designMode=False): def contextMenuEvent(self, event): menu = Qt.QMenu() - action = menu.addAction(getThemeIcon( + action = menu.addAction(Qt.QIcon.fromTheme( "view-refresh"), "Check door state", self.checkDoorState) menu.exec_(event.globalPos()) @@ -813,11 +813,7 @@ def onDoorChanged(self, doorName): return self.doorStateLed.setModel(self.doorName() + "/State") door = Device(doorName) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.stateObj.rvalue if doorState == PyTango.DevState.ON: self.playSequenceAction.setText("Start sequence") self.playSequenceAction.setToolTip("Start sequence") From 4f4bfa83526390939b410221b9069ba4ecad4018 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Mon, 30 Sep 2019 15:19:11 +0200 Subject: [PATCH 232/830] Avoid taurus deprecation warnings in extra_sardana Avoid taurus deprecation warnings in other extra_sardana widgets --- .../taurus/qt/qtgui/extra_sardana/cmdline.py | 10 ++++------ .../qt/qtgui/extra_sardana/controllertree.py | 18 +++++++----------- .../qt/qtgui/extra_sardana/environment.py | 4 ++-- .../taurus/qt/qtgui/extra_sardana/macrotree.py | 15 ++++++--------- 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py index 8c85078b34..5ab0a22b11 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py @@ -29,7 +29,6 @@ __docformat__ = 'restructuredtext' from taurus.external.qt import Qt -from taurus.qt.qtgui.resource import getIcon, getThemeIcon class CommandLineHistory(list): @@ -63,14 +62,13 @@ def __init__(self, qt_parent=None, designMode=False): self._cmdLine.setEditable(True) self._applyButton = Qt.QToolButton() - self._applyButton.setIcon( - getIcon(":/actions/media_playback_start.svg")) + self._applyButton.setIcon(Qt.QIcon("actions:media_playback_start.svg")) self._stopButton = Qt.QToolButton() - self._stopButton.setIcon(getIcon(":/actions/media_playback_stop.svg")) + self._stopButton.setIcon(Qt.QIcon("actions:media_playback_stop.svg")) self._clearButton = Qt.QToolButton() - self._clearButton.setIcon(getThemeIcon("edit-clear")) + self._clearButton.setIcon(Qt.QIcon.fromTheme("edit-clear")) l.addWidget(self._detailsButton, 0) l.addWidget(self._cmdLine, 1) @@ -101,7 +99,7 @@ def main(): app_name="Taurus command line demo", app_version="1.0", org_domain="Taurus", org_name="Tango community") - w = TaurusCommandLineWidget() + w =TaurusCommandLineWidget() w.show() if owns_app: diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py b/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py index 2824f77b1f..be09324393 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/controllertree.py @@ -29,16 +29,12 @@ __docformat__ = 'restructuredtext' -import sys -import os - import taurus.core from taurus.core.util.enumeration import Enumeration from taurus.external.qt import Qt from taurus.qt.qtcore.mimetypes import TAURUS_MODEL_MIME_TYPE, TAURUS_MODEL_LIST_MIME_TYPE from taurus.qt.qtcore.model import TaurusBaseTreeItem, TaurusBaseModel, TaurusBaseProxyModel from taurus.qt.qtgui.tree import TaurusBaseTreeWidget -from taurus.qt.qtgui.resource import getThemeIcon, getIcon PoolControllerView = Enumeration( "PoolControllerView", ("ControllerModule", "ControllerClass", "Unknown")) @@ -46,10 +42,10 @@ def getElementTypeIcon(t): if t == PoolControllerView.ControllerModule: - return getIcon(":/python-file.png") + return Qt.QIcon(":python-file.png") elif t == PoolControllerView.ControllerClass: - return getIcon(":/python.png") - return getIcon(":/tango.png") + return Qt.QIcon(":python.png") + return Qt.QIcon(":tango.png") def getElementTypeSize(t): @@ -94,7 +90,7 @@ def toolTip(self): return "The controller module '%s'" % self.display() def icon(self): - return getIcon(":/python-file.png") + return Qt.QIcon(":python-file.png") class ControllerTreeItem(ControllerBaseTreeItem): @@ -113,7 +109,7 @@ def toolTip(self): return self._itemData.doc def icon(self): - return getIcon(":/python.png") + return Qt.QIcon(":python.png") class ControllerBaseModel(TaurusBaseModel): @@ -259,13 +255,13 @@ class ControllerClassTreeWidget(TaurusBaseTreeWidget): KnownPerspectives = {PoolControllerView.ControllerModule: { "label": "By module", - "icon": ":/python-file.png", + "icon": ":python-file.png", "tooltip": "View by controller module", "model": [ControllerModuleModelProxy, ControllerModuleModel], }, PoolControllerView.ControllerClass: { "label": "By controller", - "icon": ":/python.png", + "icon": ":python.png", "tooltip": "View by controller class", "model": [PlainControllerModelProxy, PlainControllerModel], } diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/environment.py b/src/sardana/taurus/qt/qtgui/extra_sardana/environment.py index 19b1509e82..5cc583f177 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/environment.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/environment.py @@ -40,7 +40,7 @@ class SardanaEnvironmentTreeWidget(TaurusBaseTreeWidget): KnownPerspectives = {"Type": { "label": "By key", - "icon": ":/python-file.png", + "icon": ":python-file.png", "tooltip": "View elements by key", "model": [SardanaEnvironmentModel], }, @@ -55,7 +55,7 @@ def getQtDesignerPluginInfo(cls): ret = TaurusBaseTreeWidget.getQtDesignerPluginInfo() ret['module'] = 'taurus.qt.qtgui.extra_sardana' ret['group'] = 'Taurus Sardana' - ret['icon'] = ":/designer/listview.png" + ret['icon'] = "designer:listview.png" return ret diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py b/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py index 00f53add63..2ff01d2938 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/macrotree.py @@ -37,9 +37,6 @@ from taurus.qt.qtcore.model import TaurusBaseTreeItem, TaurusBaseModel, \ TaurusBaseProxyModel from taurus.qt.qtgui.tree import TaurusBaseTreeWidget -from taurus.qt.qtgui.resource import getIcon - -from sardana.taurus.core.tango.sardana.macro import MacroInfo MacroView = Enumeration("MacroView", ("MacroModule", "Macro", "Unknown")) @@ -47,10 +44,10 @@ def getElementTypeIcon(t): if t == MacroView.MacroModule: - return getIcon(":/python-file.png") + return Qt.QIcon(":python-file.png") elif t == MacroView.Macro: - return getIcon(":/python.png") - return getIcon(":/tango.png") + return Qt.QIcon(":python.png") + return Qt.QIcon(":tango.png") def getElementTypeSize(t): @@ -96,7 +93,7 @@ def toolTip(self, index): return "The macro module '%s'" % self.display() def icon(self, index): - return getIcon(":/python-file.png") + return Qt.QIcon(":python-file.png") class MacroTreeItem(MacroTreeBaseItem): @@ -115,7 +112,7 @@ def toolTip(self, index): return self._itemData.doc def icon(self, index): - return getIcon(":/python.png") + return Qt.QIcon(":python.png") class MacroBaseModel(TaurusBaseModel): @@ -236,7 +233,7 @@ class MacroTreeWidget(TaurusBaseTreeWidget): KnownPerspectives = {MacroView.MacroModule: { "label": "By module", - "icon": ":/python-file.png", + "icon": ":python-file.png", "tooltip": "View by macro module", "model": [MacroModuleModelProxy, MacroModuleModel], }, From 0aea2aa318d9d61138fff6e913092a60ccdf9851 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Mon, 30 Sep 2019 15:32:39 +0200 Subject: [PATCH 233/830] Remove try/except forks for Taurus4 compatibility --- src/sardana/macroserver/macros/hkl.py | 6 +---- .../macroserver/macros/test/macroexecutor.py | 6 +---- src/sardana/macroserver/scan/gscan.py | 6 +---- src/sardana/pool/pool.py | 7 +----- src/sardana/pool/poolbasegroup.py | 7 +----- src/sardana/pool/poolmeasurementgroup.py | 8 ++----- src/sardana/spock/magic.py | 13 +++------- src/sardana/spock/spockms.py | 9 +++---- src/sardana/taurus/core/tango/sardana/pool.py | 17 ++++--------- .../extra_macroexecutor/macroexecutor.py | 6 +---- .../sequenceeditor/sequenceeditor.py | 24 ++++--------------- 11 files changed, 22 insertions(+), 87 deletions(-) diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py index 2e5443a3b3..1f08299ade 100644 --- a/src/sardana/macroserver/macros/hkl.py +++ b/src/sardana/macroserver/macros/hkl.py @@ -1662,11 +1662,7 @@ def run(self, flagprint): while(moving): moving = 0 for angle in self.angle_names: - # TODO: For Taurus 4 / Taurus 3 compatibility - if hasattr(mot_dev, "stateObj"): - angle_state = tmp_dev[angle].stateObj.read().rvalue - else: - angle_state = tmp_dev[angle].state() + angle_state = tmp_dev[angle].stateObj.read().rvalue if angle_state == 6: moving = 1 if flagprint == 1: diff --git a/src/sardana/macroserver/macros/test/macroexecutor.py b/src/sardana/macroserver/macros/test/macroexecutor.py index 7f62a53f13..5658a9a60b 100644 --- a/src/sardana/macroserver/macros/test/macroexecutor.py +++ b/src/sardana/macroserver/macros/test/macroexecutor.py @@ -303,11 +303,7 @@ def getMacroExecutor(self, door_name=None): # For the moment I implement it by calling an internal member of # TaurusManager from taurus.core import TaurusManager - try: - scheme = TaurusManager()._get_scheme(door_name) - except AttributeError: - # TODO: For Taurus 4 compatibility - scheme = TaurusManager().getScheme(door_name) + scheme = TaurusManager().getScheme(door_name) #====================================================================== if scheme == 'tango': diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index f91bed9a42..2d7da5ac5f 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2482,11 +2482,7 @@ def _go_through_waypoints(self): timeout = 15 measurement_group.waitFinish(timeout=timeout, id=mg_id) finally: - # TODO: For Taurus 4 / Taurus 3 compatibility - if hasattr(measurement_group, "stateObj"): - state = measurement_group.stateObj.read().rvalue - else: - state = measurement_group.state() + state = measurement_group.stateObj.read().rvalue if state == PyTango.DevState.MOVING: measurement_group.Stop() if end_move: diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py index b6bde77976..eaa2011d42 100644 --- a/src/sardana/pool/pool.py +++ b/src/sardana/pool/pool.py @@ -35,12 +35,7 @@ import os.path import logging.handlers -try: - from taurus.core.taurusvalidator import AttributeNameValidator as\ - TangoAttributeNameValidator -except ImportError: - # TODO: For Taurus 4 compatibility - from taurus.core.tango.tangovalidator import TangoAttributeNameValidator +from taurus.core.tango.tangovalidator import TangoAttributeNameValidator from taurus.core.util.containers import CaselessDict from sardana import InvalidId, ElementType, TYPE_ACQUIRABLE_ELEMENTS, \ diff --git a/src/sardana/pool/poolbasegroup.py b/src/sardana/pool/poolbasegroup.py index cf09ed1401..6ef0687ac9 100644 --- a/src/sardana/pool/poolbasegroup.py +++ b/src/sardana/pool/poolbasegroup.py @@ -30,12 +30,7 @@ __docformat__ = 'restructuredtext' -try: - from taurus.core.taurusvalidator import AttributeNameValidator as\ - TangoAttributeNameValidator -except ImportError: - # TODO: For Taurus 4 compatibility - from taurus.core.tango.tangovalidator import TangoAttributeNameValidator +from taurus.core.tango.tangovalidator import TangoAttributeNameValidator from sardana import State, ElementType, TYPE_PHYSICAL_ELEMENTS from sardana.pool.poolexternal import PoolExternalObject diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index d5e1abcc02..8278813869 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -35,12 +35,8 @@ import threading import weakref -try: - from taurus.core.taurusvalidator import AttributeNameValidator as\ - TangoAttributeNameValidator -except ImportError: - # TODO: For Taurus 4 compatibility - from taurus.core.tango.tangovalidator import TangoAttributeNameValidator + +from taurus.core.tango.tangovalidator import TangoAttributeNameValidator from sardana import State, ElementType, TYPE_EXP_CHANNEL_ELEMENTS from sardana.sardanaevent import EventType diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index 8a39b55d5c..4bf2181562 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -45,11 +45,7 @@ def expconf(self, parameter_s=''): print("Error importing ExpDescriptionEditor " "(hint: is taurus extra_sardana installed?)") return - try: - doorname = get_door().name() - except TypeError: - # TODO: For Taurus 4 adaptation - doorname = get_door().fullname + doorname = get_door().fullname # ======================================================================= # ugly hack to avoid ipython/qt thread problems #e.g. see # https://sourceforge.net/p/sardana/tickets/10/ @@ -96,11 +92,8 @@ def showscan(self, parameter_s=''): print("Error importing ShowScanOnline") print(e) return - try: - doorname = get_door().name() - except TypeError: - # TODO: For Taurus 4 adaptation - doorname = get_door().fullname + + doorname = get_door().fullname # =============================================================== # ugly hack to avoid ipython/qt thread problems #e.g. see # https://sourceforge.net/p/sardana/tickets/10/ diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index fcb85e2ef2..82beb54b07 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -53,12 +53,9 @@ from sardana.taurus.core.tango.sardana.macroserver import BaseDoor, BaseMacroServer BaseGUIViewer = object -try: - RUNNING_STATE = TaurusSWDevState.Running -except RuntimeError: - # TODO: For Taurus 4 compatibility - from taurus.core import TaurusDevState - RUNNING_STATE = TaurusDevState.Ready + +from taurus.core import TaurusDevState +RUNNING_STATE = TaurusDevState.Ready class GUIViewer(BaseGUIViewer): diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 508f7a7422..23e6e04c7d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -56,12 +56,7 @@ from taurus import Factory, Device, Attribute from taurus.core.taurusbasetypes import TaurusEventType -try: - from taurus.core.taurusvalidator import AttributeNameValidator as \ - TangoAttributeNameValidator -except ImportError: - # TODO: For Taurus 4 compatibility - from taurus.core.tango.tangovalidator import TangoAttributeNameValidator +from taurus.core.tango.tangovalidator import TangoAttributeNameValidator from taurus.core.util.log import Logger from taurus.core.util.codecs import CodecFactory from taurus.core.util.containers import CaselessDict @@ -554,13 +549,9 @@ def _information(self, tab=' '): indent = "\n" + tab + 10 * ' ' msg = [self.getName() + ":"] try: - # TODO: For Taurus 4 / Taurus 3 compatibility - if hasattr(self, "stateObj"): - state_value = self.stateObj.read().rvalue - # state_value is DevState enumeration (IntEnum) - state = state_value.name.capitalize() - else: - state = str(self.state()).capitalize() + state_value = self.stateObj.read().rvalue + # state_value is DevState enumeration (IntEnum) + state = state_value.name.capitalize() except DevFailed as df: if len(df.args): state = df.args[0].desc diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 3b65d9b553..a0e00716a2 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -743,11 +743,7 @@ def contextMenuEvent(self, event): def checkDoorState(self): door = Device(self.doorName()) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.getState() if doorState == PyTango.DevState.RUNNING: self.playMacroAction.setEnabled(False) self.pauseMacroAction.setEnabled(True) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 9d563c849a..dd761e7b94 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -513,11 +513,7 @@ def checkDoorState(self): about the macro status does not reach the sequencer widget.""" door = Device(self.doorName()) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.getState() if doorState == PyTango.DevState.RUNNING: self.playSequenceAction.setEnabled(False) self.pauseSequenceAction.setEnabled(True) @@ -679,11 +675,7 @@ def onSaveSequence(self): def onPlaySequence(self): door = Device(self.doorName()) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.getState() if (doorState == PyTango.DevState.ON or doorState == PyTango.DevState.ALARM): first, last, ids = self.tree.prepareMacroIds() @@ -704,11 +696,7 @@ def onPlaySequence(self): def onStopSequence(self): door = Device(self.doorName()) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.getState() if doorState in (PyTango.DevState.RUNNING, PyTango.DevState.STANDBY): door.command_inout("StopMacro") else: @@ -721,11 +709,7 @@ def onStopSequence(self): def onPauseSequence(self): door = Device(self.doorName()) - try: - doorState = door.state() - except TypeError: - # TODO: For Taurus 4 adaptation - doorState = door.getState() + doorState = door.getState() if doorState == PyTango.DevState.RUNNING: door.command_inout("PauseMacro") else: From 67937776858075bf5fc500a8b8350979248a4cea Mon Sep 17 00:00:00 2001 From: cfalcon Date: Mon, 30 Sep 2019 15:41:19 +0200 Subject: [PATCH 234/830] Avoid taurus deprecation warnings in sardana.taurus.core.tango --- src/sardana/taurus/core/tango/sardana/macroserver.py | 4 ++-- src/sardana/taurus/core/tango/sardana/pool.py | 4 ++-- src/sardana/taurus/core/tango/sardana/sardana.py | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index f8087c160c..b004cd3909 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -651,9 +651,9 @@ def recordDataReceived(self, s, t, v): return self._processRecordData(v) def _processRecordData(self, data): - if data is None or data.value is None: + if data is None or data.rvalue is None: return - data = data.value + data = data.rvalue size = len(data[1]) if size == 0: diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 23e6e04c7d..134fb7eda8 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2279,10 +2279,10 @@ def on_elements_changed(self, evt_src, evt_type, evt_value): elif evt_type not in CHANGE_EVT_TYPES: return try: - elems = CodecFactory().decode(evt_value.value) + elems = CodecFactory().decode(evt_value.rvalue) except: self.error("Could not decode element info") - self.info("value: '%s'", evt_value.value) + self.info("value: '%s'", evt_value.rvalue) self.debug("Details:", exc_info=1) return elements = self.getElementsInfo() diff --git a/src/sardana/taurus/core/tango/sardana/sardana.py b/src/sardana/taurus/core/tango/sardana/sardana.py index 2e9ccbec73..1b0fc8c9b2 100644 --- a/src/sardana/taurus/core/tango/sardana/sardana.py +++ b/src/sardana/taurus/core/tango/sardana/sardana.py @@ -285,9 +285,8 @@ def get_model(self): def get_icon(self): # fake data ############### - import taurus.qt.qtgui.resource - - return taurus.qt.qtgui.resource.getIcon(":/designer/extra_motor.png") + from taurus.external.qt import Qt + return Qt.QIcon("designer:extra_motor.png") def get_organization(self): # fake data ############### From 3ddf373f3e2f254b6b168a375c5c87eedbb9250d Mon Sep 17 00:00:00 2001 From: cfalcon Date: Mon, 30 Sep 2019 16:05:36 +0200 Subject: [PATCH 235/830] Fix Flake8 errors --- .../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 4 ++-- .../extra_macroexecutor/sequenceeditor/sequenceeditor.py | 4 ++-- src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py | 2 +- src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index a0e00716a2..074f66a263 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -737,8 +737,8 @@ def macroId(self): def contextMenuEvent(self, event): menu = Qt.QMenu() - action = menu.addAction(Qt.QIcon.fromTheme( - "view-refresh"), "Check door state", self.checkDoorState) + menu.addAction(Qt.QIcon.fromTheme("view-refresh"), "Check door state", + self.checkDoorState) menu.exec_(event.globalPos()) def checkDoorState(self): diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index dd761e7b94..97becda18d 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -503,8 +503,8 @@ def __init__(self, parent=None, designMode=False): def contextMenuEvent(self, event): menu = Qt.QMenu() - action = menu.addAction(Qt.QIcon.fromTheme( - "view-refresh"), "Check door state", self.checkDoorState) + menu.addAction(Qt.QIcon.fromTheme("view-refresh"), "Check door state", + self.checkDoorState) menu.exec_(event.globalPos()) def checkDoorState(self): diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py index 5ab0a22b11..336b4235e1 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py @@ -99,7 +99,7 @@ def main(): app_name="Taurus command line demo", app_version="1.0", org_domain="Taurus", org_name="Tango community") - w =TaurusCommandLineWidget() + w = TaurusCommandLineWidget() w.show() if owns_app: diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index 33edb138d1..e40aea7d04 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -39,7 +39,6 @@ from sardana.taurus.qt.qtcore.tango.sardana.model import SardanaBaseProxyModel, SardanaTypeTreeItem from sardana.sardanadefs import ElementType, TYPE_ACQUIRABLE_ELEMENTS from taurus.qt.qtgui.util.ui import UILoadable -from taurus.external.qt import Qt # Using a plain model and filtering and checking 'Acquirable' in item.itemData().interfaces is more elegant, but things don't get properly sorted... #from taurus.qt.qtcore.tango.sardana.model import SardanaElementPlainModel From d95440b3b88b799b49e67b1bdf5cecd580053747 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Mon, 30 Sep 2019 16:23:48 +0200 Subject: [PATCH 236/830] Avoid taurus deprecation warnings: 'getParams() ' Adapt sardana code to avoid "TangoAttributeNameValidator.getParams() is deprecated. Use getUriGroups() instead" warning --- src/sardana/macroserver/scan/scandata.py | 6 +++--- src/sardana/pool/pool.py | 2 +- src/sardana/pool/poolbasegroup.py | 2 +- src/sardana/pool/poolexternal.py | 6 +++--- src/sardana/pool/poolmeasurementgroup.py | 2 +- src/sardana/taurus/core/tango/sardana/pool.py | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sardana/macroserver/scan/scandata.py b/src/sardana/macroserver/scan/scandata.py index 6fecb38979..1f57b9e95a 100644 --- a/src/sardana/macroserver/scan/scandata.py +++ b/src/sardana/macroserver/scan/scandata.py @@ -203,11 +203,11 @@ def __get_t3_name(self, item): params = v.getParams(proxy.getFullName()) name = '{0}:{1}/{2}'.format(params['host'].split('.')[0], params['port'], - params['devicename']) + params['devname']) - attr_name = params.get('attributename', None) + attr_name = params.get('_shortattrname', None) if attr_name is not None: - name = '{0}/{1}'.format(name, params['attributename']) + name = '{0}/{1}'.format(name, attr_name) return name diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py index eaa2011d42..11d2c799b7 100644 --- a/src/sardana/pool/pool.py +++ b/src/sardana/pool/pool.py @@ -520,7 +520,7 @@ def create_measurement_group(self, **kwargs): self.pool.get_element(id=elem_id) else: tg_attr_validator = TangoAttributeNameValidator() - params = tg_attr_validator.getParams(elem_id) + params = tg_attr_validator.getUriGroups(elem_id) if params is None: raise Exception("Invalid channel name %s" % elem_id) diff --git a/src/sardana/pool/poolbasegroup.py b/src/sardana/pool/poolbasegroup.py index 6ef0687ac9..6601ef57cf 100644 --- a/src/sardana/pool/poolbasegroup.py +++ b/src/sardana/pool/poolbasegroup.py @@ -171,7 +171,7 @@ def _build_elements(self): # in measurement group) if not internal: validator = TangoAttributeNameValidator() - params = validator.getParams(user_element_id) + params = validator.getUriGroups(user_element_id) params['pool'] = self._get_pool() user_element = PoolExternalObject(**params) self.add_user_element(user_element) diff --git a/src/sardana/pool/poolexternal.py b/src/sardana/pool/poolexternal.py index 95d01ff4b5..a52723a79c 100644 --- a/src/sardana/pool/poolexternal.py +++ b/src/sardana/pool/poolexternal.py @@ -55,10 +55,10 @@ class PoolTangoObject(PoolBaseExternalObject): def __init__(self, **kwargs): scheme = kwargs.pop('scheme', 'tango') - attribute_name = kwargs.pop('attributename') + attribute_name = kwargs.pop('_shortattrname') host, port = kwargs.pop('host', None), kwargs.pop('port', None) - devalias = kwargs.pop('devalias', None) - device_name = kwargs.pop('devicename', None) + devalias = kwargs.pop('_devalias', None) + device_name = kwargs.pop('devname', None) if host is None: db = PyTango.Database() host, port = db.get_db_host(), db.get_db_port() diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 8278813869..46721acb2c 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -729,7 +729,7 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): if external: validator = TangoAttributeNameValidator() full_name = ch_data.get('full_name', ch_name) - params = validator.getParams(full_name) + params = validator.getUriGroups(full_name) params['pool'] = pool channel = PoolExternalObject(**params) else: diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 134fb7eda8..d70b626b24 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1445,8 +1445,8 @@ def _build(self): n_tg_chs[channel_name] = channel_data else: # Handle tango channel - dev_name = params['devicename'].lower() - attr_name = params['attributename'].lower() + dev_name = params['devname'].lower() + attr_name = params['_shortattrname'].lower() host, port = params.get('host'), params.get('port') if host is not None and port is not None: dev_name = "tango://{0}:{1}/{2}".format(host, port, From b628dc996c2e24730a450f3ea2ab6abcc781dd2e Mon Sep 17 00:00:00 2001 From: cfalcon Date: Mon, 30 Sep 2019 16:33:45 +0200 Subject: [PATCH 237/830] Fix Flake8 error --- src/sardana/spock/spockms.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 82beb54b07..e9c36d2bd8 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -33,7 +33,7 @@ import ctypes import PyTango -from taurus.core import TaurusEventType, TaurusSWDevState +from taurus.core import TaurusEventType, TaurusSWDevState, TaurusDevState from sardana.sardanautils import is_pure_str, is_non_str_seq from sardana.spock import genutils @@ -54,7 +54,6 @@ BaseGUIViewer = object -from taurus.core import TaurusDevState RUNNING_STATE = TaurusDevState.Ready From 7e79800726738d943d6819f48020772e70001eab Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 2 Oct 2019 15:32:04 +0200 Subject: [PATCH 238/830] Don't use theme icon for "Add to favourites" in macroexecutor software-update-available is not accessible when using taurus-org/taurus#1012 via theme (on Debian Buster with KDE). Use directly the icon. This works correctly on Debian Stretch and Debian Buster (both with KDE). --- .../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 26cd8588db..26d2d3e6ce 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -625,8 +625,8 @@ def __init__(self, parent=None, designMode=False): self.setLayout(Qt.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) - self.addToFavouritesAction = Qt.QAction(getThemeIcon( - "software-update-available"), "Add to favourites", self) + self.addToFavouritesAction = Qt.QAction(getIcon( + ":/status/software-update-available.svg"), "Add to favourites", self) self.addToFavouritesAction.triggered.connect(self.onAddToFavourites) self.addToFavouritesAction.setToolTip("Add to favourites") self.stopMacroAction = Qt.QAction( From 71eb407272bc380918f0e0cd5cc23592450b51d2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 2 Oct 2019 16:03:31 +0200 Subject: [PATCH 239/830] flake8 --- .../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 26d2d3e6ce..9b92ac75e4 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -625,8 +625,9 @@ def __init__(self, parent=None, designMode=False): self.setLayout(Qt.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) - self.addToFavouritesAction = Qt.QAction(getIcon( - ":/status/software-update-available.svg"), "Add to favourites", self) + self.addToFavouritesAction = Qt.QAction( + getIcon(":/status/software-update-available.svg"), + "Add to favourites", self) self.addToFavouritesAction.triggered.connect(self.onAddToFavourites) self.addToFavouritesAction.setToolTip("Add to favourites") self.stopMacroAction = Qt.QAction( From f19e2d453c3ebe472393efa29cc5fac80173cb3d Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 2 Oct 2019 16:39:52 +0200 Subject: [PATCH 240/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f28fa4b05..bb070277c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Setting of environment variables in Python 3.7 (#1195) * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) +* Remove Taurus deprecated code what reduces deprecation warnings (#1206) ### Changed From 032e007cc9632eac67a3d34737658556fc3f10f9 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 3 Oct 2019 10:14:34 +0200 Subject: [PATCH 241/830] Fix #1209: Crash on spock startup on buster Some PyQt5 versions don't like that QApplication is created with empty list argument. --- src/sardana/spock/inputhandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/spock/inputhandler.py b/src/sardana/spock/inputhandler.py index 03dca9f0ae..7b9b6f93c3 100644 --- a/src/sardana/spock/inputhandler.py +++ b/src/sardana/spock/inputhandler.py @@ -148,7 +148,7 @@ def run(self, conn): self._conn = conn app = Qt.QApplication.instance() if app is None: - app = Qt.QApplication([]) + app = Qt.QApplication(['spock']) app.setQuitOnLastWindowClosed(False) self._msg_handler = MessageHandler(conn) TaurusManager().addJob(self.run_forever, None) From a856d6ee0aabe8348ace0b71be72443829c142a2 Mon Sep 17 00:00:00 2001 From: Antonio Milan Otero Date: Thu, 24 Oct 2019 17:29:28 +0200 Subject: [PATCH 242/830] Fix issue 1218. Fixing the FScan macro which was trying to use the sscan as a deterministic macro without being ready/supported. --- src/sardana/macroserver/macros/scan.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 85212a96e7..e1eca4dd14 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -881,7 +881,7 @@ def prepare(self, *args, **opts): self.paths = [[SafeEvaluator(globals).eval( func) for globals in globals_lst] for func in self.funcstrings] - self.integ_time = numpy.array(eval(args[1]), dtype='d') + self._integ_time = numpy.array(eval(args[1]), dtype='d') self.opts = opts if len(self.motors) == len(self.paths) > 0: @@ -906,14 +906,14 @@ def prepare(self, *args, **opts): (self.funcstrings[0], fs, npoints, len(p))) raise # the problem wasn't a shape mismatch - self.nr_points = npoints + self._nr_points = npoints - if self.integ_time.size == 1: - self.integ_time = self.integ_time * \ - numpy.ones(self.nr_points) # extend integ_time - elif self.integ_time.size != self.nr_points: + if self._integ_time.size == 1: + self._integ_time = self._integ_time * \ + numpy.ones(self._nr_points) # extend integ_time + elif self._integ_time.size != self._nr_points: raise ValueError('time_integ must either be a scalar or ' - 'length=npoints (%i)' % self.nr_points) + 'length=npoints (%i)' % self._nr_points) self.name = opts.get('name', 'fscan') @@ -946,9 +946,9 @@ def _generator(self): step["post-step-hooks"] = self.getHooks('post-step') step["check_func"] = [] - for i in range(self.nr_points): + for i in range(self._nr_points): step["positions"] = self.paths[:, i] - step["integ_time"] = self.integ_time[i] + step["integ_time"] = self._integ_time[i] step["point_id"] = i yield step From 47c25d99921dd976c5a41f12ed6ccde9ff39c9ea Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 25 Oct 2019 10:37:32 +0200 Subject: [PATCH 243/830] Add tests for fscan --- src/sardana/macroserver/macros/test/test_scan.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sardana/macroserver/macros/test/test_scan.py b/src/sardana/macroserver/macros/test/test_scan.py index 0aa51fa887..259fbe8d34 100644 --- a/src/sardana/macroserver/macros/test/test_scan.py +++ b/src/sardana/macroserver/macros/test/test_scan.py @@ -173,3 +173,10 @@ class MeshTest(RunStopMacroTestCase, unittest.TestCase): class TimescanTest(RunStopMacroTestCase, unittest.TestCase): macro_name = 'timescan' + + +@testRun(macro_params=["'x=[1,2]'", "0.1", _m1, "x**2"], wait_timeout=30) +@testStop(macro_params=["'x=[1,2]'", "0.1", _m1, "x**2"], wait_timeout=30) +class FscanTest(RunStopMacroTestCase, unittest.TestCase): + + macro_name = 'fscan' \ No newline at end of file From 15cc05a410f5121a573686ae4865da57df2f672f Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 25 Oct 2019 10:56:44 +0200 Subject: [PATCH 244/830] Rename nr_points to nb_points in scans Since we are going to document the API of the deterministic scans it is a good moment to unify naming in sardana and use nb instead of nr for number. --- .../macroserver/macros/examples/scans.py | 12 +++--- .../macros/examples/specific_experiments.py | 4 +- src/sardana/macroserver/macros/scan.py | 34 ++++++++--------- src/sardana/macroserver/scan/gscan.py | 38 +++++++++---------- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index a07b9d7468..b0444d3f0c 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -70,7 +70,7 @@ def prepare(self, motor, start_pos, final_pos, nr_interv, integ_time, **opts): self.final = numpy.array([final_pos], dtype='d') self.integ_time = integ_time - self.nr_points = nr_interv + 1 + self.nb_points = nr_interv + 1 self.interv_size = (self.final - self.start) / nr_interv self.name = 'ascan_demo' # the "env" dictionary may be passed as an option @@ -84,7 +84,7 @@ def _generator(self): step = {} # integ_time is the same for all steps step["integ_time"] = self.integ_time - for point_no in range(self.nr_points): + for point_no in range(self.nb_points): step["positions"] = self.start + point_no * \ self.interv_size # note that this is a numpy array step["point_id"] = point_no @@ -137,7 +137,7 @@ def prepare(self, motor, start_pos, final_pos, nr_interv, integ_time, repeat, self.repeat = repeat self.opts = opts - self.nr_points = nr_interv + 1 + self.nb_points = nr_interv + 1 self.interv_sizes = (self.finals - self.starts) / nr_interv self.name = 'ascanr' @@ -160,7 +160,7 @@ def _generator(self): step["check_func"] = [] extrainfo = {"repetition": 0} # !!! step['extrainfo'] = extrainfo # !!! - for point_no in range(self.nr_points): + for point_no in range(self.nb_points): step["positions"] = self.starts + point_no * self.interv_sizes step["point_id"] = point_no for i in range(self.repeat): @@ -211,8 +211,8 @@ def prepare(self, motor, start_pos, final_pos, nr_interv, integ_time, self.nr_cycles = nr_cycles self.nr_samples = nr_samples self.opts = opts - cycle_nr_points = self.nr_interv + 1 + (self.nr_interv + 1) - 2 - self.nr_points = cycle_nr_points * nr_samples * nr_cycles + nr_samples + cycle_nb_points = self.nr_interv + 1 + (self.nr_interv + 1) - 2 + self.nb_points = cycle_nb_points * nr_samples * nr_cycles + nr_samples self.interv_size = (self.final_pos - self.start_pos) / nr_interv self.name = 'toothedtriangle' diff --git a/src/sardana/macroserver/macros/examples/specific_experiments.py b/src/sardana/macroserver/macros/examples/specific_experiments.py index b997b935df..4a9564d8a4 100644 --- a/src/sardana/macroserver/macros/examples/specific_experiments.py +++ b/src/sardana/macroserver/macros/examples/specific_experiments.py @@ -60,7 +60,7 @@ def prepare(self, start, final, nr_interv, integ_time, **opts): self.finals = numpy.array([final], dtype='d') self.integ_time = integ_time - self.nr_points = nr_interv + 1 + self.nb_points = nr_interv + 1 self.interv_sizes = (self.finals - self.starts) / nr_interv self.name = 'xas_acq' # the "env" dictionary may be passed as an option @@ -109,7 +109,7 @@ def _generator(self): "post-acq-hooks"] = self.getHooks('post-acq') + self.getHooks('_NOHINT_') step["post-step-hooks"] = self.getHooks('post-step') step["check_func"] = [] - for point_no in range(self.nr_points): + for point_no in range(self.nb_points): step["positions"] = self.starts + point_no * self.interv_sizes step["point_id"] = point_no yield step diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index e1eca4dd14..3021f7ce22 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -155,7 +155,7 @@ def _prepare(self, motorlist, startlist, endlist, scan_length, integ_time, if mode == StepMode: self.nr_interv = scan_length - self.nr_points = self.nr_interv + 1 + self.nb_points = self.nr_interv + 1 self.interv_sizes = (self.finals - self.starts) / self.nr_interv self.name = opts.get('name', 'a%iscan' % self.N) self._gScan = SScan(self, self._stepGenerator, @@ -181,7 +181,7 @@ def _prepare(self, motorlist, startlist, endlist, scan_length, integ_time, constrains, extrainfodesc) elif mode == ContinuousHwTimeMode: self.nr_interv = scan_length - self.nr_points = self.nr_interv + 1 + self.nb_points = self.nr_interv + 1 mg_name = self.getEnv('ActiveMntGrp') mg = self.getMeasurementGroup(mg_name) mg_latency_time = mg.getLatencyTime() @@ -198,7 +198,7 @@ def _prepare(self, motorlist, startlist, endlist, scan_length, integ_time, extrainfodesc) elif mode == HybridMode: self.nr_interv = scan_length - self.nr_points = self.nr_interv + 1 + self.nb_points = self.nr_interv + 1 self.interv_sizes = (self.finals - self.starts) / self.nr_interv self.name = opts.get('name', 'a%iscanh' % self.N) self._gScan = HScan(self, self._stepGenerator, @@ -224,7 +224,7 @@ def _stepGenerator(self): step["post-step-hooks"] = self.getHooks('post-step') step["check_func"] = [] - for point_no in range(self.nr_points): + for point_no in range(self.nb_points): step["positions"] = self.starts + point_no * self.interv_sizes step["point_id"] = point_no yield step @@ -255,7 +255,7 @@ def _waypoint_generator_hwtime(self): step["post-acq-hooks"] = self.getHooks('post-acq') + self.getHooks( '_NOHINTS_') step["check_func"] = [] - step["active_time"] = self.nr_points * (self.integ_time + + step["active_time"] = self.nb_points * (self.integ_time + self.latency_time) step["positions"] = [] step["start_positions"] = [] @@ -312,7 +312,7 @@ def getTimeEstimation(self): max_step_time = max(max_step_time, path.duration) motion_time = max_step0_time + self.nr_interv * max_step_time # calculate acquisition time - acq_time = self.nr_points * self.integ_time + acq_time = self.nb_points * self.integ_time total_time = motion_time + acq_time elif mode == ContinuousMode: @@ -329,7 +329,7 @@ def getIntervalEstimation(self): def _fill_missing_records(self): # fill record list with dummy records for the final padding - nb_of_points = self.nr_points + nb_of_points = self.nb_points scan = self._gScan nb_of_records = len(scan.data.records) missing_records = nb_of_points - nb_of_records @@ -906,14 +906,14 @@ def prepare(self, *args, **opts): (self.funcstrings[0], fs, npoints, len(p))) raise # the problem wasn't a shape mismatch - self._nr_points = npoints + self._nb_points = npoints if self._integ_time.size == 1: self._integ_time = self._integ_time * \ - numpy.ones(self._nr_points) # extend integ_time - elif self._integ_time.size != self._nr_points: + numpy.ones(self._nb_points) # extend integ_time + elif self._integ_time.size != self._nb_points: raise ValueError('time_integ must either be a scalar or ' - 'length=npoints (%i)' % self._nr_points) + 'length=npoints (%i)' % self._nb_points) self.name = opts.get('name', 'fscan') @@ -946,7 +946,7 @@ def _generator(self): step["post-step-hooks"] = self.getHooks('post-step') step["check_func"] = [] - for i in range(self._nr_points): + for i in range(self._nb_points): step["positions"] = self.paths[:, i] step["integ_time"] = self._integ_time[i] step["point_id"] = i @@ -1705,7 +1705,7 @@ def prepare(self, m1, m1_start_pos, m1_final_pos, m1_nr_interv, # Number of intervals of the first motor which is doing the # continuous scan. self.nr_interv = m1_nr_interv - self.nr_points = self.nr_interv + 1 + self.nb_points = self.nr_interv + 1 self.integ_time = integ_time self.bidirectional_mode = bidirectional @@ -1768,7 +1768,7 @@ def _generator(self): 'post-move') + [self._fill_missing_records] step["post-move-hooks"] = post_move_hooks step["check_func"] = [] - step["active_time"] = self.nr_points * (self.integ_time + + step["active_time"] = self.nb_points * (self.integ_time + self.latency_time) points1, _ = self.nr_intervs + 1 @@ -1802,7 +1802,7 @@ def getIntervalEstimation(self): def _fill_missing_records(self): # fill record list with dummy records for the final padding - nb_of_points = self.nr_points + nb_of_points = self.nb_points scan = self._gScan nb_of_total_records = len(scan.data.records) nb_of_records = nb_of_total_records - self.point_id @@ -1827,7 +1827,7 @@ class timescan(Macro, Hookable): def prepare(self, nr_interv, integ_time, latency_time): self.nr_interv = nr_interv - self.nr_points = nr_interv + 1 + self.nb_points = nr_interv + 1 self.integ_time = integ_time self.latency_time = latency_time self._gScan = TScan(self) @@ -1845,7 +1845,7 @@ def run(self, *args): def getTimeEstimation(self): mg_latency_time = self._gScan.measurement_group.getLatencyTime() latency_time = max(self.latency_time, mg_latency_time) - return self.nr_points * (self.integ_time + latency_time) + return self.nb_points * (self.integ_time + latency_time) def getIntervalEstimation(self): return self.nr_interv diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 2d7da5ac5f..a2602c1052 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1059,12 +1059,12 @@ def scan_loop(self): scream = False self._deterministic_scan = False - if hasattr(macro, "nr_points"): - nr_points = float(macro.nr_points) + if hasattr(macro, "nb_points"): + nb_points = float(macro.nb_points) if hasattr(macro, "integ_time"): integ_time = macro.integ_time self.measurement_group.putIntegrationTime(integ_time) - self.measurement_group.setNbStarts(nr_points) + self.measurement_group.setNbStarts(nb_points) self.measurement_group.prepare() self._deterministic_scan = True scream = True @@ -1080,7 +1080,7 @@ def scan_loop(self): self.stepUp(i, step, lstep) lstep = step if scream: - yield ((i + 1) / nr_points) * 100 + yield ((i + 1) / nb_points) * 100 if not scream: yield 100.0 @@ -1822,8 +1822,8 @@ def scan_loop(self): sum_delay = 0 sum_integ_time = 0 - if hasattr(macro, "nr_points"): - nr_points = float(macro.nr_points) + if hasattr(macro, "nb_points"): + nb_points = float(macro.nb_points) scream = True else: yield 0.0 @@ -1937,7 +1937,7 @@ def scan_loop(self): self.data.addRecord(data_line) if scream: - yield ((point_nb + 1) / nr_points) * 100 + yield ((point_nb + 1) / nb_points) * 100 else: break old_curr_time = curr_time @@ -2077,13 +2077,13 @@ def generate_timestamps(synchronization, initial_timestamp=0): return ret -def generate_positions(motors, starts, finals, nr_points): +def generate_positions(motors, starts, finals, nb_points): ret = dict() # generate theoretical positions moveable_positions = [] for start, final in zip(starts, finals): moveable_positions.append( - np.linspace(start, final, nr_points)) + np.linspace(start, final, nb_points)) # prepare table header from moveables names dtype_spec = [] for motor in motors: @@ -2334,7 +2334,7 @@ def _go_through_waypoints(self): raise ScanException(msg) # Set the index offset used in CAcquisition class. - self._index_offset = i * self.macro.nr_points + self._index_offset = i * self.macro.nb_points startTimestamp = time.time() @@ -2350,7 +2350,7 @@ def _go_through_waypoints(self): moveable = moveables[MASTER].full_name self.measurement_group.setMoveable(moveable) path = motion_paths[MASTER] - repeats = self.macro.nr_points + repeats = self.macro.nb_points active_time = self.macro.integ_time active_position = path.max_vel * active_time if not path.positive_displacement: @@ -2432,9 +2432,9 @@ def _go_through_waypoints(self): motors = self.macro.motors starts = self.macro.starts finals = self.macro.finals - nr_points = self.macro.nr_points + nb_points = self.macro.nb_points theoretical_positions = generate_positions(motors, starts, finals, - nr_points) + nb_points) theoretical_timestamps = generate_timestamps(synch, dt_timestamp) for index, data in list(theoretical_positions.items()): data.update(theoretical_timestamps[index]) @@ -2539,8 +2539,8 @@ def scan_loop(self): sum_delay = 0 sum_integ_time = 0 - if hasattr(macro, "nr_points"): - # nr_points = float(macro.nr_points) + if hasattr(macro, "nb_points"): + # nb_points = float(macro.nb_points) scream = True else: yield 0.0 @@ -2695,7 +2695,7 @@ class TScan(GScan, CAcquisition): information in either of the following two ways: - synchronization attribute that follows the synchronization format of the measurement group - - integ_time, nr_points and latency_time (optional) attributes + - integ_time, nb_points and latency_time (optional) attributes """ def __init__(self, macro, generator=None, moveables=[], env={}, constraints=[], extrainfodesc=[]): @@ -2728,7 +2728,7 @@ def get_synchronization(self): else: try: active_time = getattr(self.macro, "integ_time") - repeats = getattr(self.macro, "nr_points") + repeats = getattr(self.macro, "nb_points") except AttributeError: msg = "Macro object is missing synchronization attributes" raise ScanSetupError(msg) @@ -2780,9 +2780,9 @@ def scan_loop(self): def _fill_missing_records(self): # fill record list with dummy records for the final padding - nr_points = self.macro.nr_points + nb_points = self.macro.nb_points records = len(self.data.records) - missing_records = nr_points - records + missing_records = nb_points - records self.data.initRecords(missing_records) def _estimate(self): From cd0f585c0d901c3d3eaa67cb744b1ac5a91676d9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 25 Oct 2019 11:25:10 +0200 Subject: [PATCH 245/830] Add backwards compatibility for nr_points nr_points was renamed to nb_points, however nr_points was a public API before. Provide a backwards compatibility property withs simultaneous deprecation log. --- .../macroserver/macros/examples/scans.py | 24 ++++++++++++++ .../macros/examples/specific_experiments.py | 8 +++++ src/sardana/macroserver/macros/scan.py | 32 +++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index b0444d3f0c..829d809902 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -98,6 +98,14 @@ def run(self, *args): def data(self): return self._gScan.data # the GScan provides scan data + def _get_nr_points(self): + msg = ("nr_points is deprecated since version Jan20. " + "Use nb_points instead.") + self.warning(msg) + return self.nb_points + + nr_points = property(_get_nr_points) + class ascanr(Macro, Hookable): """This is an example of how to handle adding extra info columns in a scan. @@ -175,6 +183,14 @@ def run(self, *args): def data(self): return self._gScan.data + def _get_nr_points(self): + msg = ("nr_points is deprecated since version Jan20. " + "Use nb_points instead.") + self.warning(msg) + return self.nb_points + + nr_points = property(_get_nr_points) + class toothedtriangle(Macro, Hookable): """toothedtriangle macro implemented with the gscan framework. @@ -279,6 +295,14 @@ def run(self, *args): def data(self): return self._gScan.data + def _get_nr_points(self): + msg = ("nr_points is deprecated since version Jan20. " + "Use nb_points instead.") + self.warning(msg) + return self.nb_points + + nr_points = property(_get_nr_points) + class regscan(Macro): """regscan. diff --git a/src/sardana/macroserver/macros/examples/specific_experiments.py b/src/sardana/macroserver/macros/examples/specific_experiments.py index 4a9564d8a4..b74d2393b4 100644 --- a/src/sardana/macroserver/macros/examples/specific_experiments.py +++ b/src/sardana/macroserver/macros/examples/specific_experiments.py @@ -121,3 +121,11 @@ def run(self, *args): @property def data(self): return self._gScan.data # the GScan provides scan data + + def _get_nr_points(self): + msg = ("nr_points is deprecated since version Jan20. " + "Use nb_points instead.") + self.warning(msg) + return self.nb_points + + nr_points = property(_get_nr_points) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 3021f7ce22..10c1f7ccf1 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -335,6 +335,13 @@ def _fill_missing_records(self): missing_records = nb_of_points - nb_of_records scan.data.initRecords(missing_records) + def _get_nr_points(self): + msg = ("nr_points is deprecated since version Jan20. " + "Use nb_points instead.") + self.warning(msg) + return self.nb_points + + nr_points = property(_get_nr_points) class dNscan(aNscan): """ @@ -956,6 +963,15 @@ def run(self, *args): for step in self._gScan.step_scan(): yield step + def _get_nr_points(self): + msg = ("nr_points is deprecated since version Jan20. " + "Use nb_points instead.") + self.warning(msg) + return self.nb_points + + nr_points = property(_get_nr_points) + + class ascanh(aNscan, Macro): """Do an absolute scan of the specified motor. @@ -1809,6 +1825,14 @@ def _fill_missing_records(self): missing_records = nb_of_points - nb_of_records scan.data.initRecords(missing_records) + def _get_nr_points(self): + msg = ("nr_points is deprecated since version Jan20. " + "Use nb_points instead.") + self.warning(msg) + return self.nb_points + + nr_points = property(_get_nr_points) + class timescan(Macro, Hookable): """Do a time scan over the specified time intervals. The scan starts @@ -1849,3 +1873,11 @@ def getTimeEstimation(self): def getIntervalEstimation(self): return self.nr_interv + + def _get_nr_points(self): + msg = ("nr_points is deprecated since version Jan20. " + "Use nb_points instead.") + self.warning(msg) + return self.nb_points + + nr_points = property(_get_nr_points) From acbc47d5fb3350b7acd65e84abafd37f24baebbf Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 25 Oct 2019 11:37:23 +0200 Subject: [PATCH 246/830] Use nb_points in documentation --- doc/source/devel/howto_macros/scan_framework.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/devel/howto_macros/scan_framework.rst b/doc/source/devel/howto_macros/scan_framework.rst index ba60aa0ee4..da08d369cc 100644 --- a/doc/source/devel/howto_macros/scan_framework.rst +++ b/doc/source/devel/howto_macros/scan_framework.rst @@ -66,18 +66,18 @@ The ``ascan_demo`` macro illustrates the most basic features of a step scan:: ['motor', Type.Moveable, None, 'Motor to move'], ['start_pos', Type.Float, None, 'Scan start position'], ['final_pos', Type.Float, None, 'Scan final position'], - ['nr_interv', Type.Integer, None, 'Number of scan intervals'], + ['nb_interv', Type.Integer, None, 'Number of scan intervals'], ['integ_time', Type.Float, None, 'Integration time'] ] - def prepare(self, motor, start_pos, final_pos, nr_interv, integ_time, **opts): + def prepare(self, motor, start_pos, final_pos, nb_interv, integ_time, **opts): #parse the user parameters self.start = numpy.array([start_pos], dtype='d') self.final = numpy.array([final_pos], dtype='d') self.integ_time = integ_time - self.nr_points = nr_interv+1 - self.interv_size = ( self.final - self.start) / nr_interv + self.nb_points = nb_interv+1 + self.interv_size = ( self.final - self.start) / nb_interv self.name='ascan_demo' env = opts.get('env',{}) #the "env" dictionary may be passed as an option @@ -88,7 +88,7 @@ The ``ascan_demo`` macro illustrates the most basic features of a step scan:: def _generator(self): step = {} step["integ_time"] = self.integ_time #integ_time is the same for all steps - for point_no in xrange(self.nr_points): + for point_no in xrange(self.nb_points): step["positions"] = self.start + point_no * self.interv_size #note that this is a numpy array step["point_id"] = point_no yield step From f82092f9670a2d74c59ec1f0cca2203f791fe8a5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 25 Oct 2019 12:08:38 +0200 Subject: [PATCH 247/830] Document deterministic scans. --- .../devel/howto_macros/scan_framework.rst | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/doc/source/devel/howto_macros/scan_framework.rst b/doc/source/devel/howto_macros/scan_framework.rst index da08d369cc..23a2a5db7d 100644 --- a/doc/source/devel/howto_macros/scan_framework.rst +++ b/doc/source/devel/howto_macros/scan_framework.rst @@ -196,6 +196,26 @@ the most basic features of a continuous scan:: :: :class:`~sardana.macroserver.macros.scan.meshc` +Deterministic scans +------------------- + +By *deterministic scans* we call these scans which know *a priori* the number +of points and the integration time. In some cases the experimental channel +controllers may take profit of this information and prepare for the whole +scan measurement up-front instead of preparing before each scan point. +When writing this type of scan macros it is enough to set two attributes: +``nb_points`` and ``integ_time`` in your macro. +When these attributes are present in your macro, the *generic scan framework* +will take care of preparing the experimental channels for the whole scan +measurement upfront. + + .. warning:: Since ``nb_points`` and ``integ_time`` attributes identify + deterministic scan macros as deterministic and you must not use these + attribute names for any other needs. + + .. seealso:: More information about deterministic scans and measurement + preparation can be found in SEP18_. + Hooks support in scans ---------------------- @@ -249,6 +269,6 @@ Finally, the documentation and code of :mod:`~sardana.macroserver.scan.GScan`, :class:`~sardana.macroserver.scan.SScan` and :class:`~sardana.macroserver.scan.CScan` may be helpful. - +.. _SEP18: http://www.sardana-controls.org/sep/?SEP18.md From 3c2e635a2d894baa555c64d8adc11d1c186dc737 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 25 Oct 2019 12:11:29 +0200 Subject: [PATCH 248/830] Fix flake8 --- src/sardana/macroserver/macros/scan.py | 8 ++++---- src/sardana/macroserver/macros/test/test_scan.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 10c1f7ccf1..43da4d64d5 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -255,8 +255,8 @@ def _waypoint_generator_hwtime(self): step["post-acq-hooks"] = self.getHooks('post-acq') + self.getHooks( '_NOHINTS_') step["check_func"] = [] - step["active_time"] = self.nb_points * (self.integ_time + - self.latency_time) + step["active_time"] = self.nb_points * (self.integ_time + + self.latency_time) step["positions"] = [] step["start_positions"] = [] starts = self.starts @@ -1784,8 +1784,8 @@ def _generator(self): 'post-move') + [self._fill_missing_records] step["post-move-hooks"] = post_move_hooks step["check_func"] = [] - step["active_time"] = self.nb_points * (self.integ_time + - self.latency_time) + step["active_time"] = self.nb_points * (self.integ_time + + self.latency_time) points1, _ = self.nr_intervs + 1 for i, waypoint in enumerate(self.waypoints): diff --git a/src/sardana/macroserver/macros/test/test_scan.py b/src/sardana/macroserver/macros/test/test_scan.py index 259fbe8d34..45b16abbb2 100644 --- a/src/sardana/macroserver/macros/test/test_scan.py +++ b/src/sardana/macroserver/macros/test/test_scan.py @@ -179,4 +179,4 @@ class TimescanTest(RunStopMacroTestCase, unittest.TestCase): @testStop(macro_params=["'x=[1,2]'", "0.1", _m1, "x**2"], wait_timeout=30) class FscanTest(RunStopMacroTestCase, unittest.TestCase): - macro_name = 'fscan' \ No newline at end of file + macro_name = 'fscan' From 5f558f2a0fab700092e41ab5eab451e1ccc42e40 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 25 Oct 2019 13:33:02 +0200 Subject: [PATCH 249/830] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb070277c4..ffd62a849e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,11 +14,17 @@ This file follows the formats and conventions from [keepachangelog.com] ### Fixed * Default macro parameter values in macroexecutor (#1153) +* fscan macro that was broken 2.6.0 (#1218, #1220) * Setting of environment variables in Python 3.7 (#1195) * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) * Remove Taurus deprecated code what reduces deprecation warnings (#1206) +### Deprecated + +* `nr_points` attribute of scan macros e.g., aNscan family of scans, `fscan` etc. + (#1218, #1220) + ### Changed * requirements are no longer checked when importing sardana (#1185) From 7d1baa51304aae0ee42f785913b73e5888f3e665 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 25 Oct 2019 16:45:37 +0200 Subject: [PATCH 250/830] Try to deploy to directly deploy to GH Pages --- .travis.yml | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2a6463b326..41b6041a6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,8 @@ env: - secure: "p/0UgVZzPKJQqcvQ/97qMgo9kPCE0cZ6vI+308YEJ2o9xj4a3FsfHCZ/vWtjdsrp1sQbtKVDesx+xmK4CLDzQeC2+Xskv8OZDjaG2jYkHcVosZEM3EGW8rLVKzoDWLr6cTy2wexLgjHPCsmrjukPs49/i5p+WU0no64YoLlZdp9TT+gvWSQJLIk6R4eqt4FHMszPybLv0pvb1SEiCzimlX1WM1pBrE0LHgchd2ZBYSUWTTwe+Koi4HCS4Bads8j20K2e3fFKcmR2u9DfmU+7Mf5HRJsj1LYJgBUF76lUG2/fZfpoDe8sWi+eUewTa3zNM4bhRLpV+pmG0ypplM4pIcdvwiHV03nGSGu6XK6OGQ/Mgsw0fmud4JR4f5g9DgEfERlyJKI4A9mPZQ327OmEwOOl33x2AFJAL05Qvm0yXCkf1dwgYXnZl44SQbAczY1NHFL90t6xbHtmTitJrE2Xb+4BLzMe3OOZj6j/0QeiXA4z1FnZr1s8UoAsm68iW194IuFg1RRG9FTISFWaBew5wzwvAJak0DxkpG0k43VkHiVC7sPHqr5CxXMXO/MuaptK2ti6iLK9xBAEUpO9HluOkeJq5WDIIxBiBS9tPi0i3vIpq87RjHkdw5n7pdIqnuJ1nXUjpWsuUyV3fLkY12fFxSbZgqmNhIE5/o9c5VP/69Y=" matrix: - - TEST="flake8" - - TEST="testsuite" DOCKER_IMG=reszelaz/sardana-test +# - TEST="flake8" +# - TEST="testsuite" DOCKER_IMG=reszelaz/sardana-test - TEST="doc" DOCKER_IMG=reszelaz/sardana-test @@ -55,23 +55,23 @@ script: docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; fi - # deploy sphinx docs to sardana-doc repo if we are on upstream - - if [[ $TEST == "doc" && $TRAVIS_REPO_SLUG == "sardana-org/sardana" ]]; then - pip install doctr ; - docker exec sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; - if [[ $TRAVIS_BRANCH == "develop" ]]; then - doctr deploy . ; - else - doctr deploy "v-$TRAVIS_BRANCH" ; - fi; - fi - -doctr: - key-path : ci/github_deploy_key.enc - deploy-repo : sardana-org/sardana-doc - require-master: false - sync: true - built-docs: build/sphinx/html/ +# # deploy sphinx docs to sardana-doc repo if we are on upstream +# - if [[ $TEST == "doc" && $TRAVIS_REPO_SLUG == "sardana-org/sardana" ]]; then +# pip install doctr ; +# docker exec sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; +# if [[ $TRAVIS_BRANCH == "develop" ]]; then +# doctr deploy . ; +# else +# doctr deploy "v-$TRAVIS_BRANCH" ; +# fi; +# fi +# +#doctr: +# key-path : ci/github_deploy_key.enc +# deploy-repo : sardana-org/sardana-doc +# require-master: false +# sync: true +# built-docs: build/sphinx/html/ deploy: # deploy to pypi when a version tag is pushed to the official repo @@ -83,3 +83,11 @@ deploy: repo: sardana-org/sardana tags: true condition: "$TEST == testsuite && $DOCKER_IMG == reszelaz/sardana-test && $TRAVIS_TAG =~ ^[0-9]+.[0-9]+.[0-9]+$" + - provider: pages + local_dir: build/sphinx/html + repo: reszelaz/sardana-doc + skip_cleanup: true + github_token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable + keep_history: true + on: + branch: deploy-ghpages From f30989354c98481fbcb1f0f79870b454013a089b Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 25 Oct 2019 16:56:41 +0200 Subject: [PATCH 251/830] Dummy change to see that deploy works --- doc/source/devel/howto_macros/scan_framework.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/devel/howto_macros/scan_framework.rst b/doc/source/devel/howto_macros/scan_framework.rst index ba60aa0ee4..500b42d2e9 100644 --- a/doc/source/devel/howto_macros/scan_framework.rst +++ b/doc/source/devel/howto_macros/scan_framework.rst @@ -3,9 +3,9 @@ .. _sardana-macros-scanframework: -============== -Scan Framework -============== +=============== +Scan Frameworkk +=============== In general terms, we call *scan* to a macro that moves one or more motors and acquires data along the path of the motor(s). See the From 89e27afd66c7e4084d64c334f1b632d43efef74f Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 25 Oct 2019 17:05:33 +0200 Subject: [PATCH 252/830] Ignore Jekyll checks of files starting with "_" --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 41b6041a6a..e9f081c193 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,6 +53,7 @@ script: # build docs - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; + docker exec sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; fi # # deploy sphinx docs to sardana-doc repo if we are on upstream From 80e318cdef1638031a3cdd52a0c659c13f46b619 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 25 Oct 2019 21:59:47 +0200 Subject: [PATCH 253/830] Try to fix nojekyll --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index e9f081c193..a027ae3c89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,10 +51,8 @@ script: # run the full testsuite - if [ $TEST == "testsuite" ]; then docker exec sardana-test sardanatestsuite; fi # build docs - - if [ $TEST == "doc" ]; then - docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; - docker exec sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; - fi + - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; fi + - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; fi # # deploy sphinx docs to sardana-doc repo if we are on upstream # - if [[ $TEST == "doc" && $TRAVIS_REPO_SLUG == "sardana-org/sardana" ]]; then From 04cdf972f0537e7fc95bcc5f9089517c5c50b20d Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 31 Oct 2019 16:01:30 +0100 Subject: [PATCH 254/830] Remove other documentation versions In the last 2 years, the other_versions documentation was never really used. Furthermore it is polluted with many documentation version for branches which were uploaded to upstream but are not really considered as Sardana versions. This may confuse users and in order to fix it manual removal is necessary. The idea is that in the future, other documentation versions will be hosted as dedicated GitHub repos and exported to GitHub Pages. Then other_verions.rst will be reintroduced again, but this time as a static page. --- doc/source/docs.rst | 1 - doc/source/other_versions.rst | 50 ----------------------------------- 2 files changed, 51 deletions(-) delete mode 100644 doc/source/other_versions.rst diff --git a/doc/source/docs.rst b/doc/source/docs.rst index b5ca06a091..74f75617ee 100644 --- a/doc/source/docs.rst +++ b/doc/source/docs.rst @@ -18,7 +18,6 @@ scientific installations. devel/index sep/index.rst Glossary - other_versions * :ref:`genindex` diff --git a/doc/source/other_versions.rst b/doc/source/other_versions.rst deleted file mode 100644 index 2887e86d2c..0000000000 --- a/doc/source/other_versions.rst +++ /dev/null @@ -1,50 +0,0 @@ - -=============================== -Docs for other Sardana versions -=============================== - -The `main sardana docs `_ are generated for the -most recent development version. - -But docs for other versions of sardana (previous releases or other branches in -`repository `_ can also be browsed here: - - -.. raw:: html - - -
-
-
- - -
-
-
-
- - - - - - From cffee796b08a3c2014dddf4f22fb76352ccfdc2e Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 31 Oct 2019 16:09:49 +0100 Subject: [PATCH 255/830] Remove old deploy way (already commented out) --- .travis.yml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index a027ae3c89..354d263813 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,24 +54,6 @@ script: - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; fi - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; fi -# # deploy sphinx docs to sardana-doc repo if we are on upstream -# - if [[ $TEST == "doc" && $TRAVIS_REPO_SLUG == "sardana-org/sardana" ]]; then -# pip install doctr ; -# docker exec sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; -# if [[ $TRAVIS_BRANCH == "develop" ]]; then -# doctr deploy . ; -# else -# doctr deploy "v-$TRAVIS_BRANCH" ; -# fi; -# fi -# -#doctr: -# key-path : ci/github_deploy_key.enc -# deploy-repo : sardana-org/sardana-doc -# require-master: false -# sync: true -# built-docs: build/sphinx/html/ - deploy: # deploy to pypi when a version tag is pushed to the official repo - provider: pypi From 36e5e89249e09c7ce29f4f771b416c8f46014a76 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 5 Nov 2019 16:20:40 +0100 Subject: [PATCH 256/830] Remove unused SSH key (doctr is not pushing to GitHub anymore) --- ci/github_deploy_key.enc | 1 - 1 file changed, 1 deletion(-) delete mode 100644 ci/github_deploy_key.enc diff --git a/ci/github_deploy_key.enc b/ci/github_deploy_key.enc deleted file mode 100644 index ad82ffa5de..0000000000 --- a/ci/github_deploy_key.enc +++ /dev/null @@ -1 +0,0 @@ -gAAAAABbRIlov7GgCqYKPx7wnaU_-rtYRBbbucfQIPnjSsA7UUntuTcOiyzb2qEr7FCyUtZ9q2fWJf6NFG-c8RFJP1CfnviMDruCzAPPmBgJilSFbtC5sXBme8gesFUylVmjo-vUWHqgcSFmtwWv-lKef9Y1Uj1IHkw7ujEPEb5CzqbqeNYLX0b2vaFJzj1ySQoAAXv5_M6Y6It5kSg2ob3DJvql-w81PkyrnqpcQbEtnSiRgrUCIAQQDpJFy2f-NPWcMqYh9yLnjvwfEKO8S6eSy8SLk26MMtzFhQ6-BO8DZSSmMkzX9gZqcEdM89jpA_Js-Cg10bAc_78Hia45GTgpfTISYGOQFLhpGun1-a5wxhdVieAr0pg2VtQ7n0mjwWlH2g28uYJF-VYuE5nrEa1KLXRVkTENLcZJlRjVD_JhfgGr6zWJ_pcebixZq_cxcvXP8_uM8YottdqrzsRt1XdhmUxe9SbBfgxZRGfvYlV_7LNs_WGsURlxxogTMVrq1bAFOB3c6tDZpEznpR0Ytgz-GJLSJv9IutdSjmeLCMQOlyvsTN33DAJtuQGoiwyJmz0SLWTC7ZSyUCqP9Wcu8ytcCK6jVoA1lYSPnvyWxH6xmTac9T9KQngJTmqvni5fOb4LjWGU0ILfWAZijgg7YlKSrqlEsuJ3gOTx0q7vpG5Z6e7cXMtGFTR4t7IhSnyaCDv7ox9kKq3Xv7p4MM00M58XH25SaY0yPnGlMqn0HWZFgAXuPHqrSz2kxWEsL4q3YRtc944ZP89Mwi-HgcWBSTPwbDmvPlls-GxEu8QB5fT51oHNY4H0VGs7Tjp9xVjysZGa9x439a-mj8LsTPqrUFuHw9_EXOENL0iCiD9vqTAWodsnGX2nm3FVXAuRwwwzeSwagrtQ6aUP47pOaBmq9uKaZT6iEE8g8eSCBNnn7WwDrGy5C4Me3zMAuwjjoB0nlr60oSps7vF9Pi-89P0AN1DS15i5pWObQ-fqQ2aKaobRXrBIhFxL1eCijkNxVDfM1mkbz087J6qw2iQwzpkkjSawZ4rdyWrZyQeGjhiVfIT22R67KUvYrVEguzUA8Q3IgmvbKhkLSPX3FUQfiSM_rKdBy_iO02bJU4glsR_G1QM4RMOy0LIEOGdcaq36H4QCSX76FNekSFGLmi1b3NQXYH58QC8rPmqNTIRQ6c90ukL33AxuQ7-VPpLkyqhaOCR_WD-SVzPOcU4RiJ6h99_-BRcUG0-3pIswrool-FrJLVZdVFqkmRxDPhLutS_m0gpShNBZYUZAtvd7s1nBYzjcDg1Q_087r7PE6EeCMyuE2sngwlgAuA8Q2p7zxaWR-D8mkxYxvlKyXII4MADXGwLjP7S27uBWcRVAjLEvjZC12vVcKA7sB8XQWcpmAvXT3PX6bZAW94qygrAWZaSzY9hSmBt5p8OXeEzNv6rNDLlK40mFl5eILbwwJfKvfl9pwlKxU67NHSKk_FQBkEsHolpyQqEv5MBI0zoBXQp4GxXaa7oGfivqxhDikjAS7MhcKjVKtx1fyEOusBRsl_4DJMZf4Oe58ZjEeCFnN2icFFv7SVjt7MoWYiEDVvh3thabHJKMk3cjlYlFFmIsU4_2HZw5FEuTt0Be1cQ4hExpPp7qWfwveFjQEo5uAjCj-3yuAzcIEPIR3-zHLh5GrvFVzkjlaEyDxnmjSeNrZW4b92puM0LiU9mX4HtZMOVElLeMkGt104anXkPmb0r62zduLazKqqY0pJDwFCtpqz8ZZRJ8gCj2DVanjGUvwNA2fDrl5Ddo3HWgrY7xpt6J-i5EnYWDLfy5-oprR3ruYaUtmhHifnWr305Ok3Q_ufdQzzF8fStuZfM8F9pFFiKOmY1v3mpBazvUgZlWI3uGIWXMoC-A15eB7bnYstNK4i_XW8lG8J4pUzNvM5GpoHPgpsSR-vyonQ8c9igB3Rd3A7eOhbvdxde360F4PubKvMQdcBCq9mfjsng3h2rGE5y33LB3o9WuyojRQxA9elnIMwfttkwnbTjX1pNfPVq6cFso5mhz_PKHSpr9b8V31hHiLh6Pdd2ODJkM72gUfsAoBvdmOcgSweu4kWkzT3urHb0T4o6P9fAlad39ibLw0qrT_ZqRpXXllnXF5j2OU0-a74gZ7p-8fGUKxTrjS6ZXlaQSABj697j52m9BHI07hKY5p8fNA0G28vZHBh3UkbYT8aZzxQFFDVMn8DJKSDZ7_F9VWXk9WvpGGNU1CTxx-d6VdohDRyp0BKbC5Qo32DEwvBHH33nLkb8NX22ghaFDbH940VlzlLcUb8-a8WIcVrYpOf0PHW2vt0lRZHfuS6CriioKf27GnQvhaGjk1mz0dxbk4gSVW3iULUxBpJqgxJkAoz-F5J-NuQpSZu4qhcfV-BRVrsweVe8obsfmxSbCZVt3hekGfYdUXVKJ8dKLDuY9H93beDryU_1AUyQ6S-7V7J992bKEPRvHzaHVeUC4jV2ySHN02Pi10XbQKHGQeF6UsajKnapmOfSpswJ6jteVePciOSy28suwX2a3LFB-t8fhUiSkh_tftosUA5_GeHE5XD8fDZ7hOV3IgKfnCzFfuPmuJ6Q4p3SlLXXpqXio8_e6DIIv7FWN4rdAVVbJ_gBp8C3RcZliAMk20VdXbIuLLAAnG8VxAzlfc40qACEI1ENoc4EcEospcqjhYKlIqyyOthOpjmPJpmD888ByRz4QfN3JIUUowt2WwLOYEekM_qIlkG-FL0qNqaXM2UXqphOQrhbVv23vTMvd0ZAwF5capxx2LOLj-99LMozheoDUCCYWTou586AH_B7zKiRFvphTUwY36maORbcQQeaUtMaV0ud5hEhHhNo-80-mJlBBs0ZPSWBYCC1u6H0Dtq1GISIde_TYl0xRxqH24xYEpIIhrTzpibpJipAH4c06RQzf1tAn70Oq6YfyjJ_Y2aekx3tHY0CCcnAa5FI-aOhHSilKLEKIuUKJ0kx0WTBv7YVElN7yDFYnHZFVJBMNQ5KOmvAzBl1T-ISGrD8VlXg2c-vY3DN1IEvQDZ8WdWupuoEDaSByTx5MOx3taOo_zVGZYHkXDQ00oP3aTDsEPIqEGsAz7B9Y_uOvGQt2-T0WJxwDlTDPbGnuHnIYHq9U9c4RbVjLr0EOWm2_4kCZ63EX9z31gkwXfp5R97rSnpFpQG_LubMx33fXvz2mxhvTmSNJdAOgHNv6ygfSFsHumAkXl6HcjV0XTKOUhVd49QRNF3J32ILKL_X5jHJCMgWTZSOvN3TzOa6VToe_zB4ndYtnNwqDQUjrKYRAcP3Wg7t4yaAhhiOTmOO1B2EhKlemf6_El8k8aWYfZDS5VHHL0V6qUu5-KExQt6lqQZvSq2a5nmMfBC6KQlKo5jNzqGbVsaBznr32H6IG0cQU6M0jTbZUozFcCFi9vmXfcBaSmC-0ZUYicazGsU46OuJJFHi2F0_TT-N-p3epLHDbAU35ig1zxbV4prBLoPi8cUMIBqOaaQiC2eHxGVSuYy9s4MdnwkmnM3fwumQTFf4QCAaRE5l2Yg6PAh92uUJ9yB4cCYZgovsRhXq5vjHRk2auc0_gk9Mu_vGqqLnn0E5W01I9hJJnvYVng-sI77zRGr9CzjT-ZvqziNN1iBX1sjpKbi13vzxTt8dWUvPpK8o26bPdBFbbdbEL2kHoALkMl_CV2d72RDdzBG0auwfXVp2XpfWuyMDBgO8IYrUSDjFiW2puwctD__MMkBSiFlouxJo6gxrgbpsMiSdshGjqD3rajyqcGcSnBI0BlgbfME6-cZhRZLz708IV_ai02M2jMq3cioYfjl7cCSpoNgLR0vbrG6Sc5-SexhnGGMVwtq4ej95FnixkAfhykHSBbF-_-SAVCI2h6_grHfYOwIr7cm2QqXCj1PtZPS-bk-3huXKzUneCHDnKjUntlgBN7ntkH0d44fP1iguT_FgWVzJUKFB2-9GJ0k6I792ChiBIFqT0BxAbWCOeoN40EKS0Bh4djTsBXzAp9GAGmrnITBKsBDhHxfzCLlE3C2m1dyS8t7AtECFlxbWFm-4e9iYmuvwTyFGjPab98-G6XE28NL877r-Q9vhe8kx7R8HMF3Egp_slP2gLwhRL6CL_PTxQNfFSFXy7wJV1H3bOV8jWYgTmkFO-gcyk8QFzuLWFPZtiZAku3eY5eC7ta1a_BTJ3e8tdcad2UXm7jjw1eQzTrZSL8Ry5_ejvprgtioukZq29hWH9xv2tApQJHCKy2QlYTkZVBFAYLq6ScNky6y28YEzzR2lcMI3sEJX50JHILn8hf7snFQ1Ns8EQRVKTcxFCnKuUA-SAOBbQYfhpql2qC4sClLlb3ieMmxOmhLAaANtcKRS7_wOdRcG6HNeC4vMZBohlvZQ4pvNkeGe9PDNGQSKPMoeSJZ01S2a-49VHXGHc2IwrGLFfMxXWoLUy6w== \ No newline at end of file From 2e66849530a378866477d74a13395af28a3d5af5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 6 Nov 2019 09:59:56 +0100 Subject: [PATCH 257/830] Revert "Remove other documentation versions" This reverts commit 04cdf972f0537e7fc95bcc5f9089517c5c50b20d. --- doc/source/docs.rst | 1 + doc/source/other_versions.rst | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 doc/source/other_versions.rst diff --git a/doc/source/docs.rst b/doc/source/docs.rst index 74f75617ee..b5ca06a091 100644 --- a/doc/source/docs.rst +++ b/doc/source/docs.rst @@ -18,6 +18,7 @@ scientific installations. devel/index sep/index.rst Glossary + other_versions * :ref:`genindex` diff --git a/doc/source/other_versions.rst b/doc/source/other_versions.rst new file mode 100644 index 0000000000..2887e86d2c --- /dev/null +++ b/doc/source/other_versions.rst @@ -0,0 +1,50 @@ + +=============================== +Docs for other Sardana versions +=============================== + +The `main sardana docs `_ are generated for the +most recent development version. + +But docs for other versions of sardana (previous releases or other branches in +`repository `_ can also be browsed here: + + +.. raw:: html + + +
+
+
+ + +
+
+
+
+ + + + + + From 3135fac20fefd16ffcfaad0b50ff1d15e920d119 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 6 Nov 2019 10:30:33 +0100 Subject: [PATCH 258/830] Update documentation about other Sardana versions --- doc/source/other_versions.rst | 52 +++++++++-------------------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/doc/source/other_versions.rst b/doc/source/other_versions.rst index 2887e86d2c..433e04d8dd 100644 --- a/doc/source/other_versions.rst +++ b/doc/source/other_versions.rst @@ -3,48 +3,22 @@ Docs for other Sardana versions =============================== -The `main sardana docs `_ are generated for the +The `main Sardana docs `_ are generated for the most recent development version. -But docs for other versions of sardana (previous releases or other branches in -`repository `_ can also be browsed here: +You can still generate the docs for other versions of Sardana. In order to do that +you first need to clone the `Sardana repository `_, +checkout the necessary version and simply build the docs. +In continuation you can find an example of how to do it for version ``2.8.3``: -.. raw:: html - - -
-
-
- - -
-
-
-
- - - - +.. code-block:: console + git clone -b 2.8.3 https://github.com/sardana-org/sardana + cd sardana + python setup.py build_sphinx + firefox build/sphinx/html/index.html +.. note:: + Sardana versions >= 3 work only with Python 3. Then you will need to build + the documentation with ``python3`` instead of ``python``. From d8d3f0148f02248b4cbfc3b667291c975504f30d Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 6 Nov 2019 12:26:01 +0100 Subject: [PATCH 259/830] Revert "Dummy change to see that deploy works" This reverts commit f3098935 --- doc/source/devel/howto_macros/scan_framework.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/devel/howto_macros/scan_framework.rst b/doc/source/devel/howto_macros/scan_framework.rst index 500b42d2e9..ba60aa0ee4 100644 --- a/doc/source/devel/howto_macros/scan_framework.rst +++ b/doc/source/devel/howto_macros/scan_framework.rst @@ -3,9 +3,9 @@ .. _sardana-macros-scanframework: -=============== -Scan Frameworkk -=============== +============== +Scan Framework +============== In general terms, we call *scan* to a macro that moves one or more motors and acquires data along the path of the motor(s). See the From acd37c1fef3efbd938e65357890596322bea8e8c Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 6 Nov 2019 12:47:02 +0100 Subject: [PATCH 260/830] Uncomment tests --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 354d263813..f87697ed5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,8 @@ env: - secure: "p/0UgVZzPKJQqcvQ/97qMgo9kPCE0cZ6vI+308YEJ2o9xj4a3FsfHCZ/vWtjdsrp1sQbtKVDesx+xmK4CLDzQeC2+Xskv8OZDjaG2jYkHcVosZEM3EGW8rLVKzoDWLr6cTy2wexLgjHPCsmrjukPs49/i5p+WU0no64YoLlZdp9TT+gvWSQJLIk6R4eqt4FHMszPybLv0pvb1SEiCzimlX1WM1pBrE0LHgchd2ZBYSUWTTwe+Koi4HCS4Bads8j20K2e3fFKcmR2u9DfmU+7Mf5HRJsj1LYJgBUF76lUG2/fZfpoDe8sWi+eUewTa3zNM4bhRLpV+pmG0ypplM4pIcdvwiHV03nGSGu6XK6OGQ/Mgsw0fmud4JR4f5g9DgEfERlyJKI4A9mPZQ327OmEwOOl33x2AFJAL05Qvm0yXCkf1dwgYXnZl44SQbAczY1NHFL90t6xbHtmTitJrE2Xb+4BLzMe3OOZj6j/0QeiXA4z1FnZr1s8UoAsm68iW194IuFg1RRG9FTISFWaBew5wzwvAJak0DxkpG0k43VkHiVC7sPHqr5CxXMXO/MuaptK2ti6iLK9xBAEUpO9HluOkeJq5WDIIxBiBS9tPi0i3vIpq87RjHkdw5n7pdIqnuJ1nXUjpWsuUyV3fLkY12fFxSbZgqmNhIE5/o9c5VP/69Y=" matrix: -# - TEST="flake8" -# - TEST="testsuite" DOCKER_IMG=reszelaz/sardana-test + - TEST="flake8" + - TEST="testsuite" DOCKER_IMG=reszelaz/sardana-test - TEST="doc" DOCKER_IMG=reszelaz/sardana-test From c5f4c5c2e4e85dfa6fd68f6481cdcdd34d338a14 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 6 Nov 2019 12:48:38 +0100 Subject: [PATCH 261/830] Use sardana-org repo and develop branch --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f87697ed5d..ab87459938 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,9 +66,9 @@ deploy: condition: "$TEST == testsuite && $DOCKER_IMG == reszelaz/sardana-test && $TRAVIS_TAG =~ ^[0-9]+.[0-9]+.[0-9]+$" - provider: pages local_dir: build/sphinx/html - repo: reszelaz/sardana-doc + repo: sardana-org/sardana-doc skip_cleanup: true github_token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable keep_history: true on: - branch: deploy-ghpages + branch: develop From 5777ba298d1b0d1cca05c28fe0b8cd3abac8b504 Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Thu, 7 Nov 2019 09:14:40 +0100 Subject: [PATCH 262/830] (doc) Give explicit instructions for building docs Document the command for building docs from sources --- doc/source/other_versions.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/other_versions.rst b/doc/source/other_versions.rst index 433e04d8dd..5612e1d774 100644 --- a/doc/source/other_versions.rst +++ b/doc/source/other_versions.rst @@ -8,7 +8,8 @@ most recent development version. You can still generate the docs for other versions of Sardana. In order to do that you first need to clone the `Sardana repository `_, -checkout the necessary version and simply build the docs. +checkout the necessary version and build the docs with `sphinx-build /doc/source/` +(this may require you to install some dependencies apart from the sardana ones, such as sphinx). In continuation you can find an example of how to do it for version ``2.8.3``: From 744153b244026571165f50ce4b8a91764ddbb834 Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Thu, 7 Nov 2019 10:40:52 +0100 Subject: [PATCH 263/830] Avoid duplicated instructions My last commit introduced unnecessary duplicated instructions for building docs. Remove them --- doc/source/other_versions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/other_versions.rst b/doc/source/other_versions.rst index 5612e1d774..ecbe6c56ba 100644 --- a/doc/source/other_versions.rst +++ b/doc/source/other_versions.rst @@ -8,8 +8,8 @@ most recent development version. You can still generate the docs for other versions of Sardana. In order to do that you first need to clone the `Sardana repository `_, -checkout the necessary version and build the docs with `sphinx-build /doc/source/` -(this may require you to install some dependencies apart from the sardana ones, such as sphinx). +checkout the necessary version and build the docs (this may require you to install +some dependencies apart from the sardana ones, such as sphinx). In continuation you can find an example of how to do it for version ``2.8.3``: From 153059463aa2cf232db769ed67cab8146d0a3dc8 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 7 Nov 2019 11:55:45 +0100 Subject: [PATCH 264/830] Deploy docs only in doc jobs --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ab87459938..3e5df30f4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,3 +72,4 @@ deploy: keep_history: true on: branch: develop + condition: "$TEST == doc" From 184ca7abc2d9fb6ef5141f1fb858e734f5b46540 Mon Sep 17 00:00:00 2001 From: John Bickmore Date: Sun, 10 Nov 2019 10:05:50 +1100 Subject: [PATCH 265/830] Add basic Readme file --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000..bf0d556e9a --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +Sardana is a software suite for Supervision, Control and Data Acquisition in scientific installations. + +Projects related to Sardana +=========================== + +* Sardana uses Taurus for control system access and user interfaces +* Sardana is based on Tango +* The command line interface for Sardana (Spock) is based on IPython From 33434a22d3545cd758d0f95bfa9e3a974e6e99f8 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 11 Nov 2019 12:59:49 +0100 Subject: [PATCH 266/830] Add badges and link to website --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index bf0d556e9a..db6b754f04 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +[![PyPI pyversions](https://img.shields.io/pypi/pyversions/sardana.svg)](https://pypi.python.org/pypi/sardana/) +[![PyPI license](https://img.shields.io/pypi/l/sardana.svg)](https://pypi.python.org/pypi/sardana/) +[![PyPI version](https://img.shields.io/pypi/v/sardana.svg)](https://pypi.python.org/pypi/sardana/) +[![GitHub tag](https://img.shields.io/github/tag/sardana-org/sardana.svg)](https://GitHub.com/sardana-org/sardana/tags/) +[![Travis status](https://travis-ci.org/sardana-org/sardana.svg?branch=develop)](https://travis-ci.org/sardana-org/sardana) +[![Appveyor status](https://ci.appveyor.com/api/projects/status/rxeo3hsycilnyn9k/branch/develop?svg=true)](https://ci.appveyor.com/project/taurusorg/sardana/branch/develop) + + Sardana is a software suite for Supervision, Control and Data Acquisition in scientific installations. Projects related to Sardana @@ -6,3 +14,5 @@ Projects related to Sardana * Sardana uses Taurus for control system access and user interfaces * Sardana is based on Tango * The command line interface for Sardana (Spock) is based on IPython + +Main web site: http://sardana-controls.org From a91321d478ee381602c4f6bbbddcbb070e5ef28b Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 11 Nov 2019 14:28:22 +0100 Subject: [PATCH 267/830] Set custom domain for docs --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3e5df30f4e..5d7ce8a1c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,6 +70,7 @@ deploy: skip_cleanup: true github_token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable keep_history: true + fqdn: sardana-controls.org # Set custom domain on: branch: develop condition: "$TEST == doc" From 9fecb481f7714f267e5f592fb348423eef153978 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 12 Nov 2019 18:49:47 +0100 Subject: [PATCH 268/830] Fix sphinx warning in docstring (unindented param continuation) --- src/sardana/taurus/core/tango/sardana/macroserver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index b004cd3909..563543a43c 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -1139,9 +1139,9 @@ def fillMacroNodeAdditionalInfos(self, macroNode): in XML file. :param macroNode: (MacroNode) macro node obj populated from XML - information + information - See Also: getMacroNodeObj + See also: getMacroNodeObj """ macroName = macroNode.name() macroInfoObj = self.getMacroInfoObj(macroName) From 545ac0d6bba8a36d3a14073d9a5f6d3c0d55383d Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 12 Nov 2019 18:50:25 +0100 Subject: [PATCH 269/830] Add module docstrings to Taurus extensions --- .../taurus/core/tango/sardana/__init__.py | 25 ++++++++++++++++++- .../qt/qtcore/tango/sardana/__init__.py | 20 +++++++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/__init__.py b/src/sardana/taurus/core/tango/sardana/__init__.py index b984503c11..8f421becdc 100644 --- a/src/sardana/taurus/core/tango/sardana/__init__.py +++ b/src/sardana/taurus/core/tango/sardana/__init__.py @@ -23,7 +23,30 @@ ## ############################################################################## -"""The sardana package. It contains specific part of sardana""" +"""Taurus extensions for Sardana devices. + +Objects obtained with :func:`taurus.Device` expose standard interfaces +e.g., allow to interact with their attributes, check their state, etc. +This module defines classes for enriched interaction with Sardana devices +(also for other elements not exported as devices), e.g. synchronous move +of a sardana motor with :meth:`~sardana.taurus.core.tango.sardana.pool.Motor.move` +method instead of writing motor's position attribute and then waiting for its +state change. + +To obtain these enriched objects with :func:`taurus.Device` you need to first +register the extension classes with +the :obj:`~sardana.taurus.core.tango.sardana.registerExtensions` function. + +The registration needs to be done before the first access to the given +:func:`taurus.Device`. + +When you would like to get back to the default :func:`taurus.Device` behavior +you need to unregister the extension classes with the +:obj:`~sardana.taurus.core.tango.sardana.unregisterExtensions` function. + +Note that the unregistration will not remove the already created devices from +the :func:`taurus.Factory` cache. +""" __docformat__ = 'restructuredtext' diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py b/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py index 5fe1959e5e..35da17a3f2 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py @@ -23,8 +23,24 @@ ## ############################################################################## -""" -Sardana extension for taurus Qt +"""Taurus Qt extensions for Sardana devices. + +Objects obtained with :func:`taurus.Device` expose standard interfaces +e.g., allow to interact with their attributes, check their state, etc. +This module defines classes for enriched interaction with Sardana devices +(also for other elements not exported as devices), e.g. synchronous move +of a sardana motor with :meth:`~sardana.taurus.core.tango.sardana.pool.Motor.move` +method instead of writing motor's position attribute and then waiting for its +state change. The difference between these classes with respect to the ones from +the :mod:`sardana.taurus.core.tango.sardana` module is the Qt friendly interface +e.g. the Sardana events are translated to Qt signals. + +To obtain these enriched objects with :func:`taurus.Device` you need to first +register the extension classes with +the :obj:`~sardana.taurus.qt.qtcore.tango.sardana.registerExtensions` function. + +The registration needs to be done before the first access to the given +:func:`taurus.Device`. """ __docformat__ = 'restructuredtext' From 3eb061899ff65a9f8b03cd9a45d235f9ac0289c9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 12 Nov 2019 18:55:40 +0100 Subject: [PATCH 270/830] Autogenerate documentation for [un]registerExtensions functions --- .../devel/api/sardana/taurus/core/tango/sardana.rst | 11 +++++++++++ .../api/sardana/taurus/core/tango/sardana/pool.rst | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/doc/source/devel/api/sardana/taurus/core/tango/sardana.rst b/doc/source/devel/api/sardana/taurus/core/tango/sardana.rst index 03382f26ab..9912a909b5 100644 --- a/doc/source/devel/api/sardana/taurus/core/tango/sardana.rst +++ b/doc/source/devel/api/sardana/taurus/core/tango/sardana.rst @@ -7,10 +7,21 @@ .. automodule:: sardana.taurus.core.tango.sardana +.. rubric:: Functions + +.. hlist:: + :columns: 3 + + * :func:`registerExtensions` + * :func:`unregisterExtensions` + .. rubric:: Modules .. toctree:: :maxdepth: 1 pool + macroserver +.. autofunction:: registerExtensions +.. autofunction:: unregisterExtensions diff --git a/doc/source/devel/api/sardana/taurus/core/tango/sardana/pool.rst b/doc/source/devel/api/sardana/taurus/core/tango/sardana/pool.rst index 0c23814c8d..d2a42938c2 100644 --- a/doc/source/devel/api/sardana/taurus/core/tango/sardana/pool.rst +++ b/doc/source/devel/api/sardana/taurus/core/tango/sardana/pool.rst @@ -4,6 +4,14 @@ Taurus Extensions ================= +.. rubric:: Functions + +.. hlist:: + :columns: 3 + + * :func:`registerExtensions` + * :func:`unregisterExtensions` + .. rubric:: Classes .. hlist:: @@ -28,6 +36,9 @@ Taurus Extensions * :class:`TwoDExpChannel` * :class:`ZeroDExpChannel` +.. autofunction:: registerExtensions +.. autofunction:: unregisterExtensions + BaseElement ----------- From 543d9def64b68e228f5103951dea15d37a0f52fd Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 12 Nov 2019 18:56:29 +0100 Subject: [PATCH 271/830] Add API documentation for sardana.core.tango.sardana.macroserver module --- .../taurus/core/tango/sardana/macroserver.rst | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 doc/source/devel/api/sardana/taurus/core/tango/sardana/macroserver.rst diff --git a/doc/source/devel/api/sardana/taurus/core/tango/sardana/macroserver.rst b/doc/source/devel/api/sardana/taurus/core/tango/sardana/macroserver.rst new file mode 100644 index 0000000000..06b5649146 --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/core/tango/sardana/macroserver.rst @@ -0,0 +1,46 @@ +.. currentmodule:: sardana.taurus.core.tango.sardana.macroserver + +================= +Taurus Extensions +================= + +.. rubric:: Functions + +.. hlist:: + :columns: 3 + + * :func:`registerExtensions` + * :func:`unregisterExtensions` + +.. rubric:: Classes + +.. hlist:: + :columns: 4 + + * :class:`BaseDoor` + * :class:`BaseMacroServer` + +.. autofunction:: registerExtensions +.. autofunction:: unregisterExtensions + +BaseDoor +-------- + +.. inheritance-diagram:: BaseDoor + :parts: 1 + +.. autoclass:: BaseDoor + :show-inheritance: + :members: + :undoc-members: + +BaseMacroServer +--------------- + +.. inheritance-diagram:: BaseMacroServer + :parts: 1 + +.. autoclass:: BaseMacroServer + :show-inheritance: + :members: + :undoc-members: From 59b241c52ae757c512e4d52c976cc7ab832ae8d5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 12 Nov 2019 18:57:03 +0100 Subject: [PATCH 272/830] Add API documentation for sardana.taurus.qt.qtcore module --- doc/source/devel/api/sardana/taurus.rst | 1 + doc/source/devel/api/sardana/taurus/qt.rst | 15 +++++++ .../devel/api/sardana/taurus/qt/qtcore.rst | 14 ++++++ .../api/sardana/taurus/qt/qtcore/tango.rst | 14 ++++++ .../taurus/qt/qtcore/tango/sardana.rst | 25 +++++++++++ .../qt/qtcore/tango/sardana/macroserver.rst | 44 +++++++++++++++++++ .../taurus/qt/qtcore/tango/sardana/pool.rst | 44 +++++++++++++++++++ .../devel/api/sardana/taurus/qt/qtgui.rst | 6 +++ 8 files changed, 163 insertions(+) create mode 100644 doc/source/devel/api/sardana/taurus/qt.rst create mode 100644 doc/source/devel/api/sardana/taurus/qt/qtcore.rst create mode 100644 doc/source/devel/api/sardana/taurus/qt/qtcore/tango.rst create mode 100644 doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst create mode 100644 doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana/macroserver.rst create mode 100644 doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana/pool.rst create mode 100644 doc/source/devel/api/sardana/taurus/qt/qtgui.rst diff --git a/doc/source/devel/api/sardana/taurus.rst b/doc/source/devel/api/sardana/taurus.rst index 02a599fdc2..ef6e60d799 100644 --- a/doc/source/devel/api/sardana/taurus.rst +++ b/doc/source/devel/api/sardana/taurus.rst @@ -11,3 +11,4 @@ :maxdepth: 1 core + qt diff --git a/doc/source/devel/api/sardana/taurus/qt.rst b/doc/source/devel/api/sardana/taurus/qt.rst new file mode 100644 index 0000000000..c450ea690e --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/qt.rst @@ -0,0 +1,15 @@ +.. currentmodule:: sardana.taurus.qt + +:mod:`~sardana.taurus.qt` +========================= + +.. automodule:: sardana.taurus.qt + +.. rubric:: Modules + +.. toctree:: + :maxdepth: 1 + + qtcore + qtgui + diff --git a/doc/source/devel/api/sardana/taurus/qt/qtcore.rst b/doc/source/devel/api/sardana/taurus/qt/qtcore.rst new file mode 100644 index 0000000000..a451eb287c --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/qt/qtcore.rst @@ -0,0 +1,14 @@ +.. currentmodule:: sardana.taurus.qt.qtcore + +:mod:`~sardana.taurus.qt.qtcore` +============================ + +.. automodule:: sardana.taurus.qt.qtcore + +.. rubric:: Modules + +.. toctree:: + :maxdepth: 1 + + tango + diff --git a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango.rst b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango.rst new file mode 100644 index 0000000000..a9b76c5c35 --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango.rst @@ -0,0 +1,14 @@ +.. currentmodule:: sardana.taurus.qt.qtcore.tango + +:mod:`~sardana.taurus.qt.qtcore.tango` +================================= + +.. automodule:: sardana.taurus.qt.qtcore.tango + +.. rubric:: Modules + +.. toctree:: + :maxdepth: 1 + + sardana + diff --git a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst new file mode 100644 index 0000000000..6f93a4fd9a --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst @@ -0,0 +1,25 @@ +.. currentmodule:: sardana.taurus.qt.qtcore.tango.sardana + +.. _sardana-taurus-api: + +:mod:`~sardana.taurus.qt.qtcore.tango.sardana` +============================================== + +.. automodule:: sardana.taurus.qt.qtcore.tango.sardana + +.. rubric:: Functions + +.. hlist:: + :columns: 3 + + * :func:`registerExtensions` + +.. rubric:: Modules + +.. toctree:: + :maxdepth: 1 + + pool + macroserver + +.. autofunction:: registerExtensions diff --git a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana/macroserver.rst b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana/macroserver.rst new file mode 100644 index 0000000000..515181a84c --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana/macroserver.rst @@ -0,0 +1,44 @@ +.. currentmodule:: sardana.taurus.qt.qtcore.tango.sardana.macroserver + +================= +Taurus Extensions +================= + +.. rubric:: Functions + +.. hlist:: + :columns: 3 + + * :func:`registerExtensions` + +.. rubric:: Classes + +.. hlist:: + :columns: 4 + + * :class:`QDoor` + * :class:`QMacroServer` + +.. autofunction:: registerExtensions + +QDoor +----- + +.. inheritance-diagram:: QDoor + :parts: 1 + +.. autoclass:: QDoor + :show-inheritance: + :members: + :undoc-members: + +QMacroServer +------------ + +.. inheritance-diagram:: QMacroServer + :parts: 1 + +.. autoclass:: QMacroServer + :show-inheritance: + :members: + :undoc-members: diff --git a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana/pool.rst b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana/pool.rst new file mode 100644 index 0000000000..73e05a967f --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana/pool.rst @@ -0,0 +1,44 @@ +.. currentmodule:: sardana.taurus.qt.qtcore.tango.sardana.pool + +================= +Taurus Extensions +================= + +.. rubric:: Functions + +.. hlist:: + :columns: 3 + + * :func:`registerExtensions` + +.. rubric:: Classes + +.. hlist:: + :columns: 4 + + * :class:`QPool` + * :class:`QMeasurementGroup` + +.. autofunction:: registerExtensions + +QPool +----- + +.. inheritance-diagram:: QPool + :parts: 1 + +.. autoclass:: QPool + :show-inheritance: + :members: + :undoc-members: + +QMeasurementGroup +----------------- + +.. inheritance-diagram:: QMeasurementGroup + :parts: 1 + +.. autoclass:: QMeasurementGroup + :show-inheritance: + :members: + :undoc-members: diff --git a/doc/source/devel/api/sardana/taurus/qt/qtgui.rst b/doc/source/devel/api/sardana/taurus/qt/qtgui.rst new file mode 100644 index 0000000000..667ef04817 --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/qt/qtgui.rst @@ -0,0 +1,6 @@ +.. currentmodule:: sardana.taurus.qt.qtgui + +:mod:`~sardana.taurus.qt.qtgui` +================================ + +.. automodule:: sardana.taurus.qt.qtgui From e19aeef0b8ae44a973ed37dfbc7839adf0dbcec3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 12 Nov 2019 18:58:00 +0100 Subject: [PATCH 273/830] Update links in FAQ "How to call procedures?" --- doc/source/users/faq.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/source/users/faq.rst b/doc/source/users/faq.rst index ef3fb9d262..a07f603777 100644 --- a/doc/source/users/faq.rst +++ b/doc/source/users/faq.rst @@ -54,14 +54,13 @@ How to call procedures? The central idea of the Sardana SCADA_ system is to execute procedures centrally. The execution can be started from either: - * *spock* offers a command line interface with commands very similar to SPEC_. - It is documented :ref:`here `. - * Procedures can also be executed with from a :term:`GUI`. Taurus provides - `generic widgets for macro execution `__. + * :ref:`sardana-spock` offers a command line interface with commands very similar to SPEC_. + * Procedures can also be executed with a :term:`GUI`. Taurus provides + :ref:`generic widgets for macro execution `. * Procedures can also be executed in specific :term:`GUI` s and specific Taurus_ widgets. The :term:`API` to execute macros from python code is documented - here ****. + in :mod:`sardana.taurus.core.tango.sardana` and from PyQt code is documented + in :mod:`sardana.taurus.qt.qtcore.tango.sardana`. How to write procedures? ------------------------ From 76236d5e11efad3c6e59e979fdaa14b6eda423cd Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 12 Nov 2019 19:00:13 +0100 Subject: [PATCH 274/830] Fix flake8 --- src/sardana/taurus/core/tango/sardana/__init__.py | 3 ++- src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/__init__.py b/src/sardana/taurus/core/tango/sardana/__init__.py index 8f421becdc..94f0cb943b 100644 --- a/src/sardana/taurus/core/tango/sardana/__init__.py +++ b/src/sardana/taurus/core/tango/sardana/__init__.py @@ -29,7 +29,8 @@ e.g., allow to interact with their attributes, check their state, etc. This module defines classes for enriched interaction with Sardana devices (also for other elements not exported as devices), e.g. synchronous move -of a sardana motor with :meth:`~sardana.taurus.core.tango.sardana.pool.Motor.move` +of a sardana motor with +:meth:`~sardana.taurus.core.tango.sardana.pool.Motor.move` method instead of writing motor's position attribute and then waiting for its state change. diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py b/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py index 35da17a3f2..0e9d163b6a 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py @@ -29,11 +29,12 @@ e.g., allow to interact with their attributes, check their state, etc. This module defines classes for enriched interaction with Sardana devices (also for other elements not exported as devices), e.g. synchronous move -of a sardana motor with :meth:`~sardana.taurus.core.tango.sardana.pool.Motor.move` +of a sardana motor with +:meth:`~sardana.taurus.core.tango.sardana.pool.Motor.move` method instead of writing motor's position attribute and then waiting for its -state change. The difference between these classes with respect to the ones from -the :mod:`sardana.taurus.core.tango.sardana` module is the Qt friendly interface -e.g. the Sardana events are translated to Qt signals. +state change. The difference between these classes with respect to the ones +from the :mod:`sardana.taurus.core.tango.sardana` module is the Qt friendly +interface e.g. the Sardana events are translated to Qt signals. To obtain these enriched objects with :func:`taurus.Device` you need to first register the extension classes with From f5e4799ba126a1eff12ea9678683644ea8df9d34 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 13 Nov 2019 10:13:13 +0100 Subject: [PATCH 275/830] Fix sphinx warnings --- doc/source/devel/api/sardana/taurus/qt/qtcore.rst | 2 +- doc/source/devel/api/sardana/taurus/qt/qtcore/tango.rst | 2 +- doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/source/devel/api/sardana/taurus/qt/qtcore.rst b/doc/source/devel/api/sardana/taurus/qt/qtcore.rst index a451eb287c..ae97ad449d 100644 --- a/doc/source/devel/api/sardana/taurus/qt/qtcore.rst +++ b/doc/source/devel/api/sardana/taurus/qt/qtcore.rst @@ -1,7 +1,7 @@ .. currentmodule:: sardana.taurus.qt.qtcore :mod:`~sardana.taurus.qt.qtcore` -============================ +================================ .. automodule:: sardana.taurus.qt.qtcore diff --git a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango.rst b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango.rst index a9b76c5c35..5ff72f93b6 100644 --- a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango.rst +++ b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango.rst @@ -1,7 +1,7 @@ .. currentmodule:: sardana.taurus.qt.qtcore.tango :mod:`~sardana.taurus.qt.qtcore.tango` -================================= +====================================== .. automodule:: sardana.taurus.qt.qtcore.tango diff --git a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst index 6f93a4fd9a..7564368efc 100644 --- a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst +++ b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst @@ -1,7 +1,5 @@ .. currentmodule:: sardana.taurus.qt.qtcore.tango.sardana -.. _sardana-taurus-api: - :mod:`~sardana.taurus.qt.qtcore.tango.sardana` ============================================== From 5fe3cc0e56e8848c4d5e8f04b96eafdd45420f70 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 13 Nov 2019 10:32:01 +0100 Subject: [PATCH 276/830] Add note that TaurusApplication already registers extensions --- src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py b/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py index 0e9d163b6a..e4cdc7df23 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py @@ -42,6 +42,10 @@ The registration needs to be done before the first access to the given :func:`taurus.Device`. + +.. note:: If you are using :class:`~taurus.qt.qtgui.application.TaurusApplication` + then the registration is done behind the scene at the moment of + :class:`~taurus.qt.qtgui.application.TaurusApplication` construction. """ __docformat__ = 'restructuredtext' From c3ebdd6455f07b0d73626c221cf64bd9c2f1cfe0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 13 Nov 2019 10:53:08 +0100 Subject: [PATCH 277/830] Fix flake8 --- src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py b/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py index e4cdc7df23..243769f157 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/__init__.py @@ -43,7 +43,8 @@ The registration needs to be done before the first access to the given :func:`taurus.Device`. -.. note:: If you are using :class:`~taurus.qt.qtgui.application.TaurusApplication` +.. note:: If you are using + :class:`~taurus.qt.qtgui.application.TaurusApplication` then the registration is done behind the scene at the moment of :class:`~taurus.qt.qtgui.application.TaurusApplication` construction. """ From ed96e874c5c51f8faadb4a7e53e7f4cf87fe5ec6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 13 Nov 2019 11:52:18 +0100 Subject: [PATCH 278/830] Remove INTERFACES and INTERFACES_EXPANDED from sardanadefs documentation These data structures are auto documented with autodata. Since these are very big and complicated strcutures, the documentation is not attractive and readable at all. Remove them from the API documentation and just leave the docstings in the code. --- doc/source/devel/api/sardana/sardanadefs.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/source/devel/api/sardana/sardanadefs.rst b/doc/source/devel/api/sardana/sardanadefs.rst index c188c70a37..937be60336 100644 --- a/doc/source/devel/api/sardana/sardanadefs.rst +++ b/doc/source/devel/api/sardana/sardanadefs.rst @@ -47,10 +47,6 @@ .. autodata:: sardana.sardanadefs.InterfacesExpanded -.. autodata:: sardana.sardanadefs.INTERFACES - -.. autodata:: sardana.sardanadefs.INTERFACES_EXPANDED - .. rubric:: Functions .. autofunction:: sardana.sardanadefs.from_dtype_str From 9c2a372783ce54d9bc8c8e54dcfb1b21110ea7cd Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 13 Nov 2019 11:58:27 +0100 Subject: [PATCH 279/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffd62a849e..1ccf6ee050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Support to Python >= 3.5 (#1089, #1173, 1201) * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) * Instruments creation and configuration in sar_demo (#1198) +* Documentation to Taurus Extensions of Sardana Devices: MacroServer part + and the whole Sardana part of the Qt Taurus Extensions (#1228, #1233) ### Fixed From e3a0b6956f0ed2815e5d8c2baeaf3da5bf504815 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 13 Nov 2019 16:58:53 +0100 Subject: [PATCH 280/830] Make static manpages Manpages are dynamically recreated on every release. This is an extra effort which does not add value. The help of Sardana commands does not change so often. Make the manpages static with a very short description of commands. --- doc/man/MacroServer.1 | 52 ++-- doc/man/Pool.1 | 51 ++-- doc/man/Sardana.1 | 51 ++-- doc/man/diffractometeralignment.1 | 51 +--- doc/man/hklscan.1 | 49 +--- doc/man/macroexecutor.1 | 51 ++-- doc/man/sardanatestsuite.1 | 31 ++- doc/man/sequencer.1 | 57 ++--- doc/man/spock.1 | 383 ++---------------------------- doc/man/ubmatrix.1 | 53 ++--- 10 files changed, 142 insertions(+), 687 deletions(-) diff --git a/doc/man/MacroServer.1 b/doc/man/MacroServer.1 index 83823ff425..338f42ebfb 100644 --- a/doc/man/MacroServer.1 +++ b/doc/man/MacroServer.1 @@ -1,38 +1,18 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH MACROSERVER "1" "September 2019" "MacroServer 2.8.3" "User Commands" +.TH MacroServer "1" .SH NAME -MacroServer \- manual page for MacroServer 2.8.3 +MacroServer \- Sardana MacroServer Tango device server .SH SYNOPSIS -.B usage: -\fI\,MacroServer instance_name \/\fR[\fI\,options\/\fR] -.SH OPTIONS -.TP -\fB\-\-version\fR -show program's version number and exit -.TP -\fB\-h\fR, \fB\-\-help\fR -show this help message and exit -.TP -\fB\-\-log\-level\fR=\fI\,LOG_LEVEL\/\fR -log output level. Possible values are (case -sensitive): critical (or 0), error (1), warning (2), -info (3) debug (4), trace (5) [default: warning] -.TP -\fB\-\-log\-file\-level\fR=\fI\,LOG_FILE_LEVEL\/\fR -log file level. Possible values are (case sensitive): -critical (or 0), error (1), warning (2), info (3) -debug (4), trace (5) [default: debug]. Ignored if -\fB\-\-without\-log\-file\fR is True -.TP -\fB\-\-log\-file\-name\fR=\fI\,LOG_FILE_NAME\/\fR -log file name. When given, MUST be absolute file name. -[default: /tmp/tango///log.txt]. Ignored if \fB\-\-without\-log\-file\fR is True -.TP -\fB\-\-without\-log\-file\fR=\fI\,WITHOUT_LOG_FILE\/\fR -When set to True disables logging into a file -[default: False] -.TP -\fB\-\-rconsole\-port\fR=\fI\,RCONSOLE_PORT\/\fR -rconsole port number. [default: 0 meaning rconsole NOT -active] +.B MacroServer +\fI\,[instance_name] \/ +.SH DESCRIPTION +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBMacroServer\fP command launches the Sardana MacroServer Tango +device server. + +The best source for information about the available options is +to run `MacroServer --help`. + +For more information about the Sardana project, visit +http://sardana-controls.org. diff --git a/doc/man/Pool.1 b/doc/man/Pool.1 index 14a3bec9c2..1c5272c8ad 100644 --- a/doc/man/Pool.1 +++ b/doc/man/Pool.1 @@ -1,38 +1,17 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH POOL "1" "September 2019" "Pool 2.8.3" "User Commands" +.TH POOL "1" .SH NAME -Pool \- manual page for Pool 2.8.3 +Pool \- Sardana Pool Tango device server .SH SYNOPSIS -.B usage: -\fI\,Pool instance_name \/\fR[\fI\,options\/\fR] -.SH OPTIONS -.TP -\fB\-\-version\fR -show program's version number and exit -.TP -\fB\-h\fR, \fB\-\-help\fR -show this help message and exit -.TP -\fB\-\-log\-level\fR=\fI\,LOG_LEVEL\/\fR -log output level. Possible values are (case -sensitive): critical (or 0), error (1), warning (2), -info (3) debug (4), trace (5) [default: warning] -.TP -\fB\-\-log\-file\-level\fR=\fI\,LOG_FILE_LEVEL\/\fR -log file level. Possible values are (case sensitive): -critical (or 0), error (1), warning (2), info (3) -debug (4), trace (5) [default: debug]. Ignored if -\fB\-\-without\-log\-file\fR is True -.TP -\fB\-\-log\-file\-name\fR=\fI\,LOG_FILE_NAME\/\fR -log file name. When given, MUST be absolute file name. -[default: /tmp/tango///log.txt]. Ignored if \fB\-\-without\-log\-file\fR is True -.TP -\fB\-\-without\-log\-file\fR=\fI\,WITHOUT_LOG_FILE\/\fR -When set to True disables logging into a file -[default: False] -.TP -\fB\-\-rconsole\-port\fR=\fI\,RCONSOLE_PORT\/\fR -rconsole port number. [default: 0 meaning rconsole NOT -active] +.B Pool +\fI\,[instance_name] \/ +.SH DESCRIPTION +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBPool\fP command launches the Sardana Pool Tango device server. + +The best source for information about the available options is +to run `Pool --help`. + +For more information about the Sardana project, visit +http://sardana-controls.org. diff --git a/doc/man/Sardana.1 b/doc/man/Sardana.1 index 74156ec91c..c42bc10658 100644 --- a/doc/man/Sardana.1 +++ b/doc/man/Sardana.1 @@ -1,38 +1,17 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH SARDANA "1" "September 2019" "Sardana 2.8.3" "User Commands" +.TH SARDANA "1" .SH NAME -Sardana \- manual page for Sardana 2.8.3 +Sardana \- Sardana Tango device server .SH SYNOPSIS -.B usage: -\fI\,Sardana instance_name \/\fR[\fI\,options\/\fR] -.SH OPTIONS -.TP -\fB\-\-version\fR -show program's version number and exit -.TP -\fB\-h\fR, \fB\-\-help\fR -show this help message and exit -.TP -\fB\-\-log\-level\fR=\fI\,LOG_LEVEL\/\fR -log output level. Possible values are (case -sensitive): critical (or 0), error (1), warning (2), -info (3) debug (4), trace (5) [default: warning] -.TP -\fB\-\-log\-file\-level\fR=\fI\,LOG_FILE_LEVEL\/\fR -log file level. Possible values are (case sensitive): -critical (or 0), error (1), warning (2), info (3) -debug (4), trace (5) [default: debug]. Ignored if -\fB\-\-without\-log\-file\fR is True -.TP -\fB\-\-log\-file\-name\fR=\fI\,LOG_FILE_NAME\/\fR -log file name. When given, MUST be absolute file name. -[default: /tmp/tango///log.txt]. Ignored if \fB\-\-without\-log\-file\fR is True -.TP -\fB\-\-without\-log\-file\fR=\fI\,WITHOUT_LOG_FILE\/\fR -When set to True disables logging into a file -[default: False] -.TP -\fB\-\-rconsole\-port\fR=\fI\,RCONSOLE_PORT\/\fR -rconsole port number. [default: 0 meaning rconsole NOT -active] +.B Sardana +\fI\,[instance_name] \/ +.SH DESCRIPTION +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBSardana\fP command launches the Sardana Tango device server. + +The best source for information about the available options is +to run `Sardana --help`. + +For more information about the Sardana project, visit +http://sardana-controls.org. diff --git a/doc/man/diffractometeralignment.1 b/doc/man/diffractometeralignment.1 index f36a7b9039..1967c24e90 100644 --- a/doc/man/diffractometeralignment.1 +++ b/doc/man/diffractometeralignment.1 @@ -1,43 +1,18 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH DIFFRACTOMETERALIGNMENT "1" "September 2019" "diffractometeralignment 2.8.3" "User Commands" +.TH DIFFRACTOMETERALIGNMENT "1" .SH NAME -diffractometeralignment \- manual page for diffractometeralignment 2.8.3 +diffractometeralignment \- Sardana diffractometer alignment GUI .SH SYNOPSIS .B diffractometeralignment \fI\, \/\fR[\fI\,door_name\/\fR] .SH DESCRIPTION -a taurus application for diffractometer alignment: h, k, l movements and -scans, go to maximum, ... -.SH OPTIONS -.TP -\fB\-h\fR, \fB\-\-help\fR -show this help message and exit -.TP -\fB\-\-version\fR -show program's version number and exit -.IP -Taurus Options: -.IP -Basic options present in any taurus application -.TP -\fB\-\-taurus\-log\-level\fR=\fI\,LEVEL\/\fR -taurus log level. Allowed values are (case -insensitive): critical, error, warning/warn, info, -debug, trace -.TP -\fB\-\-taurus\-polling\-period\fR=\fI\,MILLISEC\/\fR -taurus global polling period in milliseconds -.TP -\fB\-\-taurus\-serialization\-mode\fR=\fI\,SERIAL\/\fR -taurus serialization mode. Allowed values are (case -insensitive): serial, concurrent (default) -.TP -\fB\-\-tango\-host\fR=\fI\,TANGO_HOST\/\fR -Tango host name (either HOST:PORT or a Taurus URI, -e.g. tango://foo:1234) -.TP -\fB\-\-remote\-console\-port\fR=\fI\,PORT\/\fR -enables remote debugging using the given port -.TP -\fB\-\-default\-formatter\fR=\fI\,FORMATTER\/\fR -Override the default formatter +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBdiffractometeralignment\fP command is the GUI application +for diffractometer alignment: h, k, l movements and scans, go to maximum, ... + +The best source for information about the available options is +to run `diffractometeralignment --help`. + +For more information about the Sardana project, visit +http://sardana-controls.org. diff --git a/doc/man/hklscan.1 b/doc/man/hklscan.1 index 5f35b0b1bc..50d5f4a0d8 100644 --- a/doc/man/hklscan.1 +++ b/doc/man/hklscan.1 @@ -1,42 +1,17 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH HKLSCAN "1" "September 2019" "hklscan 2.8.3" "User Commands" +.TH HKLSCAN "1" .SH NAME -hklscan \- manual page for hklscan 2.8.3 +hklscan \- Sardana HKL scan GUI .SH SYNOPSIS .B hklscan \fI\, \/\fR[\fI\,door_name\/\fR] .SH DESCRIPTION -a taurus application for performing hkl scans -.SH OPTIONS -.TP -\fB\-h\fR, \fB\-\-help\fR -show this help message and exit -.TP -\fB\-\-version\fR -show program's version number and exit -.IP -Taurus Options: -.IP -Basic options present in any taurus application -.TP -\fB\-\-taurus\-log\-level\fR=\fI\,LEVEL\/\fR -taurus log level. Allowed values are (case -insensitive): critical, error, warning/warn, info, -debug, trace -.TP -\fB\-\-taurus\-polling\-period\fR=\fI\,MILLISEC\/\fR -taurus global polling period in milliseconds -.TP -\fB\-\-taurus\-serialization\-mode\fR=\fI\,SERIAL\/\fR -taurus serialization mode. Allowed values are (case -insensitive): serial, concurrent (default) -.TP -\fB\-\-tango\-host\fR=\fI\,TANGO_HOST\/\fR -Tango host name (either HOST:PORT or a Taurus URI, -e.g. tango://foo:1234) -.TP -\fB\-\-remote\-console\-port\fR=\fI\,PORT\/\fR -enables remote debugging using the given port -.TP -\fB\-\-default\-formatter\fR=\fI\,FORMATTER\/\fR -Override the default formatter +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBhklscan\fP command is the GUI application for performing hkl scans. + +The best source for information about the available options is +to run `hklscan --help`. + +For more information about the Sardana project, visit +http://sardana-controls.org. \ No newline at end of file diff --git a/doc/man/macroexecutor.1 b/doc/man/macroexecutor.1 index 759b60677f..12e6f5e3f0 100644 --- a/doc/man/macroexecutor.1 +++ b/doc/man/macroexecutor.1 @@ -1,40 +1,17 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH MACROEXECUTOR "1" "September 2019" "macroexecutor 2.8.3" "User Commands" +.TH MACROEXECUTOR "1" .SH NAME -macroexecutor \- manual page for macroexecutor 2.8.3 +macroexecutor \- Sardana GUI application for executing macros .SH SYNOPSIS .B macroexecutor -[\fI\,options\/\fR] -.SH OPTIONS -.TP -\fB\-h\fR, \fB\-\-help\fR -show this help message and exit -.TP -\fB\-\-version\fR -show program's version number and exit -.IP -Taurus Options: -.IP -Basic options present in any taurus application -.TP -\fB\-\-taurus\-log\-level\fR=\fI\,LEVEL\/\fR -taurus log level. Allowed values are (case -insensitive): critical, error, warning/warn, info, -debug, trace -.TP -\fB\-\-taurus\-polling\-period\fR=\fI\,MILLISEC\/\fR -taurus global polling period in milliseconds -.TP -\fB\-\-taurus\-serialization\-mode\fR=\fI\,SERIAL\/\fR -taurus serialization mode. Allowed values are (case -insensitive): serial, concurrent (default) -.TP -\fB\-\-tango\-host\fR=\fI\,TANGO_HOST\/\fR -Tango host name (either HOST:PORT or a Taurus URI, -e.g. tango://foo:1234) -.TP -\fB\-\-remote\-console\-port\fR=\fI\,PORT\/\fR -enables remote debugging using the given port -.TP -\fB\-\-default\-formatter\fR=\fI\,FORMATTER\/\fR -Override the default formatter +\fI\, \/\fR[\fI\,door_name\/\fR] +.SH DESCRIPTION +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBmacroexecutor\fP command is the GUI application for executing macros. + +The best source for information about the available options is +to run `macroexecutor --help`. + +For more information about the Sardana project, visit +http://sardana-controls.org. diff --git a/doc/man/sardanatestsuite.1 b/doc/man/sardanatestsuite.1 index 3ffab16d95..9777120f26 100644 --- a/doc/man/sardanatestsuite.1 +++ b/doc/man/sardanatestsuite.1 @@ -1,19 +1,16 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH SARDANATESTSUITE "1" "September 2019" "sardanatestsuite 2.8.3" "User Commands" +.TH SARDANATESTSUITE "1" .SH NAME -sardanatestsuite \- manual page for sardanatestsuite 2.8.3 +sardanatestsuite \- Sardana test suite +.SH SYNOPSIS +.B sardanatestsuite .SH DESCRIPTION -usage: sardanatestsuite [\-h] [\-e EXCLUDE_PATTERN] [\-\-version] -.PP -Main test suite for Sardana -.SS "optional arguments:" -.TP -\fB\-h\fR, \fB\-\-help\fR -show this help message and exit -.TP -\fB\-e\fR EXCLUDE_PATTERN, \fB\-\-exclude\-pattern\fR EXCLUDE_PATTERN -regexp pattern matching test ids to be excluded. (e.g. -\&'sardana\e.pool\e..*' would exclude sardana.pool tests) -.TP -\fB\-\-version\fR -show program's version number and exit +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBsardanatestsuite\fP command launches the Sardana test suite. + +The best source for information about the available options is +to run `sardanatestsuite --help`. + +For more information about the Sardana project, visit +http://sardana-controls.org. diff --git a/doc/man/sequencer.1 b/doc/man/sequencer.1 index 70b964460e..2e5dd048ac 100644 --- a/doc/man/sequencer.1 +++ b/doc/man/sequencer.1 @@ -1,46 +1,19 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH SEQUENCER "1" "September 2019" "sequencer 2.8.3" "User Commands" +.TH SEQUENCER "1" .SH NAME -sequencer \- manual page for sequencer 2.8.3 +sequencer \- Sardana GUI application for executing macro sequences .SH SYNOPSIS .B sequencer -[\fI\,options\/\fR] +\fI\, \/\fR[\fI\,door_name\/\fR] .SH DESCRIPTION -Sardana macro sequencer. It allows the creation of sequences of macros, -executed one after the other. The sequences can be stored under xml files -.SH OPTIONS -.TP -\fB\-h\fR, \fB\-\-help\fR -show this help message and exit -.TP -\fB\-f\fR FILE, \fB\-\-file\fR=\fI\,FILE\/\fR -load macro sequence from a file(XML or spock syntax) -.TP -\fB\-\-version\fR -show program's version number and exit -.IP -Taurus Options: -.IP -Basic options present in any taurus application -.TP -\fB\-\-taurus\-log\-level\fR=\fI\,LEVEL\/\fR -taurus log level. Allowed values are (case -insensitive): critical, error, warning/warn, info, -debug, trace -.TP -\fB\-\-taurus\-polling\-period\fR=\fI\,MILLISEC\/\fR -taurus global polling period in milliseconds -.TP -\fB\-\-taurus\-serialization\-mode\fR=\fI\,SERIAL\/\fR -taurus serialization mode. Allowed values are (case -insensitive): serial, concurrent (default) -.TP -\fB\-\-tango\-host\fR=\fI\,TANGO_HOST\/\fR -Tango host name (either HOST:PORT or a Taurus URI, -e.g. tango://foo:1234) -.TP -\fB\-\-remote\-console\-port\fR=\fI\,PORT\/\fR -enables remote debugging using the given port -.TP -\fB\-\-default\-formatter\fR=\fI\,FORMATTER\/\fR -Override the default formatter +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBsequencer\fP command is the GUI application for executing macro +sequences. It allows creation of sequences of macros, +executed one after the other. The sequences can be stored under xml files. + +The best source for information about the available options is +to run `sequencer --help`. + +For more information about the Sardana project, visit +http://sardana-controls.org. diff --git a/doc/man/spock.1 b/doc/man/spock.1 index 32ec46ba2f..a95b5b2d15 100644 --- a/doc/man/spock.1 +++ b/doc/man/spock.1 @@ -1,371 +1,16 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH SPOCK "1" "September 2019" "spock 2.8.2" "User Commands" +.TH SPOCK "1" .SH NAME -spock \- manual page for spock 2.8.2 +spock \- Sardana CLI application +.SH SYNOPSIS +.B spock .SH DESCRIPTION -========= -.IP -IPython -.PP -========= -.PP -Tools for Interactive Computing in Python -========================================= -.IP -A Python shell with automatic history (input and output), dynamic object -introspection, easier configuration, command completion, access to the -system shell and more. IPython can also be embedded in running programs. -.PP -Usage -.IP -ipython [subcommand] [options] [\-c cmd | \fB\-m\fR mod | file] [\-\-] [arg] ... -.IP -If invoked with no options, it executes the file and exits, passing the -remaining arguments to the script, just as if you had specified the same -command with python. You may need to specify `\-\-` before args to be passed -to the script, to prevent IPython from attempting to parse them. If you -specify the option `\-i` before the filename, it will enter an interactive -IPython session after running the script, rather than exiting. Files ending -in .py will be treated as normal Python, but files ending in .ipy can -contain special IPython syntax (magic commands, shell expansions, etc.). -.IP -Almost all configuration in IPython is available via the command\-line. Do -`ipython \fB\-\-help\-all\fR` to see all available options. For persistent -configuration, look into your `ipython_config.py` configuration file for -details. -.IP -This file is typically installed in the `IPYTHONDIR` directory, and there -is a separate configuration directory for each profile. The default profile -directory will be located in \fI\,$IPYTHONDIR/profile_default\/\fP. IPYTHONDIR -defaults to to `$HOME/.ipython`. For Windows users, $HOME resolves to -C:\eUsers\eYourUserName in most instances. -.IP -To initialize a profile with the default configuration file, do:: -.IP -$> ipython profile create -.IP -and start editing `IPYTHONDIR/profile_default/ipython_config.py` -.IP -In IPython's documentation, we will refer to this directory as -`IPYTHONDIR`, you can change its default location by creating an -environment variable with this name and setting it to the desired path. -.IP -For more information, see the manual available in HTML and PDF in your -installation, or online at http://ipython.org/documentation.html. -.PP -Subcommands -\fB\-\-\-\-\-\-\-\-\-\-\-\fR -.PP -Subcommands are launched as `ipython cmd [args]`. For information on using -subcommand 'cmd', do: `ipython cmd \fB\-h\fR`. -.PP -locate -.IP -print the path to the IPython dir -.PP -kernel -.IP -Start a kernel without an attached frontend. -.PP -console -.IP -DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter terminal\-based Console. -.PP -install\-nbextension -.IP -DEPRECATED, Will be removed in IPython 6.0 : Install Jupyter notebook extension files -.PP -kernelspec -.IP -DEPRECATED, Will be removed in IPython 6.0 : Manage Jupyter kernel specifications. -.PP -notebook -.IP -DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter HTML Notebook Server. -.PP -profile -.IP -Create and manage IPython profiles. -.PP -qtconsole -.IP -DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter Qt Console. -.PP -nbconvert -.IP -DEPRECATED, Will be removed in IPython 6.0 : Convert notebooks to/from other formats. -.PP -trust -.IP -DEPRECATED, Will be removed in IPython 6.0 : Sign notebooks to trust their potentially unsafe contents at load. -.PP -history -.IP -Manage the IPython history database. -.PP -Options -\fB\-\-\-\-\-\-\-\fR -.PP -Arguments that take values are actually convenience aliases to full -Configurables, whose aliases are listed on the help line. For more information -on full configurables, see '\-\-help\-all'. -.PP -\fB\-\-no\-autoindent\fR -.IP -Turn off autoindenting. -.PP -\fB\-\-autoedit\-syntax\fR -.IP -Turn on auto editing of files with syntax errors. -.PP -\fB\-\-pylab\fR -.IP -Pre\-load matplotlib and numpy for interactive use with -the default matplotlib backend. -.PP -\fB\-\-simple\-prompt\fR -.IP -Force simple minimal prompt using `raw_input` -.PP -\fB\-\-confirm\-exit\fR -.IP -Set to confirm when you try to exit IPython with an EOF (Control\-D -in Unix, Control\-Z/Enter in Windows). By typing 'exit' or 'quit', -you can force a direct exit without any confirmation. -.PP -\fB\-\-no\-autoedit\-syntax\fR -.IP -Turn off auto editing of files with syntax errors. -.PP -\fB\-\-matplotlib\fR -.IP -Configure matplotlib for interactive use with -the default matplotlib backend. -.PP -\fB\-\-term\-title\fR -.IP -Enable auto setting the terminal title. -.PP -\fB\-\-no\-confirm\-exit\fR -.IP -Don't prompt the user when exiting. -.PP -\fB\-\-autoindent\fR -.IP -Turn on autoindenting. -.PP -\fB\-\-no\-automagic\fR -.IP -Turn off the auto calling of magic commands. -.PP -\fB\-\-no\-simple\-prompt\fR -.IP -Use a rich interactive prompt with prompt_toolkit -.PP -\fB\-\-banner\fR -.IP -Display a banner upon starting IPython. -.PP -\fB\-\-automagic\fR -.IP -Turn on the auto calling of magic commands. Type %%magic at the -IPython prompt for more information. -.PP -\fB\-\-no\-term\-title\fR -.IP -Disable auto setting the terminal title. -.PP -\fB\-\-nosep\fR -.IP -Eliminate all spacing between prompts. -.PP -\fB\-i\fR -.IP -If running code from the command line, become interactive afterwards. -It is often useful to follow this with `\-\-` to treat remaining flags as -script arguments. -.PP -\fB\-\-debug\fR -.IP -set log level to logging.DEBUG (maximize logging output) -.PP -\fB\-\-classic\fR -.IP -Gives IPython a similar feel to the classic Python prompt. -.PP -\fB\-\-quiet\fR -.IP -set log level to logging.CRITICAL (minimize logging output) -.PP -\fB\-\-pprint\fR -.IP -Enable auto pretty printing of results. -.PP -\fB\-\-pdb\fR -.IP -Enable auto calling the pdb debugger after every exception. -.PP -\fB\-\-color\-info\fR -.IP -IPython can display information about objects via a set of functions, -and optionally can use colors for this, syntax highlighting -source code and various other elements. This is on by default, but can cause -problems with some pagers. If you see such problems, you can disable the -colours. -.PP -\fB\-\-init\fR -.TP -Initialize profile with default config files. -This is equivalent -.IP -to running `ipython profile create ` prior to startup. -.PP -\fB\-\-no\-pdb\fR -.IP -Disable auto calling the pdb debugger after every exception. -.PP -\fB\-\-quick\fR -.IP -Enable quick startup with no config files. -.PP -\fB\-\-no\-color\-info\fR -.IP -Disable using colors for info related things. -.PP -\fB\-\-no\-pprint\fR -.IP -Disable auto pretty printing of results. -.PP -\fB\-\-no\-banner\fR -.IP -Don't display a banner upon starting IPython. -.PP -\fB\-\-profile=\fR (BaseIPythonApplication.profile) -.IP -Default: u'default' -The IPython profile to use. -.PP -\fB\-\-pylab=\fR (InteractiveShellApp.pylab) -.IP -Default: None -Choices: [u'auto', u'gtk', u'gtk3', u'inline', u'nbagg', u'notebook', u'osx', u'qt', u'qt4', u'qt5', u'tk', u'wx'] -Pre\-load matplotlib and numpy for interactive use, selecting a particular -matplotlib backend and loop integration. -.PP -\fB\-\-matplotlib=\fR (InteractiveShellApp.matplotlib) -.IP -Default: None -Choices: [u'auto', u'gtk', u'gtk3', u'inline', u'nbagg', u'notebook', u'osx', u'qt', u'qt4', u'qt5', u'tk', u'wx'] -Configure matplotlib for interactive use with the default matplotlib -backend. -.PP -\fB\-\-colors=\fR (InteractiveShell.colors) -.IP -Default: 'Neutral' -Choices: [u'Neutral', u'NoColor', u'LightBG', u'Linux'] -Set the color scheme (NoColor, Neutral, Linux, or LightBG). -.PP -\fB\-\-cache\-size=\fR (InteractiveShell.cache_size) -.IP -Default: 1000 -Set the size of the output cache. The default is 1000, you can change it -permanently in your config file. Setting it to 0 completely disables the -caching system, and the minimum value accepted is 20 (if you provide a value -less than 20, it is reset to 0 and a warning is issued). This limit is -defined because otherwise you'll spend more time re\-flushing a too small -cache than working -.PP -\fB\-\-logfile=\fR (InteractiveShell.logfile) -.IP -Default: '' -The name of the logfile to use. -.PP -\fB\-\-profile\-dir=\fR (ProfileDir.location) -.IP -Default: u'' -Set the profile location directly. This overrides the logic used by the -`profile` option. -.PP -\fB\-c\fR (InteractiveShellApp.code_to_run) -.IP -Default: '' -Execute the given command string. -.PP -\fB\-\-autocall=\fR (InteractiveShell.autocall) -.IP -Default: 0 -Choices: (0, 1, 2) -Make IPython automatically call any callable object even if you didn't type -explicit parentheses. For example, 'str 43' becomes 'str(43)' automatically. -The value can be '0' to disable the feature, '1' for 'smart' autocall, where -it is not applied if there are no more arguments on the line, and '2' for -\&'full' autocall, where all callable objects are automatically called (even -if no arguments are present). -.PP -\fB\-\-ipython\-dir=\fR (BaseIPythonApplication.ipython_dir) -.IP -Default: u'' -The name of the IPython directory. This directory is used for logging -configuration (through profiles), history storage, etc. The default is -usually $HOME/.ipython. This option can also be specified through the -environment variable IPYTHONDIR. -.PP -\fB\-\-gui=\fR (InteractiveShellApp.gui) -.IP -Default: None -Choices: [u'glut', u'gtk', u'gtk2', u'gtk3', u'osx', u'pyglet', u'qt', u'qt4', u'qt5', u'tk', u'wx', u'gtk2', u'qt4'] -Enable GUI event loop integration with any of ('glut', 'gtk', 'gtk2', -\&'gtk3', 'osx', 'pyglet', 'qt', 'qt4', 'qt5', 'tk', 'wx', 'gtk2', 'qt4'). -.PP -\fB\-\-logappend=\fR (InteractiveShell.logappend) -.IP -Default: '' -Start logging to the given file in append mode. Use `logfile` to specify a -log file to **overwrite** logs to. -.PP -\fB\-m\fR (InteractiveShellApp.module_to_run) -.IP -Default: '' -Run the module as a script. -.PP -\fB\-\-log\-level=\fR (Application.log_level) -.IP -Default: 30 -Choices: (0, 10, 20, 30, 40, 50, 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL') -Set the log level by value or name. -.PP -\fB\-\-ext=\fR (InteractiveShellApp.extra_extension) -.IP -Default: '' -dotted module name of an IPython extension to load. -.PP -\fB\-\-config=\fR (BaseIPythonApplication.extra_config_file) -.IP -Default: u'' -Path to an extra config file to load. -If specified, load this config file in addition to any other IPython config. -.PP -To see all available configurables, use `\-\-help\-all` -.PP -Examples -\fB\-\-\-\-\-\-\-\-\fR -.TP -ipython \fB\-\-matplotlib\fR -# enable matplotlib integration -.TP -ipython \fB\-\-matplotlib\fR=\fI\,qt\/\fR -# enable matplotlib integration with qt4 backend -.TP -ipython \fB\-\-log\-level\fR=\fI\,DEBUG\/\fR -# set logging to DEBUG -.TP -ipython \fB\-\-profile\fR=\fI\,foo\/\fR -# start with profile foo -.IP -ipython profile create foo # create profile foo w/ default config files -ipython help profile # show the help for the profile subcmd -.TP -ipython locate -# print the path to the IPython directory -.IP -ipython locate profile foo # print the path to the directory for profile `foo` +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBspock\fP command launches the Sardana CLI application based on IPython. + +The best source for information about the available options is +to visit the Spock documentation: http://sardana-controls.org/users/spock.html. + +For more information about the Sardana project, visit +http://sardana-controls.org. diff --git a/doc/man/ubmatrix.1 b/doc/man/ubmatrix.1 index ae975951d0..393a976323 100644 --- a/doc/man/ubmatrix.1 +++ b/doc/man/ubmatrix.1 @@ -1,43 +1,18 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH UBMATRIX "1" "September 2019" "ubmatrix 2.8.3" "User Commands" +.TH UBMATRIX "1" .SH NAME -ubmatrix \- manual page for ubmatrix 2.8.3 +ubmatrix \- Sardana GUI application for setting diffractomer parameters .SH SYNOPSIS .B ubmatrix -\fI\,\/\fR +\fI\, \/\fR[\fI\,door_name\/\fR] .SH DESCRIPTION -a taurus application for setting diffractometer parameters: ubmatrix, lattice, -reflections, ... -.SH OPTIONS -.TP -\fB\-h\fR, \fB\-\-help\fR -show this help message and exit -.TP -\fB\-\-version\fR -show program's version number and exit -.IP -Taurus Options: -.IP -Basic options present in any taurus application -.TP -\fB\-\-taurus\-log\-level\fR=\fI\,LEVEL\/\fR -taurus log level. Allowed values are (case -insensitive): critical, error, warning/warn, info, -debug, trace -.TP -\fB\-\-taurus\-polling\-period\fR=\fI\,MILLISEC\/\fR -taurus global polling period in milliseconds -.TP -\fB\-\-taurus\-serialization\-mode\fR=\fI\,SERIAL\/\fR -taurus serialization mode. Allowed values are (case -insensitive): serial, concurrent (default) -.TP -\fB\-\-tango\-host\fR=\fI\,TANGO_HOST\/\fR -Tango host name (either HOST:PORT or a Taurus URI, -e.g. tango://foo:1234) -.TP -\fB\-\-remote\-console\-port\fR=\fI\,PORT\/\fR -enables remote debugging using the given port -.TP -\fB\-\-default\-formatter\fR=\fI\,FORMATTER\/\fR -Override the default formatter +Sardana is a software suite for Supervision, Control and Data Acquisition +in scientific installations. + +The \fBubmatrix\fP command is the GUI application for setting diffractometer +parameters: ubmatrix, lattice, reflections, ... + +The best source for information about the available options is +to run `ubmatrix --help`. + +For more information about the Sardana project, visit +http://sardana-controls.org. From 9f6895f0901cddc8ab69cc334e25ff3a702466d3 Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Thu, 14 Nov 2019 09:30:22 +0100 Subject: [PATCH 281/830] Create .gitlab-ci-alba.yml Add a yml file for configuring the CI/CD for creating (unofficial) debian packages for sardana at ALBA TODO: generalise this so that it does not depend on ALBA's infrastructure --- .gitlab-ci-alba.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .gitlab-ci-alba.yml diff --git a/.gitlab-ci-alba.yml b/.gitlab-ci-alba.yml new file mode 100644 index 0000000000..90f768f8bc --- /dev/null +++ b/.gitlab-ci-alba.yml @@ -0,0 +1,8 @@ +# This file is for configuring the CI/CD for creating (unofficial) +# debian packages for sardana at ALBA +# It has no efect unless you configure your gitlab instance to use it. +# TODO: generalise this so that it does not depend on ALBA's infrastructure + +include: +- https://git.cells.es/ctpkg/ci/ctpipeline/raw/master/ctjobdefs-ci.yml +- https://git.cells.es/ctpkg/ci/ctpipeline/raw/master/ctpipeline.yml From b52bb0cbb81f9d202d4e94f5e45978779ef7ed25 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 22 Nov 2019 07:41:50 +0100 Subject: [PATCH 282/830] Fix #1237: Macro functions with result don't return any result Macro functions are just instances of the same MacroFunction class. For every macro function call, the macroserver creates an instance of MacroFunction. The constructor of MacroFunction is already overwriting the values of param_def and result_def so promoting hasResult() from classmethod to method, should be sufficient to allow each macro function to have distinct results. --- src/sardana/macroserver/macro.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 696ddc6817..dc20375ccd 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -2235,15 +2235,14 @@ def isPaused(self): """**Unofficial Macro API**.""" return self._pause_event.isPaused() - @classmethod - def hasResult(cls): + def hasResult(self): """**Unofficial Macro API**. Returns True if the macro should return a result or False otherwise :return: True if the macro should return a result or False otherwise :rtype: bool """ - return len(cls.result_def) > 0 + return len(self.result_def) > 0 def getResult(self): """**Unofficial Macro API**. Returns the macro result object (if any) From 2db33991cea21377135820d8e46ef0dff36a8500 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 22 Nov 2019 11:55:55 +0100 Subject: [PATCH 283/830] Fix macro function environment and hints --- src/sardana/macroserver/macro.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 696ddc6817..f8f13cbc70 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -403,8 +403,8 @@ def __call__(self, fn): fn.macro_data = {} fn.param_def = self.param_def fn.result_def = self.result_def - fn.hints = self.env - fn.env = self.hints + fn.hints = self.hints + fn.env = self.env fn.interactive = self.interactive return fn From 61e22f106e5f526250cf3e1e7e048c50c3897841 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 22 Nov 2019 14:48:37 +0100 Subject: [PATCH 284/830] Fix RunMacro execution with string parameters without extra quotes In spock syntax when you use string parameters with white spaces you need to use quotes. However when you use RunMacro Door's command, you already pass string parameters as DevVarStringArray separate items. Then it should not mandatory to use quotes for string parameters with white spaces. Fix it by quoting this parameter values when necessary. --- src/sardana/macroserver/msmacromanager.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 6033769c0b..0090bec856 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -719,8 +719,28 @@ def getMacroInfo(self, macro_names, format='json'): def _createMacroNode(self, macro_name, macro_params_raw): macro = self.getMacro(macro_name) params_def = macro.get_parameter() + + def quote_string(string): + # if string contains double quotes, use single quotes, otherwise + # use double quotes + if re.search('"', string): + return "'{}'".format(string) + else: + return '"{}"'.format(string) + + # param parser relies on whitespace separation of parameter values + # quote string parameter values when necessary + macro_params_quoted = [] + for param_def, param_raw in zip(params_def, macro_params_raw): + if (not param_def["type"] == "String" # not string parameter + or not re.match(".*\s+.*", param_raw) # no white spaces + or re.match("^'.*\s+.*'$", param_raw) # already quoted + or re.match('^".*\s+.*"$', param_raw)): # already quoted + macro_params_quoted.append(param_raw) + else: + macro_params_quoted.append(quote_string(param_raw)) # merge params to a single, space separated, string (spock like) - macro_params_str = " ".join(macro_params_raw) + macro_params_str = " ".join(macro_params_quoted) param_parser = ParamParser(params_def) # parse string with macro params to the correct list representation macro_params = param_parser.parse(macro_params_str) From 774ff4c22f73774e7ef6de54163a6af28f73b40f Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 25 Nov 2019 13:03:46 +0100 Subject: [PATCH 285/830] Improvement of few documentation issues --- doc/source/devel/howto_macros/macros_general.rst | 11 +++++++++++ doc/source/users/adding_elements.rst | 13 +++++++++++-- doc/source/users/spock.rst | 4 +++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst index 03758cd313..c88f59063e 100644 --- a/doc/source/devel/howto_macros/macros_general.rst +++ b/doc/source/devel/howto_macros/macros_general.rst @@ -1530,6 +1530,17 @@ it. The job begins to run when added. When the job finishes, it sets the The second method is to use standard Python threading_ library. +Adding your macros to Sardana +----------------------------- + +To add your macros to Sardana, you need to configure the macro plugin discovery +path (MacroPath property):: + + _MACRO_SERVER.put_property({"MacroPath":[""]}) + +.. note:: + You can add more than one path, but be careful! The path order is important. + Macros in the higher position paths will take precedence over the lower position paths. .. rubric:: Footnotes diff --git a/doc/source/users/adding_elements.rst b/doc/source/users/adding_elements.rst index dc9aff1b4d..198d42732b 100644 --- a/doc/source/users/adding_elements.rst +++ b/doc/source/users/adding_elements.rst @@ -23,8 +23,14 @@ plugin class into the Sardana. To check if it is already loaded use the :class:`~sardana.macroserver.macros.lists.lsctrl` macro. If it is not, you will need to configure the :ref:`controller plugin discovery path ` (``PoolPath`` property) and either restart the Sardana server or call the -:class:`~sardana.macroserver.macros.expert.addctrllib` macro. After that -check again with the list macro if the controller class is present and if +:class:`~sardana.macroserver.macros.expert.addctrllib` macro:: + + Pool__.put_property({"PoolPath":[""]}) + + Example: + Pool_demo1_1.put_property({"PoolPath":["/home/vagrant/controllers"]}) + +After that check again with the list macro if the controller class is present and if yes let's continue... To create a controller instance you can use @@ -41,6 +47,9 @@ our IcePAP system:: defctrl IcepapController ipap01 Host 10.0.0.30 Port 5000 +.. note:: + In order to use the controller you must define also a motor and use the created controller as a parameter + .. hint:: You can use the :class:`~sardana.macroserver.macros.expert.sar_info` macro to see the roles and properties available for a controller class. diff --git a/doc/source/users/spock.rst b/doc/source/users/spock.rst index 487750e880..236d9571b8 100644 --- a/doc/source/users/spock.rst +++ b/doc/source/users/spock.rst @@ -57,7 +57,9 @@ Afterward, spock :term:`CLI` will start normally: Spock's sardana extension 1.0 loaded with profile: spockdoor (linked to door 'LAB-01-D01') - LAB-01-D01 [1]: + LAB-01-D01 [1]: +.. note:: + If you want to connect to another gate you need to create a new spock profile. Starting spock with a custom profile ------------------------------------ From a268b9d4f0d17e1f38207e4cd6ecd2bdc05d2c71 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 25 Nov 2019 14:36:00 +0100 Subject: [PATCH 286/830] Relax quoting condition and don't check param definition Relax quoting condition and don't check param definition, this way any parameter containing spaces will be extra quoted. Also move this code to a method _preprocessParameters which name seems to fit better to this code. --- src/sardana/macroserver/msmacromanager.py | 44 +++++++++++------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 0090bec856..06f647eb7e 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -719,28 +719,7 @@ def getMacroInfo(self, macro_names, format='json'): def _createMacroNode(self, macro_name, macro_params_raw): macro = self.getMacro(macro_name) params_def = macro.get_parameter() - - def quote_string(string): - # if string contains double quotes, use single quotes, otherwise - # use double quotes - if re.search('"', string): - return "'{}'".format(string) - else: - return '"{}"'.format(string) - - # param parser relies on whitespace separation of parameter values - # quote string parameter values when necessary - macro_params_quoted = [] - for param_def, param_raw in zip(params_def, macro_params_raw): - if (not param_def["type"] == "String" # not string parameter - or not re.match(".*\s+.*", param_raw) # no white spaces - or re.match("^'.*\s+.*'$", param_raw) # already quoted - or re.match('^".*\s+.*"$', param_raw)): # already quoted - macro_params_quoted.append(param_raw) - else: - macro_params_quoted.append(quote_string(param_raw)) - # merge params to a single, space separated, string (spock like) - macro_params_str = " ".join(macro_params_quoted) + macro_params_str = " ".join(macro_params_raw) param_parser = ParamParser(params_def) # parse string with macro params to the correct list representation macro_params = param_parser.parse(macro_params_str) @@ -1156,7 +1135,26 @@ def _preprocessParameters(self, par_str_list): xml_root = xml_seq = etree.Element('sequence') macro_name = par_str_list[0] macro_params = par_str_list[1:] - macro_node = self._createMacroNode(macro_name, macro_params) + + def quote_string(string): + # if string contains double quotes, use single quotes, otherwise + # use double quotes + if re.search('"', string): + return "'{}'".format(string) + else: + return '"{}"'.format(string) + + # param parser relies on whitespace separation of parameter values + # quote string parameter values containing whitespaces + macro_params_quoted = [] + for param in macro_params: + if (not re.match(".*\s+.*", param) # no white spaces + or re.match("^'.*\s+.*'$", param) # already quoted + or re.match('^".*\s+.*"$', param)): # already quoted + macro_params_quoted.append(param) + else: + macro_params_quoted.append(quote_string(param)) + macro_node = self._createMacroNode(macro_name, macro_params_quoted) xml_macro = macro_node.toXml() xml_seq.append(xml_macro) else: From 4b184f40fb0d92fda442fd50ab4a2208b261a2c0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 25 Nov 2019 16:08:54 +0100 Subject: [PATCH 287/830] Fix flake8 --- src/sardana/macroserver/msmacromanager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 06f647eb7e..c60a0bfcb0 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1148,9 +1148,9 @@ def quote_string(string): # quote string parameter values containing whitespaces macro_params_quoted = [] for param in macro_params: - if (not re.match(".*\s+.*", param) # no white spaces - or re.match("^'.*\s+.*'$", param) # already quoted - or re.match('^".*\s+.*"$', param)): # already quoted + if (not re.match(r".*\s+.*", param) # no white spaces + or re.match(r"^'.*\s+.*'$", param) # already quoted + or re.match(r'^".*\s+.*"$', param)): # already quoted macro_params_quoted.append(param) else: macro_params_quoted.append(quote_string(param)) From 5e17ef20feae10ffa1a2d94b923ae0fdad57be8f Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 25 Nov 2019 16:32:46 +0100 Subject: [PATCH 288/830] Fix flake8 --- src/sardana/macroserver/msmacromanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index c60a0bfcb0..5d64b05a95 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1137,8 +1137,8 @@ def _preprocessParameters(self, par_str_list): macro_params = par_str_list[1:] def quote_string(string): - # if string contains double quotes, use single quotes, otherwise - # use double quotes + # if string contains double quotes, use single quotes, + # otherwise use double quotes if re.search('"', string): return "'{}'".format(string) else: From 73ff6144bc6db61b9f3aa12cff4e760826c6d036 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 26 Nov 2019 09:56:27 +0100 Subject: [PATCH 289/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ccf6ee050..a43e79e581 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Default macro parameter values in macroexecutor (#1153) * fscan macro that was broken 2.6.0 (#1218, #1220) +* Executing RunMacro Door's command with string parameters containing spaces (#1240) * Setting of environment variables in Python 3.7 (#1195) * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) From e8b382af4e636e3117258c04a32d454b8949075e Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 26 Nov 2019 09:58:05 +0100 Subject: [PATCH 290/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a43e79e581..be981dae13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) * Remove Taurus deprecated code what reduces deprecation warnings (#1206) +* Use of env and hints in `macro` function decorator (#1239) ### Deprecated From c9737fdaa151a8273a4a80e05c670260982e1ef5 Mon Sep 17 00:00:00 2001 From: aalonso Date: Tue, 26 Nov 2019 16:19:19 +0100 Subject: [PATCH 291/830] New feature on the context menu of outputDoor, that allows the user to see debug information that was seen on doorDebug. doorDebug is now deprecated. --- .../qtgui/extra_macroexecutor/dooroutput.py | 34 +++++++++++++++++++ .../qt/qtgui/macrolistener/macrolistener.py | 9 ++--- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py index 1a9af58a31..b0c19991de 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py @@ -31,6 +31,7 @@ from taurus.external.qt import Qt + class DoorOutput(Qt.QPlainTextEdit): """Widget used for displaying changes of door's attributes: Output, Info, Warning and Error.""" @@ -42,6 +43,10 @@ def __init__(self, parent=None): self.stopAction.setCheckable(True) self.stopAction.setChecked(False) self._isStopped = False + self.showDebug = Qt.QAction("Show debug details", self) + self.showDebug.setCheckable(True) + self.showDebug.setChecked(False) + self._isDebugging = False def onDoorOutputChanged(self, output): """call on output attribute changed""" @@ -91,6 +96,21 @@ def onDoorErrorChanged(self, error): txt += "" self.appendHtmlText(txt) + def onDoorDebugChanged(self, debug): + """call on debug attribute changed""" + txt = "" + if self._isDebugging: + if debug is None: + return + for i, line in enumerate(debug): + if i > 0: + txt += "
" + txt += line.replace(' ', ' ') + txt += "
" + self.appendHtmlText(txt) + if not self._isStopped: + self.moveCursor(Qt.QTextCursor.End) + def appendHtmlText(self, text): self.appendHtml(text) if not self._isStopped: @@ -101,18 +121,25 @@ def contextMenuEvent(self, event): clearAction = Qt.QAction("Clear", menu) menu.addAction(clearAction) menu.addAction(self.stopAction) + menu.addAction(self.showDebug) if not len(self.toPlainText()): clearAction.setEnabled(False) clearAction.triggered.connect(self.clear) self.stopAction.toggled.connect(self.stopScrolling) + self.showDebug.toggled.connect(self.showDebugDetails) menu.exec_(event.globalPos()) def stopScrolling(self, stop): self._isStopped = stop + def showDebugDetails(self, debug): + self._isDebugging = debug + class DoorDebug(Qt.QPlainTextEdit): + """Deprecated. Do not use""" + """Widget used for displaying changes of door's Debug attribute.""" def __init__(self, parent=None): @@ -124,6 +151,12 @@ def __init__(self, parent=None): self.stopAction.setChecked(False) self._isStopped = False + from taurus.core.util.log import warning + + msg = ("DoorDebug is deprecated since version Jan20. " + "Use DoorOutput 'Show debug details' feature instead.") + warning(msg) + def onDoorDebugChanged(self, debug): """call on debug attribute changed""" if debug is None: @@ -225,6 +258,7 @@ def eventReceived(self, src, type, value): door.infoUpdated.connect(doorOutput.onDoorInfoChanged) door.warningUpdated.connect(doorOutput.onDoorWarningChanged) door.errorUpdated.connect(doorOutput.onDoorErrorChanged) + door.debugUpdated.connect(doorOutput.onDoorDebugChanged) doorOutput.show() sys.exit(app.exec_()) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 05905bcfc9..d4d3e37e32 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -552,14 +552,9 @@ def _createPermanentPanels(self): self.__doorOutput.onDoorWarningChanged) SDM.connectReader("doorErrorChanged", self.__doorOutput.onDoorErrorChanged) - mainwindow.createPanel(self.__doorOutput, 'DoorOutput', - registerconfig=False, permanent=True) - - # puts doorDebug - self.__doorDebug = DoorDebug() SDM.connectReader("doorDebugChanged", - self.__doorDebug.onDoorDebugChanged) - mainwindow.createPanel(self.__doorDebug, 'DoorDebug', + self.__doorOutput.onDoorDebugChanged) + mainwindow.createPanel(self.__doorOutput, 'DoorOutput', registerconfig=False, permanent=True) # puts doorResult From 509595a64f53328a3fd8b21b8f3e70cdf711ed59 Mon Sep 17 00:00:00 2001 From: cfalcon Date: Thu, 5 Dec 2019 17:08:15 +0100 Subject: [PATCH 292/830] PM widgets do not keep attribute references Some widgets add listeners to volatil Taurus attributes. This cause that the widgets seems to be frozen. Fix them, keeping local references. --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index acacb9f234..884c7c6f6f 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -729,9 +729,9 @@ def setModel(self, model): self.updateLimits) limits_visible = False if self.has_limits: - limits_attribute = self.motor_dev.getAttribute( + self._limits_switches = self.motor_dev.getAttribute( 'Limit_switches') - limits_attribute.addListener(self.limits_listener) + self._limits_switches.addListener(self.limits_listener) # self.updateLimits(limits_attribute.read().rvalue) limits_visible = True self.ui.btnMin.setVisible(limits_visible) @@ -1484,8 +1484,9 @@ def setModel(self, model): if self.hasHwLimits(): self.limits_listener.eventReceivedSignal.connect( self.updateLimits) - self.motor_dev.getAttribute( - 'Limit_Switches').addListener(self.limits_listener) + self._limit_switches = self.motor_dev.getAttribute( + 'Limit_Switches') + self._limit_switches.addListener(self.limits_listener) # CONFIGURE AN EVENT RECEIVER IN ORDER TO PROVIDE POWERON <- # True/False EXPERT OPERATION @@ -1493,23 +1494,23 @@ def setModel(self, model): if self.hasPowerOn(): self.poweron_listener.eventReceivedSignal.connect( self.updatePowerOn) - self.motor_dev.getAttribute( - 'PowerOn').addListener(self.poweron_listener) + self._poweron = self.motor_dev.getAttribute('PowerOn') + self._poweron.addListener(self.poweron_listener) # CONFIGURE AN EVENT RECEIVER IN ORDER TO UPDATED STATUS TOOLTIP self.status_listener = TaurusAttributeListener() self.status_listener.eventReceivedSignal.connect( self.updateStatus) - self.motor_dev.getAttribute( - 'Status').addListener(self.status_listener) + self._status = self.motor_dev.getAttribute('Status') + self._status.addListener(self.status_listener) # CONFIGURE AN EVENT RECEIVER IN ORDER TO ACTIVATE LIMIT BUTTONS ON # SOFTWARE LIMITS self.position_listener = TaurusAttributeListener() self.position_listener.eventReceivedSignal.connect( self.updatePosition) - self.motor_dev.getAttribute( - 'Position').addListener(self.position_listener) + self._position = self.motor_dev.getAttribute('Position') + self._position.addListener(self.position_listener) self.motor_dev.getAttribute('Position').enablePolling(force=True) self.setExpertView(self._expertView) From a2d3ddb4f2cd6ea615071486c90fb06b054c4659 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 6 Dec 2019 17:27:03 +0100 Subject: [PATCH 293/830] Determine console size with os.get_terminal_size() --- .../taurus/core/tango/sardana/macroserver.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 563543a43c..229f8f6366 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -66,17 +66,6 @@ CHANGE_EVT_TYPES = TaurusEventType.Change, TaurusEventType.Periodic - - - -def _get_console_width(): - try: - width = int(os.popen('stty size', 'r').read().split()[1]) - except Exception: - width = float('inf') - return width - - def _get_nb_lines(nb_chrs, max_chrs): return int(math.ceil(float(nb_chrs)/max_chrs)) @@ -691,7 +680,7 @@ def macroStatusReceived(self, s, t, v): return data def logReceived(self, log_name, output): - max_chrs = _get_console_width() + max_chrs = os.get_terminal_size().columns if not output or self._silent or self._ignore_logs: return From 8a20f9eaa11feec14de96bc3df4eeb9a6dad134e Mon Sep 17 00:00:00 2001 From: reszelaz Date: Sun, 8 Dec 2019 22:34:23 +0100 Subject: [PATCH 294/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index be981dae13..e2d2e78b53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ This file follows the formats and conventions from [keepachangelog.com] to avoid problems when using `builtins` from `future` (#1082) * Remove Taurus deprecated code what reduces deprecation warnings (#1206) * Use of env and hints in `macro` function decorator (#1239) +* PMTV widget not updating the following attributes: limit switches, state + and status (#1244) ### Deprecated From 84da7e36abc8174fd71292d6fdb8a0ba484cab8d Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 9 Dec 2019 17:55:48 +0100 Subject: [PATCH 295/830] Add getChannelForElement method Store the channels names per controller when the configuration is updated, to have a fast access --- src/sardana/taurus/core/tango/sardana/pool.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 88124b5c1b..4f59065e85 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1393,18 +1393,21 @@ def set_data(self, data, force=False): # 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 received by the MG configuration attribute self.channels = channels = CaselessDict() self.channels_names = channels_names = CaselessDict() self.channels_labels = channels_labels = CaselessDict() self.controllers_names = controllers_names = CaselessDict() + self.controllers_channels = controllers_channels = CaselessDict() # TODO private controllers attr for ctrl_name, ctrl_data in list(self.controllers.items()): try: if ctrl_name != '__tango__': proxy = DeviceProxy(ctrl_name) - controllers_names[proxy.alias()] = ctrl_data + ctrl_name = proxy.alias() + controllers_names[ctrl_name] = ctrl_data + controllers_channels[ctrl_name] = [] except Exception: pass for channel_name, channel_data in \ @@ -1414,6 +1417,8 @@ def set_data(self, data, force=False): channels_names[name] = channel_data name = channel_data['label'] channels_labels[name] = channel_data + if ctrl_name != '__tango__': + controllers_channels[ctrl_name].append(channel_name) ##################### # @todo: the for-loops above could be replaced by something like: @@ -1422,7 +1427,7 @@ def set_data(self, data, force=False): ##################### # seq each element is the channel data in form of a dict as - # receveid by the MG configuration attribute. This seq is just a cache + # received by the MG configuration attribute. This seq is just a cache # ordered by channel index in the MG. self.channel_list = len(channels) * [None] @@ -1561,6 +1566,14 @@ def prepare(self): except: pass + def getChannelsForElement(self, element): + channels = [] + if element in self.controllers_channels: + channels += self.controllers_channels[element] + else: + channels += [element] + return channels + def getChannels(self): return self.channel_list @@ -1748,7 +1761,7 @@ def _get_ctrl_data(self, ctrl_name): proxy = self._get_proxy(ctrl_name) alias = proxy.alias() if alias not in self.controllers_names: - raise KeyError('Controller "{}" is not on the ' + raise KeyError('Controller "{0}" is not on the ' 'MntGrp "{1}"'.format(alias, self.label)) return self._get_ctrl_data(alias) From 755c0e10e8dd80b3f99ac51ec84f35e86387b467 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 10 Dec 2019 10:13:53 +0100 Subject: [PATCH 296/830] Allow to set/get Output Add method to write and read the Output configuration of one channel or all channels of one controller. --- src/sardana/taurus/core/tango/sardana/pool.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 4f59065e85..229c597298 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2355,6 +2355,67 @@ def setSynchronization(self, synchronization): self.getSynchronizationObj().write(data) self._last_integ_time = None + def _get_channels_for_elements(self, elements): + config = self.getConfiguration() + channels = None + if elements: + channels = [] + for element in elements: + channels += config.getChannelsForElement(element) + return channels + + def setOutput(self, output, *elements, apply=True): + """Set the output configuration for the given elements. + + Channels and controllers are accepted as elements. Setting the output + on the controller means setting it to all channels of this controller + present in this measurement group. + + Configuration by default is directly applied on the server. + Since setting the configuration means passing to the server all the + configuration paramters of the measurement group at once this + behavior can be changed with the *apply* argument and we can keep + the configuration changes only locally. This is useful when we want + to change more then one parameter, in this case only the setting of + the last parameter should use `apply=True`. + + :param output: `True` - output enabled, `False` - output disabled + :type output: bool + :param elements: sequence of element names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + config.setOutputChannels(output, channels, apply_cfg=apply) + + def getOutput(self, *elements, ret_full_name=False): + """Get the output configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the output + from the controller means getting it from all channels of this + controller present in this measurement group. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their output + configurations. Note that even if the *elements* contained + controllers, the returned configuration will always contain + only channels. + :rtype: dict(str, bool) + """ + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + return config.getOutputChannels(channels, use_fullname=ret_full_name) # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From 31d1dc2570c66da67e9956b0e2aa63832b8b3fbd Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 10 Dec 2019 10:15:15 +0100 Subject: [PATCH 297/830] Allow to set/get Enabled Add method to write and read the Enabled configuration of one channel or all channels of one controller --- src/sardana/taurus/core/tango/sardana/pool.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 229c597298..377255ab5b 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2416,6 +2416,60 @@ def getOutput(self, *elements, ret_full_name=False): channels = self._get_channels_for_elements(elements) config = self.getConfiguration() return config.getOutputChannels(channels, use_fullname=ret_full_name) + + def setEnabled(self, enabled, *elements, apply=True): + """Set the enabled configuration for the given elements. + + Channels and controllers are accepted as elements. Setting the enabled + on the controller means setting it to all channels of this controller + present in this measurement group. + + Configuration by default is directly applied on the server. + Since setting the configuration means passing to the server all the + configuration paramters of the measurement group at once this + behavior can be changed with the *apply* argument and we can keep + the configuration changes only locally. This is useful when we want + to change more then one parameter, in this case only the setting of + the last parameter should use `apply=True`. + + :param enabled: `True` - output enabled, `False` - output disabled + :type enabled: bool + :param elements: sequence of element names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + config.setEnabledChannels(enabled, channels, apply_cfg=apply) + + def getEnabled(self, *elements, ret_full_name=False): + """Get the output configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the enabled + from the controller means getting it from all channels of this + controller present in this measurement group. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their output + configurations. Note that even if the *elements* contained + controllers, the returned configuration will always contain + only channels. + :rtype: dict(str, bool) + """ + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + return config.getEnabledChannels(channels, use_fullname=ret_full_name) + # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From 3c91c971732b9dec0bd24732e9918856fe7ea6d8 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 10 Dec 2019 11:32:21 +0100 Subject: [PATCH 298/830] Allow to set/get PlotType Add methods to write and read the plot type configuraiton of one channel or all channels of one controller. --- src/sardana/taurus/core/tango/sardana/pool.py | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 377255ab5b..15b5a33f0f 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1932,7 +1932,7 @@ def getPlotTypeChannels(self, channels=None, use_fullname=False): :return a OrderedDict where keys are channel names and value the plot axes info """ - + # TODO: Change to return enum value SEP12 return self._get_channels_key('plot_type', channels, use_fullname) def setPlotTypeChannels(self, ptype, channels=None, apply_cfg=True): @@ -2470,6 +2470,61 @@ def getEnabled(self, *elements, ret_full_name=False): config = self.getConfiguration() return config.getEnabledChannels(channels, use_fullname=ret_full_name) + def setPlotType(self, plot_type, *elements, apply=True): + """Set the enabled configuration for the given elements. + + Channels and controllers are accepted as elements. Setting the plot + type on the controller means setting it to all channels of this + controller present in this measurement group. + + Configuration by default is directly applied on the server. + Since setting the configuration means passing to the server all the + configuration paramters of the measurement group at once this + behavior can be changed with the *apply* argument and we can keep + the configuration changes only locally. This is useful when we want + to change more then one parameter, in this case only the setting of + the last parameter should use `apply=True`. + + :param plot_type: `True` - output enabled, `False` - output disabled + :type plot_type: str + :param elements: sequence of element names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + config.setPlotTypeChannels(plot_type, channels, apply_cfg=apply) + + def getPlotType(self, *elements, ret_full_name=False): + """Get the output configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the plot + type from the controller means getting it from all channels of this + controller present in this measurement group. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their output + configurations. Note that even if the *elements* contained + controllers, the returned configuration will always contain + only channels. + :rtype: dict(str, int) + """ + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + # TODO Change the documentation when getPlotTypeChannels return enum + # value + return config.getPlotTypeChannels(channels, use_fullname=ret_full_name) + # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From 944a19cc92bb0ed9d5b42fe1f3d032cbe18fcb8a Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 10 Dec 2019 11:51:51 +0100 Subject: [PATCH 299/830] Fix documentation --- src/sardana/taurus/core/tango/sardana/pool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 15b5a33f0f..dc67e032e6 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2432,7 +2432,7 @@ def setEnabled(self, enabled, *elements, apply=True): to change more then one parameter, in this case only the setting of the last parameter should use `apply=True`. - :param enabled: `True` - output enabled, `False` - output disabled + :param enabled: `True` - element enabled, `False` - element disabled :type enabled: bool :param elements: sequence of element names or full names, no elements means set to all @@ -2485,8 +2485,8 @@ def setPlotType(self, plot_type, *elements, apply=True): to change more then one parameter, in this case only the setting of the last parameter should use `apply=True`. - :param plot_type: `True` - output enabled, `False` - output disabled - :type plot_type: str + :param plot_type: 'No'/0 , 'Spectrum'/1, 'Image'/2 + :type plot_type: str or int :param elements: sequence of element names or full names, no elements means set to all :type elements: list(str) From 621a31d00ded65f1050e4c0dcb7ef1cf7e403ec8 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 10 Dec 2019 11:54:22 +0100 Subject: [PATCH 300/830] Allow to set/get PlotAxes Add method to write and read the plot axes configuration of one channel or all channels of one controller. --- src/sardana/taurus/core/tango/sardana/pool.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index dc67e032e6..4a446d0d1b 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2525,6 +2525,59 @@ def getPlotType(self, *elements, ret_full_name=False): # value return config.getPlotTypeChannels(channels, use_fullname=ret_full_name) + def setPlotAxes(self, plot_axes, *elements, apply=True): + """Set the enabled configuration for the given elements. + + Channels and controllers are accepted as elements. Setting the plot + axes on the controller means setting it to all channels of this + controller present in this measurement group. + + Configuration by default is directly applied on the server. + Since setting the configuration means passing to the server all the + configuration paramters of the measurement group at once this + behavior can be changed with the *apply* argument and we can keep + the configuration changes only locally. This is useful when we want + to change more then one parameter, in this case only the setting of + the last parameter should use `apply=True`. + + :param plot_axes: [''] / ['', ''] + :type plot_axes: list(str) + :param elements: sequence of element names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + config.setPlotAxesChannels(plot_axes, channels, apply_cfg=apply) + + def getPlotAxes(self, *elements, ret_full_name=False): + """Get the output configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the plot + axes from the controller means getting it from all channels of this + controller present in this measurement group. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their output + configurations. Note that even if the *elements* contained + controllers, the returned configuration will always contain + only channels. + :rtype: dict(str, str) + """ + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + return config.getPlotAxesChannels(channels, use_fullname=ret_full_name) + # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From 8fe886b19acb739f19d38beff0b9b16252ab7545 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 10 Dec 2019 16:49:25 +0100 Subject: [PATCH 301/830] Allow to set/get ValueRefEnabled Add method to write and read the value reference configuration of one channel or all channels of one controller. --- src/sardana/taurus/core/tango/sardana/pool.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 4a446d0d1b..1bbb4f08ee 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1873,6 +1873,30 @@ def applyConfiguration(self, timeout=3): if (time.time() - t1) >= timeout: raise RuntimeError('Timeout on applying configuration') + def getValueRefEnabledChannels(self, channels=None, use_fullname=False): + """get acquisition Enabled channels. + + :param channels: (seq) a list of channels names to get the + Enabled info + :param use_fullname: (bool) returns a full name instead sardana + element name + + :return a OrderedDict where the key are the channels and value the + Enabled state + """ + + return self._get_channels_key('value_ref_enabled', channels, + use_fullname) + + def setValueRefEnabledChannels(self, state, channels=None, apply_cfg=True): + """Enable acquisition of the indicated channels. + + :param state: The state of the channels to be set. + :param channels: (seq) a sequence of strings indicating + channel names + """ + self._set_channels_key('value_ref_enabled', state, channels, apply_cfg) + def getEnabledChannels(self, channels=None, use_fullname=False): """get acquisition Enabled channels. @@ -2578,6 +2602,61 @@ def getPlotAxes(self, *elements, ret_full_name=False): config = self.getConfiguration() return config.getPlotAxesChannels(channels, use_fullname=ret_full_name) + def setValueRefEnabled(self, value_ref_enabled, *elements, apply=True): + """Set the output configuration for the given elements. + + Channels and controllers are accepted as elements. Setting the value + reference enabled on the controller means setting it to all channels + of this controller present in this measurement group. + + Configuration by default is directly applied on the server. + Since setting the configuration means passing to the server all the + configuration paramters of the measurement group at once this + behavior can be changed with the *apply* argument and we can keep + the configuration changes only locally. This is useful when we want + to change more then one parameter, in this case only the setting of + the last parameter should use `apply=True`. + + :param value_ref_enabled: `True` - enabled, `False` - disabled + :type value_ref_enabled: bool + :param elements: sequence of element names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + config.setValueRefEnabledChannels(value_ref_enabled, channels, + apply_cfg=apply) + + def getValueRefEnabled(self, *elements, ret_full_name=False): + """Get the value reference enabled configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the value + from the controller means getting it from all channels of this + controller present in this measurement group. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their output + configurations. Note that even if the *elements* contained + controllers, the returned configuration will always contain + only channels. + :rtype: dict(str, bool) + """ + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + return config.getValueRefEnabledChannels(channels, + use_fullname=ret_full_name) + # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From 4cb5a7778fdfaac4a6e39449f78a52a72380e96e Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 10 Dec 2019 17:28:13 +0100 Subject: [PATCH 302/830] Allow to set/get ValueRefPattern Add method to write and read the value reference pattern configuration of one channel or all channels of one controller. --- src/sardana/taurus/core/tango/sardana/pool.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 1bbb4f08ee..3009c20534 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1897,6 +1897,32 @@ def setValueRefEnabledChannels(self, state, channels=None, apply_cfg=True): """ self._set_channels_key('value_ref_enabled', state, channels, apply_cfg) + def getValueRefPatternChannels(self, channels=None, use_fullname=False): + """get acquisition Enabled channels. + + :param channels: (seq) a list of channels names to get the + Enabled info + :param use_fullname: (bool) returns a full name instead sardana + element name + + :return a OrderedDict where the key are the channels and value the + Enabled state + """ + + return self._get_channels_key('value_ref_pattern', channels, + use_fullname) + + def setValueRefPatternChannels(self, pattern, channels=None, + apply_cfg=True): + """Enable acquisition of the indicated channels. + + :param pattern: The state of the channels to be set. + :param channels: (seq) a sequence of strings indicating + channel names + """ + self._set_channels_key('value_ref_pattern', pattern, channels, + apply_cfg) + def getEnabledChannels(self, channels=None, use_fullname=False): """get acquisition Enabled channels. @@ -2657,6 +2683,61 @@ def getValueRefEnabled(self, *elements, ret_full_name=False): return config.getValueRefEnabledChannels(channels, use_fullname=ret_full_name) + def setValueRefPattern(self, value_ref_pattern, *elements, apply=True): + """Set the output configuration for the given elements. + + Channels and controllers are accepted as elements. Setting the value + reference pattern on the controller means setting it to all channels + of this controller present in this measurement group. + + Configuration by default is directly applied on the server. + Since setting the configuration means passing to the server all the + configuration paramters of the measurement group at once this + behavior can be changed with the *apply* argument and we can keep + the configuration changes only locally. This is useful when we want + to change more then one parameter, in this case only the setting of + the last parameter should use `apply=True`. + + :param value_ref_pattern: `/path/file{index:03d}.txt' + :type value_ref_pattern: str + :param elements: sequence of element names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + config.setValueRefPatternChannels(value_ref_pattern, channels, + apply_cfg=apply) + + def getValueRefPattern(self, *elements, ret_full_name=False): + """Get the value reference enabled configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the value + from the controller means getting it from all channels of this + controller present in this measurement group. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their output + configurations. Note that even if the *elements* contained + controllers, the returned configuration will always contain + only channels. + :rtype: dict(str, str) + """ + channels = self._get_channels_for_elements(elements) + config = self.getConfiguration() + return config.getValueRefPatternChannels(channels, + use_fullname=ret_full_name) + # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From 81f59f86aa4ecb8da46997ebdfb2a97a8a2d20e6 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Wed, 11 Dec 2019 10:57:53 +0100 Subject: [PATCH 303/830] Change MGConfiguration public API to private --- src/sardana/taurus/core/tango/sardana/pool.py | 88 ++++++++++--------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 3009c20534..cb582bde02 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1566,7 +1566,7 @@ def prepare(self): except: pass - def getChannelsForElement(self, element): + def _getChannelsForElement(self, element): channels = [] if element in self.controllers_channels: channels += self.controllers_channels[element] @@ -1873,7 +1873,7 @@ def applyConfiguration(self, timeout=3): if (time.time() - t1) >= timeout: raise RuntimeError('Timeout on applying configuration') - def getValueRefEnabledChannels(self, channels=None, use_fullname=False): + def _getValueRefEnabledChannels(self, channels=None, use_fullname=False): """get acquisition Enabled channels. :param channels: (seq) a list of channels names to get the @@ -1888,7 +1888,7 @@ def getValueRefEnabledChannels(self, channels=None, use_fullname=False): return self._get_channels_key('value_ref_enabled', channels, use_fullname) - def setValueRefEnabledChannels(self, state, channels=None, apply_cfg=True): + def _setValueRefEnabledChannels(self, state, channels=None, apply_cfg=True): """Enable acquisition of the indicated channels. :param state: The state of the channels to be set. @@ -1897,7 +1897,7 @@ def setValueRefEnabledChannels(self, state, channels=None, apply_cfg=True): """ self._set_channels_key('value_ref_enabled', state, channels, apply_cfg) - def getValueRefPatternChannels(self, channels=None, use_fullname=False): + def _getValueRefPatternChannels(self, channels=None, use_fullname=False): """get acquisition Enabled channels. :param channels: (seq) a list of channels names to get the @@ -1912,8 +1912,8 @@ def getValueRefPatternChannels(self, channels=None, use_fullname=False): return self._get_channels_key('value_ref_pattern', channels, use_fullname) - def setValueRefPatternChannels(self, pattern, channels=None, - apply_cfg=True): + def _setValueRefPatternChannels(self, pattern, channels=None, + apply_cfg=True): """Enable acquisition of the indicated channels. :param pattern: The state of the channels to be set. @@ -1923,7 +1923,7 @@ def setValueRefPatternChannels(self, pattern, channels=None, self._set_channels_key('value_ref_pattern', pattern, channels, apply_cfg) - def getEnabledChannels(self, channels=None, use_fullname=False): + def _getEnabledChannels(self, channels=None, use_fullname=False): """get acquisition Enabled channels. :param channels: (seq) a list of channels names to get the @@ -1937,7 +1937,7 @@ def getEnabledChannels(self, channels=None, use_fullname=False): return self._get_channels_key('enabled', channels, use_fullname) - def setEnabledChannels(self, state, channels=None, apply_cfg=True): + def _setEnabledChannels(self, state, channels=None, apply_cfg=True): """Enable acquisition of the indicated channels. :param state: The state of the channels to be set. @@ -1946,7 +1946,7 @@ def setEnabledChannels(self, state, channels=None, apply_cfg=True): """ self._set_channels_key('enabled', state, channels, apply_cfg) - def getOutputChannels(self, channels=None, use_fullname=False): + def _getOutputChannels(self, channels=None, use_fullname=False): """get the output State of the channels. :param channels: (list) a string indicating the channel name, @@ -1960,7 +1960,7 @@ def getOutputChannels(self, channels=None, use_fullname=False): return self._get_channels_key('output', channels, use_fullname) - def setOutputChannels(self, state, channels=None, apply_cfg=True): + def _setOutputChannels(self, state, channels=None, apply_cfg=True): """Set the Output state of the indicated channels. :param state: (bool) Indicate the state of the output. @@ -1970,7 +1970,7 @@ def setOutputChannels(self, state, channels=None, apply_cfg=True): self._set_channels_key('output', state, channels, apply_cfg) - def getPlotTypeChannels(self, channels=None, use_fullname=False): + def _getPlotTypeChannels(self, channels=None, use_fullname=False): """get the Plot Type for the channel indicated. In case of empty channel value it will return all the Plot Type Info @@ -1985,7 +1985,7 @@ def getPlotTypeChannels(self, channels=None, use_fullname=False): # TODO: Change to return enum value SEP12 return self._get_channels_key('plot_type', channels, use_fullname) - def setPlotTypeChannels(self, ptype, channels=None, apply_cfg=True): + def _setPlotTypeChannels(self, ptype, channels=None, apply_cfg=True): """Set the Plot Type for the indicated channels. :param ptype: string indicating the type name @@ -2011,7 +2011,7 @@ def setPlotTypeChannels(self, ptype, channels=None, apply_cfg=True): raise ValueError() self._set_channels_key('plot_type', ptype, channels, apply_cfg) - def getPlotAxesChannels(self, channels=None, use_fullname=False): + def _getPlotAxesChannels(self, channels=None, use_fullname=False): """get the PlotAxes for the channel indicated. In case of empty channel value it will return all the PlotAxes Info @@ -2026,7 +2026,7 @@ def getPlotAxesChannels(self, channels=None, use_fullname=False): return self._get_channels_key('plot_axes', channels, use_fullname) - def setPlotAxesChannels(self, axes, channels_names=None, apply_cfg=True): + def _setPlotAxesChannels(self, axes, channels_names=None, apply_cfg=True): """Set the PlotAxes for the indicated channels. :param axes: string indicating the axis name @@ -2060,7 +2060,7 @@ def setPlotAxesChannels(self, axes, channels_names=None, apply_cfg=True): self._set_channels_key('plot_axes', axes, [channel_name], apply_cfg) - def getCtrlsTimer(self, ctrls=None, use_fullname=False): + def _getCtrlsTimer(self, ctrls=None, use_fullname=False): """get the acquisition Timer. :param ctrls: list of Controllers names to get the timer @@ -2074,7 +2074,7 @@ def getCtrlsTimer(self, ctrls=None, use_fullname=False): return self._get_ctrls_key('timer', ctrls, use_fullname) - def setCtrlsTimer(self, timers, apply_cfg=True): + def _setCtrlsTimer(self, timers, apply_cfg=True): """Set the acquisition Timer to the controllers compatibles, it finds the controller comptible with this timer and set it . @@ -2089,7 +2089,7 @@ def setCtrlsTimer(self, timers, apply_cfg=True): self._raw_data['timer'] = timer self._set_ctrls_key('timer', timer, [ctrl], apply_cfg) - def getCtrlsMonitor(self, ctrls=None, use_fullname=False): + def _getCtrlsMonitor(self, ctrls=None, use_fullname=False): """get the Monitor for the channel indicated. In case of empty channel value it will return all the Monitor Info @@ -2103,7 +2103,7 @@ def getCtrlsMonitor(self, ctrls=None, use_fullname=False): return self._get_ctrls_key('monitor', ctrls, use_fullname) - def setCtrlsMonitor(self, monitors, apply_cfg=True): + def _setCtrlsMonitor(self, monitors, apply_cfg=True): """Set the Monitor for to the controllers compatibles, it finds the controller comptible with this timer and set it @@ -2121,7 +2121,7 @@ def setCtrlsMonitor(self, monitors, apply_cfg=True): self._raw_data['monitor'] = monitor self._set_ctrls_key('monitor', monitor, [ctrl], apply_cfg) - def getCtrlsSynchronization(self, ctrls=None, use_fullname=False): + def _getCtrlsSynchronization(self, ctrls=None, use_fullname=False): """get the Synchronization for the channel indicated. In case of empty ctrl value it will return all the Synchronization Info @@ -2136,8 +2136,8 @@ def getCtrlsSynchronization(self, ctrls=None, use_fullname=False): return self._get_ctrls_key('synchronization', ctrls, use_fullname) - def setCtrlsSynchronization(self, synchronization, ctrls=None, - apply_cfg=True): + def _setCtrlsSynchronization(self, synchronization, ctrls=None, + apply_cfg=True): """Set the Synchronization to the indicated controllers. :param synchronization: string indicating the synchronization @@ -2165,7 +2165,7 @@ def setCtrlsSynchronization(self, synchronization, ctrls=None, self._set_ctrls_key('synchronization', synchronization, ctrls, apply_cfg) - def getCtrlsSynchronizer(self, ctrls=None, use_fullname=False): + def _getCtrlsSynchronizer(self, ctrls=None, use_fullname=False): """get the synchronizer for the channel indicated. In case of empty channel value it will return all the Synchronizers Info @@ -2179,7 +2179,7 @@ def getCtrlsSynchronizer(self, ctrls=None, use_fullname=False): return self._get_ctrls_key('synchronizer', ctrls, use_fullname) - def setCtrlsSynchronizer(self, synchronizer, ctrls=None, apply_cfg=True): + def _setCtrlsSynchronizer(self, synchronizer, ctrls=None, apply_cfg=True): """Set the synchronizer for the indicated controollers. In case of empty ctrls value it will be applied to all the controllers @@ -2270,7 +2270,7 @@ def enableChannels(self, channels, apply_cfg=True): :param channels: (seq) a sequence of strings indicating channel names """ - self.setEnabledChannels(True, channels, apply_cfg) + self._setEnabledChannels(True, channels, apply_cfg) def disableChannels(self, channels, apply_cfg=True): """ @@ -2279,7 +2279,7 @@ def disableChannels(self, channels, apply_cfg=True): :param channels: (seq) a sequence of strings indicating channel names """ - self.setEnabledChannels(False, channels, apply_cfg) + self._setEnabledChannels(False, channels, apply_cfg) def __repr__(self): return json.dumps(self._raw_data, indent=4, sort_keys=True) @@ -2411,7 +2411,7 @@ def _get_channels_for_elements(self, elements): if elements: channels = [] for element in elements: - channels += config.getChannelsForElement(element) + channels += config._getChannelsForElement(element) return channels def setOutput(self, output, *elements, apply=True): @@ -2441,7 +2441,7 @@ def setOutput(self, output, *elements, apply=True): channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - config.setOutputChannels(output, channels, apply_cfg=apply) + config._setOutputChannels(output, channels, apply_cfg=apply) def getOutput(self, *elements, ret_full_name=False): """Get the output configuration of the given elements. @@ -2465,7 +2465,7 @@ def getOutput(self, *elements, ret_full_name=False): """ channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - return config.getOutputChannels(channels, use_fullname=ret_full_name) + return config._getOutputChannels(channels, use_fullname=ret_full_name) def setEnabled(self, enabled, *elements, apply=True): """Set the enabled configuration for the given elements. @@ -2494,7 +2494,7 @@ def setEnabled(self, enabled, *elements, apply=True): channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - config.setEnabledChannels(enabled, channels, apply_cfg=apply) + config._setEnabledChannels(enabled, channels, apply_cfg=apply) def getEnabled(self, *elements, ret_full_name=False): """Get the output configuration of the given elements. @@ -2518,7 +2518,7 @@ def getEnabled(self, *elements, ret_full_name=False): """ channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - return config.getEnabledChannels(channels, use_fullname=ret_full_name) + return config._getEnabledChannels(channels, use_fullname=ret_full_name) def setPlotType(self, plot_type, *elements, apply=True): """Set the enabled configuration for the given elements. @@ -2547,7 +2547,7 @@ def setPlotType(self, plot_type, *elements, apply=True): channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - config.setPlotTypeChannels(plot_type, channels, apply_cfg=apply) + config._setPlotTypeChannels(plot_type, channels, apply_cfg=apply) def getPlotType(self, *elements, ret_full_name=False): """Get the output configuration of the given elements. @@ -2571,9 +2571,10 @@ def getPlotType(self, *elements, ret_full_name=False): """ channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - # TODO Change the documentation when getPlotTypeChannels return enum + # TODO Change the documentation when _getPlotTypeChannels return enum # value - return config.getPlotTypeChannels(channels, use_fullname=ret_full_name) + return config._getPlotTypeChannels(channels, + use_fullname=ret_full_name) def setPlotAxes(self, plot_axes, *elements, apply=True): """Set the enabled configuration for the given elements. @@ -2602,7 +2603,7 @@ def setPlotAxes(self, plot_axes, *elements, apply=True): channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - config.setPlotAxesChannels(plot_axes, channels, apply_cfg=apply) + config._setPlotAxesChannels(plot_axes, channels, apply_cfg=apply) def getPlotAxes(self, *elements, ret_full_name=False): """Get the output configuration of the given elements. @@ -2626,7 +2627,8 @@ def getPlotAxes(self, *elements, ret_full_name=False): """ channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - return config.getPlotAxesChannels(channels, use_fullname=ret_full_name) + return config._getPlotAxesChannels(channels, + use_fullname=ret_full_name) def setValueRefEnabled(self, value_ref_enabled, *elements, apply=True): """Set the output configuration for the given elements. @@ -2655,8 +2657,8 @@ def setValueRefEnabled(self, value_ref_enabled, *elements, apply=True): channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - config.setValueRefEnabledChannels(value_ref_enabled, channels, - apply_cfg=apply) + config._setValueRefEnabledChannels(value_ref_enabled, channels, + apply_cfg=apply) def getValueRefEnabled(self, *elements, ret_full_name=False): """Get the value reference enabled configuration of the given elements. @@ -2680,8 +2682,8 @@ def getValueRefEnabled(self, *elements, ret_full_name=False): """ channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - return config.getValueRefEnabledChannels(channels, - use_fullname=ret_full_name) + return config._getValueRefEnabledChannels(channels, + use_fullname=ret_full_name) def setValueRefPattern(self, value_ref_pattern, *elements, apply=True): """Set the output configuration for the given elements. @@ -2710,8 +2712,8 @@ def setValueRefPattern(self, value_ref_pattern, *elements, apply=True): channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - config.setValueRefPatternChannels(value_ref_pattern, channels, - apply_cfg=apply) + config._setValueRefPatternChannels(value_ref_pattern, channels, + apply_cfg=apply) def getValueRefPattern(self, *elements, ret_full_name=False): """Get the value reference enabled configuration of the given elements. @@ -2735,8 +2737,8 @@ def getValueRefPattern(self, *elements, ret_full_name=False): """ channels = self._get_channels_for_elements(elements) config = self.getConfiguration() - return config.getValueRefPatternChannels(channels, - use_fullname=ret_full_name) + return config._getValueRefPatternChannels(channels, + use_fullname=ret_full_name) # NbStarts Methods def getNbStartsObj(self): From b81b5d2d717abcae3f179deae6c1d2d38dbed418 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 11 Dec 2019 12:02:21 +0100 Subject: [PATCH 304/830] Use magnitude of attributes values in PMTV PMTV is not ready to work with quantities and #1206 while removing deprecation warnings introduced the use of quantities. Extract quantity magnitude so the widget works as previously. --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 884c7c6f6f..76b26f0a71 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -808,7 +808,7 @@ def __init__(self): def eventReceived(self, evt_src, evt_type, evt_value): if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: return - value = evt_value.rvalue + value = evt_value.rvalue.magnitude self.eventReceivedSignal.emit(value) @@ -904,7 +904,7 @@ def calculateExtendedTooltip(self, cache=False): status_info = '' motor_dev = self.taurusValueBuddy().motor_dev if motor_dev is not None: - status = motor_dev.getAttribute('Status').read().rvalue + status = motor_dev.getAttribute('Status').read().rvalue.magnitude # MAKE IT LOOK LIKE THE STANDARD TABLE FOR TAURUS TOOLTIPS status_lines = status.split('\n') status_info = '
Status:' + \ @@ -1291,7 +1291,7 @@ def goRelative(self, direction): motor_dev = self.taurusValueBuddy().motor_dev if motor_dev is not None: increment = direction * float(self.cb_step.currentText()) - position = float(motor_dev.getAttribute('Position').read().rvalue) + position = float(motor_dev.getAttribute('Position').read().rvalue.magnitude) target_position = position + increment motor_dev.getAttribute('Position').write(target_position) @@ -1357,7 +1357,7 @@ def setModel(self, model): self.le_write_absolute.setModel(model) return TaurusWidget.setModel(self, model + '/Position') - self.le_write_absolute.setModel(model + '/Position') + self.le_write_absolute.setModel(model + '/Position#wvalue.magnitude') # Handle User/Expert View self.setExpertView(self.taurusValueBuddy()._expertView) @@ -1543,7 +1543,7 @@ def updateLimits(self, limits, position=None): if self.motor_dev is not None: position_attribute = self.motor_dev.getAttribute('Position') if position is None: - position = position_attribute.read().rvalue + position = position_attribute.read().rvalue.magnitude max_value_str = position_attribute.max_value min_value_str = position_attribute.min_value try: @@ -1625,7 +1625,7 @@ def updatePosition(self, position='__no_argument__'): limit_switches = [False, False, False] if self.hasHwLimits(): limit_switches = self.motor_dev.getAttribute( - 'Limit_switches').read().rvalue + 'Limit_switches').read().rvalue.magnitude # print "update limits", limit_switches self.updateLimits(limit_switches, position=position) From 5483f1e1aa2cf6e25737517dba580bf33b8110a1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 11 Dec 2019 13:23:50 +0100 Subject: [PATCH 305/830] Fix flake8 --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 76b26f0a71..448e805e69 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1291,7 +1291,8 @@ def goRelative(self, direction): motor_dev = self.taurusValueBuddy().motor_dev if motor_dev is not None: increment = direction * float(self.cb_step.currentText()) - position = float(motor_dev.getAttribute('Position').read().rvalue.magnitude) + position = float( + motor_dev.getAttribute('Position').read().rvalue.magnitude) target_position = position + increment motor_dev.getAttribute('Position').write(target_position) From 6e63d42dbeedb5d73ab6c683057201f40da33061 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 12 Dec 2019 16:14:04 +0100 Subject: [PATCH 306/830] Add MGConfiguration helpers Add internal variables and method to help the finding of the different configuration keys values. --- src/sardana/taurus/core/tango/sardana/pool.py | 73 ++++++++++++++----- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index cb582bde02..55b447cc19 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1415,10 +1415,13 @@ def set_data(self, data, force=False): channels[channel_name] = channel_data name = channel_data['name'] channels_names[name] = channel_data - name = channel_data['label'] + label = channel_data['label'] channels_labels[name] = channel_data if ctrl_name != '__tango__': - controllers_channels[ctrl_name].append(channel_name) + ch_data = {'fullname': channel_name, + 'label': label, + 'name': name} + controllers_channels[ctrl_name].append(ch_data) ##################### # @todo: the for-loops above could be replaced by something like: @@ -1566,14 +1569,6 @@ def prepare(self): except: pass - def _getChannelsForElement(self, element): - channels = [] - if element in self.controllers_channels: - channels += self.controllers_channels[element] - else: - channels += [element] - return channels - def getChannels(self): return self.channel_list @@ -1841,7 +1836,7 @@ def _get_ctrls_key(self, key, ctrls_names=None, use_fullname=False): result[label] = value return result - def _get_ctrl_from_channels(self, channels_names, unique=False): + def _get_ctrl_for_channel(self, channels_names, unique=False): result = collections.OrderedDict({}) if channels_names is None: @@ -1857,6 +1852,34 @@ def _get_ctrl_from_channels(self, channels_names, unique=False): return result + def _get_ctrl_channels(self, ctrl, use_fullname=False): + channels = [] + channels_datas = self.controllers_channels[ctrl] + for channel_data in channels_datas: + if use_fullname: + name = channel_data['fullname'] + else: + name = channel_data['label'] + channels.append(name) + return channels + + def _get_channels_for_element(self, element, use_fullname=False): + channels = [] + if element in self.controllers_channels: + channels += self._get_ctrl_channels(element, use_fullname) + else: + channels += [element] + return channels + + def _get_ctrl_for_element(self, element): + if element in self.controllers_channels: + ctrl = element + else: + # TODO: find more elegant way + channel_ctrl = self._get_ctrl_for_channel([element]) + ctrl = list(channel_ctrl.values())[0] + return ctrl + def applyConfiguration(self, timeout=3): if not self._local_changes: return @@ -2080,7 +2103,7 @@ def _setCtrlsTimer(self, timers, apply_cfg=True): . :param timer_name: strings indicating the timer name """ - result = self._get_ctrl_from_channels(timers, unique=True) + result = self._get_ctrl_for_channel(timers, unique=True) meas_ctrl = self.channels[self.timer]['_controller_name'] for timer, ctrl in result.items(): @@ -2112,7 +2135,7 @@ def _setCtrlsMonitor(self, monitors, apply_cfg=True): :param monitor: string indicating the monitor name """ - result = self._get_ctrl_from_channels(monitors, unique=True) + result = self._get_ctrl_for_channel(monitors, unique=True) meas_ctrl = self.channels[self.monitor]['_controller_name'] for monitor, ctrl in result.items(): @@ -2224,7 +2247,7 @@ def setTimer(self, timer, apply_cfg=True): :param timer: timer name """ - result = self._get_ctrl_from_channels([timer], unique=True) + result = self._get_ctrl_for_channel([timer], unique=True) for timer, ctrl in result.items(): self._local_changes = True @@ -2406,14 +2429,26 @@ def setSynchronization(self, synchronization): self._last_integ_time = None def _get_channels_for_elements(self, elements): + if not elements: + return None config = self.getConfiguration() - channels = None - if elements: - channels = [] - for element in elements: - channels += config._getChannelsForElement(element) + channels = [] + for element in elements: + channels += config._get_channels_for_element(element) return channels + def _get_ctrl_for_elements(self, elements): + if not elements: + return None + ctrls = [] + config = self.getConfiguration() + for element in elements: + ctrl = config._get_ctrl_for_element(element) + if ctrl in ctrls: + continue + ctrls.append(ctrl) + return ctrls + def setOutput(self, output, *elements, apply=True): """Set the output configuration for the given elements. From a26924a3037ec5b666e6c0a70a062e6f8469cfc3 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 12 Dec 2019 16:15:31 +0100 Subject: [PATCH 307/830] Allow to set/get Timer Add methods to write and read the timer configure for a controller. --- src/sardana/taurus/core/tango/sardana/pool.py | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 55b447cc19..45b3e1d5a9 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2775,6 +2775,72 @@ def getValueRefPattern(self, *elements, ret_full_name=False): return config._getValueRefPatternChannels(channels, use_fullname=ret_full_name) + def _get_value_per_channel(self, config, ctrls_values): + channels_values = collections.OrderedDict({}) + for ctrl, value in ctrls_values.items(): + for channel in config._get_ctrl_channels(ctrl): + channels_values[channel] = value + return channels_values + + def setTimer(self, timer, *elements, apply=True): + """Set the timer configuration for the given channels of the same + controller. NOTE: The current configuration does not allow to set + the different value per channel of the same controller, it allows to + set per controller. + + Configuration by default is directly applied on the server. + Since setting the configuration means passing to the server all the + configuration paramters of the measurement group at once this + behavior can be changed with the *apply* argument and we can keep + the configuration changes only locally. This is useful when we want + to change more then one parameter, in this case only the setting of + the last parameter should use `apply=True`. + + :param timer: channel use as timer + :type timer: str + :param elements: sequence of channels names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + config = self.getConfiguration() + # TODO: Implement solution to set the timer per channel when it is + # allowed. + config._setCtrlsTimer([timer], apply_cfg=apply) + + def getTimer(self, *elements, ret_full_name=False, ret_by_ctrl=False): + """Get the timer configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the output + from the controller means getting it from all channels of this + controller present in this measurement group, unless + `ret_by_ctrl=True`. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :param ret_by_ctrl: whether keys in the returned dictionary are + controllers or channels (default: `False` means return channels) + :type ret_by_ctrl: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their synchronization + configurations + :rtype: dict(str, str) + """ + # TODO: Implement solution to set the timer per channel when it is + # allowed. + ctrls = self._get_ctrl_for_elements(elements) + config = self.getConfiguration() + ctrls_timers = config._getCtrlsTimer(ctrls, use_fullname=ret_full_name) + if ret_by_ctrl: + return ctrls_timers + else: + return self._get_value_per_channel(config, ctrls_timers) # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From 28a684fdd80a1e737f5e5185740b14ef9ba27cf0 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 12 Dec 2019 16:16:54 +0100 Subject: [PATCH 308/830] Allow to set/get Monitor Add methods to write and read the monitor channel configure for a controller. --- src/sardana/taurus/core/tango/sardana/pool.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 45b3e1d5a9..42954ef205 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2841,6 +2841,67 @@ def getTimer(self, *elements, ret_full_name=False, ret_by_ctrl=False): return ctrls_timers else: return self._get_value_per_channel(config, ctrls_timers) + + def setMonitor(self, monitor, *elements, apply=True): + """Set the monitor configuration for the given channels of the same + controller. NOTE: The current configuration does not allow to set + the different value per channel of the same controller, it allows to + set per controller. + + Configuration by default is directly applied on the server. + Since setting the configuration means passing to the server all the + configuration paramters of the measurement group at once this + behavior can be changed with the *apply* argument and we can keep + the configuration changes only locally. This is useful when we want + to change more then one parameter, in this case only the setting of + the last parameter should use `apply=True`. + + :param monitor: channel use as monitor + :type monitor: str + :param elements: sequence of channels names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + config = self.getConfiguration() + # TODO: Implement solution to set the timer per channel when it is + # allowed. + config._setCtrlsMonitor([monitor], apply_cfg=apply) + + def getMonitor(self, *elements, ret_full_name=False, ret_by_ctrl=False): + """Get the monitor configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the output + from the controller means getting it from all channels of this + controller present in this measurement group, unless + `ret_by_ctrl=True`. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :param ret_by_ctrl: whether keys in the returned dictionary are + controllers or channels (default: `False` means return channels) + :type ret_by_ctrl: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their synchronization + configurations + :rtype: dict(str, str) + """ + # TODO: Implement solution to set the timer per channel when it is + # allowed. + ctrls = self._get_ctrl_for_elements(elements) + config = self.getConfiguration() + ctrls_monitor = config._getCtrlsMonitor(ctrls, + use_fullname=ret_full_name) + if ret_by_ctrl: + return ctrls_monitor + else: + return self._get_value_per_channel(config, ctrls_monitor) # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From 77de98198f448f3cb274a2793b22fffb93e661ea Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 12 Dec 2019 16:18:23 +0100 Subject: [PATCH 309/830] Allow to set/get the Synchronizer Add methods to write and read the synchronizer configuration for the controllers. --- src/sardana/taurus/core/tango/sardana/pool.py | 70 ++++++++++++++++++- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 42954ef205..af28d73387 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2215,11 +2215,10 @@ def _setCtrlsSynchronizer(self, synchronizer, ctrls=None, apply_cfg=True): else: # TODO: Improve how to check if the element is a trigger_gate sync = Device(synchronizer) - if 'triggergate' not in sync.name(): + if 'triggergate' not in sync.fullname: raise ValueError('The "{0}" is not a ' 'triggergate'.format(synchronizer)) - synchronizer = sync.getFullName() - + synchronizer = sync.fullname self._set_ctrls_key('synchronizer', synchronizer, ctrls, apply_cfg) def getTimerName(self): @@ -2902,6 +2901,71 @@ def getMonitor(self, *elements, ret_full_name=False, ret_by_ctrl=False): return ctrls_monitor else: return self._get_value_per_channel(config, ctrls_monitor) + + def setSynchronizer(self, synchronizer, *elements, apply=True): + """Set the synchronizer configuration for the given channels or + controller. NOTE: The current configuration does not allow to set + the different value per channel of the same controller, it allows to + set per controller. + + Configuration by default is directly applied on the server. + Since setting the configuration means passing to the server all the + configuration paramters of the measurement group at once this + behavior can be changed with the *apply* argument and we can keep + the configuration changes only locally. This is useful when we want + to change more then one parameter, in this case only the setting of + the last parameter should use `apply=True`. + + :param synchronizer: triger/gate element name or software + :type synchronizer: str + :param elements: sequence of channels names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + config = self.getConfiguration() + # TODO: Implement solution to set the timer per channel when it is + # allowed. + ctrls = self._get_ctrl_for_elements(elements) + config._setCtrlsSynchronizer(synchronizer, ctrls, apply_cfg=apply) + + def getSynchronizer(self, *elements, ret_full_name=False, + ret_by_ctrl=False): + """Get the synchronizer configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the output + from the controller means getting it from all channels of this + controller present in this measurement group, unless + `ret_by_ctrl=True`. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :param ret_by_ctrl: whether keys in the returned dictionary are + controllers or channels (default: `False` means return channels) + :type ret_by_ctrl: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their synchronization + configurations + :rtype: dict(str, str) + """ + # TODO: Implement solution to set the timer per channel when it is + # allowed. + ctrls = self._get_ctrl_for_elements(elements) + config = self.getConfiguration() + ctrls_sync = config._getCtrlsSynchronizer(ctrls, + use_fullname=ret_full_name) + if ret_by_ctrl: + return ctrls_sync + else: + return self._get_value_per_channel(config, ctrls_sync) + + # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From 78226afcf74521fe040150e210d30ef1651e82c3 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 12 Dec 2019 17:08:28 +0100 Subject: [PATCH 310/830] Fix flake8 errors --- src/sardana/taurus/core/tango/sardana/pool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index af28d73387..5e5e82fdea 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1911,7 +1911,8 @@ def _getValueRefEnabledChannels(self, channels=None, use_fullname=False): return self._get_channels_key('value_ref_enabled', channels, use_fullname) - def _setValueRefEnabledChannels(self, state, channels=None, apply_cfg=True): + def _setValueRefEnabledChannels(self, state, channels=None, + apply_cfg=True): """Enable acquisition of the indicated channels. :param state: The state of the channels to be set. @@ -2965,7 +2966,6 @@ def getSynchronizer(self, *elements, ret_full_name=False, else: return self._get_value_per_channel(config, ctrls_sync) - # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From b48cce09bf4a2c4eadc4202a075a7bec36a4c3ea Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 16 Dec 2019 10:51:07 +0100 Subject: [PATCH 311/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2d2e78b53..0654ce8e59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Setting of environment variables in Python 3.7 (#1195) * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) -* Remove Taurus deprecated code what reduces deprecation warnings (#1206) +* Remove Taurus deprecated code what reduces deprecation warnings (#1206, #1252) * Use of env and hints in `macro` function decorator (#1239) * PMTV widget not updating the following attributes: limit switches, state and status (#1244) From f0eb792ea30b600ee1d1ccc927869c72a78dff9e Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 16 Dec 2019 13:05:59 +0100 Subject: [PATCH 312/830] Correct wrong usage of Quantities for non-scalar non-numeric attrs Status attribute or spectrum/images attribute values are not Quantities. Don't try to extract the their magnitudes. --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 448e805e69..b7b342396e 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -808,7 +808,11 @@ def __init__(self): def eventReceived(self, evt_src, evt_type, evt_value): if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: return - value = evt_value.rvalue.magnitude + try: + value = evt_value.rvalue.magnitude + except AttributeError: + # spectrum/images or str attribute values are not Quantities + value = evt_value.rvalue self.eventReceivedSignal.emit(value) @@ -904,7 +908,7 @@ def calculateExtendedTooltip(self, cache=False): status_info = '' motor_dev = self.taurusValueBuddy().motor_dev if motor_dev is not None: - status = motor_dev.getAttribute('Status').read().rvalue.magnitude + status = motor_dev.getAttribute('Status').read().rvalue # MAKE IT LOOK LIKE THE STANDARD TABLE FOR TAURUS TOOLTIPS status_lines = status.split('\n') status_info = '
Status:' + \ @@ -1626,7 +1630,7 @@ def updatePosition(self, position='__no_argument__'): limit_switches = [False, False, False] if self.hasHwLimits(): limit_switches = self.motor_dev.getAttribute( - 'Limit_switches').read().rvalue.magnitude + 'Limit_switches').read().rvalue # print "update limits", limit_switches self.updateLimits(limit_switches, position=position) From 8817b3314582e5f30082da8a433145cc43054a1e Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 16 Dec 2019 16:24:31 +0100 Subject: [PATCH 313/830] Fix docstring --- src/sardana/taurus/core/tango/sardana/pool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 5e5e82fdea..8833aa7aa1 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2735,7 +2735,7 @@ def setValueRefPattern(self, value_ref_pattern, *elements, apply=True): to change more then one parameter, in this case only the setting of the last parameter should use `apply=True`. - :param value_ref_pattern: `/path/file{index:03d}.txt' + :param value_ref_pattern: `/path/file{index:03d}.txt` :type value_ref_pattern: str :param elements: sequence of element names or full names, no elements means set to all From 078a0c603613e490fed8096e88ea73fb7bd38199 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 16 Dec 2019 17:07:32 +0100 Subject: [PATCH 314/830] Update test to new measurement group API --- .../taurus/core/tango/sardana/test/test_pool.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_pool.py b/src/sardana/taurus/core/tango/sardana/test/test_pool.py index a85f5d0543..5eef95de28 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_pool.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_pool.py @@ -84,15 +84,6 @@ def tearDown(self): SarTestTestCase.tearDown(self) -def _set_value_ref_enabled(conf, channel, value_ref_enabled): - ctrl = channel.getControllerObj() - ctrl_full_name = ctrl.getFullName() - channel_full_name = channel.getFullName() - ctrl_conf = conf["controllers"][ctrl_full_name] - channel_conf = ctrl_conf["channels"][channel_full_name] - channel_conf["value_ref_enabled"] = value_ref_enabled - - class TestMeasurementGroupValueRef(SarTestTestCase, TestCase): def setUp(self): @@ -107,9 +98,7 @@ def test_value_ref_enabled(self): try: mg = Device(mg_name) channel = Device(channel_name) - conf = mg.getConfiguration().raw_data - _set_value_ref_enabled(conf, channel, True) - mg.setConfiguration(conf) + mg.setValueRefEnabled(True, [channel_name]) _, values = mg.count(.1) for channel_name, value in values.items(): msg = "ValueRef (%s) for %s is not string" %\ @@ -128,9 +117,7 @@ def test_value_ref_disabled(self): try: mg = Device(mg_name) channel = Device(channel_name) - conf = mg.getConfiguration().raw_data - _set_value_ref_enabled(conf, channel, False) - mg.setConfiguration(conf) + mg.setValueRefEnabled(False, [channel_name]) _, values = mg.count(.1) for channel_name, value in values.items(): msg = "Value (%s) for %s is not numerical" %\ From b7ce6c9462a65c8d4f29975b00972126907cb7a7 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 16 Dec 2019 17:38:09 +0100 Subject: [PATCH 315/830] Fix bug on test --- src/sardana/taurus/core/tango/sardana/test/test_pool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_pool.py b/src/sardana/taurus/core/tango/sardana/test/test_pool.py index 5eef95de28..cb0190db1e 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_pool.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_pool.py @@ -98,7 +98,7 @@ def test_value_ref_enabled(self): try: mg = Device(mg_name) channel = Device(channel_name) - mg.setValueRefEnabled(True, [channel_name]) + mg.setValueRefEnabled(True, channel_name) _, values = mg.count(.1) for channel_name, value in values.items(): msg = "ValueRef (%s) for %s is not string" %\ @@ -117,7 +117,7 @@ def test_value_ref_disabled(self): try: mg = Device(mg_name) channel = Device(channel_name) - mg.setValueRefEnabled(False, [channel_name]) + mg.setValueRefEnabled(False, channel_name) _, values = mg.count(.1) for channel_name, value in values.items(): msg = "Value (%s) for %s is not numerical" %\ From aa5aa8aff4ca2dc39d3d1e9cfb91dbfb34f1ff24 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 16 Dec 2019 23:30:59 +0100 Subject: [PATCH 316/830] Add raise_in_thread helper function Move _asyncexc helper ctypes function together with raise_in_thread. --- src/sardana/macroserver/macro.py | 8 ++----- src/sardana/util/thread.py | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index f8f13cbc70..8b9775c4c2 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -55,6 +55,7 @@ from sardana.sardanadefs import State from sardana.util.wrap import wraps +from sardana.util.thread import _asyncexc from sardana.macroserver.msparameter import Type, ParamType, ParamRepeat, \ Optional @@ -64,11 +65,6 @@ from sardana.taurus.core.tango.sardana.pool import PoolElement -asyncexc = ctypes.pythonapi.PyThreadState_SetAsyncExc -# first define the async exception function args. This is -# absolutely necessary for 64 bits machines. -asyncexc.argtypes = (ctypes.c_long, ctypes.py_object) - class OverloadPrint(object): @@ -2410,7 +2406,7 @@ def abort(self): th = self._macro_thread th_id = ctypes.c_long(th.ident) Logger.debug(self, "Sending AbortException to %s", th.name) - ret = asyncexc(th_id, ctypes.py_object(AbortException)) + ret = _asyncexc(th_id, ctypes.py_object(AbortException)) i += 1 if ret == 0: # try again diff --git a/src/sardana/util/thread.py b/src/sardana/util/thread.py index 2070afbea9..4573746eb1 100644 --- a/src/sardana/util/thread.py +++ b/src/sardana/util/thread.py @@ -21,6 +21,8 @@ ## ############################################################################## +import time +import ctypes from threading import Condition @@ -59,3 +61,42 @@ def wait(self): while self.count > 0: self.condition.wait() self.condition.release() + + +_asyncexc = ctypes.pythonapi.PyThreadState_SetAsyncExc +# first define the async exception function args. This is +# absolutely necessary for 64 bits machines. +_asyncexc.argtypes = (ctypes.c_long, ctypes.py_object) + + +def raise_in_thread(exception, thread, logger=None): + """Raise exception in a thread. + + Inspired on :meth:sardana.macroserver.macro.Macro.abort + + :param exception: Exception to be raised + :param thread: thread in which raise the exception. + """ + ret, i = 0, 0 + while ret != 1: + th_id = ctypes.c_long(thread.ident) + if logger: + logger.debug("Sending AbortException to %s", thread.name) + ret = _asyncexc(th_id, ctypes.py_object(exception)) + i += 1 + if ret == 0: + # try again + if i > 2: + if logger: + logger.error("Failed to abort after three tries!") + break + time.sleep(0.1) + if ret > 1: + # if it returns a number greater than one, you're in trouble, + # and you should call it again with exc=NULL to revert the + # effect + _asyncexc(th_id, None) + if logger: + logger.error("Failed to abort (unknown error code {})". + format(ret)) + break \ No newline at end of file From 907701cc26646ea827697066e87b9bbcf3c888cd Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 16 Dec 2019 23:37:27 +0100 Subject: [PATCH 317/830] Improve stopping and aborting macros in MacroExecutor (core) Advance setting on _stopped and _aborted flags (set immediatelly instead of doing it in worker thread). Break stopping (by raising exception in stop thread) when on abort. Reverse order of interpreting what happend - if aborted don't send macro status of stopped (even if it was earlier stopped). --- src/sardana/macroserver/msmacromanager.py | 35 ++++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 5d64b05a95..9269c8a7fd 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -71,6 +71,7 @@ LibraryError, UnknownMacro, MissingEnv, AbortException, StopException, \ MacroServerException, UnknownEnv from sardana.util.parser import ParamParser +from sardana.util.thread import raise_in_thread # These classes are imported from the "client" part of sardana, if finally # both the client and the server side needs them, place them in some @@ -1075,7 +1076,9 @@ def __init__(self, door): self._macro_stack = [] self._xml_stack = [] self._macro_pointer = None + self._abort_thread = None self._aborted = False + self._stop_thread = None self._stopped = False self._paused = False self._last_macro_status = None @@ -1392,7 +1395,11 @@ def clearRunningMacro(self): def __stopObjects(self): """Stops all the reserved objects in the executor""" for _, objs in list(self._reserved_macro_objs.items()): + if self._aborted: + break for obj in objs: + if self._aborted: + break # someone aborted, no sense to stop anymore try: obj.stop() except AttributeError: @@ -1419,29 +1426,43 @@ def _setStopDone(self, _): def _waitStopDone(self, timeout=None): self._stop_done.wait(timeout) + def _isStopDone(self): + return self._stop_done.is_set() + def _setAbortDone(self, _): self._abort_done.set() def _waitAbortDone(self, timeout=None): self._abort_done.wait(timeout) + def _isAbortDone(self): + return self._abort_done.is_set() + def abort(self): + """**Internal method**. Aborts the macro abruptly.""" + # carefull: Inside this method never call a method that has the + # mAPI decorator + self._aborted = True + if not self._isStopDone(): + Logger.debug(self, "Break stopping...") + raise_in_thread(AbortException, self._stop_thread) self.macro_server.add_job(self._abort, self._setAbortDone) def stop(self): + self._stopped = True self.macro_server.add_job(self._stop, self._setStopDone) def _abort(self): + self._abort_thread = threading.current_thread() m = self.getRunningMacro() if m is not None: - self._aborted = True m.abort() self.__abortObjects() def _stop(self): + self._stop_thread = threading.current_thread() m = self.getRunningMacro() if m is not None: - self._stopped = True m.stop() if m.isPaused(): m.resume(cb=self._macroResumed) @@ -1616,14 +1637,14 @@ def runMacro(self, macro_obj): # make sure the macro's on_abort is called and that a proper macro # status is sent - if self._stopped: - self._waitStopDone() - macro_obj._stopOnError() - self.sendMacroStatusStop() - elif self._aborted: + if self._aborted: self._waitAbortDone() macro_obj._abortOnError() self.sendMacroStatusAbort() + elif self._stopped: + self._waitStopDone() + macro_obj._stopOnError() + self.sendMacroStatusStop() # From this point on don't call any method of macro_obj which is part # of the mAPI (methods decorated with @mAPI) to avoid throwing an From 88558ff855fc09ed0a36310c905bd918776b9507 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 16 Dec 2019 23:40:03 +0100 Subject: [PATCH 318/830] Add release method to MacroExecutor (core) Release the aborting process in progress. --- src/sardana/macroserver/msmacromanager.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 9269c8a7fd..32b29f175c 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1081,6 +1081,7 @@ def __init__(self, door): self._stop_thread = None self._stopped = False self._paused = False + self._released = False self._last_macro_status = None # threading events for synchronization of stopping/abortting of # reserved objects @@ -1448,6 +1449,15 @@ def abort(self): raise_in_thread(AbortException, self._stop_thread) self.macro_server.add_job(self._abort, self._setAbortDone) + def release(self): + """**Internal method**. Release the macro.""" + # carefull: Inside this method never call a method that has the + # mAPI decorator + self._released = True + if not self._isAbortDone(): + Logger.debug(self, "Break aborting...") + raise_in_thread(AbortException, self._abort_thread) + def stop(self): self._stopped = True self.macro_server.add_job(self._stop, self._setStopDone) From d6f6a1cfd226e5848cfdc684c566af4b47988d5a Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 16 Dec 2019 23:46:08 +0100 Subject: [PATCH 319/830] Add ReleaseMacro Tango command to Door ReleaseMacro command simply breaks the already in progress macro aborting. --- src/sardana/tango/macroserver/Door.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py index fb1ca618cb..3675144698 100644 --- a/src/sardana/tango/macroserver/Door.py +++ b/src/sardana/tango/macroserver/Door.py @@ -366,6 +366,19 @@ def AbortMacro(self): def is_Abort_allowed(self): return True + def ReleaseMacro(self): + macro = self.getRunningMacro() + if macro is None: + return + self.debug("releasing %s" % macro._getDescription()) + self.macro_executor.release() + + def is_ReleaseMacro_allowed(self): + is_release_allowed = (self.get_state() == Macro.Running or + self.get_state() == Macro.Pause) + return is_release_allowed + + def PauseMacro(self): macro = self.getRunningMacro() if macro is None: @@ -466,6 +479,9 @@ class DoorClass(SardanaDeviceClass): 'AbortMacro': [[DevVoid, ""], [DevVoid, ""]], + 'ReleaseMacro': + [[DevVoid, ""], + [DevVoid, ""]], 'StopMacro': [[DevVoid, ""], [DevVoid, ""]], From 1be795cdb87b4b984762bdc5cc98e7bf3ca1b2bf Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 16 Dec 2019 23:49:47 +0100 Subject: [PATCH 320/830] Add release method to Door's Taurus extension --- .../taurus/core/tango/sardana/macroserver.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 563543a43c..28844e0b5a 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -480,6 +480,22 @@ def abort(self, synch=True): evt_wait.unlock() evt_wait.disconnect() + def release(self, synch=True): + if not synch: + self.command_inout("ReleaseMacro") + return + + evt_wait = AttributeEventWait(self.getAttribute("state")) + evt_wait.lock() + try: + time_stamp = time.time() + self.command_inout("ReleaseMacro") + return evt_wait.waitEvent(self.Running, equal=False, after=time_stamp, + timeout=self.InteractiveTimeout) + finally: + evt_wait.unlock() + evt_wait.disconnect() + def stop(self, synch=True): if not synch: self.command_inout("StopMacro") From f673e34b6500251b21afb437df3dce7eca581269 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 16 Dec 2019 23:51:02 +0100 Subject: [PATCH 321/830] Handle 2nd and 3rd Ctrl+C in spock 2nd Ctrl+C - AbortMacro 3rd Ctrl+C - ReleaseMacro --- src/sardana/spock/spockms.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index e9c36d2bd8..2cfedff677 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -357,10 +357,20 @@ def _runMacro(self, xml, **kwargs): try: return BaseDoor._runMacro(self, xml, **kwargs) except KeyboardInterrupt: - self.write('\nCtrl-C received: Stopping... ') - self.block_lines = 0 - self.stop() - self.writeln("Done!") + try: + self.write('\nCtrl-C received: Stopping... ') + self.block_lines = 0 + self.stop() + self.writeln("Stopping done!") + except KeyboardInterrupt: + try: + self.write('\n2nd Ctrl-C received: Aborting... ') + self.block_lines = 0 + self.abort() + self.writeln("Aborting done!") + except KeyboardInterrupt: + self.write('\n3rd Ctrl-C received: Releasing...') + self.release() except PyTango.DevFailed as e: if is_non_str_seq(e.args) and \ not isinstance(e.args, str): From 892ae581cd153ed27aed3e65be03e29be08b82cb Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 17 Dec 2019 00:07:39 +0100 Subject: [PATCH 322/830] Allow other threads to abort waiting until PoolElement finish --- src/sardana/taurus/core/tango/sardana/pool.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index d70b626b24..7375cff9e3 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -486,17 +486,23 @@ def waitFinish(self, timeout=None, id=None): :param id: id of the opertation returned by start :type id: tuple(float) """ - # Due to taurus-org/taurus #573 we need to divide the timeout - # in two intervals - if timeout is not None: + if timeout is None: + # 0.1 s of timeout with infinite retries facilitates aborting + # by raising exceptions from a different threads + timeout = 0.1 + retries = -1 + else: + # Due to taurus-org/taurus #573 we need to divide the timeout + # in two intervals timeout = timeout / 2 + retries = 1 if id is not None: id = id[0] evt_wait = self._getEventWait() evt_wait.lock() try: evt_wait.waitEvent(DevState.MOVING, after=id, equal=False, - timeout=timeout, retries=1) + timeout=timeout, retries=retries) finally: self.__go_end_time = time.time() self.__go_time = self.__go_end_time - self.__go_start_time From e367716ff2b1bc9911134abc6d0ab4e6008ecbca Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 17 Dec 2019 00:09:19 +0100 Subject: [PATCH 323/830] fix flake8 --- src/sardana/tango/macroserver/Door.py | 4 ++-- src/sardana/taurus/core/tango/sardana/macroserver.py | 3 ++- src/sardana/util/thread.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py index 3675144698..cddb1d26da 100644 --- a/src/sardana/tango/macroserver/Door.py +++ b/src/sardana/tango/macroserver/Door.py @@ -374,8 +374,8 @@ def ReleaseMacro(self): self.macro_executor.release() def is_ReleaseMacro_allowed(self): - is_release_allowed = (self.get_state() == Macro.Running or - self.get_state() == Macro.Pause) + is_release_allowed = (self.get_state() == Macro.Running + or self.get_state() == Macro.Pause) return is_release_allowed diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 28844e0b5a..11ad92a75c 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -490,7 +490,8 @@ def release(self, synch=True): try: time_stamp = time.time() self.command_inout("ReleaseMacro") - return evt_wait.waitEvent(self.Running, equal=False, after=time_stamp, + return evt_wait.waitEvent(self.Running, equal=False, + after=time_stamp, timeout=self.InteractiveTimeout) finally: evt_wait.unlock() diff --git a/src/sardana/util/thread.py b/src/sardana/util/thread.py index 4573746eb1..678e5e20de 100644 --- a/src/sardana/util/thread.py +++ b/src/sardana/util/thread.py @@ -99,4 +99,4 @@ def raise_in_thread(exception, thread, logger=None): if logger: logger.error("Failed to abort (unknown error code {})". format(ret)) - break \ No newline at end of file + break From e4f3433a4dd5d8ef19bfdff62568a8075c4f7443 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 17 Dec 2019 10:30:08 +0100 Subject: [PATCH 324/830] Adapt to new measurement group API --- src/sardana/macroserver/macros/standard.py | 2 +- src/sardana/macroserver/scan/gscan.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 7cc2ffba2d..4f8ac529d8 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -832,7 +832,7 @@ def run(self, timer): return try: - mnt_grp.setTimer(timer.getName()) + mnt_grp.getConfiguration().setTimer(timer.getName()) except Exception as e: self.output(str(e)) self.output( diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index a2602c1052..21554f27a0 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -313,7 +313,7 @@ def __init__(self, macro, generator=None, moveables=[], env={}, raise ScanSetupError("ActiveMntGrp has invalid value: '%s'" % mnt_grp_name) - self._master = mnt_grp.getTimer() + self._master = mnt_grp.getConfiguration().getTimer() if self._master is None: raise ScanSetupError('%s has no timer defined' % mnt_grp.getName()) From e7dbc015aa747f7f07ab992daa0d05cd16ba87b3 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 17 Dec 2019 11:43:05 +0100 Subject: [PATCH 325/830] Fix bug on docstring --- src/sardana/taurus/core/tango/sardana/pool.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 8833aa7aa1..ec008f1e9d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2736,7 +2736,7 @@ def setValueRefPattern(self, value_ref_pattern, *elements, apply=True): the last parameter should use `apply=True`. :param value_ref_pattern: `/path/file{index:03d}.txt` - :type value_ref_pattern: str + :type value_ref_pattern: :py:obj:`str` :param elements: sequence of element names or full names, no elements means set to all :type elements: list(str) @@ -2797,7 +2797,7 @@ def setTimer(self, timer, *elements, apply=True): the last parameter should use `apply=True`. :param timer: channel use as timer - :type timer: str + :type timer: :py:obj:`str` :param elements: sequence of channels names or full names, no elements means set to all :type elements: list(str) @@ -2857,7 +2857,7 @@ def setMonitor(self, monitor, *elements, apply=True): the last parameter should use `apply=True`. :param monitor: channel use as monitor - :type monitor: str + :type monitor: :py:obj:`str` :param elements: sequence of channels names or full names, no elements means set to all :type elements: list(str) @@ -2918,7 +2918,7 @@ def setSynchronizer(self, synchronizer, *elements, apply=True): the last parameter should use `apply=True`. :param synchronizer: triger/gate element name or software - :type synchronizer: str + :type synchronizer: :py:obj:`str` :param elements: sequence of channels names or full names, no elements means set to all :type elements: list(str) From 4ae52d133cc79becf1fc8214d3a023d4a6befb49 Mon Sep 17 00:00:00 2001 From: aalonso Date: Tue, 17 Dec 2019 15:55:19 +0100 Subject: [PATCH 326/830] Remove show plots button from experiment configuration and update documentation --- doc/source/users/spock.rst | 1 - .../users/taurus/experimentconfiguration.rst | 23 ++++------- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 34 +++++++++------- .../qt/qtgui/extra_sardana/expdescription.py | 40 +------------------ .../extra_sardana/ui/ExpDescriptionEditor.ui | 13 ------ .../qt/qtgui/macrolistener/macrolistener.py | 2 +- 6 files changed, 30 insertions(+), 83 deletions(-) diff --git a/doc/source/users/spock.rst b/doc/source/users/spock.rst index 487750e880..f2fdb1ea18 100644 --- a/doc/source/users/spock.rst +++ b/doc/source/users/spock.rst @@ -391,7 +391,6 @@ Viewing scan data You can show plots for the current scan (i.e. plotting the scan *online*) by: * launching the :func:`showscan online ` command -* using the :ref:`show/hide button from the expconf widget ` Sardana provides also a scan data viewer for scans which were stored in a `NeXus`_ file: :ref:`showscan_ui`. It can be launched using :func:`showscan ` diff --git a/doc/source/users/taurus/experimentconfiguration.rst b/doc/source/users/taurus/experimentconfiguration.rst index 5d75849511..75e80ff5a1 100644 --- a/doc/source/users/taurus/experimentconfiguration.rst +++ b/doc/source/users/taurus/experimentconfiguration.rst @@ -102,25 +102,18 @@ a given channel or its controller: .. _expconf_ui_showplots: -Show / Hide current scan plot(s) +View current scan plot(s) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The Experiment Configuration widget provides a button to show/hide plots of the -current scan. When this button is checked, the values of `plot type` and -`plot axes` :ref:`in the channel configuration ` -determine how many plot widgets will be spawned to show the channels involved -in a scan when the scan is run. +Plots need to be configured previously as explained +:ref:`in the channel configuration ` +once the values of plot type and plot axes are set, and storage path is defined. +Running a scan will spawn a tab on taurusgui with the plot. The number of plots that will spawn is +defined :ref:`in the channel configuration ` +If the configuration hasn't been changed, a new scan will overwrite the previous plots. -.. figure:: /_static/expconf-showplot.png - :width: 100% - :figwidth: 100% - :align: center - - Button for enabling/disabling plots of the current scan. - -.. note:: This button may in some contexts be disabled (e.g. by default on - sardana-aware TaurusGUIs) +Plots can also be seen with spock's command ``showscan online`` .. _expconf_ui_snapshot_group: diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index acacb9f234..b7b342396e 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -729,9 +729,9 @@ def setModel(self, model): self.updateLimits) limits_visible = False if self.has_limits: - limits_attribute = self.motor_dev.getAttribute( + self._limits_switches = self.motor_dev.getAttribute( 'Limit_switches') - limits_attribute.addListener(self.limits_listener) + self._limits_switches.addListener(self.limits_listener) # self.updateLimits(limits_attribute.read().rvalue) limits_visible = True self.ui.btnMin.setVisible(limits_visible) @@ -808,7 +808,11 @@ def __init__(self): def eventReceived(self, evt_src, evt_type, evt_value): if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: return - value = evt_value.rvalue + try: + value = evt_value.rvalue.magnitude + except AttributeError: + # spectrum/images or str attribute values are not Quantities + value = evt_value.rvalue self.eventReceivedSignal.emit(value) @@ -1291,7 +1295,8 @@ def goRelative(self, direction): motor_dev = self.taurusValueBuddy().motor_dev if motor_dev is not None: increment = direction * float(self.cb_step.currentText()) - position = float(motor_dev.getAttribute('Position').read().rvalue) + position = float( + motor_dev.getAttribute('Position').read().rvalue.magnitude) target_position = position + increment motor_dev.getAttribute('Position').write(target_position) @@ -1357,7 +1362,7 @@ def setModel(self, model): self.le_write_absolute.setModel(model) return TaurusWidget.setModel(self, model + '/Position') - self.le_write_absolute.setModel(model + '/Position') + self.le_write_absolute.setModel(model + '/Position#wvalue.magnitude') # Handle User/Expert View self.setExpertView(self.taurusValueBuddy()._expertView) @@ -1484,8 +1489,9 @@ def setModel(self, model): if self.hasHwLimits(): self.limits_listener.eventReceivedSignal.connect( self.updateLimits) - self.motor_dev.getAttribute( - 'Limit_Switches').addListener(self.limits_listener) + self._limit_switches = self.motor_dev.getAttribute( + 'Limit_Switches') + self._limit_switches.addListener(self.limits_listener) # CONFIGURE AN EVENT RECEIVER IN ORDER TO PROVIDE POWERON <- # True/False EXPERT OPERATION @@ -1493,23 +1499,23 @@ def setModel(self, model): if self.hasPowerOn(): self.poweron_listener.eventReceivedSignal.connect( self.updatePowerOn) - self.motor_dev.getAttribute( - 'PowerOn').addListener(self.poweron_listener) + self._poweron = self.motor_dev.getAttribute('PowerOn') + self._poweron.addListener(self.poweron_listener) # CONFIGURE AN EVENT RECEIVER IN ORDER TO UPDATED STATUS TOOLTIP self.status_listener = TaurusAttributeListener() self.status_listener.eventReceivedSignal.connect( self.updateStatus) - self.motor_dev.getAttribute( - 'Status').addListener(self.status_listener) + self._status = self.motor_dev.getAttribute('Status') + self._status.addListener(self.status_listener) # CONFIGURE AN EVENT RECEIVER IN ORDER TO ACTIVATE LIMIT BUTTONS ON # SOFTWARE LIMITS self.position_listener = TaurusAttributeListener() self.position_listener.eventReceivedSignal.connect( self.updatePosition) - self.motor_dev.getAttribute( - 'Position').addListener(self.position_listener) + self._position = self.motor_dev.getAttribute('Position') + self._position.addListener(self.position_listener) self.motor_dev.getAttribute('Position').enablePolling(force=True) self.setExpertView(self._expertView) @@ -1542,7 +1548,7 @@ def updateLimits(self, limits, position=None): if self.motor_dev is not None: position_attribute = self.motor_dev.getAttribute('Position') if position is None: - position = position_attribute.read().rvalue + position = position_attribute.read().rvalue.magnitude max_value_str = position_attribute.max_value min_value_str = position_attribute.min_value try: diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index e40aea7d04..fc1255f387 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -175,8 +175,7 @@ class ExpDescriptionEditor(Qt.QWidget, TaurusBaseWidget): createExpConfChangedDialog = Qt.pyqtSignal() experimentConfigurationChanged = Qt.pyqtSignal(compat.PY_OBJECT) - def __init__(self, parent=None, door=None, plotsButton=True, - autoUpdate=False): + def __init__(self, parent=None, door=None, autoUpdate=False): Qt.QWidget.__init__(self, parent) TaurusBaseWidget.__init__(self, 'ExpDescriptionEditor') self.loadUi() @@ -239,32 +238,6 @@ def __init__(self, parent=None, door=None, plotsButton=True, self.ui.choosePathBT.clicked.connect( self.onChooseScanDirButtonClicked) - self.__plotManager = None - tooltip = None - - # TODO: Disable show scan button since scan plot have to be - # adapted to support QT5 - # -------------------------------------------------------------------- - from taurus.external.qt import PYQT4, API - if not PYQT4: - self.debug('Show plots is only supported with PyQt4 for now') - plotsButton = False - tooltip = "Show/Hide plots is not ready for %s" % API - # -------------------------------------------------------------------- - - icon = Qt.QIcon("actions:view.svg") - measGrpTab = self.ui.tabWidget.widget(0) - self.togglePlotsAction = Qt.QAction(icon, "Show/Hide plots", self) - if tooltip is not None: - self.togglePlotsAction.setToolTip(tooltip) - self.togglePlotsAction.setCheckable(True) - self.togglePlotsAction.setChecked(False) - self.togglePlotsAction.setEnabled(plotsButton) - measGrpTab.addAction(self.togglePlotsAction) - measGrpTab.setContextMenuPolicy(Qt.Qt.ActionsContextMenu) - self.togglePlotsAction.toggled.connect(self.onPlotsButtonToggled) - self.ui.plotsButton.setDefaultAction(self.togglePlotsAction) - if door is not None: self.setModel(door) @@ -700,17 +673,6 @@ def onPreScanSnapshotChanged(self, items): self._localConfig['PreScanSnapshot'] = preScanList self._setDirty(True) - def onPlotsButtonToggled(self, checked): - if checked: - from sardana.taurus.qt.qtgui.macrolistener import \ - DynamicPlotManager - self.__plotManager = DynamicPlotManager(self) - self.__plotManager.setModel(self.getModelName()) - else: - self.__plotManager.removePanels() - self.__plotManager.setModel(None) - self.__plotManager = None - def demo(model=None, autoUpdate=False): """Experiment configuration""" diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/ui/ExpDescriptionEditor.ui b/src/sardana/taurus/qt/qtgui/extra_sardana/ui/ExpDescriptionEditor.ui index 206409196f..b4cc99cfdf 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/ui/ExpDescriptionEditor.ui +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/ui/ExpDescriptionEditor.ui @@ -72,19 +72,6 @@ - - - - If checked, the configured plots are shown - - - plots - - - true - - - diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 05905bcfc9..982b46e67c 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -482,7 +482,7 @@ def _createPermanentPanels(self): 'doorNameChanged') # Create ExpDescriptionEditor dialog - self.__expDescriptionEditor = ExpDescriptionEditor(plotsButton=False) + self.__expDescriptionEditor = ExpDescriptionEditor() SDM.connectReader("doorName", self.__expDescriptionEditor.setModel) mainwindow.createPanel(self.__expDescriptionEditor, 'Experiment Config', From d63e691901a7c3f91410af01c22868d49336bd6b Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 18 Dec 2019 23:13:36 +0100 Subject: [PATCH 327/830] Add verbosity in stop/abort/release macro Print messages to the user of what is going on in the stop/abort/release macro process. --- src/sardana/macroserver/msmacromanager.py | 12 ++++++++++-- src/sardana/spock/spockms.py | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 32b29f175c..bec1ec1f43 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1395,12 +1395,14 @@ def clearRunningMacro(self): def __stopObjects(self): """Stops all the reserved objects in the executor""" - for _, objs in list(self._reserved_macro_objs.items()): + for macro, objs in list(self._reserved_macro_objs.items()): if self._aborted: break for obj in objs: if self._aborted: break # someone aborted, no sense to stop anymore + self.output( + "Stopping {} reserved by {}".format(obj, macro._name)) try: obj.stop() except AttributeError: @@ -1411,8 +1413,10 @@ def __stopObjects(self): def __abortObjects(self): """Aborts all the reserved objects in the executor""" - for _, objs in list(self._reserved_macro_objs.items()): + for macro, objs in list(self._reserved_macro_objs.items()): for obj in objs: + self.output( + "Aborting {} reserved by {}".format(obj, macro._name)) try: obj.abort() except AttributeError: @@ -1464,6 +1468,8 @@ def stop(self): def _abort(self): self._abort_thread = threading.current_thread() + if self._stopped: + self._waitStopDone() m = self.getRunningMacro() if m is not None: m.abort() @@ -1649,10 +1655,12 @@ def runMacro(self, macro_obj): # status is sent if self._aborted: self._waitAbortDone() + self.output("Executing {}.on_abort method...".format(name)) macro_obj._abortOnError() self.sendMacroStatusAbort() elif self._stopped: self._waitStopDone() + self.output("Executing {}.on_stop method...".format(name)) macro_obj._stopOnError() self.sendMacroStatusStop() diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 2cfedff677..225ebb6d6f 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -358,18 +358,18 @@ def _runMacro(self, xml, **kwargs): return BaseDoor._runMacro(self, xml, **kwargs) except KeyboardInterrupt: try: - self.write('\nCtrl-C received: Stopping... ') + self.write('\nCtrl-C received: Stopping...\n') self.block_lines = 0 self.stop() self.writeln("Stopping done!") except KeyboardInterrupt: try: - self.write('\n2nd Ctrl-C received: Aborting... ') + self.write('2nd Ctrl-C received: Aborting...\n') self.block_lines = 0 self.abort() self.writeln("Aborting done!") except KeyboardInterrupt: - self.write('\n3rd Ctrl-C received: Releasing...') + self.write('3rd Ctrl-C received: Releasing...\n') self.release() except PyTango.DevFailed as e: if is_non_str_seq(e.args) and \ From 37ce8a61852d5cc5fb85b07b15a61c646bec4811 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 18 Dec 2019 23:16:58 +0100 Subject: [PATCH 328/830] advertise newfile macro in case no ScanDir or ScanFile is set --- src/sardana/macroserver/scan/gscan.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index a2602c1052..96dff199a3 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -488,8 +488,9 @@ def _getFileRecorders(self): raise except Exception: macro.warning('ScanDir is not defined. This operation will not be ' - 'stored persistently. Use Use "expconf" (or "senv ' - 'ScanDir ") to enable it') + 'stored persistently. Use "expconf" or "newfile" ' + 'to configure data storage (or eventually "senv ' + 'ScanDir ")') return () if not isinstance(scan_dir, str): @@ -501,9 +502,10 @@ def _getFileRecorders(self): except InterruptException: raise except Exception: - macro.warning('ScanFile is not defined. This operation will not ' - 'be stored persistently. Use "expconf" (or "senv ' - 'ScanFile ") to enable it') + macro.warning('ScanFile is not defined. This operation will not be ' + 'stored persistently. Use "expconf" or "newfile" ' + 'to configure data storage (or eventually "senv ' + 'ScanFile ")') return () scan_recorders = [] From fe264685d1e39d5e224f8f231cacb6d26f4d00f7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 18 Dec 2019 23:21:48 +0100 Subject: [PATCH 329/830] Don't abort already stopped objects Also print more messages to the user of what is going on. --- src/sardana/macroserver/msmacromanager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index bec1ec1f43..d6c34bf2f2 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1410,6 +1410,8 @@ def __stopObjects(self): except: self.warning("Unable to stop %s" % obj) self.debug("Details:", exc_info=1) + self.output("{} stopped".format(obj)) + objs.remove(obj) def __abortObjects(self): """Aborts all the reserved objects in the executor""" @@ -1424,6 +1426,8 @@ def __abortObjects(self): except: self.warning("Unable to abort %s" % obj) self.debug("Details:", exc_info=1) + self.output("{} aborted".format(obj)) + objs.remove(obj) def _setStopDone(self, _): self._stop_done.set() From 6dd034b3f984d46db7dd7042835e74a261806183 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Thu, 19 Dec 2019 08:48:55 +0100 Subject: [PATCH 330/830] fix flake8 --- src/sardana/macroserver/scan/gscan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 96dff199a3..492d81459b 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -502,8 +502,8 @@ def _getFileRecorders(self): except InterruptException: raise except Exception: - macro.warning('ScanFile is not defined. This operation will not be ' - 'stored persistently. Use "expconf" or "newfile" ' + macro.warning('ScanFile is not defined. This operation will not ' + 'be stored persistently. Use "expconf" or "newfile" ' 'to configure data storage (or eventually "senv ' 'ScanFile ")') return () From d53930ecd140b1e50623923d3299939d4794cc63 Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 19 Dec 2019 09:53:20 +0100 Subject: [PATCH 331/830] fix documentation issues --- doc/source/users/spock.rst | 5 ++--- doc/source/users/taurus/experimentconfiguration.rst | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/source/users/spock.rst b/doc/source/users/spock.rst index f2fdb1ea18..f11d944654 100644 --- a/doc/source/users/spock.rst +++ b/doc/source/users/spock.rst @@ -388,9 +388,8 @@ of files: Viewing scan data ~~~~~~~~~~~~~~~~~ -You can show plots for the current scan (i.e. plotting the scan *online*) by: - -* launching the :func:`showscan online ` command +You can show plots for the current scan (i.e. plotting the scan *online*) by +launching the :func:`showscan online ` command Sardana provides also a scan data viewer for scans which were stored in a `NeXus`_ file: :ref:`showscan_ui`. It can be launched using :func:`showscan ` diff --git a/doc/source/users/taurus/experimentconfiguration.rst b/doc/source/users/taurus/experimentconfiguration.rst index 75e80ff5a1..0aa1bb6e54 100644 --- a/doc/source/users/taurus/experimentconfiguration.rst +++ b/doc/source/users/taurus/experimentconfiguration.rst @@ -105,11 +105,11 @@ a given channel or its controller: View current scan plot(s) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Plots need to be configured previously as explained -:ref:`in the channel configuration ` -once the values of plot type and plot axes are set, and storage path is defined. -Running a scan will spawn a tab on taurusgui with the plot. The number of plots that will spawn is -defined :ref:`in the channel configuration ` +Plots need to be configured previously as explained in the +:ref:`channel configuration ` +plot type and plot axes. +Running a scan will spawn a panel on taurusgui with the plot. The number of panels that will spawn is +defined in the :ref:`channel configuration ` If the configuration hasn't been changed, a new scan will overwrite the previous plots. From a025b27e62e260b73c5a0712870c9948097941a9 Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 19 Dec 2019 10:18:34 +0100 Subject: [PATCH 332/830] add correct poolmotor --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 1763 ----------------- 1 file changed, 1763 deletions(-) delete mode 100644 src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py deleted file mode 100644 index b7b342396e..0000000000 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ /dev/null @@ -1,1763 +0,0 @@ -#!/usr/bin/env python - -############################################################################## -## -# This file is part of Sardana -## -# http://www.sardana-controls.org/ -## -# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -# Sardana 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. -## -# Sardana 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 Sardana. If not, see . -## -############################################################################## - -import PyTango - -from taurus.external.qt import Qt, compat - -import taurus -from taurus.core.util.colors import DEVICE_STATE_PALETTE -from taurus.core.taurusbasetypes import TaurusEventType -from taurus.core.tango.tangovalidator import TangoDeviceNameValidator -import taurus.qt.qtcore.mimetypes -from taurus.qt.qtgui.compact import TaurusReadWriteSwitcher -from taurus.qt.qtgui.dialog import ProtectTaurusMessageBox -from taurus.qt.qtgui.container import TaurusWidget -from taurus.qt.qtgui.container import TaurusFrame -from taurus.qt.qtgui.display import TaurusLabel -from taurus.qt.qtgui.input import TaurusValueLineEdit -from taurus.qt.qtgui.panel import DefaultLabelWidget -from taurus.qt.qtgui.panel import DefaultUnitsWidget -from taurus.qt.qtgui.panel import TaurusValue, TaurusAttrForm -from taurus.qt.qtcore.mimetypes import TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE -from taurus.qt.qtgui.util.ui import UILoadable - - -class LimitsListener(Qt.QObject): - """ - A class that listens to changes on motor limits. - If that is the case it emits a signal so the application - can do whatever with it. - """ - - updateLimits = Qt.pyqtSignal(compat.PY_OBJECT) - - def __init__(self): - Qt.QObject.__init__(self) - - def eventReceived(self, evt_src, evt_type, evt_value): - if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: - return - limits = evt_value.value - self.updateLimits.emit(limits.tolist()) - - -class PoolMotorClient(): - - maxint_in_32_bits = 2147483647 - - def __init__(self): - self.motor_dev = None - self.has_limits = False - self.has_encoder = False - - def setMotor(self, pool_motor_dev_name): - # AT SOME POINT THIS WILL BE USING THE 'POOL' TAURUS EXTENSION - # TO OPERATE THE MOTOR INSTEAD OF A 'TANGO' TAURUSDEVICE - try: - self.motor_dev = taurus.Device(pool_motor_dev_name) - # IT IS IMPORTANT TO KNOW IF IT IS AN ICEPAP MOTOR, SO EXTRA FEATURES CAN BE PROVIDED - # PENDING. - self.has_limits = hasattr(self.motor_dev, 'Limit_Switches') - self.has_encoder = hasattr(self.motor_dev, 'Encoder') - except Exception as e: - taurus.warning('Exception Creating Motor Device %s', str(e)) - - def moveMotor(self, pos): - #self.motor_dev['position'] = pos - # Make use of Taurus operations (being logged) - self.motor_dev.getAttribute('Position').write(pos) - - def moveInc(self, inc): - self.moveMotor(self.motor_dev['position'].value + inc) - - @Qt.pyqtSlot() - def jogNeg(self): - neg_limit = -((self.maxint_in_32_bits // 2) - 1) - # THERE IS A BUG IN THE ICEPAP THAT DOES NOT ALLOW MOVE ABSOLUTE FURTHER THAN 32 BIT - # SO IF THERE ARE STEPS PER UNIT, max_int HAS TO BE REDUCED - if hasattr(self.motor_dev, 'step_per_unit'): - neg_limit = neg_limit / self.motor_dev['step_per_unit'].value - try: - min_value = self.motor_dev.getAttribute( - 'Position').getConfig().getValueObj().min_value - neg_limit = float(min_value) - except Exception: - pass - self.moveMotor(neg_limit) - - @Qt.pyqtSlot() - def jogPos(self): - pos_limit = (self.maxint_in_32_bits // 2) - 1 - # THERE IS A BUG IN THE ICEPAP THAT DOES NOT ALLOW MOVE ABSOLUTE FURTHER THAN 32 BIT - # SO IF THERE ARE STEPS PER UNIT, max_int HAS TO BE REDUCED - if hasattr(self.motor_dev, 'step_per_unit'): - pos_limit = pos_limit / self.motor_dev['step_per_unit'].value - try: - max_value = self.motor_dev.getAttribute( - 'Position').getConfig().getValueObj().max_value - pos_limit = float(max_value) - except Exception: - pass - self.moveMotor(pos_limit) - - @Qt.pyqtSlot() - def goHome(self): - pass - - @Qt.pyqtSlot() - def abort(self): - self.motor_dev.abort() - - -class LabelWidgetDragsDeviceAndAttribute(DefaultLabelWidget): - """ Offer richer mime data with taurus-device, taurus-attribute, and plain-text. """ - - def mouseMoveEvent(self, event): - model = self.taurusValueBuddy().getModelName().encode('utf-8') - mimeData = Qt.QMimeData() - mimeData.setText(self.text()) - attr_name = model - dev_name = model.rpartition(b'/')[0] - mimeData.setData(TAURUS_DEV_MIME_TYPE, dev_name) - mimeData.setData(TAURUS_ATTR_MIME_TYPE, attr_name) - - drag = Qt.QDrag(self) - drag.setMimeData(mimeData) - drag.setHotSpot(event.pos() - self.rect().topLeft()) - drag.exec_(Qt.Qt.CopyAction, Qt.Qt.CopyAction) - - -class PoolMotorConfigurationForm(TaurusAttrForm): - - def __init__(self, parent=None, designMode=False): - TaurusAttrForm.__init__(self, parent, designMode) - self._form.setWithButtons(False) - - def getMotorControllerType(self): - modelObj = self.getModelObj() - modelNormalName = modelObj.getNormalName() - poolDsId = modelObj.getHWObj().info().server_id - db = taurus.Database() - pool_devices = tuple(db.get_device_class_list(poolDsId).value_string) - pool_dev_name = pool_devices[pool_devices.index('Pool') - 1] - pool = taurus.Device(pool_dev_name) - poolMotorInfos = pool["MotorList"].value - for motorInfo in poolMotorInfos: - # BE CAREFUL, THIS ONLY WORKS IF NOBODY CHANGES THE DEVICE NAME OF A MOTOR!!! - # ALSO THERE COULD BE A CASE PROBLEM, BETTER DO COMPARISONS WITH .lower() - # to better understand following actions - # this is an example of one motor info record - #'dummymotor10 (motor/dummymotorctrl/10) (dummymotorctrl/10) Motor', - motorInfos = motorInfo.split() - if modelNormalName.lower() == motorInfos[1][1:-1].lower(): - controllerName = motorInfos[2][1:-1].split("/")[0] - - poolControllerInfos = pool["ControllerList"].value - for controllerInfo in poolControllerInfos: - # to better understand following actions - # this is an example of one controller info record - #'dummymotorctrl (DummyMotorController.DummyMotorController/dummymotorctrl) - Motor Python ctrl (DummyMotorController.py)' - controllerInfos = controllerInfo.split() - if controllerName.lower() == controllerInfos[0].lower(): - controllerType = controllerInfos[1][1:-1].split("/")[0] - return controllerType - - def getDisplayAttributes(self, controllerType): - attributes = ['position', - 'state', - 'status', - 'velocity', - 'acceleration', - 'base_rate', - 'step_per_unit', - 'dialposition', - 'sign', - 'offset', - 'backlash'] - - if controllerType == "IcePAPCtrl.IcepapController": - attributes.insert(1, "encoder") - attributes.extend(['frequency', - 'poweron', - 'closedloop', - 'useencodersource', - 'encodersource', - 'encodersourceformula', - 'statusstopcode', - 'statusdisable', - 'statusready', - 'statuslim-', - 'statuslim+', - 'statushome']) - - elif controllerType == "PmacCtrl.PmacController": - attributes.extend(["motoractivated", - "negativeendlimitset", - "positiveendlimitset", - "handwheelenabled", - "phasedmotor", - "openloopmode", - "runningdefine-timemove", - "integrationmode", - "dwellinprogress", - "datablockerror", - "desiredvelocityzero", - "abortdeceleration", - "blockrequest", - "homesearchinprogress", - "assignedtocoordinatesystem", - "coordinatesystem", - "amplifierenabled", - "stoppedonpositionlimit", - "homecomplete", - "phasingsearcherror", - "triggermove", - "integratedfatalfollowingerror", - "i2t_amplifierfaulterror", - "backlashdirectionflag", - "amplifierfaulterror", - "fatalfollowingerror", - "warningfollowingerror", - "inposition", - "motionprogramrunning"]) - - elif controllerType == "TurboPmacCtrl.TurboPmacController": - attributes.extend(["motoractivated", - "negativeendlimitset", - "positiveendlimitset", - "extendedservoalgorithmenabled" - "amplifierenabled", - "openloopmode", - "movetimeractive", - "integrationmode", - "dwellinprogress", - "datablockerror", - "desiredvelocityzero", - "abortdeceleration", - "blockrequest", - "homesearchinprogress", - "user-writtenphaseenable", - "user-writtenservoenable", - "alternatesource/destination", - "phasedmotor", - "followingoffsetmode", - "followingenabled", - "errortriger", - "softwarepositioncapture", - "integratorinvelocityloop", - "alternatecommand-outputmode", - "coordinatesystem", - "coordinatedefinition", - "assignedtocoordinatesystem", - "foregroundinposition", - "stoppedondesiredpositionlimit", - "stoppedonpositionlimit", - "homecomplete", - "phasing_search/read_active", - "triggermove", - "integratedfatalfollowingerror", - "i2t_amplifierfaulterror", - "backlashdirectionflag", - "amplifierfaulterror", - "fatalfollowingerror", - "warningfollowingerror", - "inposition"]) - return attributes - - def setModel(self, modelName): - TaurusAttrForm.setModel(self, modelName) - controllerType = self.getMotorControllerType() - attributes = self.getDisplayAttributes(controllerType) - #self.setViewFilters([lambda a: a.name.lower() in attributes]) - self.setSortKey(lambda att: attributes.index( - att.name.lower()) if att.name.lower() in attributes else 1) - - -@UILoadable(with_ui='ui') -class PoolMotorSlim(TaurusWidget, PoolMotorClient): - - __pyqtSignals__ = ("modelChanged(const QString &)",) - - def __init__(self, parent=None, designMode=False): - TaurusWidget.__init__(self, parent) - msg = ("PoolMotorSlim is deprecated since 2.5.0. Use PoolMotorTV " - "instead.") - self.deprecated(msg) - - #self.call__init__wo_kw(Qt.QWidget, parent) - #self.call__init__(TaurusBaseWidget, str(self.objectName()), designMode=designMode) - PoolMotorClient.__init__(self) - self.loadUi() - self.recheckTaurusParent() - - self.show_context_menu = True - - self.setAcceptDrops(True) - - if designMode: - self.__setTaurusIcons() - return - - # CREATE THE TaurusValue that can not be configured in the Designer - self.taurus_value = TaurusValue(self.ui.taurusValueContainer) - - # Use a DragDevAndAttributeLabelWidget to provide a richer QMimeData - # content - self.taurus_value.setLabelWidgetClass( - LabelWidgetDragsDeviceAndAttribute) - - # Make the label to be the device alias - self.taurus_value.setLabelConfig('dev_alias') - - self.taurus_value_enc = TaurusValue(self.ui.taurusValueContainer) - - # THIS WILL BE DONE IN THE DESIGNER - # Config Button will launch a PoolMotorConfigurationForm -# 19.08.2011 after discussion between cpascual, gcui and zreszela, Configuration Panel was rolled back to -# standard TaurusAttrForm - list of all attributes alphabetically ordered -# taurus_attr_form = PoolMotorConfigurationForm() - taurus_attr_form = TaurusAttrForm() - - taurus_attr_form.setMinimumSize(Qt.QSize(470, 800)) - self.ui.btnCfg.setWidget(taurus_attr_form) - self.ui.btnCfg.setUseParentModel(True) - - # ADD AN EVENT FILTER FOR THE STATUS LABEL IN ORDER TO PROVIDE JUST THE - # STRING FROM THE CONTROLLER (LAST LINE) - def just_ctrl_status_line(evt_src, evt_type, evt_value): - if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: - return evt_src, evt_type, evt_value - try: - status = evt_value.value - last_line = status.split('\n')[-1] - new_evt_value = PyTango.DeviceAttribute(evt_value) - new_evt_value.value = last_line - return evt_src, evt_type, new_evt_value - except: - return evt_src, evt_type, evt_value - self.ui.lblStatus.insertEventFilter(just_ctrl_status_line) - - # These buttons are just for showing if the limit is active or not - self.ui.btnMin.setEnabled(False) - self.ui.btnMax.setEnabled(False) - - # HOMING NOT IMPLMENTED YET - self.ui.btnHome.setEnabled(False) - - # DEFAULT VISIBLE COMPONENTS - self.toggleHideAll() - self.toggleMoveAbsolute(True) - self.toggleStopMove(True) - - # SET TAURUS ICONS - self.__setTaurusIcons() - - self.ui.motorGroupBox.setContextMenuPolicy(Qt.Qt.CustomContextMenu) - - self.ui.motorGroupBox.customContextMenuRequested.connect( - self.buildContextMenu) - self.ui.btnGoToNeg.clicked.connect(self.jogNeg) - self.ui.btnGoToNegPress.pressed.connect(self.jogNeg) - self.ui.btnGoToNegPress.released.connect(self.abort) - self.ui.btnGoToNegInc.clicked.connect(self.goToNegInc) - self.ui.btnGoToPos.clicked.connect(self.jogPos) - self.ui.btnGoToPosPress.pressed.connect(self.jogPos) - self.ui.btnGoToPosPress.released.connect(self.abort) - self.ui.btnGoToPosInc.clicked.connect(self.goToPosInc) - - self.ui.btnHome.clicked.connect(self.goHome) - self.ui.btnStop.clicked.connect(self.abort) - - # ALSO UPDATE THE WIDGETS EVERYTIME THE FORM HAS TO BE SHOWN - self.ui.btnCfg.clicked.connect(taurus_attr_form._updateAttrWidgets) - self.ui.btnCfg.clicked.connect(self.buildBetterCfgDialogTitle) - - ####################################################################### - ######################################## - # LET TAURUS CONFIGURATION MECANISM SHINE! - ######################################## - self.registerConfigProperty( - self.ui.inc.isVisible, self.toggleMoveRelative, 'MoveRelative') - self.registerConfigProperty( - self.ui.btnGoToNegPress.isVisible, self.toggleMoveContinuous, 'MoveContinuous') - self.registerConfigProperty( - self.ui.btnGoToNeg.isVisible, self.toggleMoveToLimits, 'MoveToLimits') - self.registerConfigProperty( - self.ui.btnStop.isVisible, self.toggleStopMove, 'StopMove') - self.registerConfigProperty( - self.ui.btnHome.isVisible, self.toggleHoming, 'Homing') - self.registerConfigProperty( - self.ui.btnCfg.isVisible, self.toggleConfig, 'Config') - self.registerConfigProperty( - self.ui.lblStatus.isVisible, self.toggleStatus, 'Status') - ####################################################################### - - def __setTaurusIcons(self): - self.ui.btnMin.setText('') - self.ui.btnMin.setIcon(Qt.QIcon("actions:list-remove.svg")) - self.ui.btnMax.setText('') - self.ui.btnMax.setIcon(Qt.QIcon("actions:list-add.svg")) - - self.ui.btnGoToNeg.setText('') - self.ui.btnGoToNeg.setIcon( - Qt.QIcon("actions:media_skip_backward.svg")) - self.ui.btnGoToNegPress.setText('') - self.ui.btnGoToNegPress.setIcon( - Qt.QIcon("actions:media_seek_backward.svg")) - self.ui.btnGoToNegInc.setText('') - self.ui.btnGoToNegInc.setIcon( - Qt.QIcon("actions:media_playback_backward.svg")) - self.ui.btnGoToPos.setText('') - self.ui.btnGoToPos.setIcon(Qt.QIcon("actions:media_skip_forward.svg")) - self.ui.btnGoToPosPress.setText('') - self.ui.btnGoToPosPress.setIcon( - Qt.QIcon("actions:media_seek_forward.svg")) - self.ui.btnGoToPosInc.setText('') - self.ui.btnGoToPosInc.setIcon( - Qt.QIcon("actions:media_playback_start.svg")) - self.ui.btnStop.setText('') - self.ui.btnStop.setIcon(Qt.QIcon("actions:media_playback_stop.svg")) - self.ui.btnHome.setText('') - self.ui.btnHome.setIcon(Qt.QIcon("actions:go-home.svg")) - self.ui.btnCfg.setText('') - self.ui.btnCfg.setIcon(Qt.QIcon("categories:preferences-system.svg")) - ####################################################################### - - #@Qt.pyqtSlot(list) - def updateLimits(self, limits): - if isinstance(limits, dict): - limits = limits["limits"] - pos_lim = limits[1] - pos_btnstylesheet = '' - enabled = True - if pos_lim: - pos_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( - PyTango.DevState.ALARM) - enabled = False - self.ui.btnMax.setStyleSheet(pos_btnstylesheet) - self.ui.btnGoToPos.setEnabled(enabled) - self.ui.btnGoToPosPress.setEnabled(enabled) - self.ui.btnGoToPosInc.setEnabled(enabled) - - neg_lim = limits[2] - neg_btnstylesheet = '' - enabled = True - if neg_lim: - neg_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( - PyTango.DevState.ALARM) - enabled = False - self.ui.btnMin.setStyleSheet(neg_btnstylesheet) - self.ui.btnGoToNeg.setEnabled(enabled) - self.ui.btnGoToNegPress.setEnabled(enabled) - self.ui.btnGoToNegInc.setEnabled(enabled) - - # def sizeHint(self): - # return Qt.QSize(300,30) - - @Qt.pyqtSlot() - def goToNegInc(self): - self.moveInc(-1 * self.ui.inc.value()) - - @Qt.pyqtSlot() - def goToPosInc(self): - self.moveInc(self.ui.inc.value()) - - def buildContextMenu(self, point): - if not self.show_context_menu: - return - menu = Qt.QMenu(self) - - action_hide_all = Qt.QAction(self) - action_hide_all.setText('Hide All') - menu.addAction(action_hide_all) - - action_show_all = Qt.QAction(self) - action_show_all.setText('Show All') - menu.addAction(action_show_all) - - action_move_absolute = Qt.QAction(self) - action_move_absolute.setText('Move Absolute') - action_move_absolute.setCheckable(True) - action_move_absolute.setChecked( - self.taurus_value.writeWidget().isVisible()) - menu.addAction(action_move_absolute) - - action_move_relative = Qt.QAction(self) - action_move_relative.setText('Move Relative') - action_move_relative.setCheckable(True) - action_move_relative.setChecked(self.ui.inc.isVisible()) - menu.addAction(action_move_relative) - - action_move_continuous = Qt.QAction(self) - action_move_continuous.setText('Move Continuous') - action_move_continuous.setCheckable(True) - action_move_continuous.setChecked(self.ui.btnGoToNegPress.isVisible()) - menu.addAction(action_move_continuous) - - action_move_to_limits = Qt.QAction(self) - action_move_to_limits.setText('Move to Limits') - action_move_to_limits.setCheckable(True) - action_move_to_limits.setChecked(self.ui.btnGoToNeg.isVisible()) - menu.addAction(action_move_to_limits) - - action_encoder = Qt.QAction(self) - action_encoder.setText('Encoder Read') - action_encoder.setCheckable(True) - action_encoder.setChecked(self.taurus_value_enc.isVisible()) - if self.has_encoder: - menu.addAction(action_encoder) - - action_stop_move = Qt.QAction(self) - action_stop_move.setText('Stop Movement') - action_stop_move.setCheckable(True) - action_stop_move.setChecked(self.ui.btnStop.isVisible()) - menu.addAction(action_stop_move) - - action_homing = Qt.QAction(self) - action_homing.setText('Homing') - action_homing.setCheckable(True) - action_homing.setChecked(self.ui.btnHome.isVisible()) - menu.addAction(action_homing) - - action_config = Qt.QAction(self) - action_config.setText('Config') - action_config.setCheckable(True) - action_config.setChecked(self.ui.btnCfg.isVisible()) - menu.addAction(action_config) - - action_status = Qt.QAction(self) - action_status.setText('Status') - action_status.setCheckable(True) - action_status.setChecked(self.ui.lblStatus.isVisible()) - menu.addAction(action_status) - - action_hide_all.triggered.connect(self.toggleHideAll) - action_show_all.triggered.connect(self.toggleShowAll) - action_move_absolute.toggled.connect(self.toggleMoveAbsolute) - action_move_relative.toggled.connect(self.toggleMoveRelative) - action_move_continuous.toggled.connect(self.toggleMoveContinuous) - action_move_to_limits.toggled.connect(self.toggleMoveToLimits) - action_encoder.toggled.connect(self.toggleEncoder) - action_stop_move.toggled.connect(self.toggleStopMove) - action_homing.toggled.connect(self.toggleHoming) - action_config.toggled.connect(self.toggleConfig) - action_status.toggled.connect(self.toggleStatus) - - menu.popup(self.cursor().pos()) - - def toggleHideAll(self): - self.toggleAll(False) - - def toggleShowAll(self): - self.toggleAll(True) - - def toggleAll(self, visible): - self.toggleMoveAbsolute(visible) - self.toggleMoveRelative(visible) - self.toggleMoveContinuous(visible) - self.toggleMoveToLimits(visible) - self.toggleEncoder(visible) - self.toggleStopMove(visible) - self.toggleHoming(visible) - self.toggleConfig(visible) - self.toggleStatus(visible) - - def toggleMoveAbsolute(self, visible): - if self.taurus_value.writeWidget() is not None: - self.taurus_value.writeWidget().setVisible(visible) - - def toggleMoveRelative(self, visible): - self.ui.btnGoToNegInc.setVisible(visible) - self.ui.inc.setVisible(visible) - self.ui.btnGoToPosInc.setVisible(visible) - - def toggleMoveContinuous(self, visible): - self.ui.btnGoToNegPress.setVisible(visible) - self.ui.btnGoToPosPress.setVisible(visible) - - def toggleMoveToLimits(self, visible): - self.ui.btnGoToNeg.setVisible(visible) - self.ui.btnGoToPos.setVisible(visible) - - def toggleEncoder(self, visible): - self.taurus_value_enc.setVisible(visible) - - def toggleStopMove(self, visible): - self.ui.btnStop.setVisible(visible) - - def toggleHoming(self, visible): - self.ui.btnHome.setVisible(visible) - - def toggleConfig(self, visible): - self.ui.btnCfg.setVisible(visible) - - def toggleStatus(self, visible): - self.ui.lblStatus.setVisible(visible) - - def dragEnterEvent(self, event): - event.accept() - - def dropEvent(self, event): - mimeData = event.mimeData() - if mimeData.hasFormat(TAURUS_DEV_MIME_TYPE): - model = str(mimeData.data(TAURUS_DEV_MIME_TYPE)) - elif mimeData.hasFormat(TAURUS_ATTR_MIME_TYPE): - model = str(mimeData.data(TAURUS_ATTR_MIME_TYPE)) - else: - model = str(mimeData.text()) - self.setModel(model) - - def keyPressEvent(self, key_event): - if key_event.key() == Qt.Qt.Key_Escape: - self.abort() - key_event.accept() - TaurusWidget.keyPressEvent(self, key_event) - - def buildBetterCfgDialogTitle(self): - while self.ui.btnCfg._dialog is None: - pass - model = self.getModel() - self.ui.btnCfg._dialog.setWindowTitle( - '%s config' % taurus.Factory().getDevice(model).getSimpleName()) - - @classmethod - def getQtDesignerPluginInfo(cls): - ret = TaurusWidget.getQtDesignerPluginInfo() - ret['module'] = 'taurus.qt.qtgui.extra_pool' - ret['group'] = 'Taurus Sardana' - ret['icon'] = ':/designer/extra_motor.png' - ret['container'] = False - return ret - - def showEvent(self, event): - TaurusWidget.showEvent(self, event) - try: - self.motor_dev.getAttribute('Position').enablePolling(force=True) - except AttributeError as e: - self.debug('Error in showEvent: %s', repr(e)) - - def hideEvent(self, event): - TaurusWidget.hideEvent(self, event) - try: - self.motor_dev.getAttribute('Position').disablePolling() - except AttributeError as e: - self.debug('Error in hideEvent: %s', repr(e)) - - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - # QT properties - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - @Qt.pyqtSlot() - def getModel(self): - return self.ui.motorGroupBox.getModel() - - @Qt.pyqtSlot("QString") - def setModel(self, model): - # DUE TO A BUG IN TAUGROUPBOX, WE NEED THE FULL MODEL NAME - try: - # In case the model is an attribute of a motor, get the device name - # TODO: When sardana is moved to Taurus 4 replace this line by - # taurushelper.isValidName(model, [TaurusElementType.Device]) - if not TangoDeviceNameValidator().isValid(model): - model = model.rpartition('/')[0] - model = taurus.Factory().getDevice(model).getFullName() - self.setMotor(model) - self.ui.motorGroupBox.setModel(model) - self.ui.motorGroupBox.setEnabled(True) - - self.taurus_value.setModel(model + '/Position') - - # DUE TO A BUG IN TAURUSVALUE, THAT DO NOT USE PARENT MODEL WE NEED - # TO ALWAYS SET THE MODEL - self.taurus_value.setUseParentModel(False) - - # THE FORCED APPLY HAS TO BE DONE AFTER THE MODEL IS SET, SO THE - # WRITEWIDGET IS AVAILABLE - if self.taurus_value.writeWidget() is not None: - self.taurus_value.writeWidget().setForcedApply(True) - - show_enc = self.taurus_value_enc.isVisible() - if self.has_encoder: - self.taurus_value_enc.setModel(model + '/Encoder') - self.taurus_value_enc.setUseParentModel(False) - self.taurus_value_enc.readWidget().setBgRole('none') - else: - self.taurus_value_enc.setModel(None) - show_enc = False - if not show_enc: - self.toggleEncoder(False) - - try: - self.unregisterConfigurableItem('MoveAbsolute') - self.unregisterConfigurableItem('Encoder') - except: - pass - self.registerConfigProperty(self.taurus_value.writeWidget( - ).isVisible, self.toggleMoveAbsolute, 'MoveAbsolute') - self.registerConfigProperty( - self.taurus_value_enc.isVisible, self.toggleEncoder, 'Encoder') - - # SINCE TAURUSLAUNCHERBUTTON HAS NOT THIS PROPERTY IN THE - # DESIGNER, WE MUST SET IT HERE - self.ui.btnCfg.setUseParentModel(True) - - # CONFIGURE A LISTENER IN ORDER TO UPDATE LIMIT SWITCHES STATES - self.limits_listener = LimitsListener() - self.limits_listener.updateLimits.connect( - self.updateLimits) - limits_visible = False - if self.has_limits: - self._limits_switches = self.motor_dev.getAttribute( - 'Limit_switches') - self._limits_switches.addListener(self.limits_listener) - # self.updateLimits(limits_attribute.read().rvalue) - limits_visible = True - self.ui.btnMin.setVisible(limits_visible) - self.ui.btnMax.setVisible(limits_visible) - except Exception as e: - self.ui.motorGroupBox.setEnabled(False) - self.info('Error setting model "%s". Reason: %s' % - (model, repr(e))) - self.traceback() - - @Qt.pyqtSlot() - def resetModel(self): - self.ui.motorGroupBox.resetModel() - - @Qt.pyqtSlot() - def getShowContextMenu(self): - return self.show_context_menu - - @Qt.pyqtSlot() - def setShowContextMenu(self, showContextMenu): - self.show_context_menu = showContextMenu - - @Qt.pyqtSlot() - def resetShowContextMenu(self): - self.show_context_menu = True - - @Qt.pyqtSlot() - def getStepSize(self): - return self.ui.inc.value() - - @Qt.pyqtSlot(float) - def setStepSize(self, stepSize): - self.ui.inc.setValue(stepSize) - - @Qt.pyqtSlot() - def resetStepSize(self): - self.setStepSize(1) - - @Qt.pyqtSlot() - def getStepSizeIncrement(self): - return self.ui.inc.singleStep() - - @Qt.pyqtSlot(float) - def setStepSizeIncrement(self, stepSizeIncrement): - self.ui.inc.setSingleStep(stepSizeIncrement) - - @Qt.pyqtSlot() - def resetStepSizeIncrement(self): - self.setStepSizeIncrement(1) - - model = Qt.pyqtProperty("QString", getModel, setModel, resetModel) - stepSize = Qt.pyqtProperty( - "double", getStepSize, setStepSize, resetStepSize) - stepSizeIncrement = Qt.pyqtProperty( - "double", getStepSizeIncrement, setStepSizeIncrement, resetStepSizeIncrement) - - -########################################################################## -# NEW APPROACH TO OPERATE POOL MOTORS FROM A TAURUS FORM INHERITTING DIRECTLY FROM TaurusVALUE # -# AND USING PARTICULAR CLASSES THAT KNOW THEY ARE PART OF A TAURUSVALUE AND CAN INTERACT # -########################################################################## - -class TaurusAttributeListener(Qt.QObject): - """ - A class that recieves events on tango attribute changes. - If that is the case it emits a signal with the event's value. - """ - - eventReceivedSignal = Qt.pyqtSignal(compat.PY_OBJECT) - - def __init__(self): - Qt.QObject.__init__(self) - - def eventReceived(self, evt_src, evt_type, evt_value): - if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: - return - try: - value = evt_value.rvalue.magnitude - except AttributeError: - # spectrum/images or str attribute values are not Quantities - value = evt_value.rvalue - self.eventReceivedSignal.emit(value) - - -################################################## -# LABEL WIDGET # -################################################## -class PoolMotorTVLabelWidget(TaurusWidget): - ''' - @TODO tooltip should be extended with status info - @TODO context menu should be the lbl_alias extended - @TODO default tooltip extended with the complete (multiline) status - @TODO rightclick popup menu with actions: (1) switch user/expert view, (2) Config -all attributes-, (3) change motor - For the (3), a drop event should accept if it is a device, and add it to the 'change-motor' list and select - @TODO on the 'expert' row, it could be an ENABLE section with a button to set PowerOn to True/False - ''' - - layoutAlignment = Qt.Qt.AlignTop - - def __init__(self, parent=None, designMode=False): - TaurusWidget.__init__(self, parent, designMode) - self.setLayout(Qt.QGridLayout()) - self.layout().setContentsMargins(0, 0, 0, 0) - self.layout().setSpacing(0) - - self.lbl_alias = DefaultLabelWidget(parent, designMode) - self.lbl_alias.setBgRole('none') - self.layout().addWidget(self.lbl_alias) - - self.btn_poweron = Qt.QPushButton() - self.btn_poweron.setText('Set ON') - self.layout().addWidget(self.btn_poweron) - - # Align everything on top - self.layout().addItem(Qt.QSpacerItem( - 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding)) - - # I don't like this approach, there should be something like - # self.lbl_alias.addAction(...) - self.lbl_alias.contextMenuEvent = \ - lambda event: self.contextMenuEvent(event) - - # I' don't like this approach, there should be something like - # self.lbl_alias.addToolTipCallback(self.calculate_extra_tooltip) - self.lbl_alias.getFormatedToolTip = self.calculateExtendedTooltip - - # I' don't like this approach, there should be something like - # self.lbl_alias.disableDrag() or self.lbl_alias.setDragEnabled(False) - # or better, define if Attribute or Device or Both have to be included - # in the mimeData - self.lbl_alias.mouseMoveEvent = self.mouseMoveEvent - - def setExpertView(self, expertView): - btn_poweron_visible = expertView \ - and self.taurusValueBuddy().hasPowerOn() - self.btn_poweron.setVisible(btn_poweron_visible) - - @Qt.pyqtSlot() - @ProtectTaurusMessageBox( - msg='An error occurred trying to write PowerOn Attribute.') - def setPowerOn(self): - motor_dev = self.taurusValueBuddy().motor_dev - if motor_dev is not None: - poweron = (self.btn_poweron.text() == 'Set ON') - motor_dev.getAttribute('PowerOn').write(poweron) - - def setModel(self, model): - # Handle User/Expert view - try: - self.taurusValueBuddy().expertViewChanged.disconnect( - self.setExpertView) - except TypeError: - pass - try: - self.btn_poweron.clicked.disconnect(self.setPowerOn) - except TypeError: - pass - if model in (None, ''): - self.lbl_alias.setModel(model) - TaurusWidget.setModel(self, model) - return - self.lbl_alias.taurusValueBuddy = self.taurusValueBuddy - self.lbl_alias.setModel(model) - TaurusWidget.setModel(self, model + '/Status') - self.taurusValueBuddy().expertViewChanged.connect( - self.setExpertView) - # Handle Power ON/OFF - self.btn_poweron.clicked.connect(self.setPowerOn) - self.setExpertView(self.taurusValueBuddy()._expertView) - - def calculateExtendedTooltip(self, cache=False): - default_label_widget_tooltip = DefaultLabelWidget.getFormatedToolTip( - self.lbl_alias, cache) - status_info = '' - motor_dev = self.taurusValueBuddy().motor_dev - if motor_dev is not None: - status = motor_dev.getAttribute('Status').read().rvalue - # MAKE IT LOOK LIKE THE STANDARD TABLE FOR TAURUS TOOLTIPS - status_lines = status.split('\n') - status_info = '' - for status_extra_line in status_lines[1:]: - status_info += '' - status_info += '
Status:' + \ - status_lines[0] + '
' + status_extra_line + '
' - return default_label_widget_tooltip + status_info - - def contextMenuEvent(self, event): - # Overwrite the default taurus label behaviour - menu = Qt.QMenu(self) - action_expert_view = Qt.QAction(self) - action_expert_view.setText('Expert View') - action_expert_view.setCheckable(True) - action_expert_view.setChecked(self.taurusValueBuddy()._expertView) - menu.addAction(action_expert_view) - action_expert_view.toggled.connect( - self.taurusValueBuddy().setExpertView) - - action_tango_attributes = Qt.QAction(self) - action_tango_attributes.setIcon( - Qt.QIcon("categories:preferences-system.svg")) - action_tango_attributes.setText('Tango Attributes') - menu.addAction(action_tango_attributes) - action_tango_attributes.triggered.connect( - self.taurusValueBuddy().showTangoAttributes) - - cm_action = menu.addAction("Compact") - cm_action.setCheckable(True) - cm_action.setChecked(self.taurusValueBuddy().isCompact()) - cm_action.toggled.connect(self.taurusValueBuddy().setCompact) - - menu.exec_(event.globalPos()) - event.accept() - - def mouseMoveEvent(self, event): - model = self.taurusValueBuddy().getModelObj() - mimeData = Qt.QMimeData() - mimeData.setText(self.lbl_alias.text()) - dev_name = model.getFullName().encode('utf-8') - attr_name = dev_name + b'/Position' - mimeData.setData(TAURUS_DEV_MIME_TYPE, dev_name) - mimeData.setData(TAURUS_ATTR_MIME_TYPE, attr_name) - - drag = Qt.QDrag(self) - drag.setMimeData(mimeData) - drag.setHotSpot(event.pos() - self.rect().topLeft()) - drag.exec_(Qt.Qt.CopyAction, Qt.Qt.CopyAction) - -################################################## -# READ WIDGET # -################################################## - - -class PoolMotorTVReadWidget(TaurusWidget): - ''' - @TODO on the 'expert' row, there should be an Indexer/Encoder radiobuttongroup to show units from raw dial/indx/enc - @TODO TaurusLCD may be used but, now it does not display the sign, and color is WHITE... - ''' - - layoutAlignment = Qt.Qt.AlignTop - - def __init__(self, parent=None, designMode=False): - TaurusWidget.__init__(self, parent, designMode) - - self.setLayout(Qt.QGridLayout()) - self.layout().setContentsMargins(0, 0, 0, 0) - self.layout().setSpacing(0) - - limits_layout = Qt.QHBoxLayout() - limits_layout.setContentsMargins(0, 0, 0, 0) - limits_layout.setSpacing(0) - - self.btn_lim_neg = Qt.QPushButton() - self.btn_lim_neg.setToolTip('Negative Limit') - # self.btn_lim_neg.setEnabled(False) - self.prepare_button(self.btn_lim_neg) - self.btn_lim_neg.setIcon(Qt.QIcon("actions:list-remove.svg")) - limits_layout.addWidget(self.btn_lim_neg) - - self.btn_lim_pos = Qt.QPushButton() - self.btn_lim_pos.setToolTip('Positive Limit') - # self.btn_lim_pos.setEnabled(False) - self.prepare_button(self.btn_lim_pos) - self.btn_lim_pos.setIcon(Qt.QIcon("actions:list-add.svg")) - limits_layout.addWidget(self.btn_lim_pos) - - self.layout().addLayout(limits_layout, 0, 0) - - self.lbl_read = TaurusLabel() - self.lbl_read.setBgRole('quality') - self.lbl_read.setSizePolicy(Qt.QSizePolicy( - Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) - self.layout().addWidget(self.lbl_read, 0, 1) - - # WITH A COMPACT VIEW, BETTER TO BE ABLE TO STOP! - self.btn_stop = Qt.QPushButton() - self.btn_stop.setToolTip('Stops the motor') - self.prepare_button(self.btn_stop) - self.btn_stop.setIcon(Qt.QIcon("actions:media_playback_stop.svg")) - self.layout().addWidget(self.btn_stop, 0, 2) - - self.btn_stop.clicked.connect(self.abort) - - # WITH COMPACT VIEW, WE NEED TO FORWARD DOUBLE CLICK EVENT - self.lbl_read.installEventFilter(self) - - # @TODO right now, no options here... - #self.cb_expertRead = Qt.QComboBox() - # self.cb_expertRead.addItems(['Enc']) - #self.layout().addWidget(self.cb_expertRead, 1, 0) - - self.lbl_enc = Qt.QLabel('Encoder') - self.layout().addWidget(self.lbl_enc, 1, 0) - - self.lbl_enc_read = TaurusLabel() - self.lbl_enc_read.setBgRole('none') - self.lbl_enc_read.setSizePolicy(Qt.QSizePolicy( - Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) - self.layout().addWidget(self.lbl_enc_read, 1, 1) - - # Align everything on top - self.layout().addItem(Qt.QSpacerItem( - 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 2) - - # IN ORDER TO BEHAVE AS EXPECTED REGARDING THE 'COMPACT VIEW' FEATURE - # WE NEED TO SET THE 'EXPERTVIEW' WITHOUT ACCESSING THE taurusValueBuddy WHICH IS STILL NOT LINKED - # SO WE ASSUME 'expertview is FALSE' AND WE HAVE TO AVOID self.setExpertView :-( - # WOULD BE NICE THAT THE taurusValueBuddy COULD EMIT THE PROPER - # SIGNAL... - self.lbl_enc.setVisible(False) - self.lbl_enc_read.setVisible(False) - - def eventFilter(self, obj, event): - if event.type() == Qt.QEvent.MouseButtonDblClick: - if isinstance(self.parent(), TaurusReadWriteSwitcher): - self.parent().enterEdit() - return True - try: - if obj is self.lbl_read: - return self.lbl_read.eventFilter(obj, event) - except AttributeError: - # self.lbl_read may not exist now - pass - return True - - @Qt.pyqtSlot() - @ProtectTaurusMessageBox(msg='An error occurred trying to abort the motion.') - def abort(self): - motor_dev = self.taurusValueBuddy().motor_dev - if motor_dev is not None: - motor_dev.abort() - - def setExpertView(self, expertView): - self.lbl_enc.setVisible(False) - self.lbl_enc_read.setVisible(False) - if self.taurusValueBuddy().motor_dev is not None: - hw_limits = self.taurusValueBuddy().hasHwLimits() - self.btn_lim_neg.setEnabled(hw_limits) - self.btn_lim_pos.setEnabled(hw_limits) - - if expertView and self.taurusValueBuddy().motor_dev is not None: - encoder = self.taurusValueBuddy().hasEncoder() - self.lbl_enc.setVisible(encoder) - self.lbl_enc_read.setVisible(encoder) - - def prepare_button(self, btn): - btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) - btn_policy.setHorizontalStretch(0) - btn_policy.setVerticalStretch(0) - btn.setSizePolicy(btn_policy) - btn.setMinimumSize(25, 25) - btn.setMaximumSize(25, 25) - btn.setText('') - - def setModel(self, model): - if hasattr(self, 'taurusValueBuddy'): - try: - self.taurusValueBuddy().expertViewChanged.disconnect( - self.setExpertView) - except TypeError: - pass - if model in (None, ''): - TaurusWidget.setModel(self, model) - self.lbl_read.setModel(model) - self.lbl_enc_read.setModel(model) - return - TaurusWidget.setModel(self, model + '/Position') - self.lbl_read.setModel(model + '/Position') - self.lbl_enc_read.setModel(model + '/Encoder') - # Handle User/Expert view - self.setExpertView(self.taurusValueBuddy()._expertView) - self.taurusValueBuddy().expertViewChanged.connect( - self.setExpertView) - -################################################## -# WRITE WIDGET # -################################################## - -class PoolMotorTVWriteWidget(TaurusWidget): - - layoutAlignment = Qt.Qt.AlignTop - - applied = Qt.pyqtSignal() - - def __init__(self, parent=None, designMode=False): - TaurusWidget.__init__(self, parent, designMode) - - # ------------------------------------------------------------ - # Workaround for Taurus3 support - if int(taurus.Release.version.split('.')[0]) < 4: - from taurus.qt.qtgui.base.taurusbase import baseOldSignal - self.applied = baseOldSignal('applied', self) - # ------------------------------------------------------------ - - self.setLayout(Qt.QGridLayout()) - self.layout().setContentsMargins(0, 0, 0, 0) - self.layout().setSpacing(0) - - self.le_write_absolute = TaurusValueLineEdit() - self.layout().addWidget(self.le_write_absolute, 0, 0) - - self.qw_write_relative = Qt.QWidget() - self.qw_write_relative.setLayout(Qt.QHBoxLayout()) - self.qw_write_relative.layout().setContentsMargins(0, 0, 0, 0) - self.qw_write_relative.layout().setSpacing(0) - - self.cb_step = Qt.QComboBox() - self.cb_step.setSizePolicy(Qt.QSizePolicy( - Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) - self.cb_step.setEditable(True) - self.cb_step.lineEdit().setValidator(Qt.QDoubleValidator(self)) - self.cb_step.lineEdit().setAlignment(Qt.Qt.AlignRight) - self.cb_step.addItem('1') - self.qw_write_relative.layout().addWidget(self.cb_step) - - self.btn_step_down = Qt.QPushButton() - self.btn_step_down.setToolTip('Decrements motor position') - self.prepare_button(self.btn_step_down) - self.btn_step_down.setIcon( - Qt.QIcon("actions:media_playback_backward.svg")) - self.qw_write_relative.layout().addWidget(self.btn_step_down) - - self.btn_step_up = Qt.QPushButton() - self.btn_step_up.setToolTip('Increments motor position') - self.prepare_button(self.btn_step_up) - self.btn_step_up.setIcon(Qt.QIcon("actions:media_playback_start.svg")) - self.qw_write_relative.layout().addWidget(self.btn_step_up) - - self.layout().addWidget(self.qw_write_relative, 0, 0) - - self.cbAbsoluteRelative = Qt.QComboBox() - self.cbAbsoluteRelative.currentIndexChanged['QString'].connect( - self.cbAbsoluteRelativeChanged) - self.cbAbsoluteRelative.addItems(['Abs', 'Rel']) - self.layout().addWidget(self.cbAbsoluteRelative, 0, 1) - - # WITH THE COMPACCT VIEW FEATURE, BETTER TO HAVE IT IN THE READ WIDGET - # WOULD BE BETTER AS AN 'EXTRA WIDGET' (SOME DAY...) - #self.btn_stop = Qt.QPushButton() - #self.btn_stop.setToolTip('Stops the motor') - # self.prepare_button(self.btn_stop) - # self.btn_stop.setIcon(getIcon(':/actions/media_playback_stop.svg')) - #self.layout().addWidget(self.btn_stop, 0, 2) - - btns_layout = Qt.QHBoxLayout() - btns_layout.setContentsMargins(0, 0, 0, 0) - btns_layout.setSpacing(0) - - btns_layout.addItem(Qt.QSpacerItem( - 1, 1, Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum)) - - self.btn_to_neg = Qt.QPushButton() - self.btn_to_neg.setToolTip( - 'Moves the motor towards the Negative Software Limit') - self.prepare_button(self.btn_to_neg) - self.btn_to_neg.setIcon(Qt.QIcon("actions:media_skip_backward.svg")) - btns_layout.addWidget(self.btn_to_neg) - - self.btn_to_neg_press = Qt.QPushButton() - self.btn_to_neg_press.setToolTip( - 'Moves the motor (while pressed) towards the Negative Software Limit') - self.prepare_button(self.btn_to_neg_press) - self.btn_to_neg_press.setIcon( - Qt.QIcon("actions:media_seek_backward.svg")) - btns_layout.addWidget(self.btn_to_neg_press) - - self.btn_to_pos_press = Qt.QPushButton() - self.prepare_button(self.btn_to_pos_press) - self.btn_to_pos_press.setToolTip( - 'Moves the motor (while pressed) towards the Positive Software Limit') - self.btn_to_pos_press.setIcon( - Qt.QIcon("actions:media_seek_forward.svg")) - btns_layout.addWidget(self.btn_to_pos_press) - - self.btn_to_pos = Qt.QPushButton() - self.btn_to_pos.setToolTip( - 'Moves the motor towards the Positive Software Limit') - self.prepare_button(self.btn_to_pos) - self.btn_to_pos.setIcon(Qt.QIcon("actions:media_skip_forward.svg")) - btns_layout.addWidget(self.btn_to_pos) - - btns_layout.addItem(Qt.QSpacerItem( - 1, 1, Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum)) - - self.layout().addLayout(btns_layout, 1, 0, 1, 3) - - self.btn_step_down.clicked.connect(self.stepDown) - self.btn_step_up.clicked.connect(self.stepUp) - # self.btn_stop.clicked.connect(self.abort) - self.btn_to_neg.clicked.connect(self.goNegative) - self.btn_to_neg_press.pressed.connect(self.goNegative) - self.btn_to_neg_press.released.connect(self.abort) - self.btn_to_pos.clicked.connect(self.goPositive) - self.btn_to_pos_press.pressed.connect(self.goPositive) - self.btn_to_pos_press.released.connect(self.abort) - - # Align everything on top - self.layout().addItem(Qt.QSpacerItem( - 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 3) - - # IN ORDER TO BEHAVE AS EXPECTED REGARDING THE 'COMPACT VIEW' FEATURE - # WE NEED TO SET THE 'EXPERTVIEW' WITHOUT ACCESSING THE taurusValueBuddy WHICH IS STILL NOT LINKED - # SO WE ASSUME 'expertview is FALSE' AND WE HAVE TO AVOID self.setExpertView :-( - # WOULD BE NICE THAT THE taurusValueBuddy COULD EMIT THE PROPER - # SIGNAL... - self.btn_to_neg.setVisible(False) - self.btn_to_neg_press.setVisible(False) - self.btn_to_pos.setVisible(False) - self.btn_to_pos_press.setVisible(False) - - # IN EXPERT VIEW, WE HAVE TO FORWARD THE ''editingFinished()' SIGNAL - # FROM TaurusValueLineEdit TO Switcher - self.le_write_absolute.applied.connect(self.emitEditingFinished) - self.btn_step_down.clicked.connect(self.emitEditingFinished) - self.btn_step_up.clicked.connect(self.emitEditingFinished) - self.btn_to_neg.clicked.connect(self.emitEditingFinished) - self.btn_to_pos.clicked.connect(self.emitEditingFinished) - - # list of widgets used for edition - editingWidgets = (self.le_write_absolute, self.cbAbsoluteRelative, - self.cb_step, self.btn_step_down, - self.btn_step_up, self.btn_to_neg, - self.btn_to_pos, self.btn_to_neg_press, - self.btn_to_pos_press) - - for w in editingWidgets: - w.installEventFilter(self) - - def eventFilter(self, obj, event): - '''reimplemented to intercept events from the subwidgets''' - try: - if obj in (self.btn_to_neg_press, self.btn_to_pos_press): - if event.type() == Qt.QEvent.MouseButtonRelease: - self.emitEditingFinished() - except AttributeError: - # self.btn_to_neg_press, self.btn_to_pos_press may not exist now - pass - # emit editingFinished when focus out to a non-editing widget - if event.type() == Qt.QEvent.FocusOut: - focused = Qt.qApp.focusWidget() - focusInChild = focused in self.findChildren(focused.__class__) - if not focusInChild: - self.emitEditingFinished() - return False - - def cbAbsoluteRelativeChanged(self, abs_rel_option): - abs_visible = abs_rel_option == 'Abs' - rel_visible = abs_rel_option == 'Rel' - self.le_write_absolute.setVisible(abs_visible) - self.qw_write_relative.setVisible(rel_visible) - - @Qt.pyqtSlot() - def stepDown(self): - self.goRelative(-1) - - @Qt.pyqtSlot() - def stepUp(self): - self.goRelative(+1) - - @ProtectTaurusMessageBox(msg='An error occurred trying to move the motor.') - def goRelative(self, direction): - motor_dev = self.taurusValueBuddy().motor_dev - if motor_dev is not None: - increment = direction * float(self.cb_step.currentText()) - position = float( - motor_dev.getAttribute('Position').read().rvalue.magnitude) - target_position = position + increment - motor_dev.getAttribute('Position').write(target_position) - - @Qt.pyqtSlot() - @ProtectTaurusMessageBox(msg='An error occurred trying to move the motor.') - def goNegative(self): - motor_dev = self.taurusValueBuddy().motor_dev - if motor_dev is not None: - min_value = float(motor_dev.getAttribute('Position').min_value) - motor_dev.getAttribute('Position').write(min_value) - - @Qt.pyqtSlot() - @ProtectTaurusMessageBox(msg='An error occurred trying to move the motor.') - def goPositive(self): - motor_dev = self.taurusValueBuddy().motor_dev - if motor_dev is not None: - max_value = float(motor_dev.getAttribute('Position').max_value) - motor_dev.getAttribute('Position').write(max_value) - - @Qt.pyqtSlot() - @ProtectTaurusMessageBox(msg='An error occurred trying to abort the motion.') - def abort(self): - motor_dev = self.taurusValueBuddy().motor_dev - if motor_dev is not None: - motor_dev.abort() - - def prepare_button(self, btn): - btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) - btn_policy.setHorizontalStretch(0) - btn_policy.setVerticalStretch(0) - btn.setSizePolicy(btn_policy) - btn.setMinimumSize(25, 25) - btn.setMaximumSize(25, 25) - btn.setText('') - - def setExpertView(self, expertView): - self.btn_to_neg.setVisible(expertView) - self.btn_to_neg_press.setVisible(expertView) - - self.btn_to_pos.setVisible(expertView) - self.btn_to_pos_press.setVisible(expertView) - - if expertView and self.taurusValueBuddy().motor_dev is not None: - neg_sw_limit_enabled = self.taurusValueBuddy().motor_dev.getAttribute( - 'Position').min_value.lower() != 'not specified' - self.btn_to_neg.setEnabled(neg_sw_limit_enabled) - self.btn_to_neg_press.setEnabled(neg_sw_limit_enabled) - - pos_sw_limit_enabled = self.taurusValueBuddy().motor_dev.getAttribute( - 'Position').max_value.lower() != 'not specified' - self.btn_to_pos.setEnabled(pos_sw_limit_enabled) - self.btn_to_pos_press.setEnabled(pos_sw_limit_enabled) - - def setModel(self, model): - if hasattr(self, 'taurusValueBuddy'): - try: - self.taurusValueBuddy().expertViewChanged.disconnect( - self.setExpertView) - except TypeError: - pass - if model in (None, ''): - TaurusWidget.setModel(self, model) - self.le_write_absolute.setModel(model) - return - TaurusWidget.setModel(self, model + '/Position') - self.le_write_absolute.setModel(model + '/Position#wvalue.magnitude') - - # Handle User/Expert View - self.setExpertView(self.taurusValueBuddy()._expertView) - self.taurusValueBuddy().expertViewChanged.connect( - self.setExpertView) - - def keyPressEvent(self, key_event): - if key_event.key() == Qt.Qt.Key_Escape: - self.abort() - key_event.accept() - TaurusWidget.keyPressEvent(self, key_event) - - @Qt.pyqtSlot() - def emitEditingFinished(self): - self.applied.emit() - - -################################################## -# UNITS WIDGET # -################################################## -class PoolMotorTVUnitsWidget(DefaultUnitsWidget): - - layoutAlignment = Qt.Qt.AlignTop - - def __init__(self, parent=None, designMode=False): - DefaultUnitsWidget.__init__(self, parent, designMode) - - def setModel(self, model): - if model in (None, ''): - DefaultUnitsWidget.setModel(self, model) - return - DefaultUnitsWidget.setModel(self, model + '/Position') - -################################################## -# TV MOTOR WIDGET # -################################################## - - -class PoolMotorTV(TaurusValue): - ''' A widget that displays and controls a pool Motor device. It - behaves as a TaurusValue. - @TODO the view mode should be stored in the configuration - @TODO the motor list should be stored in the configuration - @TODO the selected radiobuttons (dial/indx/enc) and (abs/rel) should be stored in configuration - @TODO it would be nice if the neg/pos limits could react also when software limits are 'active' - @TODO expert view for read widget should include signals (indexer/encoder/inpos)... - ''' - - expertViewChanged = Qt.pyqtSignal(bool) - - def __init__(self, parent=None, designMode=False): - TaurusValue.__init__(self, parent=parent, designMode=designMode) - self.setLabelWidgetClass(PoolMotorTVLabelWidget) - self.setReadWidgetClass(PoolMotorTVReadWidget) - self.setWriteWidgetClass(PoolMotorTVWriteWidget) - self.setUnitsWidgetClass(PoolMotorTVUnitsWidget) - self.motor_dev = None - self._expertView = False - self.limits_listener = None - self.poweron_listener = None - self.status_listener = None - self.position_listener = None - self.setExpertView(False) - - def setExpertView(self, expertView): - self._expertView = expertView - self.expertViewChanged.emit(expertView) - - def minimumHeight(self): - return None # @todo: UGLY HACK to avoid subwidgets being forced to minimumheight=20 - - def setModel(self, model): - TaurusValue.setModel(self, model) - - # disconnect signals - try: - if self.limits_listener is not None: - self.limits_listener.eventReceivedSignal.disconnect( - self.updateLimits) - except TypeError: - pass - - try: - if self.poweron_listener is not None: - self.poweron_listener.eventReceivedSignal.disconnect( - self.updatePowerOn) - except TypeError: - pass - - try: - if self.status_listener is not None: - self.status_listener.eventReceivedSignal.disconnect( - self.updateStatus) - except TypeError: - pass - - try: - if self.position_listener is not None: - self.position_listener.eventReceivedSignal.disconnect( - self.updatePosition) - except TypeError: - pass - - try: - # remove listeners - if self.motor_dev is not None: - if self.hasHwLimits(): - self.motor_dev.getAttribute( - 'Limit_Switches').removeListener(self.limits_listener) - if self.hasPowerOn(): - 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.eventReceivedSignal.connect( - self.updateLimits) - self._limit_switches = self.motor_dev.getAttribute( - 'Limit_Switches') - self._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.eventReceivedSignal.connect( - self.updatePowerOn) - self._poweron = self.motor_dev.getAttribute('PowerOn') - self._poweron.addListener(self.poweron_listener) - - # CONFIGURE AN EVENT RECEIVER IN ORDER TO UPDATED STATUS TOOLTIP - self.status_listener = TaurusAttributeListener() - self.status_listener.eventReceivedSignal.connect( - self.updateStatus) - self._status = self.motor_dev.getAttribute('Status') - self._status.addListener(self.status_listener) - - # CONFIGURE AN EVENT RECEIVER IN ORDER TO ACTIVATE LIMIT BUTTONS ON - # SOFTWARE LIMITS - self.position_listener = TaurusAttributeListener() - self.position_listener.eventReceivedSignal.connect( - self.updatePosition) - self._position = self.motor_dev.getAttribute('Position') - self._position.addListener(self.position_listener) - self.motor_dev.getAttribute('Position').enablePolling(force=True) - - self.setExpertView(self._expertView) - except Exception as e: - self.warning("Exception caught while setting model: %s", repr(e)) - self.motor_dev = None - return - - def hasPowerOn(self): - try: - return hasattr(self.motor_dev, 'PowerOn') - except: - return False - - def hasHwLimits(self): - try: - return hasattr(self.motor_dev, 'Limit_Switches') - except: - return False - - def updateLimits(self, limits, position=None): - if isinstance(limits, dict): - limits = limits["limits"] - limits = list(limits) - HOME = 0 - POS = 1 - NEG = 2 - - # Check also if the software limit is 'active' - if self.motor_dev is not None: - position_attribute = self.motor_dev.getAttribute('Position') - if position is None: - position = position_attribute.read().rvalue.magnitude - max_value_str = position_attribute.max_value - min_value_str = position_attribute.min_value - try: - max_value = float(max_value_str) - limits[POS] = limits[POS] or (position >= max_value) - except: - pass - try: - min_value = float(min_value_str) - limits[NEG] = limits[NEG] or (position <= min_value) - except: - pass - - pos_lim = limits[POS] - - pos_btnstylesheet = '' - enabled = True - if pos_lim: - pos_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( - PyTango.DevState.ALARM) - enabled = False - self.readWidget(followCompact=True).btn_lim_pos.setStyleSheet( - pos_btnstylesheet) - - self.writeWidget(followCompact=True).btn_step_up.setEnabled(enabled) - self.writeWidget(followCompact=True).btn_step_up.setStyleSheet( - pos_btnstylesheet) - enabled = enabled and self.motor_dev.getAttribute( - 'Position').max_value.lower() != 'not specified' - self.writeWidget(followCompact=True).btn_to_pos.setEnabled(enabled) - self.writeWidget( - followCompact=True).btn_to_pos_press.setEnabled(enabled) - - neg_lim = limits[NEG] - neg_btnstylesheet = '' - enabled = True - if neg_lim: - neg_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( - PyTango.DevState.ALARM) - enabled = False - self.readWidget(followCompact=True).btn_lim_neg.setStyleSheet( - neg_btnstylesheet) - - self.writeWidget(followCompact=True).btn_step_down.setEnabled(enabled) - self.writeWidget(followCompact=True).btn_step_down.setStyleSheet( - neg_btnstylesheet) - enabled = enabled and self.motor_dev.getAttribute( - 'Position').min_value.lower() != 'not specified' - self.writeWidget(followCompact=True).btn_to_neg.setEnabled(enabled) - self.writeWidget( - followCompact=True).btn_to_neg_press.setEnabled(enabled) - - def updatePowerOn(self, poweron='__no_argument__'): - if poweron == '__no_argument__': - msg = 'updatePowerOn called without args (bug in old PyQt). Ignored' - self.debug(msg) - return - btn_text = 'Set ON' - if poweron: - btn_text = 'Set OFF' - self.labelWidget().btn_poweron.setText(btn_text) - - def updateStatus(self, status): - # SHOULD THERE BE A BETTER METHOD FOR THIS UPDATE? - # IF THIS IS NOT DONE, THE TOOLTIP IS NOT CALCULATED EVERY TIME - # 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='__no_argument__'): - if position == '__no_argument__': - msg = 'updatePowerOn called without args (bug in old PyQt). Ignored' - self.debug(msg) - return - # we do not need the position for nothing... - # we just want to check if any software limit is 'active' - # and updateLimits takes care of it - if self.motor_dev is not None: - limit_switches = [False, False, False] - if self.hasHwLimits(): - limit_switches = self.motor_dev.getAttribute( - 'Limit_switches').read().rvalue - # print "update limits", limit_switches - self.updateLimits(limit_switches, position=position) - - def hasEncoder(self): - try: - return hasattr(self.motor_dev, 'Encoder') - except: - return False - - def showTangoAttributes(self): - model = self.getModel() - taurus_attr_form = TaurusAttrForm() - taurus_attr_form.setMinimumSize(Qt.QSize(555, 800)) - taurus_attr_form.setModel(model) - 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() - -################################################### -# A SIMPLER WIDGET THAT MAY BE USED OUTSIDE FORMS # -################################################### - - -class PoolMotor(TaurusFrame): - ''' A widget that displays and controls a pool Motor device. - ''' - - def __init__(self, parent=None, designMode=False): - TaurusFrame.__init__(self, parent, designMode) - - self.setLayout(Qt.QGridLayout()) - self.layout().setContentsMargins(0, 0, 0, 0) - self.layout().setSpacing(0) - - self.setFrameShape(Qt.QFrame.Box) - - self.pool_motor_tv = PoolMotorTV(self) - - def setModel(self, model): - self.pool_motor_tv.setModel(model) - try: - self.motor_dev = taurus.Device(model) - except: - return - - -def main(): - - import sys - import taurus.qt.qtgui.application - import taurus.core.util.argparse - from taurus.qt.qtgui.panel import TaurusForm - parser = taurus.core.util.argparse.get_taurus_parser() - parser.usage = "%prog [options] [ [] ...]" - - app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser) - args = app.get_command_line_args() - - #models = ['tango://controls02:10000/motor/gcipap10ctrl/8'] - models = ['motor/motctrl13/3'] - - if len(args) > 0: - models = args - - w = Qt.QWidget() - w.setLayout(Qt.QVBoxLayout()) - - tests = [] - # tests.append(1) - tests.append(2) - # tests.append(3) - # tests.append(4) - - # 1) Test PoolMotorSlim motor widget - form_pms = TaurusForm() - pms_widget_class = 'sardana.taurus.qt.qtgui.extra_pool.PoolMotorSlim' - pms_tgclass_map = {'SimuMotor': (pms_widget_class, (), {}), - 'Motor': (pms_widget_class, (), {}), - 'PseudoMotor': (pms_widget_class, (), {})} - form_pms.setCustomWidgetMap(pms_tgclass_map) - if 1 in tests: - form_pms.setModel(models) - w.layout().addWidget(form_pms) - - # 2) Test PoolMotorTV motor widget - form_tv = TaurusForm() - form_tv.setModifiableByUser(True) - tv_widget_class = 'sardana.taurus.qt.qtgui.extra_pool.PoolMotorTV' - tv_tgclass_map = {'SimuMotor': (tv_widget_class, (), {}), - 'Motor': (tv_widget_class, (), {}), - 'PseudoMotor': (tv_widget_class, (), {})} - form_tv.setCustomWidgetMap(tv_tgclass_map) - - if 2 in tests: - form_tv.setModel(models) - w.layout().addWidget(form_tv) - form_tv.setCompact(True) - - # 3) Test Stand-Alone PoolMotor widget - # New approach would be to let PoolMotorTV live outside a TaurusForm.... but inside a GridLayout - # Carlos already said this is not a good approach but... - if 3 in tests: - for motor in models: - motor_widget = PoolMotor() - motor_widget.setModel(motor) - w.layout().addWidget(motor_widget) - - # 4) Test Stand-Alone PoolMotorSlim widget - if 4 in tests: - for motor in models: - motor_widget = PoolMotorSlim() - motor_widget.setModel(motor) - w.layout().addWidget(motor_widget) - - w.show() - - sys.exit(app.exec_()) - -if __name__ == '__main__': - main() From d86e5c520815958b91042c8db78af083d1feff49 Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 19 Dec 2019 10:25:35 +0100 Subject: [PATCH 333/830] Remove show plots button from experiment configuration and update documentation --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a2d3dfa54c..6d7150c949 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /src/sardana.egg-info *.pyc .idea/ +/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py From 90ed7b5b62bf186dada66528d02e9f6ad702f6c2 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 19 Dec 2019 10:28:57 +0100 Subject: [PATCH 334/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0654ce8e59..2e92cc2f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Instruments creation and configuration in sar_demo (#1198) * Documentation to Taurus Extensions of Sardana Devices: MacroServer part and the whole Sardana part of the Qt Taurus Extensions (#1228, #1233) +* Advertise newfile macro in case no ScanDir or ScanFile is set (#1254, #1258) ### Fixed From 8f81f26654d93d274579c401e633e249fbccd0dc Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 19 Dec 2019 10:40:40 +0100 Subject: [PATCH 335/830] overwrite with other_branch's --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 1757 +++++++++++++++++ 1 file changed, 1757 insertions(+) create mode 100644 src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py new file mode 100644 index 0000000000..acacb9f234 --- /dev/null +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -0,0 +1,1757 @@ +#!/usr/bin/env python + +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +# Sardana 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. +## +# Sardana 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 Sardana. If not, see . +## +############################################################################## + +import PyTango + +from taurus.external.qt import Qt, compat + +import taurus +from taurus.core.util.colors import DEVICE_STATE_PALETTE +from taurus.core.taurusbasetypes import TaurusEventType +from taurus.core.tango.tangovalidator import TangoDeviceNameValidator +import taurus.qt.qtcore.mimetypes +from taurus.qt.qtgui.compact import TaurusReadWriteSwitcher +from taurus.qt.qtgui.dialog import ProtectTaurusMessageBox +from taurus.qt.qtgui.container import TaurusWidget +from taurus.qt.qtgui.container import TaurusFrame +from taurus.qt.qtgui.display import TaurusLabel +from taurus.qt.qtgui.input import TaurusValueLineEdit +from taurus.qt.qtgui.panel import DefaultLabelWidget +from taurus.qt.qtgui.panel import DefaultUnitsWidget +from taurus.qt.qtgui.panel import TaurusValue, TaurusAttrForm +from taurus.qt.qtcore.mimetypes import TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE +from taurus.qt.qtgui.util.ui import UILoadable + + +class LimitsListener(Qt.QObject): + """ + A class that listens to changes on motor limits. + If that is the case it emits a signal so the application + can do whatever with it. + """ + + updateLimits = Qt.pyqtSignal(compat.PY_OBJECT) + + def __init__(self): + Qt.QObject.__init__(self) + + def eventReceived(self, evt_src, evt_type, evt_value): + if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: + return + limits = evt_value.value + self.updateLimits.emit(limits.tolist()) + + +class PoolMotorClient(): + + maxint_in_32_bits = 2147483647 + + def __init__(self): + self.motor_dev = None + self.has_limits = False + self.has_encoder = False + + def setMotor(self, pool_motor_dev_name): + # AT SOME POINT THIS WILL BE USING THE 'POOL' TAURUS EXTENSION + # TO OPERATE THE MOTOR INSTEAD OF A 'TANGO' TAURUSDEVICE + try: + self.motor_dev = taurus.Device(pool_motor_dev_name) + # IT IS IMPORTANT TO KNOW IF IT IS AN ICEPAP MOTOR, SO EXTRA FEATURES CAN BE PROVIDED + # PENDING. + self.has_limits = hasattr(self.motor_dev, 'Limit_Switches') + self.has_encoder = hasattr(self.motor_dev, 'Encoder') + except Exception as e: + taurus.warning('Exception Creating Motor Device %s', str(e)) + + def moveMotor(self, pos): + #self.motor_dev['position'] = pos + # Make use of Taurus operations (being logged) + self.motor_dev.getAttribute('Position').write(pos) + + def moveInc(self, inc): + self.moveMotor(self.motor_dev['position'].value + inc) + + @Qt.pyqtSlot() + def jogNeg(self): + neg_limit = -((self.maxint_in_32_bits // 2) - 1) + # THERE IS A BUG IN THE ICEPAP THAT DOES NOT ALLOW MOVE ABSOLUTE FURTHER THAN 32 BIT + # SO IF THERE ARE STEPS PER UNIT, max_int HAS TO BE REDUCED + if hasattr(self.motor_dev, 'step_per_unit'): + neg_limit = neg_limit / self.motor_dev['step_per_unit'].value + try: + min_value = self.motor_dev.getAttribute( + 'Position').getConfig().getValueObj().min_value + neg_limit = float(min_value) + except Exception: + pass + self.moveMotor(neg_limit) + + @Qt.pyqtSlot() + def jogPos(self): + pos_limit = (self.maxint_in_32_bits // 2) - 1 + # THERE IS A BUG IN THE ICEPAP THAT DOES NOT ALLOW MOVE ABSOLUTE FURTHER THAN 32 BIT + # SO IF THERE ARE STEPS PER UNIT, max_int HAS TO BE REDUCED + if hasattr(self.motor_dev, 'step_per_unit'): + pos_limit = pos_limit / self.motor_dev['step_per_unit'].value + try: + max_value = self.motor_dev.getAttribute( + 'Position').getConfig().getValueObj().max_value + pos_limit = float(max_value) + except Exception: + pass + self.moveMotor(pos_limit) + + @Qt.pyqtSlot() + def goHome(self): + pass + + @Qt.pyqtSlot() + def abort(self): + self.motor_dev.abort() + + +class LabelWidgetDragsDeviceAndAttribute(DefaultLabelWidget): + """ Offer richer mime data with taurus-device, taurus-attribute, and plain-text. """ + + def mouseMoveEvent(self, event): + model = self.taurusValueBuddy().getModelName().encode('utf-8') + mimeData = Qt.QMimeData() + mimeData.setText(self.text()) + attr_name = model + dev_name = model.rpartition(b'/')[0] + mimeData.setData(TAURUS_DEV_MIME_TYPE, dev_name) + mimeData.setData(TAURUS_ATTR_MIME_TYPE, attr_name) + + drag = Qt.QDrag(self) + drag.setMimeData(mimeData) + drag.setHotSpot(event.pos() - self.rect().topLeft()) + drag.exec_(Qt.Qt.CopyAction, Qt.Qt.CopyAction) + + +class PoolMotorConfigurationForm(TaurusAttrForm): + + def __init__(self, parent=None, designMode=False): + TaurusAttrForm.__init__(self, parent, designMode) + self._form.setWithButtons(False) + + def getMotorControllerType(self): + modelObj = self.getModelObj() + modelNormalName = modelObj.getNormalName() + poolDsId = modelObj.getHWObj().info().server_id + db = taurus.Database() + pool_devices = tuple(db.get_device_class_list(poolDsId).value_string) + pool_dev_name = pool_devices[pool_devices.index('Pool') - 1] + pool = taurus.Device(pool_dev_name) + poolMotorInfos = pool["MotorList"].value + for motorInfo in poolMotorInfos: + # BE CAREFUL, THIS ONLY WORKS IF NOBODY CHANGES THE DEVICE NAME OF A MOTOR!!! + # ALSO THERE COULD BE A CASE PROBLEM, BETTER DO COMPARISONS WITH .lower() + # to better understand following actions + # this is an example of one motor info record + #'dummymotor10 (motor/dummymotorctrl/10) (dummymotorctrl/10) Motor', + motorInfos = motorInfo.split() + if modelNormalName.lower() == motorInfos[1][1:-1].lower(): + controllerName = motorInfos[2][1:-1].split("/")[0] + + poolControllerInfos = pool["ControllerList"].value + for controllerInfo in poolControllerInfos: + # to better understand following actions + # this is an example of one controller info record + #'dummymotorctrl (DummyMotorController.DummyMotorController/dummymotorctrl) - Motor Python ctrl (DummyMotorController.py)' + controllerInfos = controllerInfo.split() + if controllerName.lower() == controllerInfos[0].lower(): + controllerType = controllerInfos[1][1:-1].split("/")[0] + return controllerType + + def getDisplayAttributes(self, controllerType): + attributes = ['position', + 'state', + 'status', + 'velocity', + 'acceleration', + 'base_rate', + 'step_per_unit', + 'dialposition', + 'sign', + 'offset', + 'backlash'] + + if controllerType == "IcePAPCtrl.IcepapController": + attributes.insert(1, "encoder") + attributes.extend(['frequency', + 'poweron', + 'closedloop', + 'useencodersource', + 'encodersource', + 'encodersourceformula', + 'statusstopcode', + 'statusdisable', + 'statusready', + 'statuslim-', + 'statuslim+', + 'statushome']) + + elif controllerType == "PmacCtrl.PmacController": + attributes.extend(["motoractivated", + "negativeendlimitset", + "positiveendlimitset", + "handwheelenabled", + "phasedmotor", + "openloopmode", + "runningdefine-timemove", + "integrationmode", + "dwellinprogress", + "datablockerror", + "desiredvelocityzero", + "abortdeceleration", + "blockrequest", + "homesearchinprogress", + "assignedtocoordinatesystem", + "coordinatesystem", + "amplifierenabled", + "stoppedonpositionlimit", + "homecomplete", + "phasingsearcherror", + "triggermove", + "integratedfatalfollowingerror", + "i2t_amplifierfaulterror", + "backlashdirectionflag", + "amplifierfaulterror", + "fatalfollowingerror", + "warningfollowingerror", + "inposition", + "motionprogramrunning"]) + + elif controllerType == "TurboPmacCtrl.TurboPmacController": + attributes.extend(["motoractivated", + "negativeendlimitset", + "positiveendlimitset", + "extendedservoalgorithmenabled" + "amplifierenabled", + "openloopmode", + "movetimeractive", + "integrationmode", + "dwellinprogress", + "datablockerror", + "desiredvelocityzero", + "abortdeceleration", + "blockrequest", + "homesearchinprogress", + "user-writtenphaseenable", + "user-writtenservoenable", + "alternatesource/destination", + "phasedmotor", + "followingoffsetmode", + "followingenabled", + "errortriger", + "softwarepositioncapture", + "integratorinvelocityloop", + "alternatecommand-outputmode", + "coordinatesystem", + "coordinatedefinition", + "assignedtocoordinatesystem", + "foregroundinposition", + "stoppedondesiredpositionlimit", + "stoppedonpositionlimit", + "homecomplete", + "phasing_search/read_active", + "triggermove", + "integratedfatalfollowingerror", + "i2t_amplifierfaulterror", + "backlashdirectionflag", + "amplifierfaulterror", + "fatalfollowingerror", + "warningfollowingerror", + "inposition"]) + return attributes + + def setModel(self, modelName): + TaurusAttrForm.setModel(self, modelName) + controllerType = self.getMotorControllerType() + attributes = self.getDisplayAttributes(controllerType) + #self.setViewFilters([lambda a: a.name.lower() in attributes]) + self.setSortKey(lambda att: attributes.index( + att.name.lower()) if att.name.lower() in attributes else 1) + + +@UILoadable(with_ui='ui') +class PoolMotorSlim(TaurusWidget, PoolMotorClient): + + __pyqtSignals__ = ("modelChanged(const QString &)",) + + def __init__(self, parent=None, designMode=False): + TaurusWidget.__init__(self, parent) + msg = ("PoolMotorSlim is deprecated since 2.5.0. Use PoolMotorTV " + "instead.") + self.deprecated(msg) + + #self.call__init__wo_kw(Qt.QWidget, parent) + #self.call__init__(TaurusBaseWidget, str(self.objectName()), designMode=designMode) + PoolMotorClient.__init__(self) + self.loadUi() + self.recheckTaurusParent() + + self.show_context_menu = True + + self.setAcceptDrops(True) + + if designMode: + self.__setTaurusIcons() + return + + # CREATE THE TaurusValue that can not be configured in the Designer + self.taurus_value = TaurusValue(self.ui.taurusValueContainer) + + # Use a DragDevAndAttributeLabelWidget to provide a richer QMimeData + # content + self.taurus_value.setLabelWidgetClass( + LabelWidgetDragsDeviceAndAttribute) + + # Make the label to be the device alias + self.taurus_value.setLabelConfig('dev_alias') + + self.taurus_value_enc = TaurusValue(self.ui.taurusValueContainer) + + # THIS WILL BE DONE IN THE DESIGNER + # Config Button will launch a PoolMotorConfigurationForm +# 19.08.2011 after discussion between cpascual, gcui and zreszela, Configuration Panel was rolled back to +# standard TaurusAttrForm - list of all attributes alphabetically ordered +# taurus_attr_form = PoolMotorConfigurationForm() + taurus_attr_form = TaurusAttrForm() + + taurus_attr_form.setMinimumSize(Qt.QSize(470, 800)) + self.ui.btnCfg.setWidget(taurus_attr_form) + self.ui.btnCfg.setUseParentModel(True) + + # ADD AN EVENT FILTER FOR THE STATUS LABEL IN ORDER TO PROVIDE JUST THE + # STRING FROM THE CONTROLLER (LAST LINE) + def just_ctrl_status_line(evt_src, evt_type, evt_value): + if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: + return evt_src, evt_type, evt_value + try: + status = evt_value.value + last_line = status.split('\n')[-1] + new_evt_value = PyTango.DeviceAttribute(evt_value) + new_evt_value.value = last_line + return evt_src, evt_type, new_evt_value + except: + return evt_src, evt_type, evt_value + self.ui.lblStatus.insertEventFilter(just_ctrl_status_line) + + # These buttons are just for showing if the limit is active or not + self.ui.btnMin.setEnabled(False) + self.ui.btnMax.setEnabled(False) + + # HOMING NOT IMPLMENTED YET + self.ui.btnHome.setEnabled(False) + + # DEFAULT VISIBLE COMPONENTS + self.toggleHideAll() + self.toggleMoveAbsolute(True) + self.toggleStopMove(True) + + # SET TAURUS ICONS + self.__setTaurusIcons() + + self.ui.motorGroupBox.setContextMenuPolicy(Qt.Qt.CustomContextMenu) + + self.ui.motorGroupBox.customContextMenuRequested.connect( + self.buildContextMenu) + self.ui.btnGoToNeg.clicked.connect(self.jogNeg) + self.ui.btnGoToNegPress.pressed.connect(self.jogNeg) + self.ui.btnGoToNegPress.released.connect(self.abort) + self.ui.btnGoToNegInc.clicked.connect(self.goToNegInc) + self.ui.btnGoToPos.clicked.connect(self.jogPos) + self.ui.btnGoToPosPress.pressed.connect(self.jogPos) + self.ui.btnGoToPosPress.released.connect(self.abort) + self.ui.btnGoToPosInc.clicked.connect(self.goToPosInc) + + self.ui.btnHome.clicked.connect(self.goHome) + self.ui.btnStop.clicked.connect(self.abort) + + # ALSO UPDATE THE WIDGETS EVERYTIME THE FORM HAS TO BE SHOWN + self.ui.btnCfg.clicked.connect(taurus_attr_form._updateAttrWidgets) + self.ui.btnCfg.clicked.connect(self.buildBetterCfgDialogTitle) + + ####################################################################### + ######################################## + # LET TAURUS CONFIGURATION MECANISM SHINE! + ######################################## + self.registerConfigProperty( + self.ui.inc.isVisible, self.toggleMoveRelative, 'MoveRelative') + self.registerConfigProperty( + self.ui.btnGoToNegPress.isVisible, self.toggleMoveContinuous, 'MoveContinuous') + self.registerConfigProperty( + self.ui.btnGoToNeg.isVisible, self.toggleMoveToLimits, 'MoveToLimits') + self.registerConfigProperty( + self.ui.btnStop.isVisible, self.toggleStopMove, 'StopMove') + self.registerConfigProperty( + self.ui.btnHome.isVisible, self.toggleHoming, 'Homing') + self.registerConfigProperty( + self.ui.btnCfg.isVisible, self.toggleConfig, 'Config') + self.registerConfigProperty( + self.ui.lblStatus.isVisible, self.toggleStatus, 'Status') + ####################################################################### + + def __setTaurusIcons(self): + self.ui.btnMin.setText('') + self.ui.btnMin.setIcon(Qt.QIcon("actions:list-remove.svg")) + self.ui.btnMax.setText('') + self.ui.btnMax.setIcon(Qt.QIcon("actions:list-add.svg")) + + self.ui.btnGoToNeg.setText('') + self.ui.btnGoToNeg.setIcon( + Qt.QIcon("actions:media_skip_backward.svg")) + self.ui.btnGoToNegPress.setText('') + self.ui.btnGoToNegPress.setIcon( + Qt.QIcon("actions:media_seek_backward.svg")) + self.ui.btnGoToNegInc.setText('') + self.ui.btnGoToNegInc.setIcon( + Qt.QIcon("actions:media_playback_backward.svg")) + self.ui.btnGoToPos.setText('') + self.ui.btnGoToPos.setIcon(Qt.QIcon("actions:media_skip_forward.svg")) + self.ui.btnGoToPosPress.setText('') + self.ui.btnGoToPosPress.setIcon( + Qt.QIcon("actions:media_seek_forward.svg")) + self.ui.btnGoToPosInc.setText('') + self.ui.btnGoToPosInc.setIcon( + Qt.QIcon("actions:media_playback_start.svg")) + self.ui.btnStop.setText('') + self.ui.btnStop.setIcon(Qt.QIcon("actions:media_playback_stop.svg")) + self.ui.btnHome.setText('') + self.ui.btnHome.setIcon(Qt.QIcon("actions:go-home.svg")) + self.ui.btnCfg.setText('') + self.ui.btnCfg.setIcon(Qt.QIcon("categories:preferences-system.svg")) + ####################################################################### + + #@Qt.pyqtSlot(list) + def updateLimits(self, limits): + if isinstance(limits, dict): + limits = limits["limits"] + pos_lim = limits[1] + pos_btnstylesheet = '' + enabled = True + if pos_lim: + pos_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( + PyTango.DevState.ALARM) + enabled = False + self.ui.btnMax.setStyleSheet(pos_btnstylesheet) + self.ui.btnGoToPos.setEnabled(enabled) + self.ui.btnGoToPosPress.setEnabled(enabled) + self.ui.btnGoToPosInc.setEnabled(enabled) + + neg_lim = limits[2] + neg_btnstylesheet = '' + enabled = True + if neg_lim: + neg_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( + PyTango.DevState.ALARM) + enabled = False + self.ui.btnMin.setStyleSheet(neg_btnstylesheet) + self.ui.btnGoToNeg.setEnabled(enabled) + self.ui.btnGoToNegPress.setEnabled(enabled) + self.ui.btnGoToNegInc.setEnabled(enabled) + + # def sizeHint(self): + # return Qt.QSize(300,30) + + @Qt.pyqtSlot() + def goToNegInc(self): + self.moveInc(-1 * self.ui.inc.value()) + + @Qt.pyqtSlot() + def goToPosInc(self): + self.moveInc(self.ui.inc.value()) + + def buildContextMenu(self, point): + if not self.show_context_menu: + return + menu = Qt.QMenu(self) + + action_hide_all = Qt.QAction(self) + action_hide_all.setText('Hide All') + menu.addAction(action_hide_all) + + action_show_all = Qt.QAction(self) + action_show_all.setText('Show All') + menu.addAction(action_show_all) + + action_move_absolute = Qt.QAction(self) + action_move_absolute.setText('Move Absolute') + action_move_absolute.setCheckable(True) + action_move_absolute.setChecked( + self.taurus_value.writeWidget().isVisible()) + menu.addAction(action_move_absolute) + + action_move_relative = Qt.QAction(self) + action_move_relative.setText('Move Relative') + action_move_relative.setCheckable(True) + action_move_relative.setChecked(self.ui.inc.isVisible()) + menu.addAction(action_move_relative) + + action_move_continuous = Qt.QAction(self) + action_move_continuous.setText('Move Continuous') + action_move_continuous.setCheckable(True) + action_move_continuous.setChecked(self.ui.btnGoToNegPress.isVisible()) + menu.addAction(action_move_continuous) + + action_move_to_limits = Qt.QAction(self) + action_move_to_limits.setText('Move to Limits') + action_move_to_limits.setCheckable(True) + action_move_to_limits.setChecked(self.ui.btnGoToNeg.isVisible()) + menu.addAction(action_move_to_limits) + + action_encoder = Qt.QAction(self) + action_encoder.setText('Encoder Read') + action_encoder.setCheckable(True) + action_encoder.setChecked(self.taurus_value_enc.isVisible()) + if self.has_encoder: + menu.addAction(action_encoder) + + action_stop_move = Qt.QAction(self) + action_stop_move.setText('Stop Movement') + action_stop_move.setCheckable(True) + action_stop_move.setChecked(self.ui.btnStop.isVisible()) + menu.addAction(action_stop_move) + + action_homing = Qt.QAction(self) + action_homing.setText('Homing') + action_homing.setCheckable(True) + action_homing.setChecked(self.ui.btnHome.isVisible()) + menu.addAction(action_homing) + + action_config = Qt.QAction(self) + action_config.setText('Config') + action_config.setCheckable(True) + action_config.setChecked(self.ui.btnCfg.isVisible()) + menu.addAction(action_config) + + action_status = Qt.QAction(self) + action_status.setText('Status') + action_status.setCheckable(True) + action_status.setChecked(self.ui.lblStatus.isVisible()) + menu.addAction(action_status) + + action_hide_all.triggered.connect(self.toggleHideAll) + action_show_all.triggered.connect(self.toggleShowAll) + action_move_absolute.toggled.connect(self.toggleMoveAbsolute) + action_move_relative.toggled.connect(self.toggleMoveRelative) + action_move_continuous.toggled.connect(self.toggleMoveContinuous) + action_move_to_limits.toggled.connect(self.toggleMoveToLimits) + action_encoder.toggled.connect(self.toggleEncoder) + action_stop_move.toggled.connect(self.toggleStopMove) + action_homing.toggled.connect(self.toggleHoming) + action_config.toggled.connect(self.toggleConfig) + action_status.toggled.connect(self.toggleStatus) + + menu.popup(self.cursor().pos()) + + def toggleHideAll(self): + self.toggleAll(False) + + def toggleShowAll(self): + self.toggleAll(True) + + def toggleAll(self, visible): + self.toggleMoveAbsolute(visible) + self.toggleMoveRelative(visible) + self.toggleMoveContinuous(visible) + self.toggleMoveToLimits(visible) + self.toggleEncoder(visible) + self.toggleStopMove(visible) + self.toggleHoming(visible) + self.toggleConfig(visible) + self.toggleStatus(visible) + + def toggleMoveAbsolute(self, visible): + if self.taurus_value.writeWidget() is not None: + self.taurus_value.writeWidget().setVisible(visible) + + def toggleMoveRelative(self, visible): + self.ui.btnGoToNegInc.setVisible(visible) + self.ui.inc.setVisible(visible) + self.ui.btnGoToPosInc.setVisible(visible) + + def toggleMoveContinuous(self, visible): + self.ui.btnGoToNegPress.setVisible(visible) + self.ui.btnGoToPosPress.setVisible(visible) + + def toggleMoveToLimits(self, visible): + self.ui.btnGoToNeg.setVisible(visible) + self.ui.btnGoToPos.setVisible(visible) + + def toggleEncoder(self, visible): + self.taurus_value_enc.setVisible(visible) + + def toggleStopMove(self, visible): + self.ui.btnStop.setVisible(visible) + + def toggleHoming(self, visible): + self.ui.btnHome.setVisible(visible) + + def toggleConfig(self, visible): + self.ui.btnCfg.setVisible(visible) + + def toggleStatus(self, visible): + self.ui.lblStatus.setVisible(visible) + + def dragEnterEvent(self, event): + event.accept() + + def dropEvent(self, event): + mimeData = event.mimeData() + if mimeData.hasFormat(TAURUS_DEV_MIME_TYPE): + model = str(mimeData.data(TAURUS_DEV_MIME_TYPE)) + elif mimeData.hasFormat(TAURUS_ATTR_MIME_TYPE): + model = str(mimeData.data(TAURUS_ATTR_MIME_TYPE)) + else: + model = str(mimeData.text()) + self.setModel(model) + + def keyPressEvent(self, key_event): + if key_event.key() == Qt.Qt.Key_Escape: + self.abort() + key_event.accept() + TaurusWidget.keyPressEvent(self, key_event) + + def buildBetterCfgDialogTitle(self): + while self.ui.btnCfg._dialog is None: + pass + model = self.getModel() + self.ui.btnCfg._dialog.setWindowTitle( + '%s config' % taurus.Factory().getDevice(model).getSimpleName()) + + @classmethod + def getQtDesignerPluginInfo(cls): + ret = TaurusWidget.getQtDesignerPluginInfo() + ret['module'] = 'taurus.qt.qtgui.extra_pool' + ret['group'] = 'Taurus Sardana' + ret['icon'] = ':/designer/extra_motor.png' + ret['container'] = False + return ret + + def showEvent(self, event): + TaurusWidget.showEvent(self, event) + try: + self.motor_dev.getAttribute('Position').enablePolling(force=True) + except AttributeError as e: + self.debug('Error in showEvent: %s', repr(e)) + + def hideEvent(self, event): + TaurusWidget.hideEvent(self, event) + try: + self.motor_dev.getAttribute('Position').disablePolling() + except AttributeError as e: + self.debug('Error in hideEvent: %s', repr(e)) + + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # QT properties + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + @Qt.pyqtSlot() + def getModel(self): + return self.ui.motorGroupBox.getModel() + + @Qt.pyqtSlot("QString") + def setModel(self, model): + # DUE TO A BUG IN TAUGROUPBOX, WE NEED THE FULL MODEL NAME + try: + # In case the model is an attribute of a motor, get the device name + # TODO: When sardana is moved to Taurus 4 replace this line by + # taurushelper.isValidName(model, [TaurusElementType.Device]) + if not TangoDeviceNameValidator().isValid(model): + model = model.rpartition('/')[0] + model = taurus.Factory().getDevice(model).getFullName() + self.setMotor(model) + self.ui.motorGroupBox.setModel(model) + self.ui.motorGroupBox.setEnabled(True) + + self.taurus_value.setModel(model + '/Position') + + # DUE TO A BUG IN TAURUSVALUE, THAT DO NOT USE PARENT MODEL WE NEED + # TO ALWAYS SET THE MODEL + self.taurus_value.setUseParentModel(False) + + # THE FORCED APPLY HAS TO BE DONE AFTER THE MODEL IS SET, SO THE + # WRITEWIDGET IS AVAILABLE + if self.taurus_value.writeWidget() is not None: + self.taurus_value.writeWidget().setForcedApply(True) + + show_enc = self.taurus_value_enc.isVisible() + if self.has_encoder: + self.taurus_value_enc.setModel(model + '/Encoder') + self.taurus_value_enc.setUseParentModel(False) + self.taurus_value_enc.readWidget().setBgRole('none') + else: + self.taurus_value_enc.setModel(None) + show_enc = False + if not show_enc: + self.toggleEncoder(False) + + try: + self.unregisterConfigurableItem('MoveAbsolute') + self.unregisterConfigurableItem('Encoder') + except: + pass + self.registerConfigProperty(self.taurus_value.writeWidget( + ).isVisible, self.toggleMoveAbsolute, 'MoveAbsolute') + self.registerConfigProperty( + self.taurus_value_enc.isVisible, self.toggleEncoder, 'Encoder') + + # SINCE TAURUSLAUNCHERBUTTON HAS NOT THIS PROPERTY IN THE + # DESIGNER, WE MUST SET IT HERE + self.ui.btnCfg.setUseParentModel(True) + + # CONFIGURE A LISTENER IN ORDER TO UPDATE LIMIT SWITCHES STATES + self.limits_listener = LimitsListener() + self.limits_listener.updateLimits.connect( + self.updateLimits) + limits_visible = False + if self.has_limits: + limits_attribute = self.motor_dev.getAttribute( + 'Limit_switches') + limits_attribute.addListener(self.limits_listener) + # self.updateLimits(limits_attribute.read().rvalue) + limits_visible = True + self.ui.btnMin.setVisible(limits_visible) + self.ui.btnMax.setVisible(limits_visible) + except Exception as e: + self.ui.motorGroupBox.setEnabled(False) + self.info('Error setting model "%s". Reason: %s' % + (model, repr(e))) + self.traceback() + + @Qt.pyqtSlot() + def resetModel(self): + self.ui.motorGroupBox.resetModel() + + @Qt.pyqtSlot() + def getShowContextMenu(self): + return self.show_context_menu + + @Qt.pyqtSlot() + def setShowContextMenu(self, showContextMenu): + self.show_context_menu = showContextMenu + + @Qt.pyqtSlot() + def resetShowContextMenu(self): + self.show_context_menu = True + + @Qt.pyqtSlot() + def getStepSize(self): + return self.ui.inc.value() + + @Qt.pyqtSlot(float) + def setStepSize(self, stepSize): + self.ui.inc.setValue(stepSize) + + @Qt.pyqtSlot() + def resetStepSize(self): + self.setStepSize(1) + + @Qt.pyqtSlot() + def getStepSizeIncrement(self): + return self.ui.inc.singleStep() + + @Qt.pyqtSlot(float) + def setStepSizeIncrement(self, stepSizeIncrement): + self.ui.inc.setSingleStep(stepSizeIncrement) + + @Qt.pyqtSlot() + def resetStepSizeIncrement(self): + self.setStepSizeIncrement(1) + + model = Qt.pyqtProperty("QString", getModel, setModel, resetModel) + stepSize = Qt.pyqtProperty( + "double", getStepSize, setStepSize, resetStepSize) + stepSizeIncrement = Qt.pyqtProperty( + "double", getStepSizeIncrement, setStepSizeIncrement, resetStepSizeIncrement) + + +########################################################################## +# NEW APPROACH TO OPERATE POOL MOTORS FROM A TAURUS FORM INHERITTING DIRECTLY FROM TaurusVALUE # +# AND USING PARTICULAR CLASSES THAT KNOW THEY ARE PART OF A TAURUSVALUE AND CAN INTERACT # +########################################################################## + +class TaurusAttributeListener(Qt.QObject): + """ + A class that recieves events on tango attribute changes. + If that is the case it emits a signal with the event's value. + """ + + eventReceivedSignal = Qt.pyqtSignal(compat.PY_OBJECT) + + def __init__(self): + Qt.QObject.__init__(self) + + def eventReceived(self, evt_src, evt_type, evt_value): + if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: + return + value = evt_value.rvalue + self.eventReceivedSignal.emit(value) + + +################################################## +# LABEL WIDGET # +################################################## +class PoolMotorTVLabelWidget(TaurusWidget): + ''' + @TODO tooltip should be extended with status info + @TODO context menu should be the lbl_alias extended + @TODO default tooltip extended with the complete (multiline) status + @TODO rightclick popup menu with actions: (1) switch user/expert view, (2) Config -all attributes-, (3) change motor + For the (3), a drop event should accept if it is a device, and add it to the 'change-motor' list and select + @TODO on the 'expert' row, it could be an ENABLE section with a button to set PowerOn to True/False + ''' + + layoutAlignment = Qt.Qt.AlignTop + + def __init__(self, parent=None, designMode=False): + TaurusWidget.__init__(self, parent, designMode) + self.setLayout(Qt.QGridLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.layout().setSpacing(0) + + self.lbl_alias = DefaultLabelWidget(parent, designMode) + self.lbl_alias.setBgRole('none') + self.layout().addWidget(self.lbl_alias) + + self.btn_poweron = Qt.QPushButton() + self.btn_poweron.setText('Set ON') + self.layout().addWidget(self.btn_poweron) + + # Align everything on top + self.layout().addItem(Qt.QSpacerItem( + 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding)) + + # I don't like this approach, there should be something like + # self.lbl_alias.addAction(...) + self.lbl_alias.contextMenuEvent = \ + lambda event: self.contextMenuEvent(event) + + # I' don't like this approach, there should be something like + # self.lbl_alias.addToolTipCallback(self.calculate_extra_tooltip) + self.lbl_alias.getFormatedToolTip = self.calculateExtendedTooltip + + # I' don't like this approach, there should be something like + # self.lbl_alias.disableDrag() or self.lbl_alias.setDragEnabled(False) + # or better, define if Attribute or Device or Both have to be included + # in the mimeData + self.lbl_alias.mouseMoveEvent = self.mouseMoveEvent + + def setExpertView(self, expertView): + btn_poweron_visible = expertView \ + and self.taurusValueBuddy().hasPowerOn() + self.btn_poweron.setVisible(btn_poweron_visible) + + @Qt.pyqtSlot() + @ProtectTaurusMessageBox( + msg='An error occurred trying to write PowerOn Attribute.') + def setPowerOn(self): + motor_dev = self.taurusValueBuddy().motor_dev + if motor_dev is not None: + poweron = (self.btn_poweron.text() == 'Set ON') + motor_dev.getAttribute('PowerOn').write(poweron) + + def setModel(self, model): + # Handle User/Expert view + try: + self.taurusValueBuddy().expertViewChanged.disconnect( + self.setExpertView) + except TypeError: + pass + try: + self.btn_poweron.clicked.disconnect(self.setPowerOn) + except TypeError: + pass + if model in (None, ''): + self.lbl_alias.setModel(model) + TaurusWidget.setModel(self, model) + return + self.lbl_alias.taurusValueBuddy = self.taurusValueBuddy + self.lbl_alias.setModel(model) + TaurusWidget.setModel(self, model + '/Status') + self.taurusValueBuddy().expertViewChanged.connect( + self.setExpertView) + # Handle Power ON/OFF + self.btn_poweron.clicked.connect(self.setPowerOn) + self.setExpertView(self.taurusValueBuddy()._expertView) + + def calculateExtendedTooltip(self, cache=False): + default_label_widget_tooltip = DefaultLabelWidget.getFormatedToolTip( + self.lbl_alias, cache) + status_info = '' + motor_dev = self.taurusValueBuddy().motor_dev + if motor_dev is not None: + status = motor_dev.getAttribute('Status').read().rvalue + # MAKE IT LOOK LIKE THE STANDARD TABLE FOR TAURUS TOOLTIPS + status_lines = status.split('\n') + status_info = '' + for status_extra_line in status_lines[1:]: + status_info += '' + status_info += '
Status:' + \ + status_lines[0] + '
' + status_extra_line + '
' + return default_label_widget_tooltip + status_info + + def contextMenuEvent(self, event): + # Overwrite the default taurus label behaviour + menu = Qt.QMenu(self) + action_expert_view = Qt.QAction(self) + action_expert_view.setText('Expert View') + action_expert_view.setCheckable(True) + action_expert_view.setChecked(self.taurusValueBuddy()._expertView) + menu.addAction(action_expert_view) + action_expert_view.toggled.connect( + self.taurusValueBuddy().setExpertView) + + action_tango_attributes = Qt.QAction(self) + action_tango_attributes.setIcon( + Qt.QIcon("categories:preferences-system.svg")) + action_tango_attributes.setText('Tango Attributes') + menu.addAction(action_tango_attributes) + action_tango_attributes.triggered.connect( + self.taurusValueBuddy().showTangoAttributes) + + cm_action = menu.addAction("Compact") + cm_action.setCheckable(True) + cm_action.setChecked(self.taurusValueBuddy().isCompact()) + cm_action.toggled.connect(self.taurusValueBuddy().setCompact) + + menu.exec_(event.globalPos()) + event.accept() + + def mouseMoveEvent(self, event): + model = self.taurusValueBuddy().getModelObj() + mimeData = Qt.QMimeData() + mimeData.setText(self.lbl_alias.text()) + dev_name = model.getFullName().encode('utf-8') + attr_name = dev_name + b'/Position' + mimeData.setData(TAURUS_DEV_MIME_TYPE, dev_name) + mimeData.setData(TAURUS_ATTR_MIME_TYPE, attr_name) + + drag = Qt.QDrag(self) + drag.setMimeData(mimeData) + drag.setHotSpot(event.pos() - self.rect().topLeft()) + drag.exec_(Qt.Qt.CopyAction, Qt.Qt.CopyAction) + +################################################## +# READ WIDGET # +################################################## + + +class PoolMotorTVReadWidget(TaurusWidget): + ''' + @TODO on the 'expert' row, there should be an Indexer/Encoder radiobuttongroup to show units from raw dial/indx/enc + @TODO TaurusLCD may be used but, now it does not display the sign, and color is WHITE... + ''' + + layoutAlignment = Qt.Qt.AlignTop + + def __init__(self, parent=None, designMode=False): + TaurusWidget.__init__(self, parent, designMode) + + self.setLayout(Qt.QGridLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.layout().setSpacing(0) + + limits_layout = Qt.QHBoxLayout() + limits_layout.setContentsMargins(0, 0, 0, 0) + limits_layout.setSpacing(0) + + self.btn_lim_neg = Qt.QPushButton() + self.btn_lim_neg.setToolTip('Negative Limit') + # self.btn_lim_neg.setEnabled(False) + self.prepare_button(self.btn_lim_neg) + self.btn_lim_neg.setIcon(Qt.QIcon("actions:list-remove.svg")) + limits_layout.addWidget(self.btn_lim_neg) + + self.btn_lim_pos = Qt.QPushButton() + self.btn_lim_pos.setToolTip('Positive Limit') + # self.btn_lim_pos.setEnabled(False) + self.prepare_button(self.btn_lim_pos) + self.btn_lim_pos.setIcon(Qt.QIcon("actions:list-add.svg")) + limits_layout.addWidget(self.btn_lim_pos) + + self.layout().addLayout(limits_layout, 0, 0) + + self.lbl_read = TaurusLabel() + self.lbl_read.setBgRole('quality') + self.lbl_read.setSizePolicy(Qt.QSizePolicy( + Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) + self.layout().addWidget(self.lbl_read, 0, 1) + + # WITH A COMPACT VIEW, BETTER TO BE ABLE TO STOP! + self.btn_stop = Qt.QPushButton() + self.btn_stop.setToolTip('Stops the motor') + self.prepare_button(self.btn_stop) + self.btn_stop.setIcon(Qt.QIcon("actions:media_playback_stop.svg")) + self.layout().addWidget(self.btn_stop, 0, 2) + + self.btn_stop.clicked.connect(self.abort) + + # WITH COMPACT VIEW, WE NEED TO FORWARD DOUBLE CLICK EVENT + self.lbl_read.installEventFilter(self) + + # @TODO right now, no options here... + #self.cb_expertRead = Qt.QComboBox() + # self.cb_expertRead.addItems(['Enc']) + #self.layout().addWidget(self.cb_expertRead, 1, 0) + + self.lbl_enc = Qt.QLabel('Encoder') + self.layout().addWidget(self.lbl_enc, 1, 0) + + self.lbl_enc_read = TaurusLabel() + self.lbl_enc_read.setBgRole('none') + self.lbl_enc_read.setSizePolicy(Qt.QSizePolicy( + Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) + self.layout().addWidget(self.lbl_enc_read, 1, 1) + + # Align everything on top + self.layout().addItem(Qt.QSpacerItem( + 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 2) + + # IN ORDER TO BEHAVE AS EXPECTED REGARDING THE 'COMPACT VIEW' FEATURE + # WE NEED TO SET THE 'EXPERTVIEW' WITHOUT ACCESSING THE taurusValueBuddy WHICH IS STILL NOT LINKED + # SO WE ASSUME 'expertview is FALSE' AND WE HAVE TO AVOID self.setExpertView :-( + # WOULD BE NICE THAT THE taurusValueBuddy COULD EMIT THE PROPER + # SIGNAL... + self.lbl_enc.setVisible(False) + self.lbl_enc_read.setVisible(False) + + def eventFilter(self, obj, event): + if event.type() == Qt.QEvent.MouseButtonDblClick: + if isinstance(self.parent(), TaurusReadWriteSwitcher): + self.parent().enterEdit() + return True + try: + if obj is self.lbl_read: + return self.lbl_read.eventFilter(obj, event) + except AttributeError: + # self.lbl_read may not exist now + pass + return True + + @Qt.pyqtSlot() + @ProtectTaurusMessageBox(msg='An error occurred trying to abort the motion.') + def abort(self): + motor_dev = self.taurusValueBuddy().motor_dev + if motor_dev is not None: + motor_dev.abort() + + def setExpertView(self, expertView): + self.lbl_enc.setVisible(False) + self.lbl_enc_read.setVisible(False) + if self.taurusValueBuddy().motor_dev is not None: + hw_limits = self.taurusValueBuddy().hasHwLimits() + self.btn_lim_neg.setEnabled(hw_limits) + self.btn_lim_pos.setEnabled(hw_limits) + + if expertView and self.taurusValueBuddy().motor_dev is not None: + encoder = self.taurusValueBuddy().hasEncoder() + self.lbl_enc.setVisible(encoder) + self.lbl_enc_read.setVisible(encoder) + + def prepare_button(self, btn): + btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) + btn_policy.setHorizontalStretch(0) + btn_policy.setVerticalStretch(0) + btn.setSizePolicy(btn_policy) + btn.setMinimumSize(25, 25) + btn.setMaximumSize(25, 25) + btn.setText('') + + def setModel(self, model): + if hasattr(self, 'taurusValueBuddy'): + try: + self.taurusValueBuddy().expertViewChanged.disconnect( + self.setExpertView) + except TypeError: + pass + if model in (None, ''): + TaurusWidget.setModel(self, model) + self.lbl_read.setModel(model) + self.lbl_enc_read.setModel(model) + return + TaurusWidget.setModel(self, model + '/Position') + self.lbl_read.setModel(model + '/Position') + self.lbl_enc_read.setModel(model + '/Encoder') + # Handle User/Expert view + self.setExpertView(self.taurusValueBuddy()._expertView) + self.taurusValueBuddy().expertViewChanged.connect( + self.setExpertView) + +################################################## +# WRITE WIDGET # +################################################## + +class PoolMotorTVWriteWidget(TaurusWidget): + + layoutAlignment = Qt.Qt.AlignTop + + applied = Qt.pyqtSignal() + + def __init__(self, parent=None, designMode=False): + TaurusWidget.__init__(self, parent, designMode) + + # ------------------------------------------------------------ + # Workaround for Taurus3 support + if int(taurus.Release.version.split('.')[0]) < 4: + from taurus.qt.qtgui.base.taurusbase import baseOldSignal + self.applied = baseOldSignal('applied', self) + # ------------------------------------------------------------ + + self.setLayout(Qt.QGridLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.layout().setSpacing(0) + + self.le_write_absolute = TaurusValueLineEdit() + self.layout().addWidget(self.le_write_absolute, 0, 0) + + self.qw_write_relative = Qt.QWidget() + self.qw_write_relative.setLayout(Qt.QHBoxLayout()) + self.qw_write_relative.layout().setContentsMargins(0, 0, 0, 0) + self.qw_write_relative.layout().setSpacing(0) + + self.cb_step = Qt.QComboBox() + self.cb_step.setSizePolicy(Qt.QSizePolicy( + Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) + self.cb_step.setEditable(True) + self.cb_step.lineEdit().setValidator(Qt.QDoubleValidator(self)) + self.cb_step.lineEdit().setAlignment(Qt.Qt.AlignRight) + self.cb_step.addItem('1') + self.qw_write_relative.layout().addWidget(self.cb_step) + + self.btn_step_down = Qt.QPushButton() + self.btn_step_down.setToolTip('Decrements motor position') + self.prepare_button(self.btn_step_down) + self.btn_step_down.setIcon( + Qt.QIcon("actions:media_playback_backward.svg")) + self.qw_write_relative.layout().addWidget(self.btn_step_down) + + self.btn_step_up = Qt.QPushButton() + self.btn_step_up.setToolTip('Increments motor position') + self.prepare_button(self.btn_step_up) + self.btn_step_up.setIcon(Qt.QIcon("actions:media_playback_start.svg")) + self.qw_write_relative.layout().addWidget(self.btn_step_up) + + self.layout().addWidget(self.qw_write_relative, 0, 0) + + self.cbAbsoluteRelative = Qt.QComboBox() + self.cbAbsoluteRelative.currentIndexChanged['QString'].connect( + self.cbAbsoluteRelativeChanged) + self.cbAbsoluteRelative.addItems(['Abs', 'Rel']) + self.layout().addWidget(self.cbAbsoluteRelative, 0, 1) + + # WITH THE COMPACCT VIEW FEATURE, BETTER TO HAVE IT IN THE READ WIDGET + # WOULD BE BETTER AS AN 'EXTRA WIDGET' (SOME DAY...) + #self.btn_stop = Qt.QPushButton() + #self.btn_stop.setToolTip('Stops the motor') + # self.prepare_button(self.btn_stop) + # self.btn_stop.setIcon(getIcon(':/actions/media_playback_stop.svg')) + #self.layout().addWidget(self.btn_stop, 0, 2) + + btns_layout = Qt.QHBoxLayout() + btns_layout.setContentsMargins(0, 0, 0, 0) + btns_layout.setSpacing(0) + + btns_layout.addItem(Qt.QSpacerItem( + 1, 1, Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum)) + + self.btn_to_neg = Qt.QPushButton() + self.btn_to_neg.setToolTip( + 'Moves the motor towards the Negative Software Limit') + self.prepare_button(self.btn_to_neg) + self.btn_to_neg.setIcon(Qt.QIcon("actions:media_skip_backward.svg")) + btns_layout.addWidget(self.btn_to_neg) + + self.btn_to_neg_press = Qt.QPushButton() + self.btn_to_neg_press.setToolTip( + 'Moves the motor (while pressed) towards the Negative Software Limit') + self.prepare_button(self.btn_to_neg_press) + self.btn_to_neg_press.setIcon( + Qt.QIcon("actions:media_seek_backward.svg")) + btns_layout.addWidget(self.btn_to_neg_press) + + self.btn_to_pos_press = Qt.QPushButton() + self.prepare_button(self.btn_to_pos_press) + self.btn_to_pos_press.setToolTip( + 'Moves the motor (while pressed) towards the Positive Software Limit') + self.btn_to_pos_press.setIcon( + Qt.QIcon("actions:media_seek_forward.svg")) + btns_layout.addWidget(self.btn_to_pos_press) + + self.btn_to_pos = Qt.QPushButton() + self.btn_to_pos.setToolTip( + 'Moves the motor towards the Positive Software Limit') + self.prepare_button(self.btn_to_pos) + self.btn_to_pos.setIcon(Qt.QIcon("actions:media_skip_forward.svg")) + btns_layout.addWidget(self.btn_to_pos) + + btns_layout.addItem(Qt.QSpacerItem( + 1, 1, Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum)) + + self.layout().addLayout(btns_layout, 1, 0, 1, 3) + + self.btn_step_down.clicked.connect(self.stepDown) + self.btn_step_up.clicked.connect(self.stepUp) + # self.btn_stop.clicked.connect(self.abort) + self.btn_to_neg.clicked.connect(self.goNegative) + self.btn_to_neg_press.pressed.connect(self.goNegative) + self.btn_to_neg_press.released.connect(self.abort) + self.btn_to_pos.clicked.connect(self.goPositive) + self.btn_to_pos_press.pressed.connect(self.goPositive) + self.btn_to_pos_press.released.connect(self.abort) + + # Align everything on top + self.layout().addItem(Qt.QSpacerItem( + 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 3) + + # IN ORDER TO BEHAVE AS EXPECTED REGARDING THE 'COMPACT VIEW' FEATURE + # WE NEED TO SET THE 'EXPERTVIEW' WITHOUT ACCESSING THE taurusValueBuddy WHICH IS STILL NOT LINKED + # SO WE ASSUME 'expertview is FALSE' AND WE HAVE TO AVOID self.setExpertView :-( + # WOULD BE NICE THAT THE taurusValueBuddy COULD EMIT THE PROPER + # SIGNAL... + self.btn_to_neg.setVisible(False) + self.btn_to_neg_press.setVisible(False) + self.btn_to_pos.setVisible(False) + self.btn_to_pos_press.setVisible(False) + + # IN EXPERT VIEW, WE HAVE TO FORWARD THE ''editingFinished()' SIGNAL + # FROM TaurusValueLineEdit TO Switcher + self.le_write_absolute.applied.connect(self.emitEditingFinished) + self.btn_step_down.clicked.connect(self.emitEditingFinished) + self.btn_step_up.clicked.connect(self.emitEditingFinished) + self.btn_to_neg.clicked.connect(self.emitEditingFinished) + self.btn_to_pos.clicked.connect(self.emitEditingFinished) + + # list of widgets used for edition + editingWidgets = (self.le_write_absolute, self.cbAbsoluteRelative, + self.cb_step, self.btn_step_down, + self.btn_step_up, self.btn_to_neg, + self.btn_to_pos, self.btn_to_neg_press, + self.btn_to_pos_press) + + for w in editingWidgets: + w.installEventFilter(self) + + def eventFilter(self, obj, event): + '''reimplemented to intercept events from the subwidgets''' + try: + if obj in (self.btn_to_neg_press, self.btn_to_pos_press): + if event.type() == Qt.QEvent.MouseButtonRelease: + self.emitEditingFinished() + except AttributeError: + # self.btn_to_neg_press, self.btn_to_pos_press may not exist now + pass + # emit editingFinished when focus out to a non-editing widget + if event.type() == Qt.QEvent.FocusOut: + focused = Qt.qApp.focusWidget() + focusInChild = focused in self.findChildren(focused.__class__) + if not focusInChild: + self.emitEditingFinished() + return False + + def cbAbsoluteRelativeChanged(self, abs_rel_option): + abs_visible = abs_rel_option == 'Abs' + rel_visible = abs_rel_option == 'Rel' + self.le_write_absolute.setVisible(abs_visible) + self.qw_write_relative.setVisible(rel_visible) + + @Qt.pyqtSlot() + def stepDown(self): + self.goRelative(-1) + + @Qt.pyqtSlot() + def stepUp(self): + self.goRelative(+1) + + @ProtectTaurusMessageBox(msg='An error occurred trying to move the motor.') + def goRelative(self, direction): + motor_dev = self.taurusValueBuddy().motor_dev + if motor_dev is not None: + increment = direction * float(self.cb_step.currentText()) + position = float(motor_dev.getAttribute('Position').read().rvalue) + target_position = position + increment + motor_dev.getAttribute('Position').write(target_position) + + @Qt.pyqtSlot() + @ProtectTaurusMessageBox(msg='An error occurred trying to move the motor.') + def goNegative(self): + motor_dev = self.taurusValueBuddy().motor_dev + if motor_dev is not None: + min_value = float(motor_dev.getAttribute('Position').min_value) + motor_dev.getAttribute('Position').write(min_value) + + @Qt.pyqtSlot() + @ProtectTaurusMessageBox(msg='An error occurred trying to move the motor.') + def goPositive(self): + motor_dev = self.taurusValueBuddy().motor_dev + if motor_dev is not None: + max_value = float(motor_dev.getAttribute('Position').max_value) + motor_dev.getAttribute('Position').write(max_value) + + @Qt.pyqtSlot() + @ProtectTaurusMessageBox(msg='An error occurred trying to abort the motion.') + def abort(self): + motor_dev = self.taurusValueBuddy().motor_dev + if motor_dev is not None: + motor_dev.abort() + + def prepare_button(self, btn): + btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) + btn_policy.setHorizontalStretch(0) + btn_policy.setVerticalStretch(0) + btn.setSizePolicy(btn_policy) + btn.setMinimumSize(25, 25) + btn.setMaximumSize(25, 25) + btn.setText('') + + def setExpertView(self, expertView): + self.btn_to_neg.setVisible(expertView) + self.btn_to_neg_press.setVisible(expertView) + + self.btn_to_pos.setVisible(expertView) + self.btn_to_pos_press.setVisible(expertView) + + if expertView and self.taurusValueBuddy().motor_dev is not None: + neg_sw_limit_enabled = self.taurusValueBuddy().motor_dev.getAttribute( + 'Position').min_value.lower() != 'not specified' + self.btn_to_neg.setEnabled(neg_sw_limit_enabled) + self.btn_to_neg_press.setEnabled(neg_sw_limit_enabled) + + pos_sw_limit_enabled = self.taurusValueBuddy().motor_dev.getAttribute( + 'Position').max_value.lower() != 'not specified' + self.btn_to_pos.setEnabled(pos_sw_limit_enabled) + self.btn_to_pos_press.setEnabled(pos_sw_limit_enabled) + + def setModel(self, model): + if hasattr(self, 'taurusValueBuddy'): + try: + self.taurusValueBuddy().expertViewChanged.disconnect( + self.setExpertView) + except TypeError: + pass + if model in (None, ''): + TaurusWidget.setModel(self, model) + self.le_write_absolute.setModel(model) + return + TaurusWidget.setModel(self, model + '/Position') + self.le_write_absolute.setModel(model + '/Position') + + # Handle User/Expert View + self.setExpertView(self.taurusValueBuddy()._expertView) + self.taurusValueBuddy().expertViewChanged.connect( + self.setExpertView) + + def keyPressEvent(self, key_event): + if key_event.key() == Qt.Qt.Key_Escape: + self.abort() + key_event.accept() + TaurusWidget.keyPressEvent(self, key_event) + + @Qt.pyqtSlot() + def emitEditingFinished(self): + self.applied.emit() + + +################################################## +# UNITS WIDGET # +################################################## +class PoolMotorTVUnitsWidget(DefaultUnitsWidget): + + layoutAlignment = Qt.Qt.AlignTop + + def __init__(self, parent=None, designMode=False): + DefaultUnitsWidget.__init__(self, parent, designMode) + + def setModel(self, model): + if model in (None, ''): + DefaultUnitsWidget.setModel(self, model) + return + DefaultUnitsWidget.setModel(self, model + '/Position') + +################################################## +# TV MOTOR WIDGET # +################################################## + + +class PoolMotorTV(TaurusValue): + ''' A widget that displays and controls a pool Motor device. It + behaves as a TaurusValue. + @TODO the view mode should be stored in the configuration + @TODO the motor list should be stored in the configuration + @TODO the selected radiobuttons (dial/indx/enc) and (abs/rel) should be stored in configuration + @TODO it would be nice if the neg/pos limits could react also when software limits are 'active' + @TODO expert view for read widget should include signals (indexer/encoder/inpos)... + ''' + + expertViewChanged = Qt.pyqtSignal(bool) + + def __init__(self, parent=None, designMode=False): + TaurusValue.__init__(self, parent=parent, designMode=designMode) + self.setLabelWidgetClass(PoolMotorTVLabelWidget) + self.setReadWidgetClass(PoolMotorTVReadWidget) + self.setWriteWidgetClass(PoolMotorTVWriteWidget) + self.setUnitsWidgetClass(PoolMotorTVUnitsWidget) + self.motor_dev = None + self._expertView = False + self.limits_listener = None + self.poweron_listener = None + self.status_listener = None + self.position_listener = None + self.setExpertView(False) + + def setExpertView(self, expertView): + self._expertView = expertView + self.expertViewChanged.emit(expertView) + + def minimumHeight(self): + return None # @todo: UGLY HACK to avoid subwidgets being forced to minimumheight=20 + + def setModel(self, model): + TaurusValue.setModel(self, model) + + # disconnect signals + try: + if self.limits_listener is not None: + self.limits_listener.eventReceivedSignal.disconnect( + self.updateLimits) + except TypeError: + pass + + try: + if self.poweron_listener is not None: + self.poweron_listener.eventReceivedSignal.disconnect( + self.updatePowerOn) + except TypeError: + pass + + try: + if self.status_listener is not None: + self.status_listener.eventReceivedSignal.disconnect( + self.updateStatus) + except TypeError: + pass + + try: + if self.position_listener is not None: + self.position_listener.eventReceivedSignal.disconnect( + self.updatePosition) + except TypeError: + pass + + try: + # remove listeners + if self.motor_dev is not None: + if self.hasHwLimits(): + self.motor_dev.getAttribute( + 'Limit_Switches').removeListener(self.limits_listener) + if self.hasPowerOn(): + 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.eventReceivedSignal.connect( + 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.eventReceivedSignal.connect( + 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.status_listener.eventReceivedSignal.connect( + 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.position_listener.eventReceivedSignal.connect( + 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 as e: + self.warning("Exception caught while setting model: %s", repr(e)) + self.motor_dev = None + return + + def hasPowerOn(self): + try: + return hasattr(self.motor_dev, 'PowerOn') + except: + return False + + def hasHwLimits(self): + try: + return hasattr(self.motor_dev, 'Limit_Switches') + except: + return False + + def updateLimits(self, limits, position=None): + if isinstance(limits, dict): + limits = limits["limits"] + limits = list(limits) + HOME = 0 + POS = 1 + NEG = 2 + + # Check also if the software limit is 'active' + if self.motor_dev is not None: + position_attribute = self.motor_dev.getAttribute('Position') + if position is None: + position = position_attribute.read().rvalue + max_value_str = position_attribute.max_value + min_value_str = position_attribute.min_value + try: + max_value = float(max_value_str) + limits[POS] = limits[POS] or (position >= max_value) + except: + pass + try: + min_value = float(min_value_str) + limits[NEG] = limits[NEG] or (position <= min_value) + except: + pass + + pos_lim = limits[POS] + + pos_btnstylesheet = '' + enabled = True + if pos_lim: + pos_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( + PyTango.DevState.ALARM) + enabled = False + self.readWidget(followCompact=True).btn_lim_pos.setStyleSheet( + pos_btnstylesheet) + + self.writeWidget(followCompact=True).btn_step_up.setEnabled(enabled) + self.writeWidget(followCompact=True).btn_step_up.setStyleSheet( + pos_btnstylesheet) + enabled = enabled and self.motor_dev.getAttribute( + 'Position').max_value.lower() != 'not specified' + self.writeWidget(followCompact=True).btn_to_pos.setEnabled(enabled) + self.writeWidget( + followCompact=True).btn_to_pos_press.setEnabled(enabled) + + neg_lim = limits[NEG] + neg_btnstylesheet = '' + enabled = True + if neg_lim: + neg_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( + PyTango.DevState.ALARM) + enabled = False + self.readWidget(followCompact=True).btn_lim_neg.setStyleSheet( + neg_btnstylesheet) + + self.writeWidget(followCompact=True).btn_step_down.setEnabled(enabled) + self.writeWidget(followCompact=True).btn_step_down.setStyleSheet( + neg_btnstylesheet) + enabled = enabled and self.motor_dev.getAttribute( + 'Position').min_value.lower() != 'not specified' + self.writeWidget(followCompact=True).btn_to_neg.setEnabled(enabled) + self.writeWidget( + followCompact=True).btn_to_neg_press.setEnabled(enabled) + + def updatePowerOn(self, poweron='__no_argument__'): + if poweron == '__no_argument__': + msg = 'updatePowerOn called without args (bug in old PyQt). Ignored' + self.debug(msg) + return + btn_text = 'Set ON' + if poweron: + btn_text = 'Set OFF' + self.labelWidget().btn_poweron.setText(btn_text) + + def updateStatus(self, status): + # SHOULD THERE BE A BETTER METHOD FOR THIS UPDATE? + # IF THIS IS NOT DONE, THE TOOLTIP IS NOT CALCULATED EVERY TIME + # 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='__no_argument__'): + if position == '__no_argument__': + msg = 'updatePowerOn called without args (bug in old PyQt). Ignored' + self.debug(msg) + return + # we do not need the position for nothing... + # we just want to check if any software limit is 'active' + # and updateLimits takes care of it + if self.motor_dev is not None: + limit_switches = [False, False, False] + if self.hasHwLimits(): + limit_switches = self.motor_dev.getAttribute( + 'Limit_switches').read().rvalue + # print "update limits", limit_switches + self.updateLimits(limit_switches, position=position) + + def hasEncoder(self): + try: + return hasattr(self.motor_dev, 'Encoder') + except: + return False + + def showTangoAttributes(self): + model = self.getModel() + taurus_attr_form = TaurusAttrForm() + taurus_attr_form.setMinimumSize(Qt.QSize(555, 800)) + taurus_attr_form.setModel(model) + 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() + +################################################### +# A SIMPLER WIDGET THAT MAY BE USED OUTSIDE FORMS # +################################################### + + +class PoolMotor(TaurusFrame): + ''' A widget that displays and controls a pool Motor device. + ''' + + def __init__(self, parent=None, designMode=False): + TaurusFrame.__init__(self, parent, designMode) + + self.setLayout(Qt.QGridLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.layout().setSpacing(0) + + self.setFrameShape(Qt.QFrame.Box) + + self.pool_motor_tv = PoolMotorTV(self) + + def setModel(self, model): + self.pool_motor_tv.setModel(model) + try: + self.motor_dev = taurus.Device(model) + except: + return + + +def main(): + + import sys + import taurus.qt.qtgui.application + import taurus.core.util.argparse + from taurus.qt.qtgui.panel import TaurusForm + parser = taurus.core.util.argparse.get_taurus_parser() + parser.usage = "%prog [options] [ [] ...]" + + app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser) + args = app.get_command_line_args() + + #models = ['tango://controls02:10000/motor/gcipap10ctrl/8'] + models = ['motor/motctrl13/3'] + + if len(args) > 0: + models = args + + w = Qt.QWidget() + w.setLayout(Qt.QVBoxLayout()) + + tests = [] + # tests.append(1) + tests.append(2) + # tests.append(3) + # tests.append(4) + + # 1) Test PoolMotorSlim motor widget + form_pms = TaurusForm() + pms_widget_class = 'sardana.taurus.qt.qtgui.extra_pool.PoolMotorSlim' + pms_tgclass_map = {'SimuMotor': (pms_widget_class, (), {}), + 'Motor': (pms_widget_class, (), {}), + 'PseudoMotor': (pms_widget_class, (), {})} + form_pms.setCustomWidgetMap(pms_tgclass_map) + if 1 in tests: + form_pms.setModel(models) + w.layout().addWidget(form_pms) + + # 2) Test PoolMotorTV motor widget + form_tv = TaurusForm() + form_tv.setModifiableByUser(True) + tv_widget_class = 'sardana.taurus.qt.qtgui.extra_pool.PoolMotorTV' + tv_tgclass_map = {'SimuMotor': (tv_widget_class, (), {}), + 'Motor': (tv_widget_class, (), {}), + 'PseudoMotor': (tv_widget_class, (), {})} + form_tv.setCustomWidgetMap(tv_tgclass_map) + + if 2 in tests: + form_tv.setModel(models) + w.layout().addWidget(form_tv) + form_tv.setCompact(True) + + # 3) Test Stand-Alone PoolMotor widget + # New approach would be to let PoolMotorTV live outside a TaurusForm.... but inside a GridLayout + # Carlos already said this is not a good approach but... + if 3 in tests: + for motor in models: + motor_widget = PoolMotor() + motor_widget.setModel(motor) + w.layout().addWidget(motor_widget) + + # 4) Test Stand-Alone PoolMotorSlim widget + if 4 in tests: + for motor in models: + motor_widget = PoolMotorSlim() + motor_widget.setModel(motor) + w.layout().addWidget(motor_widget) + + w.show() + + sys.exit(app.exec_()) + +if __name__ == '__main__': + main() From c7a91d3d0cf62b4dd3aa3e25e76cfdaa0cde6c49 Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 19 Dec 2019 10:42:49 +0100 Subject: [PATCH 336/830] gitignore changes --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6d7150c949..a2d3dfa54c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,3 @@ /src/sardana.egg-info *.pyc .idea/ -/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py From 6ed4c66fe2c3cf98965b831408cf8726f5d7e16c Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 19 Dec 2019 14:37:47 +0100 Subject: [PATCH 337/830] Minor fixes in the documentation --- doc/source/users/spock.rst | 2 +- doc/source/users/taurus/experimentconfiguration.rst | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/source/users/spock.rst b/doc/source/users/spock.rst index f11d944654..c01135d488 100644 --- a/doc/source/users/spock.rst +++ b/doc/source/users/spock.rst @@ -389,7 +389,7 @@ Viewing scan data ~~~~~~~~~~~~~~~~~ You can show plots for the current scan (i.e. plotting the scan *online*) by -launching the :func:`showscan online ` command +launching the :func:`showscan online ` command. Sardana provides also a scan data viewer for scans which were stored in a `NeXus`_ file: :ref:`showscan_ui`. It can be launched using :func:`showscan ` diff --git a/doc/source/users/taurus/experimentconfiguration.rst b/doc/source/users/taurus/experimentconfiguration.rst index 0aa1bb6e54..25fe0a7308 100644 --- a/doc/source/users/taurus/experimentconfiguration.rst +++ b/doc/source/users/taurus/experimentconfiguration.rst @@ -107,13 +107,14 @@ View current scan plot(s) Plots need to be configured previously as explained in the :ref:`channel configuration ` -plot type and plot axes. -Running a scan will spawn a panel on taurusgui with the plot. The number of panels that will spawn is -defined in the :ref:`channel configuration ` +(plot type and plot axes). +Running a scan will spawn a panel on taurusgui with the plot. The number of +panels that will spawn is defined in the +:ref:`channel configuration `. If the configuration hasn't been changed, a new scan will overwrite the previous plots. -Plots can also be seen with spock's command ``showscan online`` +Plots can also be seen with spock's command ``showscan online``. .. _expconf_ui_snapshot_group: From b8b9ad91d5b009a2ad83a6e8761c51847593d5c3 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 19 Dec 2019 17:28:56 +0100 Subject: [PATCH 338/830] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e92cc2f45..f88d4488c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,9 @@ This file follows the formats and conventions from [keepachangelog.com] ### Removed -* Support to Python < 3.5 (#1089, #1173, 1201) +* Support to Python < 3.5 (#1089, #1173, #1201) +* "Show/hide plots" button in `expconf` (#960, #1255, #1257) +* `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `sardana.requirements` (#1185) ## [2.8.3] 2019-09-16 From ff42b9037b3b8f563177034b533548f7e3855ded Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 19 Dec 2019 17:35:16 +0100 Subject: [PATCH 339/830] Fix a showscan bug that loaded regular "showscan" and "showscan online" while only calling "showscan online" --- src/sardana/spock/magic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index 4bf2181562..ca70429177 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -84,6 +84,7 @@ def showscan(self, parameter_s=''): online, scan_nb = False, None if len(params) > 0: if params[0].lower() == 'online': + online = True try: from sardana.taurus.qt.qtgui.extra_sardana import \ ShowScanOnline @@ -116,6 +117,7 @@ def showscan(self, parameter_s=''): online = True else: scan_nb = int(params[0]) + door.show_scan(scan_nb, online=online) From 9952712269790c8f522cd2c5aab5963d74b10039 Mon Sep 17 00:00:00 2001 From: aalonso Date: Fri, 20 Dec 2019 11:17:51 +0100 Subject: [PATCH 340/830] Improve gscan to detect if a file or dir path is set but empty in the environment --- src/sardana/macroserver/scan/gscan.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index a2602c1052..e1eb6522c0 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -484,6 +484,9 @@ def _getFileRecorders(self): macro = self.macro try: scan_dir = macro.getEnv('ScanDir') + if scan_dir == '' or None: + macro.warning('ScanDir value is empty') + raise Exception('ScanDir value is empty') except InterruptException: raise except Exception: @@ -498,6 +501,9 @@ def _getFileRecorders(self): try: file_names = macro.getEnv('ScanFile') + if file_names == [''] or None: + macro.warning('ScanFile value is empty') + raise Exception('ScanFile value is empty') except InterruptException: raise except Exception: From 3e21d8fff3d85e0568f107e7f32e78290c272de0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 20 Dec 2019 11:56:11 +0100 Subject: [PATCH 341/830] Improve stopping/aborting of the reserved objects * keep a dict of stopped_macro_objs to have reference of which were successfully stopped so we don't abort them - we can not use the reserved_macro_objs dict - it is for another purpose. * returnObjs only after stopping/aborting them and also after calling on_stop and on_abort macro methods --- src/sardana/macroserver/msmacromanager.py | 26 +++++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index d6c34bf2f2..d9e245e813 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1069,6 +1069,11 @@ def __init__(self, door): # key Macro - macro object # value - sequence of reserverd objects by the macro self._reserved_macro_objs = {} + # dict> + # key Macro - macro object + # value - sequence of reserverd objects by the macro + # which were already successfully stopped + self._stopped_macro_objs = {} # reset the stacks # self._macro_stack = None @@ -1397,7 +1402,8 @@ def __stopObjects(self): """Stops all the reserved objects in the executor""" for macro, objs in list(self._reserved_macro_objs.items()): if self._aborted: - break + break # someone aborted, no sense to stop anymore + self._stopped_macro_objs[macro] = stopped_macro_objs = [] for obj in objs: if self._aborted: break # someone aborted, no sense to stop anymore @@ -1410,13 +1416,17 @@ def __stopObjects(self): except: self.warning("Unable to stop %s" % obj) self.debug("Details:", exc_info=1) - self.output("{} stopped".format(obj)) - objs.remove(obj) + else: + self.output("{} stopped".format(obj)) + stopped_macro_objs.append(obj) def __abortObjects(self): """Aborts all the reserved objects in the executor""" for macro, objs in list(self._reserved_macro_objs.items()): + stopped_macro_objs = self._stopped_macro_objs[macro] for obj in objs: + if obj in stopped_macro_objs: + continue self.output( "Aborting {} reserved by {}".format(obj, macro._name)) try: @@ -1426,8 +1436,8 @@ def __abortObjects(self): except: self.warning("Unable to abort %s" % obj) self.debug("Details:", exc_info=1) - self.output("{} aborted".format(obj)) - objs.remove(obj) + else: + self.output("{} aborted".format(obj)) def _setStopDone(self, _): self._stop_done.set() @@ -1652,8 +1662,6 @@ def runMacro(self, macro_obj): 'args': err.args, 'traceback': traceback.format_exc()} macro_exp = MacroServerException(exp_pars) - finally: - self.returnObjs(self._macro_pointer) # make sure the macro's on_abort is called and that a proper macro # status is sent @@ -1668,6 +1676,8 @@ def runMacro(self, macro_obj): macro_obj._stopOnError() self.sendMacroStatusStop() + self.returnObjs(self._macro_pointer) + # From this point on don't call any method of macro_obj which is part # of the mAPI (methods decorated with @mAPI) to avoid throwing an # AbortException if an Abort has been performed. @@ -1801,6 +1811,8 @@ def returnObjs(self, macro_obj): """Free the macro reserved objects""" if macro_obj is None: return + # remove stopped objects to not keep reference to them + self._stopped_macro_obj.pop(macro_obj) objs = self._reserved_macro_objs.get(macro_obj) if objs is None: return From 3a9f4a243964cacb04f0559893b9ba044b2eff30 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 20 Dec 2019 12:32:28 +0100 Subject: [PATCH 342/830] Use dedicated exception: ReleaseException --- src/sardana/macroserver/macro.py | 5 ++++- src/sardana/macroserver/msexception.py | 4 ++-- src/sardana/macroserver/msmacromanager.py | 8 +++++--- src/sardana/taurus/core/tango/sardana/pool.py | 5 +++++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 8b9775c4c2..96b184c322 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -60,7 +60,8 @@ from sardana.macroserver.msparameter import Type, ParamType, ParamRepeat, \ Optional from sardana.macroserver.msexception import StopException, AbortException, \ - MacroWrongParameterType, UnknownEnv, UnknownMacro, LibraryError + ReleaseException, MacroWrongParameterType, UnknownEnv, UnknownMacro, \ + LibraryError from sardana.macroserver.msoptions import ViewOption from sardana.taurus.core.tango.sardana.pool import PoolElement @@ -2379,6 +2380,8 @@ def _abortOnError(self): protecting it against exceptions""" try: self.on_abort() + except ReleaseException: + pass except Exception: Logger.error(self, "Error in on_abort(): %s", traceback.format_exc()) diff --git a/src/sardana/macroserver/msexception.py b/src/sardana/macroserver/msexception.py index ca2ac2e22b..260f4960b2 100644 --- a/src/sardana/macroserver/msexception.py +++ b/src/sardana/macroserver/msexception.py @@ -30,12 +30,12 @@ "UnknownEnv", "UnknownMacro", "UnknownMacroLibrary", "UnknownRecorder", "MacroWrongParameterType", "LibraryError", "InterruptException", "StopException", "AbortException", - "InputCancelled"] + "ReleaseException", "InputCancelled"] __docformat__ = 'restructuredtext' from sardana.taurus.core.tango.sardana.pool import InterruptException, \ - StopException, AbortException + StopException, AbortException, ReleaseException from sardana.sardanaexception import SardanaException, SardanaExceptionList, \ UnknownCode, UnknownLibrary, LibraryError diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index d9e245e813..cdd93caf42 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -69,7 +69,7 @@ Hookable from sardana.macroserver.msexception import UnknownMacroLibrary, \ LibraryError, UnknownMacro, MissingEnv, AbortException, StopException, \ - MacroServerException, UnknownEnv + ReleaseException, MacroServerException, UnknownEnv from sardana.util.parser import ParamParser from sardana.util.thread import raise_in_thread @@ -1464,7 +1464,7 @@ def abort(self): self._aborted = True if not self._isStopDone(): Logger.debug(self, "Break stopping...") - raise_in_thread(AbortException, self._stop_thread) + raise_in_thread(ReleaseException, self._stop_thread) self.macro_server.add_job(self._abort, self._setAbortDone) def release(self): @@ -1474,7 +1474,7 @@ def release(self): self._released = True if not self._isAbortDone(): Logger.debug(self, "Break aborting...") - raise_in_thread(AbortException, self._abort_thread) + raise_in_thread(ReleaseException, self._abort_thread) def stop(self): self._stopped = True @@ -1483,6 +1483,8 @@ def stop(self): def _abort(self): self._abort_thread = threading.current_thread() if self._stopped: + # stopping did not finish on its own - we are aborting it + # but need to wait anyway so its thread finishes self._waitStopDone() m = self.getRunningMacro() if m is not None: diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 7375cff9e3..1fdf69c9ac 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -30,6 +30,7 @@ import collections __all__ = ["InterruptException", "StopException", "AbortException", + "ReleaseException", "BaseElement", "ControllerClass", "ControllerLibrary", "PoolElement", "Controller", "ComChannel", "ExpChannel", "CTExpChannel", "ZeroDExpChannel", "OneDExpChannel", @@ -108,6 +109,10 @@ class AbortException(InterruptException): pass +class ReleaseException(InterruptException): + pass + + class BaseElement(object): """ The base class for elements in the Pool (Pool itself, Motor, ControllerClass, ExpChannel all should inherit from this class directly or From 9a348752b1677974d470d84039bd4b93963030ee Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 20 Dec 2019 12:39:23 +0100 Subject: [PATCH 343/830] Allow to release macro from hanged on_abort method with 4rd Ctrl+C --- src/sardana/macroserver/msmacromanager.py | 14 ++++++++++++-- src/sardana/spock/spockms.py | 13 +++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index cdd93caf42..54ac40bb14 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1468,14 +1468,24 @@ def abort(self): self.macro_server.add_job(self._abort, self._setAbortDone) def release(self): - """**Internal method**. Release the macro.""" + """**Internal method**. Release the macro from hang situations + + Hanged situations: + * hanged process of aborting reserved objects + * hanged macro on_abort method. + """ # carefull: Inside this method never call a method that has the # mAPI decorator self._released = True - if not self._isAbortDone(): + if self._isAbortDone(): + m = self.getRunningMacro() + Logger.debug(self, "Break {}.on_abort...".format(m._name)) + raise_in_thread(ReleaseException, m._macro_thread) + else: Logger.debug(self, "Break aborting...") raise_in_thread(ReleaseException, self._abort_thread) + def stop(self): self._stopped = True self.macro_server.add_job(self._stop, self._setStopDone) diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 225ebb6d6f..ce89bc9a5e 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -369,8 +369,17 @@ def _runMacro(self, xml, **kwargs): self.abort() self.writeln("Aborting done!") except KeyboardInterrupt: - self.write('3rd Ctrl-C received: Releasing...\n') - self.release() + try: + self.write('3rd Ctrl-C received: Releasing...\n') + self.block_lines = 0 + self.release() + self.writeln("Releasing done!") + except KeyboardInterrupt: + self.write('4th Ctrl-C received: Releasing...\n') + self.release() + self.block_lines = 0 + self.release() + self.writeln("Releasing done!") except PyTango.DevFailed as e: if is_non_str_seq(e.args) and \ not isinstance(e.args, str): From 0a8c838271e6547899e379ce582859a96ae9a4b4 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 20 Dec 2019 12:39:55 +0100 Subject: [PATCH 344/830] Protect release method when macro already finished --- .../taurus/core/tango/sardana/macroserver.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 11ad92a75c..ac815499ad 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -482,17 +482,27 @@ def abort(self, synch=True): def release(self, synch=True): if not synch: - self.command_inout("ReleaseMacro") + try: + self.command_inout("ReleaseMacro") + except PyTango.DevFailed as df: + # Macro already finished - no need to release + if df.args[0].reason == "API_CommandNotAllowed": + pass return evt_wait = AttributeEventWait(self.getAttribute("state")) evt_wait.lock() try: time_stamp = time.time() - self.command_inout("ReleaseMacro") - return evt_wait.waitEvent(self.Running, equal=False, - after=time_stamp, - timeout=self.InteractiveTimeout) + try: + self.command_inout("ReleaseMacro") + except PyTango.DevFailed as df: + # Macro already finished - no need to release + if df.args[0].reason == "API_CommandNotAllowed": + return + evt_wait.waitEvent(self.Running, equal=False, + after=time_stamp, + timeout=self.InteractiveTimeout) finally: evt_wait.unlock() evt_wait.disconnect() From 2ba1c456a19d9e1c5ba459494beb2fd0b5531dea Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 20 Dec 2019 14:07:01 +0100 Subject: [PATCH 345/830] Refactor stopping/aborting/releasing code Also allow 5th Ctrl+C which will Give-up and leave the door in running state --- src/sardana/spock/spockms.py | 63 +++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index ce89bc9a5e..33abd2d357 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -345,6 +345,45 @@ def preRunMacro(self, obj, parameters): def runMacro(self, obj, parameters=[], synch=False): return BaseDoor.runMacro(self, obj, parameters=parameters, synch=synch) + def _handle_second_release(self): + try: + self.write('4th Ctrl-C received: Releasing...\n') + self.release() + self.block_lines = 0 + self.release() + self.writeln("Releasing done!") + except KeyboardInterrupt: + self.write('5th Ctrl-C received: Giving up...\n') + self.write('(The macro is probably still executing the on_abort ' + 'method. Either wait or restart the server.)\n') + + def _handle_first_release(self): + try: + self.write('3rd Ctrl-C received: Releasing...\n') + self.block_lines = 0 + self.release() + self.writeln("Releasing done!") + except KeyboardInterrupt: + self._handle_second_release() + + def _handle_abort(self): + try: + self.write('2nd Ctrl-C received: Aborting...\n') + self.block_lines = 0 + self.abort() + self.writeln("Aborting done!") + except KeyboardInterrupt: + self._handle_first_release() + + def _handle_stop(self): + try: + self.write('\nCtrl-C received: Stopping...\n') + self.block_lines = 0 + self.stop() + self.writeln("Stopping done!") + except KeyboardInterrupt: + self._handle_abort() + def _runMacro(self, xml, **kwargs): # kwargs like 'synch' are ignored in this re-implementation if self._spock_state != RUNNING_STATE: @@ -357,29 +396,7 @@ def _runMacro(self, xml, **kwargs): try: return BaseDoor._runMacro(self, xml, **kwargs) except KeyboardInterrupt: - try: - self.write('\nCtrl-C received: Stopping...\n') - self.block_lines = 0 - self.stop() - self.writeln("Stopping done!") - except KeyboardInterrupt: - try: - self.write('2nd Ctrl-C received: Aborting...\n') - self.block_lines = 0 - self.abort() - self.writeln("Aborting done!") - except KeyboardInterrupt: - try: - self.write('3rd Ctrl-C received: Releasing...\n') - self.block_lines = 0 - self.release() - self.writeln("Releasing done!") - except KeyboardInterrupt: - self.write('4th Ctrl-C received: Releasing...\n') - self.release() - self.block_lines = 0 - self.release() - self.writeln("Releasing done!") + self._handle_stop() except PyTango.DevFailed as e: if is_non_str_seq(e.args) and \ not isinstance(e.args, str): From 992c9b4ca9ced58119292cb8b812bd0fbbe34e77 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 20 Dec 2019 14:07:32 +0100 Subject: [PATCH 346/830] Does not allow to run macros when Door is in RUNNING state --- src/sardana/spock/spockms.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 33abd2d357..b52f8115f3 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -390,6 +390,9 @@ def _runMacro(self, xml, **kwargs): print("Unable to run macro: No connection to door '%s'" % self.getSimpleName()) raise Exception("Unable to run macro: No connection") + if self.stateObj.read().rvalue == PyTango.DevState.RUNNING: + print("Another macro is running. Wait until it finishes...") + raise Exception("Unable to run macro: door in RUNNING state") if xml is None: xml = self.getRunningXML() kwargs['synch'] = True From 76f8a8f249f6c5cb73cd44987c1315468665e11b Mon Sep 17 00:00:00 2001 From: aalonso Date: Fri, 20 Dec 2019 16:33:00 +0100 Subject: [PATCH 347/830] Remove unused imports of ordereddict for python ver < 2.7 --- src/sardana/macroserver/msmacromanager.py | 6 +----- src/sardana/macroserver/msrecordermanager.py | 6 +----- src/sardana/macroserver/scan/gscan.py | 6 +----- src/sardana/pool/poolaction.py | 6 +----- src/sardana/pool/poolcontrollermanager.py | 6 +----- src/sardana/sardanabuffer.py | 6 +----- 6 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 5d64b05a95..70daae1f3c 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -46,11 +46,7 @@ from PyTango import DevFailed -try: - from collections import OrderedDict -except ImportError: - # For Python < 2.7 - from ordereddict import OrderedDict +from collections import OrderedDict from taurus.core.util.log import Logger from taurus.core.util.codecs import CodecFactory diff --git a/src/sardana/macroserver/msrecordermanager.py b/src/sardana/macroserver/msrecordermanager.py index 4e2f53cba7..39b6179e07 100644 --- a/src/sardana/macroserver/msrecordermanager.py +++ b/src/sardana/macroserver/msrecordermanager.py @@ -35,11 +35,7 @@ import copy import inspect -try: - from collections import OrderedDict -except ImportError: - # For Python < 2.7 - from ordereddict import OrderedDict +from collections import OrderedDict from sardana import sardanacustomsettings from sardana.sardanaexception import format_exception_only_str diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index a2602c1052..cc2085859e 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -43,11 +43,7 @@ import taurus import collections -try: - from collections import OrderedDict -except ImportError: - # For Python < 2.7 - from ordereddict import OrderedDict +from collections import OrderedDict from taurus.core.util.log import Logger from taurus.core.util.user import USER_NAME diff --git a/src/sardana/pool/poolaction.py b/src/sardana/pool/poolaction.py index 5786903e9c..25f692d8ec 100644 --- a/src/sardana/pool/poolaction.py +++ b/src/sardana/pool/poolaction.py @@ -36,11 +36,7 @@ import traceback import threading -try: - from collections import OrderedDict -except ImportError: - # For Python < 2.7 - from ordereddict import OrderedDict +from collections import OrderedDict from taurus.core.util.log import Logger diff --git a/src/sardana/pool/poolcontrollermanager.py b/src/sardana/pool/poolcontrollermanager.py index 7ce19cdd69..89283b7484 100644 --- a/src/sardana/pool/poolcontrollermanager.py +++ b/src/sardana/pool/poolcontrollermanager.py @@ -37,11 +37,7 @@ import types import inspect -try: - from collections import OrderedDict -except ImportError: - # For Python < 2.7 - from ordereddict import OrderedDict +from collections import OrderedDict from taurus.core import ManagerState from taurus.core.util.log import Logger diff --git a/src/sardana/sardanabuffer.py b/src/sardana/sardanabuffer.py index c481441e65..16c51e447a 100644 --- a/src/sardana/sardanabuffer.py +++ b/src/sardana/sardanabuffer.py @@ -32,11 +32,7 @@ import weakref -try: - from collections import OrderedDict -except ImportError: - # For Python < 2.7 - from ordereddict import OrderedDict +from collections import OrderedDict from .sardanavalue import SardanaValue from .sardanaevent import EventGenerator, EventType From b01734afd7d10afaaa8a2fb569fe7139dcee1bff Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 20 Dec 2019 23:27:18 +0100 Subject: [PATCH 348/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f88d4488c2..cd02639f03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ This file follows the formats and conventions from [keepachangelog.com] ### Removed -* Support to Python < 3.5 (#1089, #1173, #1201) +* Support to Python < 3.5 (#1089, #1173, #1201, #1263) * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `sardana.requirements` (#1185) From 97c4881630a060a006c03c8f47fe0717e44bed5e Mon Sep 17 00:00:00 2001 From: aalonso Date: Tue, 7 Jan 2020 16:21:08 +0100 Subject: [PATCH 349/830] migrate tests to python 3 and solve some deprecated problems. Still some problems with poolpath remaining --- test/HTMLTestRunner.py | 13 +++--- test/pool.py | 3 +- test/poolunittest.py | 89 +++++++++++++++++++++++++++--------------- 3 files changed, 67 insertions(+), 38 deletions(-) diff --git a/test/HTMLTestRunner.py b/test/HTMLTestRunner.py index b16e553ea7..778e12981f 100644 --- a/test/HTMLTestRunner.py +++ b/test/HTMLTestRunner.py @@ -87,10 +87,12 @@ # TODO: simplify javascript using ,ore than 1 class in the class attribute? import datetime -import StringIO +#TODO aalonso import StringIO +import io import sys import time -from taurus.external import unittest +#TODO aalonso from taurus.external import unittest +import unittest from xml.sax import saxutils @@ -477,7 +479,7 @@ def __init__(self, verbosity=1): def startTest(self, test): TestResult.startTest(self, test) # just one buffer for both stdout and stderr - self.outputBuffer = StringIO.StringIO() + self.outputBuffer = io.StringIO() stdout_redirector.fp = self.outputBuffer stderr_redirector.fp = self.outputBuffer self.stdout0 = sys.stdout @@ -567,8 +569,8 @@ def run(self, test): test(result) self.stopTime = datetime.datetime.now() self.generateReport(test, result) - print >>sys.stderr, '\nTime Elapsed: %s' % ( - self.stopTime - self.startTime) + print('\nTime Elapsed: %s' % ( + self.stopTime - self.startTime), file=sys.stderr) return result def sortResult(self, result_list): @@ -743,6 +745,7 @@ def _generate_ending(self): # build our own launcher to support more specific command line # parameters like test title, CSS, etc. class TestProgram(unittest.TestProgram): + """ A variation of the unittest.TestProgram. Please refer to the base class for command line parameters. diff --git a/test/pool.py b/test/pool.py index 136da2f245..8fbe1937d6 100644 --- a/test/pool.py +++ b/test/pool.py @@ -4,7 +4,8 @@ """ import os -from taurus.external import unittest +# from taurus.external import unittest #TODO aalonso +import unittest import poolunittest import HTMLTestRunner import time diff --git a/test/poolunittest.py b/test/poolunittest.py index 5d26fc2f2f..619a9e831a 100644 --- a/test/poolunittest.py +++ b/test/poolunittest.py @@ -1,22 +1,24 @@ """ An extension to the original PyUnit providing specific Device Pool test utilities """ -from taurus.external import unittest +# from taurus.external import unittest #TODO aalonso +import logging +import unittest import PyTango import sys import os -import user +# TODO aalonso import user import subprocess import time import signal -import exceptions +# TODO aalonso import exceptions import imp try: import pexpect except: - print "The Pool Unit test requires pexpect python module which was not found." - print "This module can be found at http://www.noah.org/wiki/Pexpect" + print("The Pool Unit test requires pexpect python module which was not found.") + print("This module can be found at http://www.noah.org/wiki/Pexpect") sys.exit(-1) @@ -84,7 +86,7 @@ def check_empty_attribute(self, dev, att_name): if len(c_list.value) != 0: self.assert_(False, "The %s attribute is not empty !! It contains: %s" % ( att_name, c_list.value)) - except PyTango.DevFailed, e: + except PyTango.DevFailed as e: except_value = sys.exc_info()[1] self.assertEqual( except_value[0]["reason"], "API_EmptyDeviceAttribute") @@ -103,7 +105,7 @@ def attribute_error(self, dev, att_name, err, pr=False): c_list = dev.read_attribute(att_name) self.assert_( False, "The %s attribute is not in fault!!" % (att_name)) - except PyTango.DevFailed, e: + except PyTango.DevFailed as e: except_value = sys.exc_info()[1] if pr: self._printException(except_value) @@ -122,7 +124,7 @@ def wr_attribute_error(self, dev, att_val, err, pr=False): dev.write_attribute(att_val) self.assert_(False, "The %s attribute is not in fault!!" % (att_val.name)) - except PyTango.DevFailed, e: + except PyTango.DevFailed as e: except_value = sys.exc_info()[1] if pr: self._printException(except_value) @@ -141,7 +143,7 @@ def wrong_argument(self, dev, cmd_name, arg_list, err, pr=False): dev.command_inout(cmd_name, arg_list) self.assert_( False, "The %s command succeed with wrong arguments!!" % (cmd_name)) - except PyTango.DevFailed, e: + except PyTango.DevFailed as e: except_value = sys.exc_info()[1] if pr: self._printException(except_value) @@ -159,14 +161,14 @@ def _write_attribute(self, dev, att_name, att_val): dev.write_attribute(val) def _printException(self, except_value): - print "\nERROR desc" - print "origin =", except_value[0]["origin"] - print "desc =", except_value[0]["desc"] - print "origin =", except_value[0]['origin'] + print("\nERROR desc") + print("origin =", except_value[0]["origin"]) + print("desc =", except_value[0]["desc"]) + print("origin =", except_value[0]['origin']) - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # Default setup. Overwrite this methods in each test scenario when necessary - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- def getPoolPath(self): """getPoolPath() -> list """ @@ -202,12 +204,12 @@ def getCounterTimerSimulators(self): ret.append({"properties": {"Average": ['1.0'], "Sigma": ['250.0'], "MotorName": [mot_name]}, - },) + }, ) return ret - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # Generic methods - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- def deleteFromDB(self, server_name, server_instance): server_name = server_name.lower() @@ -226,9 +228,9 @@ def deleteFromDB(self, server_name, server_instance): self.tango_db.delete_server(server) - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # Test requirements. Overwrite as necessary - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- def needsPool(self): return True @@ -239,9 +241,9 @@ def needsMotorSimulator(self): def needsCounterTimerSimulator(self): return False - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # Pre and Post test methods - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- def setUp(self): """Default setUp.""" @@ -299,8 +301,15 @@ def prepareMotorSimulator(self): f.close() f, path, desc = imp.find_module('SimuMotor') f.close() - except exceptions.ImportError, e: - self.assert_(False, e.message) + # except exceptions.ImportError as e: + # self.assert_(False, e.message) + # TODO aalonso + except ImportError as e: + self.assertTrue(False, e) + logging.log_exception(e) + # Include the name and path attributes in output. + logging.log(f'error.name: {e.name}') + logging.log(f'error.path: {e.path}') # Cleanup the database self.deleteMotorSimulatorFromDB() @@ -365,8 +374,15 @@ def prepareCounterTimerSimulator(self): f.close() f, path, desc = imp.find_module('SimuCounter') f.close() - except exceptions.ImportError, e: - self.assert_(False, e.message) + # except exceptions.ImportError as e: + # self.assert_(False, e.message) + # TODO aalonso + except ImportError as e: + self.assert_(False, e) + logging.log_exception(e) + # Include the name and path attributes in output. + logging.log(f'error.name: {e.name}') + logging.log(f'error.path: {e.path}') self.ctsim_exec = self.ctsim_exec @@ -429,8 +445,10 @@ def prepareDevicePool(self): self.pool_exec = path break - self.failIf(self.pool_exec is None, - "Could not find Pool executable. Make sure it is in the PATH") + # self.failIf(self.pool_exec is None, + # "Could not find Pool executable. Make sure it is in the PATH") #TODO aalonso + self.assertFalse(self.pool_exec is None, + "Could not find Pool executable. Make sure it is in the PATH") self.pool_bin_dir = os.path.dirname(self.pool_exec) @@ -529,11 +547,11 @@ def startPool(self): if idx == 0: return elif idx == 1: - self.assert_(False, PoolTestCase.PoolAlreadyRunning) + self.assertTrue(False, PoolTestCase.PoolAlreadyRunning) elif idx == 2: - self.assert_(False, "Device Pool terminated unexpectedly") + self.assertTrue(False, "Device Pool terminated unexpectedly") elif idx == 3: - self.assert_(False, "Device Pool startup time exceeded") + self.assertTrue(False, "Device Pool startup time exceeded") def stopPool(self): """Stops the Device Pool""" @@ -686,7 +704,14 @@ def startPool_PopenStyle(self): self.pool_ds_instance], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - except exceptions.OSError, e: + # except exceptions.OSError as e: + # if e.strerror == PoolTestCase.PoolExecNotFound: + # self.assert_( + # False, "Could not find Pool executable. Make sure it is in the PATH") + # else: + # raise + # TODO aalonso + except OSError as e: if e.strerror == PoolTestCase.PoolExecNotFound: self.assert_( False, "Could not find Pool executable. Make sure it is in the PATH") From c441a0651cdc83b673a04a4f51639352735c676e Mon Sep 17 00:00:00 2001 From: aalonso Date: Wed, 8 Jan 2020 14:27:53 +0100 Subject: [PATCH 350/830] Remove unnecessary comments --- test/HTMLTestRunner.py | 2 -- test/pool.py | 1 - test/poolunittest.py | 18 ------------------ 3 files changed, 21 deletions(-) diff --git a/test/HTMLTestRunner.py b/test/HTMLTestRunner.py index 778e12981f..1373f5255c 100644 --- a/test/HTMLTestRunner.py +++ b/test/HTMLTestRunner.py @@ -87,11 +87,9 @@ # TODO: simplify javascript using ,ore than 1 class in the class attribute? import datetime -#TODO aalonso import StringIO import io import sys import time -#TODO aalonso from taurus.external import unittest import unittest from xml.sax import saxutils diff --git a/test/pool.py b/test/pool.py index 8fbe1937d6..de91cae440 100644 --- a/test/pool.py +++ b/test/pool.py @@ -4,7 +4,6 @@ """ import os -# from taurus.external import unittest #TODO aalonso import unittest import poolunittest import HTMLTestRunner diff --git a/test/poolunittest.py b/test/poolunittest.py index 619a9e831a..75b46b3911 100644 --- a/test/poolunittest.py +++ b/test/poolunittest.py @@ -1,17 +1,14 @@ """ An extension to the original PyUnit providing specific Device Pool test utilities """ -# from taurus.external import unittest #TODO aalonso import logging import unittest import PyTango import sys import os -# TODO aalonso import user import subprocess import time import signal -# TODO aalonso import exceptions import imp try: @@ -301,9 +298,6 @@ def prepareMotorSimulator(self): f.close() f, path, desc = imp.find_module('SimuMotor') f.close() - # except exceptions.ImportError as e: - # self.assert_(False, e.message) - # TODO aalonso except ImportError as e: self.assertTrue(False, e) logging.log_exception(e) @@ -374,9 +368,6 @@ def prepareCounterTimerSimulator(self): f.close() f, path, desc = imp.find_module('SimuCounter') f.close() - # except exceptions.ImportError as e: - # self.assert_(False, e.message) - # TODO aalonso except ImportError as e: self.assert_(False, e) logging.log_exception(e) @@ -445,8 +436,6 @@ def prepareDevicePool(self): self.pool_exec = path break - # self.failIf(self.pool_exec is None, - # "Could not find Pool executable. Make sure it is in the PATH") #TODO aalonso self.assertFalse(self.pool_exec is None, "Could not find Pool executable. Make sure it is in the PATH") @@ -704,13 +693,6 @@ def startPool_PopenStyle(self): self.pool_ds_instance], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - # except exceptions.OSError as e: - # if e.strerror == PoolTestCase.PoolExecNotFound: - # self.assert_( - # False, "Could not find Pool executable. Make sure it is in the PATH") - # else: - # raise - # TODO aalonso except OSError as e: if e.strerror == PoolTestCase.PoolExecNotFound: self.assert_( From cceb09872871e85e9e3c93a03e261b40a27eb578 Mon Sep 17 00:00:00 2001 From: aalonso Date: Wed, 8 Jan 2020 16:51:51 +0100 Subject: [PATCH 351/830] Remove show plots button from experiment configuration and update documentation --- sandbox/pool_gui | 5 ++-- src/sardana/macroserver/macros/standard.py | 2 +- src/sardana/macroserver/macros/test/base.py | 2 +- .../macroserver/macros/test/test_ct.py | 2 +- .../macroserver/macros/test/test_env.py | 2 +- .../macroserver/macros/test/test_expert.py | 2 +- .../macroserver/macros/test/test_gh.py | 2 +- .../macros/test/test_ioregister.py | 2 +- .../macroserver/macros/test/test_list.py | 2 +- .../macroserver/macros/test/test_macro.py | 2 +- .../macroserver/macros/test/test_scan.py | 2 +- .../macroserver/macros/test/test_scanct.py | 2 +- .../macroserver/macros/test/test_standard.py | 2 +- .../macroserver/macros/test/test_wm.py | 2 +- src/sardana/macroserver/recorders/output.py | 2 +- src/sardana/macroserver/recorders/storage.py | 2 +- .../recorders/test/test_h5storage.py | 2 +- src/sardana/macroserver/scan/scandata.py | 2 +- .../macroserver/scan/test/test_gscan.py | 2 +- .../macroserver/scan/test/test_recorddata.py | 2 +- .../macroserver/test/test_msparameter.py | 2 +- .../test/test_msrecordermanager.py | 2 +- src/sardana/pool/poolcontrollers/test/base.py | 2 +- .../test/test_DummyTriggerGateController.py | 2 +- src/sardana/pool/test/test_acquisition.py | 2 +- src/sardana/pool/test/test_ctacquisition.py | 2 +- .../pool/test/test_measurementgroup.py | 2 +- src/sardana/pool/test/test_poolcontroller.py | 2 +- .../pool/test/test_poolcontrollermanager.py | 2 +- .../pool/test/test_poolcountertimer.py | 2 +- src/sardana/pool/test/test_poolmotion.py | 2 +- .../pool/test/test_poolpseudocounter.py | 2 +- .../pool/test/test_poolsynchronization.py | 2 +- src/sardana/pool/test/test_synchronization.py | 2 +- src/sardana/sardanacustomsettings.py | 6 +++-- src/sardana/spock/spockms.py | 2 +- src/sardana/spock/test/test_parameter.py | 2 +- src/sardana/tango/pool/test/base.py | 2 +- src/sardana/tango/pool/test/test_Motor.py | 2 +- .../tango/pool/test/test_measurementgroup.py | 2 +- .../tango/pool/test/test_persistence.py | 2 +- src/sardana/taurus/core/tango/sardana/pool.py | 4 +-- .../core/tango/sardana/test/test_macro.py | 2 +- .../core/tango/sardana/test/test_pool.py | 2 +- .../extra_hkl/diffractometeralignment.py | 13 +++------- .../taurus/qt/qtgui/extra_hkl/hklscan.py | 7 +++-- .../taurus/qt/qtgui/extra_hkl/ubmatrix.py | 8 +++--- .../favouriteseditor/favouriteseditor.py | 2 ++ .../favouriteseditor/historyviewer.py | 2 ++ .../qtgui/extra_macroexecutor/macrobutton.py | 7 +++-- .../sequenceeditor/sequenceeditor.py | 26 ++++++++++--------- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 7 ++--- .../taurus/qt/qtgui/extra_sardana/cmdline.py | 4 +-- .../qt/qtgui/extra_sardana/expdescription.py | 26 +++++++++++-------- .../qt/qtgui/extra_sardana/sardanaeditor.py | 5 ++-- .../qt/qtgui/extra_sardana/showscanonline.py | 5 ++-- src/sardana/test/test_sardanabuffer.py | 2 +- src/sardana/test/test_sardanavalue.py | 2 +- src/sardana/test/testsuite.py | 4 +-- src/sardana/util/test/test_funcgenerator.py | 2 +- src/sardana/util/test/test_parser.py | 2 +- src/sardana/util/test/test_thread.py | 2 +- 62 files changed, 112 insertions(+), 111 deletions(-) diff --git a/sandbox/pool_gui b/sandbox/pool_gui index b0ac2c2268..bb9c47dd72 100644 --- a/sandbox/pool_gui +++ b/sandbox/pool_gui @@ -4,7 +4,7 @@ import sys import datetime import taurus -from taurus.core.util.argparse import get_taurus_parser +from argparse import ArgumentParser from taurus.core.util import CodecFactory from taurus.qt import Qt from taurus.qt.qtgui.application import TaurusApplication @@ -277,8 +277,7 @@ def gui(model): def main(): - parser = get_taurus_parser() - parser.usage = "%prog [options] " + parser = ArgumentParser(usage="%prog [options] ") app = TaurusApplication(cmd_line_parser=parser) app.setTaurusStyle("nebula") diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 7cc2ffba2d..1c3b506152 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -652,7 +652,7 @@ def run(self, motor, delta): def _value_to_repr(data): if data is None: return "" - elif np.rank(data) > 0: + elif np.ndim(data) > 0: return list(np.shape(data)) else: return data diff --git a/src/sardana/macroserver/macros/test/base.py b/src/sardana/macroserver/macros/test/base.py index 0b0ea45db0..d463ce6738 100644 --- a/src/sardana/macroserver/macros/test/base.py +++ b/src/sardana/macroserver/macros/test/base.py @@ -302,7 +302,7 @@ class member) if __name__ == '__main__': - from taurus.external import unittest + import unittest from sardana.macroserver.macros.test import SarDemoEnv _m1 = SarDemoEnv().getMotors()[0] diff --git a/src/sardana/macroserver/macros/test/test_ct.py b/src/sardana/macroserver/macros/test/test_ct.py index de3f1bcfaa..94261967db 100644 --- a/src/sardana/macroserver/macros/test/test_ct.py +++ b/src/sardana/macroserver/macros/test/test_ct.py @@ -25,7 +25,7 @@ """Tests for ct macros""" -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import RunStopMacroTestCase from sardana.macroserver.macros.test import testRun from sardana.macroserver.macros.test import testStop diff --git a/src/sardana/macroserver/macros/test/test_env.py b/src/sardana/macroserver/macros/test/test_env.py index 499b9a03f2..db54b2210a 100644 --- a/src/sardana/macroserver/macros/test/test_env.py +++ b/src/sardana/macroserver/macros/test/test_env.py @@ -25,7 +25,7 @@ """Tests for environment macros""" -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import RunMacroTestCase, testRun diff --git a/src/sardana/macroserver/macros/test/test_expert.py b/src/sardana/macroserver/macros/test/test_expert.py index 7a3e48d002..bc02615926 100644 --- a/src/sardana/macroserver/macros/test/test_expert.py +++ b/src/sardana/macroserver/macros/test/test_expert.py @@ -25,7 +25,7 @@ """Tests for expert macros""" -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import RunMacroTestCase, testRun, getCTs,\ getControllers diff --git a/src/sardana/macroserver/macros/test/test_gh.py b/src/sardana/macroserver/macros/test/test_gh.py index 17ab4fb352..e849297522 100644 --- a/src/sardana/macroserver/macros/test/test_gh.py +++ b/src/sardana/macroserver/macros/test/test_gh.py @@ -23,7 +23,7 @@ ## ############################################################################## -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import RunMacroTestCase, testRun from sardana.tango.macroserver.test import BaseMacroServerTestCase diff --git a/src/sardana/macroserver/macros/test/test_ioregister.py b/src/sardana/macroserver/macros/test/test_ioregister.py index 1fbc7315f7..f4220a715f 100644 --- a/src/sardana/macroserver/macros/test/test_ioregister.py +++ b/src/sardana/macroserver/macros/test/test_ioregister.py @@ -25,7 +25,7 @@ """Tests for ioregister macros""" -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import RunMacroTestCase, testRun, getIORs IOR_NAME = getIORs()[0] diff --git a/src/sardana/macroserver/macros/test/test_list.py b/src/sardana/macroserver/macros/test/test_list.py index ff24c16d12..99f5650474 100644 --- a/src/sardana/macroserver/macros/test/test_list.py +++ b/src/sardana/macroserver/macros/test/test_list.py @@ -26,7 +26,7 @@ """Tests for list macros""" import time -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import (RunMacroTestCase, testRun, SarDemoEnv) diff --git a/src/sardana/macroserver/macros/test/test_macro.py b/src/sardana/macroserver/macros/test/test_macro.py index 6b70d5ea28..ad813b649d 100644 --- a/src/sardana/macroserver/macros/test/test_macro.py +++ b/src/sardana/macroserver/macros/test/test_macro.py @@ -25,7 +25,7 @@ import os -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import RunMacroTestCase, testRun from sardana.tango.macroserver.test import BaseMacroServerTestCase diff --git a/src/sardana/macroserver/macros/test/test_scan.py b/src/sardana/macroserver/macros/test/test_scan.py index 45b16abbb2..fcbca22e45 100644 --- a/src/sardana/macroserver/macros/test/test_scan.py +++ b/src/sardana/macroserver/macros/test/test_scan.py @@ -25,7 +25,7 @@ """Tests for scan macros""" -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import (RunStopMacroTestCase, testRun, testStop, getMotors) diff --git a/src/sardana/macroserver/macros/test/test_scanct.py b/src/sardana/macroserver/macros/test/test_scanct.py index e680f2c42b..702013cadb 100644 --- a/src/sardana/macroserver/macros/test/test_scanct.py +++ b/src/sardana/macroserver/macros/test/test_scanct.py @@ -26,7 +26,7 @@ """Tests for continuous scans (ct-like)""" import time import PyTango -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import (RunStopMacroTestCase, testRun, testStop) from sardana.pool import AcqSynchType diff --git a/src/sardana/macroserver/macros/test/test_standard.py b/src/sardana/macroserver/macros/test/test_standard.py index b7a51006c7..01764f4fe9 100644 --- a/src/sardana/macroserver/macros/test/test_standard.py +++ b/src/sardana/macroserver/macros/test/test_standard.py @@ -25,7 +25,7 @@ """Tests for standard macros""" -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import RunMacroTestCase, testRun,\ getMotors diff --git a/src/sardana/macroserver/macros/test/test_wm.py b/src/sardana/macroserver/macros/test/test_wm.py index 2388fd36bf..00bb7ce36c 100644 --- a/src/sardana/macroserver/macros/test/test_wm.py +++ b/src/sardana/macroserver/macros/test/test_wm.py @@ -25,7 +25,7 @@ """Tests for wm macros""" -from taurus.external import unittest +import unittest from sardana.macroserver.macros.test import (RunMacroTestCase, testRun, getMotors) diff --git a/src/sardana/macroserver/recorders/output.py b/src/sardana/macroserver/recorders/output.py index c7224bc28b..79983b640e 100644 --- a/src/sardana/macroserver/recorders/output.py +++ b/src/sardana/macroserver/recorders/output.py @@ -278,7 +278,7 @@ def _addCustomData(self, value, name, **kwargs): The custom data will be added as an info line in the form: Custom data: name : value ''' - if numpy.rank(value) > 0: + if numpy.ndim(value) > 0: v = 'Array(%s)' % str(numpy.shape(value)) else: v = str(value) diff --git a/src/sardana/macroserver/recorders/storage.py b/src/sardana/macroserver/recorders/storage.py index b54605385f..729a1b7d6c 100644 --- a/src/sardana/macroserver/recorders/storage.py +++ b/src/sardana/macroserver/recorders/storage.py @@ -508,7 +508,7 @@ def _addCustomData(self, value, name, **kwargs): self.info( 'Custom data "%s" will not be stored in SPEC file. Reason: uninitialized file', name) return - if numpy.rank(value) > 0: # ignore non-scalars + if numpy.ndim(value) > 0: # ignore non-scalars self.info( 'Custom data "%s" will not be stored in SPEC file. Reason: value is non-scalar', name) return diff --git a/src/sardana/macroserver/recorders/test/test_h5storage.py b/src/sardana/macroserver/recorders/test/test_h5storage.py index d47449eb94..413f4e4797 100644 --- a/src/sardana/macroserver/recorders/test/test_h5storage.py +++ b/src/sardana/macroserver/recorders/test/test_h5storage.py @@ -31,7 +31,7 @@ import h5py import numpy -from taurus.external.unittest import TestCase +from unittest import TestCase from sardana.macroserver.scan import ColumnDesc from sardana.macroserver.recorders.h5storage import NXscanH5_FileRecorder diff --git a/src/sardana/macroserver/scan/scandata.py b/src/sardana/macroserver/scan/scandata.py index 1f57b9e95a..c4057f7352 100644 --- a/src/sardana/macroserver/scan/scandata.py +++ b/src/sardana/macroserver/scan/scandata.py @@ -200,7 +200,7 @@ def __get_t3_name(self, item): raise KeyError(item) v = proxy.getNameValidator() - params = v.getParams(proxy.getFullName()) + params = v.getUriGroups(proxy.getFullName()) name = '{0}:{1}/{2}'.format(params['host'].split('.')[0], params['port'], params['devname']) diff --git a/src/sardana/macroserver/scan/test/test_gscan.py b/src/sardana/macroserver/scan/test/test_gscan.py index 621e80da62..8b5e8de2cb 100644 --- a/src/sardana/macroserver/scan/test/test_gscan.py +++ b/src/sardana/macroserver/scan/test/test_gscan.py @@ -25,7 +25,7 @@ import sys -from taurus.external import unittest +import unittest from taurus.test import insertTest diff --git a/src/sardana/macroserver/scan/test/test_recorddata.py b/src/sardana/macroserver/scan/test/test_recorddata.py index 7961c503dd..5b0683a87b 100644 --- a/src/sardana/macroserver/scan/test/test_recorddata.py +++ b/src/sardana/macroserver/scan/test/test_recorddata.py @@ -25,7 +25,7 @@ import math import os -from taurus.external import unittest +import unittest from taurus.test import insertTest from sardana.macroserver.scan.scandata import ScanData from sardana.macroserver.scan.recorder import DataHandler diff --git a/src/sardana/macroserver/test/test_msparameter.py b/src/sardana/macroserver/test/test_msparameter.py index e28d7241d6..3151081d39 100644 --- a/src/sardana/macroserver/test/test_msparameter.py +++ b/src/sardana/macroserver/test/test_msparameter.py @@ -1,4 +1,4 @@ -from taurus.external import unittest +import unittest from taurus.test import insertTest from sardana.macroserver.macro import Type diff --git a/src/sardana/macroserver/test/test_msrecordermanager.py b/src/sardana/macroserver/test/test_msrecordermanager.py index 329fabcc7d..f9c91e328f 100644 --- a/src/sardana/macroserver/test/test_msrecordermanager.py +++ b/src/sardana/macroserver/test/test_msrecordermanager.py @@ -25,7 +25,7 @@ import os -from taurus.external import unittest +import unittest from taurus.test import insertTest from sardana.macroserver.macroserver import MacroServer diff --git a/src/sardana/pool/poolcontrollers/test/base.py b/src/sardana/pool/poolcontrollers/test/base.py index c11a5175b1..0d7e6609bf 100644 --- a/src/sardana/pool/poolcontrollers/test/base.py +++ b/src/sardana/pool/poolcontrollers/test/base.py @@ -29,7 +29,7 @@ import threading import numpy -from taurus.external import unittest +import unittest from sardana import State from sardana.pool.poolcontrollers.DummyMotorController import Motion diff --git a/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py b/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py index bb54e75288..d9e0a680f2 100644 --- a/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py +++ b/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py @@ -1,5 +1,5 @@ from taurus.test.base import insertTest -from taurus.external import unittest +import unittest from sardana.pool.poolsynchronization import PoolSynchronization from sardana.pool.pooldefs import SynchDomain, SynchParam diff --git a/src/sardana/pool/test/test_acquisition.py b/src/sardana/pool/test/test_acquisition.py index 2a6ed1ca29..6d5d3dbda6 100644 --- a/src/sardana/pool/test/test_acquisition.py +++ b/src/sardana/pool/test/test_acquisition.py @@ -27,7 +27,7 @@ import numpy -from taurus.external.unittest import TestCase +from unittest import TestCase from taurus.test import insertTest from sardana.sardanautils import is_number, is_pure_str diff --git a/src/sardana/pool/test/test_ctacquisition.py b/src/sardana/pool/test/test_ctacquisition.py index 37e6344150..e946d19936 100644 --- a/src/sardana/pool/test/test_ctacquisition.py +++ b/src/sardana/pool/test/test_ctacquisition.py @@ -24,7 +24,7 @@ ############################################################################## import time -from taurus.external import unittest +import unittest from sardana.pool.poolmeasurementgroup import PoolMeasurementGroup from sardana.pool.test import (FakePool, createPoolController, createPoolMeasurementGroup, diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index e8c359ae32..7f288f2418 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -26,7 +26,7 @@ import threading import copy -from taurus.external import unittest +import unittest from taurus.test import insertTest from sardana.sardanathreadpool import get_thread_pool diff --git a/src/sardana/pool/test/test_poolcontroller.py b/src/sardana/pool/test/test_poolcontroller.py index fea20f15fb..01c83de2b4 100644 --- a/src/sardana/pool/test/test_poolcontroller.py +++ b/src/sardana/pool/test/test_poolcontroller.py @@ -23,7 +23,7 @@ ## ############################################################################## -from taurus.external import unittest +import unittest from sardana.pool.test import (FakePool, createPoolController, dummyPoolCTCtrlConf01) from sardana.pool.poolcontroller import PoolController diff --git a/src/sardana/pool/test/test_poolcontrollermanager.py b/src/sardana/pool/test/test_poolcontrollermanager.py index 2fee8cf8ec..228833a02a 100644 --- a/src/sardana/pool/test/test_poolcontrollermanager.py +++ b/src/sardana/pool/test/test_poolcontrollermanager.py @@ -23,7 +23,7 @@ ## ############################################################################## -from taurus.external import unittest +import unittest from sardana.pool.poolcontrollermanager import ControllerManager diff --git a/src/sardana/pool/test/test_poolcountertimer.py b/src/sardana/pool/test/test_poolcountertimer.py index dec8347bc3..29aa921d2e 100644 --- a/src/sardana/pool/test/test_poolcountertimer.py +++ b/src/sardana/pool/test/test_poolcountertimer.py @@ -25,7 +25,7 @@ import time -from taurus.external import unittest +import unittest from sardana.pool.poolcountertimer import PoolCounterTimer from sardana.pool.test import (FakePool, createPoolController, diff --git a/src/sardana/pool/test/test_poolmotion.py b/src/sardana/pool/test/test_poolmotion.py index 46dc95cf3e..b97c8f7a62 100644 --- a/src/sardana/pool/test/test_poolmotion.py +++ b/src/sardana/pool/test/test_poolmotion.py @@ -23,7 +23,7 @@ ## ############################################################################## -from taurus.external import unittest +import unittest from sardana.pool.poolmotion import PoolMotion from sardana.sardanadefs import State diff --git a/src/sardana/pool/test/test_poolpseudocounter.py b/src/sardana/pool/test/test_poolpseudocounter.py index bb16cf9264..d78e666668 100644 --- a/src/sardana/pool/test/test_poolpseudocounter.py +++ b/src/sardana/pool/test/test_poolpseudocounter.py @@ -23,7 +23,7 @@ ## ############################################################################## -from taurus.external.unittest import TestCase +from unittest import TestCase from sardana.pool.test.base import BasePoolTestCase diff --git a/src/sardana/pool/test/test_poolsynchronization.py b/src/sardana/pool/test/test_poolsynchronization.py index 4401796561..dd23ff9af5 100644 --- a/src/sardana/pool/test/test_poolsynchronization.py +++ b/src/sardana/pool/test/test_poolsynchronization.py @@ -24,7 +24,7 @@ ############################################################################## import threading -from taurus.external import unittest +import unittest from sardana.pool.poolsynchronization import PoolSynchronization from sardana.pool.poolacquisition import get_acq_ctrls diff --git a/src/sardana/pool/test/test_synchronization.py b/src/sardana/pool/test/test_synchronization.py index 061d19820e..df9d296fc4 100644 --- a/src/sardana/pool/test/test_synchronization.py +++ b/src/sardana/pool/test/test_synchronization.py @@ -31,7 +31,7 @@ import threading from taurus.test import insertTest -from taurus.external import unittest +import unittest from sardana.pool.poolsynchronization import PoolSynchronization from sardana.pool.poolacquisition import get_acq_ctrls diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py index 8714c976c0..b79a2a79ae 100644 --- a/src/sardana/sardanacustomsettings.py +++ b/src/sardana/sardanacustomsettings.py @@ -31,11 +31,13 @@ #: UnitTest door name: the door to be used by unit tests. #: UNITTEST_DOOR_NAME Must be defined for running sardana unittests. -UNITTEST_DOOR_NAME = "door/demo1/1" +# UNITTEST_DOOR_NAME = "door/demo1/1" +UNITTEST_DOOR_NAME = "door/test01/1" #: UnitTests Pool DS name: Pool DS to use in unit tests. UNITTEST_POOL_DS_NAME = "unittest1" #: UnitTests Pool Device name: Pool Device to use in unit tests. -UNITTEST_POOL_NAME = "pool/demo1/1" +# UNITTEST_POOL_NAME = "pool/demo1/1" +UNITTEST_POOL_NAME = "pool/test01/1" #: Size and number of rotating backups of the log files. #: The Pool and MacroServer Device servers will use these values for their diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index e9c36d2bd8..23f271573e 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -590,7 +590,7 @@ def _addElement(self, element_data): # TODO: when it becomes possible to do: # some taurus.Device. = # replace device_proxy with element - device_proxy = element.getObj().getHWObj() + device_proxy = element.getObj().getDeviceProxy() genutils.expose_variable(element.name, device_proxy) return element diff --git a/src/sardana/spock/test/test_parameter.py b/src/sardana/spock/test/test_parameter.py index 89783d635a..bc5e42a99e 100644 --- a/src/sardana/spock/test/test_parameter.py +++ b/src/sardana/spock/test/test_parameter.py @@ -25,7 +25,7 @@ """test_parameter module documentation""" -from taurus.external import unittest +import unittest from sardana.spock import parameter diff --git a/src/sardana/tango/pool/test/base.py b/src/sardana/tango/pool/test/base.py index b9c024ed6e..31c34466e0 100644 --- a/src/sardana/tango/pool/test/base.py +++ b/src/sardana/tango/pool/test/base.py @@ -29,7 +29,7 @@ 'ControllerCreationTestCase', 'ElementCreationTestCase'] import PyTango -from taurus.external import unittest +import unittest from taurus.core.tango.starter import ProcessStarter from sardana import sardanacustomsettings from sardana.tango.core.util import (get_free_server, get_free_device, diff --git a/src/sardana/tango/pool/test/test_Motor.py b/src/sardana/tango/pool/test/test_Motor.py index f0578923a4..52f1d8f683 100644 --- a/src/sardana/tango/pool/test/test_Motor.py +++ b/src/sardana/tango/pool/test/test_Motor.py @@ -25,7 +25,7 @@ """Tests Read Position from Sardana using PyTango""" import PyTango -from taurus.external import unittest +import unittest from sardana.tango.pool.test import BasePoolTestCase from sardana.tango.core.util import get_free_alias import numbers diff --git a/src/sardana/tango/pool/test/test_measurementgroup.py b/src/sardana/tango/pool/test/test_measurementgroup.py index ed6d7be083..9ca57e7232 100644 --- a/src/sardana/tango/pool/test/test_measurementgroup.py +++ b/src/sardana/tango/pool/test/test_measurementgroup.py @@ -32,7 +32,7 @@ # TODO: decide what to use: taurus or PyTango import PyTango from PyTango import DeviceProxy -from taurus.external import unittest +import unittest from taurus.test import insertTest from taurus.core.util import CodecFactory diff --git a/src/sardana/tango/pool/test/test_persistence.py b/src/sardana/tango/pool/test/test_persistence.py index 81bbbffadf..6b7d6d2004 100644 --- a/src/sardana/tango/pool/test/test_persistence.py +++ b/src/sardana/tango/pool/test/test_persistence.py @@ -24,7 +24,7 @@ ############################################################################## import PyTango -from taurus.external import unittest +import unittest from taurus.test import insertTest from sardana.tango.pool.test import BasePoolTestCase from sardana.tango.core.util import get_free_alias diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index d70b626b24..9805072b3d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -301,7 +301,7 @@ def __init__(self, name, **kwargs): self.getStateEG() def _find_pool_obj(self): - pool = get_pool_for_device(self.getParentObj(), self.getHWObj()) + pool = get_pool_for_device(self.getParentObj(), self.getDeviceProxy()) return pool def _find_pool_data(self): @@ -1439,7 +1439,7 @@ def _build(self): for channel_name, channel_data in list(self.channels.items()): cache[channel_name] = None data_source = channel_data['source'] - params = tg_attr_validator.getParams(data_source) + params = tg_attr_validator.getUriGroups(data_source) if params is None: # Handle NON tango channel n_tg_chs[channel_name] = channel_data diff --git a/src/sardana/taurus/core/tango/sardana/test/test_macro.py b/src/sardana/taurus/core/tango/sardana/test/test_macro.py index 99e9c685f8..21362cdf74 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_macro.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_macro.py @@ -27,7 +27,7 @@ from lxml import etree -from taurus.external import unittest +import unittest from taurus.test import insertTest from sardana.taurus.core.tango.sardana.macro import MacroNode from sardana.taurus.core.tango.sardana.macro import createMacroNode diff --git a/src/sardana/taurus/core/tango/sardana/test/test_pool.py b/src/sardana/taurus/core/tango/sardana/test/test_pool.py index a85f5d0543..063fb87d17 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_pool.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_pool.py @@ -28,7 +28,7 @@ import numpy from taurus import Device -from taurus.external.unittest import TestCase +from unittest import TestCase from taurus.test.base import insertTest from sardana.sardanautils import is_number, is_non_str_seq, is_pure_str from sardana.taurus.core.tango.sardana.pool import registerExtensions diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py index be0c7ef55c..04e8e5c5e6 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py @@ -42,14 +42,12 @@ from taurus.qt.qtcore.communication import SharedDataManager from taurus.qt.qtgui.input import TaurusValueLineEdit - -import taurus.core.util.argparse +import argparse import taurus.qt.qtgui.application from taurus.qt.qtgui.util.ui import UILoadable from sardana.taurus.qt.qtgui.extra_macroexecutor import TaurusMacroConfigurationDialog - from .selectsignal import SelectSignal @@ -277,7 +275,7 @@ def exec_scan(self, imot): macro_command.append(str(self.selectsignal._ui.SignallineEdit.text())) self.door_device.RunMacro(macro_command) - while(self.door_device.State()) == PyTango.DevState.RUNNING: + while (self.door_device.State()) == PyTango.DevState.RUNNING: time.sleep(0.01) # TODO: the string parsing should be eliminated and the sardana # generic "goto_peak" feature should be used instead - when available @@ -353,12 +351,9 @@ def open_selectsignal_panel(self): def main(): + parser = argparse.ArgumentParser(usage="%prog [door_name]", description="a taurus application for " + "diffractometer alignment: h, k, l movements and scans, go to maximum, ...") - parser = taurus.core.util.argparse.get_taurus_parser() - parser.usage = "%prog [door_name]" - desc = ("a taurus application for diffractometer alignment: h, k, l " + - "movements and scans, go to maximum, ...") - parser.set_description(desc) app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser, app_version=sardana.Release.version) diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py index d07f8b2f6c..4b98d25186 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py @@ -41,7 +41,7 @@ from .displayscanangles import DisplayScanAngles -import taurus.core.util.argparse +import argparse import taurus.qt.qtgui.application from taurus.qt.qtgui.util.ui import UILoadable @@ -357,9 +357,8 @@ def onDoorChanged(self, doorName): def main(): - parser = taurus.core.util.argparse.get_taurus_parser() - parser.usage = "%prog [door_name]" - parser.set_description("a taurus application for performing hkl scans") + parser = argparse.ArgumentParser(description="a taurus application for performing hkl scans", + usage="%prog [door_name]") app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser, app_version=sardana.Release.version) diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py index daaf9306eb..e73ac8e55c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py @@ -40,7 +40,7 @@ from taurus.qt.qtgui.input import TaurusValueLineEdit import taurus.core -import taurus.core.util.argparse +import argparse import taurus.qt.qtgui.application from taurus.qt.qtgui.util.ui import UILoadable @@ -464,10 +464,8 @@ def affine(self): def main(): - parser = taurus.core.util.argparse.get_taurus_parser() - parser.usage = "%prog " - parser.set_description( - "a taurus application for setting diffractometer parameters: ubmatrix, lattice, reflections, ...") + parser = argparse.ArgumentParser(description="a taurus application for setting diffractometer parameters: ubmatrix," + + " lattice, reflections, ...", usage="%prog ") app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser, app_version=sardana.Release.version) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py index 68fe626764..d6e652a8c7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py @@ -85,6 +85,8 @@ def toXmlString(self): return self.list.toXmlString() def fromXmlString(self, xmlString): + # pass + #TODO aalonso self.list.fromXmlString(xmlString) favouritesList = self.list.model().list macroServerObj = self.getModelObj() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py index 6300fdaf38..0661226573 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py @@ -93,6 +93,8 @@ def toXmlString(self): return self.list.toXmlString() def fromXmlString(self, xmlString): + # pass + #TODO aalonso self.list.fromXmlString(xmlString) historyList = self.list.model().list macroServerObj = self.getModelObj() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 9542fbdbc9..9e37f56cd3 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -371,14 +371,13 @@ def abort(self): if __name__ == '__main__': import sys from taurus.qt.qtgui.application import TaurusApplication - from taurus.core.util.argparse import get_taurus_parser + from argparse import ArgumentParser from sardana.taurus.qt.qtcore.tango.sardana.macroserver import \ registerExtensions registerExtensions() - parser = get_taurus_parser() - parser.set_usage("python macrobutton.py [door_name] [macro_name]") - parser.set_description("Macro button for macro execution") + parser = ArgumentParser(usage="python macrobutton.py [door_name] [macro_name]", + description="Macro button for macro execution") app = TaurusApplication(app_name="macrobutton", app_version=taurus.Release.version) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 97becda18d..8004d97beb 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -45,7 +45,7 @@ MacroExecutionWindow, MacroComboBox, standardPlotablesFilter from sardana.taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor \ import ParamEditorManager, StandardMacroParametersEditor -from sardana.taurus.qt.qtgui.extra_macroexecutor.\ +from sardana.taurus.qt.qtgui.extra_macroexecutor. \ macroparameterseditor.delegate import ParamEditorDelegate from sardana.taurus.core.tango.sardana.macro import MacroRunException, \ MacroNode @@ -986,19 +986,21 @@ def createSequencer(args, options): def main(): - from taurus.core.util import argparse + import argparse from taurus.qt.qtgui.application import TaurusApplication - parser = argparse.get_taurus_parser() - parser.set_usage("%prog [options]") - parser.set_description("Sardana macro sequencer.\n" - "It allows the creation of sequences of " - "macros, executed one after the other.\n" - "The sequences can be stored under xml files") - parser.add_option("-f", "--file", - dest="file", default=None, - help="load macro sequence from a file(XML or spock " - "syntax)") + parser = argparse.ArgumentParser(usage="%prog [options]", + description="Sardana macro sequencer.\n" + "It allows the creation of sequences of " + "macros, executed one after the other.\n" + "The sequences can be stored under xml files") + parser.add_argument("-f", "--file", dest="file", default=None, help="load macro sequence from a file(XML or spock " + "syntax)") + # TODO aalonso + # parser.add_option("-f", "--file", + # dest="file", default=None, + # help="load macro sequence from a file(XML or spock " + # "syntax)") app = TaurusApplication(cmd_line_parser=parser, app_name="sequencer", diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index acacb9f234..482bc7a786 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -159,7 +159,7 @@ def __init__(self, parent=None, designMode=False): def getMotorControllerType(self): modelObj = self.getModelObj() modelNormalName = modelObj.getNormalName() - poolDsId = modelObj.getHWObj().info().server_id + poolDsId = modelObj.getDeviceProxy().info().server_id db = taurus.Database() pool_devices = tuple(db.get_device_class_list(poolDsId).value_string) pool_dev_name = pool_devices[pool_devices.index('Pool') - 1] @@ -1685,10 +1685,11 @@ def main(): import sys import taurus.qt.qtgui.application - import taurus.core.util.argparse + import argparse from taurus.qt.qtgui.panel import TaurusForm + import taurus.core.util.argparse parser = taurus.core.util.argparse.get_taurus_parser() - parser.usage = "%prog [options] [ [] ...]" + parser = argparse.ArgumentParser(usage="%prog [options] [ [] ...]") app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser) args = app.get_command_line_args() diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py index 336b4235e1..ec0ab46f70 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py @@ -93,8 +93,8 @@ def main(): owns_app = app is None if owns_app: - import taurus.core.util.argparse - parser = taurus.core.util.argparse.get_taurus_parser() + import argparse + parser = argparse.ArgumentParser() app = Application(sys.argv, cmd_line_parser=parser, app_name="Taurus command line demo", app_version="1.0", org_domain="Taurus", org_name="Tango community") diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index e40aea7d04..7b88def008 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -28,7 +28,6 @@ __all__ = ["ExpDescriptionEditor"] - import json from taurus.external.qt import Qt, QtCore, QtGui, compat import copy @@ -40,8 +39,9 @@ from sardana.sardanadefs import ElementType, TYPE_ACQUIRABLE_ELEMENTS from taurus.qt.qtgui.util.ui import UILoadable + # Using a plain model and filtering and checking 'Acquirable' in item.itemData().interfaces is more elegant, but things don't get properly sorted... -#from taurus.qt.qtcore.tango.sardana.model import SardanaElementPlainModel +# from taurus.qt.qtcore.tango.sardana.model import SardanaElementPlainModel def _to_fqdn(name, logger=None): @@ -186,7 +186,7 @@ def __init__(self, parent=None, door=None, plotsButton=True, newperspectivesDict = copy.deepcopy( self.ui.sardanaElementTree.KnownPerspectives) - #newperspectivesDict[self.ui.sardanaElementTree.DftPerspective]['model'] = [SardanaAcquirableProxyModel, SardanaElementPlainModel] + # newperspectivesDict[self.ui.sardanaElementTree.DftPerspective]['model'] = [SardanaAcquirableProxyModel, SardanaElementPlainModel] newperspectivesDict[self.ui.sardanaElementTree.DftPerspective][ 'model'][0] = SardanaAcquirableProxyModel # assign a copy because if just a key of this class memberwas modified, @@ -467,7 +467,6 @@ def _reloadConf(self, force=False): self.ui.channelEditor.getQModel().setAvailableTriggers(avail_triggers) self.experimentConfigurationChanged.emit(copy.deepcopy(conf)) - def _setDirty(self, dirty): self._dirty = dirty self._updateButtonBox() @@ -714,7 +713,7 @@ def onPlotsButtonToggled(self, checked): def demo(model=None, autoUpdate=False): """Experiment configuration""" - #w = main_ChannelEditor() + # w = main_ChannelEditor() w = ExpDescriptionEditor(autoUpdate=autoUpdate) if model is None: from sardana.taurus.qt.qtgui.extra_macroexecutor import \ @@ -736,12 +735,17 @@ def main(): app = Application.instance() owns_app = app is None if owns_app: - import taurus.core.util.argparse - parser = taurus.core.util.argparse.get_taurus_parser() - parser.usage = "%prog [options] " - parser.add_option('--auto-update', dest='auto_update', - action='store_true', - help='Set auto update of experiment configuration') + import argparse + parser = argparse.ArgumentParser(usage="%prog [options] ") + parser.add_argument('--auto-update', dest='auto_update', + action='store_true', + help='Set auto update of experiment configuration') + + # TODO aalonso + # parser.add_option('--auto-update', dest='auto_update', + # action='store_true', + # help='Set auto update of experiment configuration') + app = Application(app_name="Exp. Description demo", app_version="1.0", org_domain="Sardana", org_name="Tango community", cmd_line_parser=parser) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py index 5f9d402ed7..801dad53f4 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py @@ -564,9 +564,8 @@ def main(): owns_app = app is None if owns_app: - import taurus.core.util.argparse - parser = taurus.core.util.argparse.get_taurus_parser() - parser.usage = "%prog [options] " + import argparse + parser = argparse.ArgumentParser(usage="%prog [options] ") app = Application(sys.argv, cmd_line_parser=parser, app_name="Macro editor demo", app_version="1.0", org_domain="Sardana", org_name="Tango community") diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index 3a39c1e606..5ea9a466e5 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -88,10 +88,9 @@ def main(): from taurus.qt.qtgui.application import TaurusApplication import sys - from taurus.core.util.argparse import get_taurus_parser + from argparse import ArgumentParser - parser = get_taurus_parser() - parser.set_usage("python showscanonline.py [door_name]") + parser = ArgumentParser(usage="python showscanonline.py [door_name]") app = TaurusApplication(app_name='Showscan Online', org_domain="Sardana", org_name="Tango communinity", cmd_line_parser=parser) diff --git a/src/sardana/test/test_sardanabuffer.py b/src/sardana/test/test_sardanabuffer.py index 60e5a16991..1a645b93e1 100644 --- a/src/sardana/test/test_sardanabuffer.py +++ b/src/sardana/test/test_sardanabuffer.py @@ -23,7 +23,7 @@ ## ############################################################################## -from taurus.external.unittest import TestCase +from unittest import TestCase from sardana.sardanabuffer import SardanaBuffer diff --git a/src/sardana/test/test_sardanavalue.py b/src/sardana/test/test_sardanavalue.py index 9ffc3a009d..eefcfa0ba9 100644 --- a/src/sardana/test/test_sardanavalue.py +++ b/src/sardana/test/test_sardanavalue.py @@ -25,7 +25,7 @@ """Unit tests for sardanavalue module""" -from taurus.external import unittest +import unittest from sardana.sardanavalue import SardanaValue diff --git a/src/sardana/test/testsuite.py b/src/sardana/test/testsuite.py index c230b82aef..1924164076 100644 --- a/src/sardana/test/testsuite.py +++ b/src/sardana/test/testsuite.py @@ -36,7 +36,7 @@ import os import re -from taurus.external import unittest +import unittest import sardana @@ -90,7 +90,7 @@ def run(exclude_pattern='(?!)'): def main(): import sys - from taurus.external import argparse + import argparse from sardana import Release parser = argparse.ArgumentParser(description='Main test suite for Sardana') diff --git a/src/sardana/util/test/test_funcgenerator.py b/src/sardana/util/test/test_funcgenerator.py index a221b0444a..d816b8e76c 100644 --- a/src/sardana/util/test/test_funcgenerator.py +++ b/src/sardana/util/test/test_funcgenerator.py @@ -27,7 +27,7 @@ import numpy from threading import Event, Timer -from taurus.external.unittest import TestCase +from unittest import TestCase from taurus.core.util import ThreadPool from sardana.pool.pooldefs import SynchDomain, SynchParam diff --git a/src/sardana/util/test/test_parser.py b/src/sardana/util/test/test_parser.py index 2fe88d9358..cb889fa02c 100644 --- a/src/sardana/util/test/test_parser.py +++ b/src/sardana/util/test/test_parser.py @@ -25,7 +25,7 @@ """Tests for parser utilities.""" -from taurus.external import unittest +import unittest from taurus.test import insertTest from sardana.util.parser import ParamParser diff --git a/src/sardana/util/test/test_thread.py b/src/sardana/util/test/test_thread.py index 61e45b3721..7099754675 100644 --- a/src/sardana/util/test/test_thread.py +++ b/src/sardana/util/test/test_thread.py @@ -26,7 +26,7 @@ from sardana.sardanathreadpool import get_thread_pool from sardana.util.thread import CountLatch -from taurus.external.unittest import TestCase +from unittest import TestCase def job(i, duration): From 6abcdd594a704f524162032888cfd45e5eba8915 Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Thu, 9 Jan 2020 17:33:08 +0100 Subject: [PATCH 352/830] Allow MacroButton widget to be smaller The minimum size the MacroButton calculated by default by Qt) is too large in some cases. Set the minimum size of the MacroButton widget to a smaller value (25x15 pixels for the button, plus whatever it is for the frame and the bar) --- .../extra_macroexecutor/ui/MacroButton.ui | 75 +++++++++++-------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/ui/MacroButton.ui b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/ui/MacroButton.ui index 600d533ceb..8bab1901d9 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/ui/MacroButton.ui +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/ui/MacroButton.ui @@ -1,7 +1,8 @@ - + + MacroButton - - + + 0 0 @@ -9,76 +10,88 @@ 79 - + Form - - + + 0 - + 0 - - - + + + QFrame::Box - + QFrame::Plain - + 3 - + 3 - - + + 0 - + 0 - - - - + + + + 0 0 - + + + 25 + 15 + + + PushButton - + true - + false - - - - + + + + 0 0 - + + + 1 + 0 + + + 16777215 10 - + 6 - + 24 @@ -93,9 +106,9 @@ TaurusWidget QWidget
taurus.qt.qtgui.container
+ 1
- From aa2056d62a07b0705de74acd4c98e0c39ea0d8ad Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 11 Jan 2020 12:51:17 +0100 Subject: [PATCH 353/830] Fix meas grp configuration URIs back compat MeasurementGroup's configurations created with Taurus3 were supposed to be backwards compatible when migrating to Taurus 4. Now this is broken (most probably after accepting SEP18). Fix it by converting timer and monitor configurations to use full URIS (scheme + FQDN in authority). --- src/sardana/pool/poolmeasurementgroup.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 46721acb2c..19f8e8e75b 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -716,8 +716,14 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): acq_synch = AcqSynch.from_synch_type(is_software, synchronization) ctrl_acq_synch[ctrl] = acq_synch - ctrl_conf["timer"] = ctrl_data.get("timer") - ctrl_conf["monitor"] = ctrl_data.get("monitor") + timer = ctrl_data.get("timer") + if timer is not None and to_fqdn: + timer = _to_fqdn(timer, self._parent) + ctrl_conf["timer"] = timer + monitor = ctrl_data.get("monitor") + if monitor is not None and to_fqdn: + monitor = _to_fqdn(monitor, self._parent) + ctrl_conf["monitor"] = monitor ctrl_item = TimerableControllerConfiguration(ctrl, ctrl_conf) else: ctrl_item = ControllerConfiguration(ctrl, ctrl_conf) @@ -762,11 +768,17 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): msg_error = '' if ctrl_item.timer is None: timer_name = ctrl_data['timer'] + if to_fqdn: + timer_name = _to_fqdn(timer_name, + logger=self._parent) ch_timer = pool.get_element_by_full_name(timer_name) msg_error += 'Channel {0} is not present but used as ' \ 'timer. '.format(ch_timer.name) if ctrl_item.monitor is None: monitor_name = ctrl_data['monitor'] + if to_fqdn: + monitor_name = _to_fqdn(monitor_name, + logger=self._parent) ch_monitor = pool.get_element_by_full_name(monitor_name) msg_error += 'Channel {0} is not present but used as ' \ 'monitor.'.format(ch_monitor.name) From 4c8b6cc49458adf0bdcdf72a1b4ea7d20bf978e0 Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 13 Jan 2020 12:57:07 +0100 Subject: [PATCH 354/830] Solve, rvalue/value deprecated usages, no previous preparation usages and some other deprecated functions --- src/sardana/macroserver/macros/standard.py | 16 ++++++++-------- src/sardana/pool/test/test_ctacquisition.py | 1 + src/sardana/pool/test/test_measurementgroup.py | 7 +++++-- src/sardana/tango/macroserver/MacroServer.py | 2 +- src/sardana/tango/pool/CTExpChannel.py | 1 + src/sardana/tango/pool/IORegister.py | 1 + src/sardana/tango/pool/MeasurementGroup.py | 2 ++ src/sardana/tango/pool/OneDExpChannel.py | 1 + src/sardana/tango/pool/TwoDExpChannel.py | 1 + src/sardana/tango/pool/ZeroDExpChannel.py | 1 + src/sardana/taurus/core/tango/sardana/pool.py | 8 ++++---- .../sequenceeditor/sequenceeditor.py | 5 ----- .../qt/qtgui/extra_sardana/expdescription.py | 5 ----- 13 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 1c3b506152..9cde33f391 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -309,7 +309,7 @@ def run(self, motor, pos): name = motor.getName() old_pos = motor.getPosition(force=True) offset_attr = motor.getAttribute('Offset') - old_offset = offset_attr.read().value + old_offset = offset_attr.read().rvalue new_offset = pos - (old_pos - old_offset) offset_attr.write(new_offset) msg = "%s reset from %.4f (offset %.4f) to %.4f (offset %.4f)" % ( @@ -368,8 +368,8 @@ def run(self, motor_list): except: val1 = str_fmt % motor.getPosition(force=True) - val2 = str_fmt % posObj.getMaxValue() - val3 = str_fmt % posObj.getMinValue() + val2 = str_fmt % posObj.getMaxRange() + val3 = str_fmt % posObj.getMinRange() if show_ctrlaxis: valctrl = str_fmt % (ctrl_name) @@ -387,8 +387,8 @@ def run(self, motor_list): val1 = str_fmt % motor.getDialPosition(force=True) dPosObj = motor.getDialPositionObj() - val2 = str_fmt % dPosObj.getMaxValue() - val3 = str_fmt % dPosObj.getMinValue() + val2 = str_fmt % dPosObj.getMaxRange() + val3 = str_fmt % dPosObj.getMinRange() dpos = list(map(str, [val2, val1, val3])) pos_data += [''] + dpos @@ -431,8 +431,8 @@ def run(self, motor_list): name = motor.getName() motor_names.append([name]) posObj = motor.getPositionObj() - upos = list(map(str, [posObj.getMaxValue(), motor.getPosition( - force=True), posObj.getMinValue()])) + upos = list(map(str, [posObj.getMaxRange(), motor.getPosition( + force=True), posObj.getMinRange()])) pos_data = [''] + upos motor_pos.append(pos_data) @@ -491,7 +491,7 @@ class mstate(Macro): param_def = [['motor', Type.Moveable, None, 'Motor to check state']] def run(self, motor): - self.info("Motor %s" % str(motor.getState())) + self.info("Motor %s" % str(motor.stateObj.read())) class umv(Macro): diff --git a/src/sardana/pool/test/test_ctacquisition.py b/src/sardana/pool/test/test_ctacquisition.py index e946d19936..6e58e7f135 100644 --- a/src/sardana/pool/test/test_ctacquisition.py +++ b/src/sardana/pool/test/test_ctacquisition.py @@ -73,6 +73,7 @@ def test_acquisition(self): msg = 'Pool Measurement Group does not acquire' integ_time = .1 self.pmg.integration_time = integ_time + self.pmg.prepare() self.pmg.start_acquisition() acq = self.pmg.acquisition diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index 7f288f2418..9f72257cd8 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -55,7 +55,7 @@ def prepare_meas(self, config): conf["user_elements"] = channel_ids self.pmg = createPoolMeasurementGroup(pool, conf) pool.add_element(self.pmg) - self.pmg.set_configuration_from_user(mg_conf, to_fqdn=False) + self.pmg.set_configuration_from_user(mg_conf, to_fqdn=True) return channel_names def prepare_attribute_listener(self): @@ -74,6 +74,7 @@ def remove_attribute_listener(self): def acquire(self): """ Run acquisition """ + self.pmg.prepare() self.pmg.start_acquisition() acq = self.pmg.acquisition while acq.is_running(): @@ -150,7 +151,7 @@ def consecutive_acquisitions(self, pool, config, synchronization): pool, config) # setting mg configuration - this cleans the action cache! - self.pmg.set_configuration_from_user(mg_conf, to_fqdn=False) + self.pmg.set_configuration_from_user(mg_conf, to_fqdn=True) repetitions = 0 for group in synchronization: repetitions += group[SynchParam.Repeats] @@ -198,6 +199,7 @@ def meas_cont_stop_acquisition(self, config, synchronization, self.pmg.set_moveable(moveable, to_fqdn=False) self.prepare_attribute_listener() + self.pmg.prepare() self.pmg.start_acquisition() # retrieving the acquisition since it was cleaned when applying mg conf acq = self.pmg.acquisition @@ -237,6 +239,7 @@ def meas_contpos_acquisition(self, config, synchronization, moveable, for group in synchronization: repetitions += group[SynchParam.Repeats] self.prepare_attribute_listener() + self.pmg.prepare() self.pmg.start_acquisition() mot.set_position(position) acq = self.pmg.acquisition diff --git a/src/sardana/tango/macroserver/MacroServer.py b/src/sardana/tango/macroserver/MacroServer.py index 23363c3a2c..b21c0f14c6 100644 --- a/src/sardana/tango/macroserver/MacroServer.py +++ b/src/sardana/tango/macroserver/MacroServer.py @@ -153,7 +153,7 @@ def on_macro_server_changed(self, evt_src, evt_type, evt_value): # force the element list cache to be rebuild next time someone reads # the element list self.ElementsCache = None - self.set_attribute(elems_attr, value=evt_value.value) + self.set_attribute(elems_attr, value=evt_value.rvalue) #self.push_change_event('Elements', *evt_value.value) elif evt_name in ("elementcreated", "elementdeleted"): # force the element list cache to be rebuild next time someone reads diff --git a/src/sardana/tango/pool/CTExpChannel.py b/src/sardana/tango/pool/CTExpChannel.py index 8c193abc58..e6d9609c81 100644 --- a/src/sardana/tango/pool/CTExpChannel.py +++ b/src/sardana/tango/pool/CTExpChannel.py @@ -214,6 +214,7 @@ def is_Value_allowed(self, req_type): return True def Start(self): + self.ct.prepare() self.ct.start_acquisition() diff --git a/src/sardana/tango/pool/IORegister.py b/src/sardana/tango/pool/IORegister.py index b6f4bd2378..5f239a1322 100644 --- a/src/sardana/tango/pool/IORegister.py +++ b/src/sardana/tango/pool/IORegister.py @@ -234,6 +234,7 @@ def is_Value_allowed(self, req_type): return True def Start(self): + self.ior.prepare() self.ior.start_acquisition() diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 3a9e4d219e..16f40fb8ec 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -298,6 +298,7 @@ def Start(self): self.wait_for_operation() except: raise Exception("Cannot acquire: already involved in an operation") + self.measurement_group.prepare() self.measurement_group.start_acquisition() def Stop(self): @@ -308,6 +309,7 @@ def StartMultiple(self, n): self.wait_for_operation() except: raise Exception("Cannot acquire: already involved in an operation") + self.measurement_group.prepare(n) self.measurement_group.start_acquisition(multiple=n) diff --git a/src/sardana/tango/pool/OneDExpChannel.py b/src/sardana/tango/pool/OneDExpChannel.py index db3ffc917c..a8fe1aba48 100644 --- a/src/sardana/tango/pool/OneDExpChannel.py +++ b/src/sardana/tango/pool/OneDExpChannel.py @@ -223,6 +223,7 @@ def read_DataSource(self, attr): attr.set_value(data_source) def Start(self): + self.oned.prepare() self.oned.start_acquisition() diff --git a/src/sardana/tango/pool/TwoDExpChannel.py b/src/sardana/tango/pool/TwoDExpChannel.py index 2ae70a102d..aaae989fdd 100644 --- a/src/sardana/tango/pool/TwoDExpChannel.py +++ b/src/sardana/tango/pool/TwoDExpChannel.py @@ -275,6 +275,7 @@ def read_DataSource(self, attr): attr.set_value(data_source) def Start(self): + self.twod.prepare() self.twod.start_acquisition() diff --git a/src/sardana/tango/pool/ZeroDExpChannel.py b/src/sardana/tango/pool/ZeroDExpChannel.py index cd9bcb781f..4dc159e174 100644 --- a/src/sardana/tango/pool/ZeroDExpChannel.py +++ b/src/sardana/tango/pool/ZeroDExpChannel.py @@ -194,6 +194,7 @@ def read_CurrentValue(self, attr): priority=0, timestamp=value.timestamp) def Start(self): + self.zerod.prepare() self.zerod.start_acquisition() def read_AccumulationBuffer(self, attr): diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 9805072b3d..aa38291ae3 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -226,12 +226,12 @@ def eventReceived(self, evt_src, evt_type, evt_value): if evt_value is None: v = None else: - v = evt_value.value + v = evt_value.rvalue EventGenerator.fireEvent(self, v) def read(self, force=False): try: - self.last_val = self._attr.read(cache=not force).value + self.last_val = self._attr.read(cache=not force).rvalue except: self.error("Read error") self.debug("Details:", exc_info=1) @@ -549,7 +549,7 @@ def _information(self, tab=' '): indent = "\n" + tab + 10 * ' ' msg = [self.getName() + ":"] try: - state_value = self.stateObj.read().rvalue + state_value = self.stateObj.read().value # state_value is DevState enumeration (IntEnum) state = state_value.name.capitalize() except DevFailed as df: @@ -1727,7 +1727,7 @@ 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) + self._setConfiguration(evt_value.rvalue) def getTimerName(self): return self.getTimer()['name'] diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 8004d97beb..0bf295aae1 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -996,11 +996,6 @@ def main(): "The sequences can be stored under xml files") parser.add_argument("-f", "--file", dest="file", default=None, help="load macro sequence from a file(XML or spock " "syntax)") - # TODO aalonso - # parser.add_option("-f", "--file", - # dest="file", default=None, - # help="load macro sequence from a file(XML or spock " - # "syntax)") app = TaurusApplication(cmd_line_parser=parser, app_name="sequencer", diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index 7b88def008..d52e60cd17 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -741,11 +741,6 @@ def main(): action='store_true', help='Set auto update of experiment configuration') - # TODO aalonso - # parser.add_option('--auto-update', dest='auto_update', - # action='store_true', - # help='Set auto update of experiment configuration') - app = Application(app_name="Exp. Description demo", app_version="1.0", org_domain="Sardana", org_name="Tango community", cmd_line_parser=parser) From d5d5b3194f14ff4e5fcf4a3a72f15f1def60287e Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 13 Jan 2020 13:02:01 +0100 Subject: [PATCH 355/830] restore defult settings --- src/sardana/sardanacustomsettings.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py index b79a2a79ae..8714c976c0 100644 --- a/src/sardana/sardanacustomsettings.py +++ b/src/sardana/sardanacustomsettings.py @@ -31,13 +31,11 @@ #: UnitTest door name: the door to be used by unit tests. #: UNITTEST_DOOR_NAME Must be defined for running sardana unittests. -# UNITTEST_DOOR_NAME = "door/demo1/1" -UNITTEST_DOOR_NAME = "door/test01/1" +UNITTEST_DOOR_NAME = "door/demo1/1" #: UnitTests Pool DS name: Pool DS to use in unit tests. UNITTEST_POOL_DS_NAME = "unittest1" #: UnitTests Pool Device name: Pool Device to use in unit tests. -# UNITTEST_POOL_NAME = "pool/demo1/1" -UNITTEST_POOL_NAME = "pool/test01/1" +UNITTEST_POOL_NAME = "pool/demo1/1" #: Size and number of rotating backups of the log files. #: The Pool and MacroServer Device servers will use these values for their From 2c516c1c0e2032e5bdbdea086b273b5651fa8e75 Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 13 Jan 2020 14:42:06 +0100 Subject: [PATCH 356/830] fix some pep8 --- .../taurus/qt/qtgui/extra_hkl/diffractometeralignment.py | 2 +- .../favouriteseditor/favouriteseditor.py | 2 -- .../extra_macroexecutor/favouriteseditor/historyviewer.py | 2 -- .../extra_macroexecutor/sequenceeditor/sequenceeditor.py | 2 +- .../taurus/qt/qtgui/extra_sardana/expdescription.py | 7 ++++--- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py index 04e8e5c5e6..fe774df6cb 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py @@ -275,7 +275,7 @@ def exec_scan(self, imot): macro_command.append(str(self.selectsignal._ui.SignallineEdit.text())) self.door_device.RunMacro(macro_command) - while (self.door_device.State()) == PyTango.DevState.RUNNING: + while(self.door_device.State()) == PyTango.DevState.RUNNING: time.sleep(0.01) # TODO: the string parsing should be eliminated and the sardana # generic "goto_peak" feature should be used instead - when available diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py index d6e652a8c7..68fe626764 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py @@ -85,8 +85,6 @@ def toXmlString(self): return self.list.toXmlString() def fromXmlString(self, xmlString): - # pass - #TODO aalonso self.list.fromXmlString(xmlString) favouritesList = self.list.model().list macroServerObj = self.getModelObj() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py index 0661226573..6300fdaf38 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py @@ -93,8 +93,6 @@ def toXmlString(self): return self.list.toXmlString() def fromXmlString(self, xmlString): - # pass - #TODO aalonso self.list.fromXmlString(xmlString) historyList = self.list.model().list macroServerObj = self.getModelObj() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 0bf295aae1..eddf1930d7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -45,7 +45,7 @@ MacroExecutionWindow, MacroComboBox, standardPlotablesFilter from sardana.taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor \ import ParamEditorManager, StandardMacroParametersEditor -from sardana.taurus.qt.qtgui.extra_macroexecutor. \ +from sardana.taurus.qt.qtgui.extra_macroexecutor.\ macroparameterseditor.delegate import ParamEditorDelegate from sardana.taurus.core.tango.sardana.macro import MacroRunException, \ MacroNode diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index d52e60cd17..e6877d879c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -28,6 +28,7 @@ __all__ = ["ExpDescriptionEditor"] + import json from taurus.external.qt import Qt, QtCore, QtGui, compat import copy @@ -41,7 +42,7 @@ # Using a plain model and filtering and checking 'Acquirable' in item.itemData().interfaces is more elegant, but things don't get properly sorted... -# from taurus.qt.qtcore.tango.sardana.model import SardanaElementPlainModel +#from taurus.qt.qtcore.tango.sardana.model import SardanaElementPlainModel def _to_fqdn(name, logger=None): @@ -186,7 +187,7 @@ def __init__(self, parent=None, door=None, plotsButton=True, newperspectivesDict = copy.deepcopy( self.ui.sardanaElementTree.KnownPerspectives) - # newperspectivesDict[self.ui.sardanaElementTree.DftPerspective]['model'] = [SardanaAcquirableProxyModel, SardanaElementPlainModel] + #newperspectivesDict[self.ui.sardanaElementTree.DftPerspective]['model'] = [SardanaAcquirableProxyModel, SardanaElementPlainModel] newperspectivesDict[self.ui.sardanaElementTree.DftPerspective][ 'model'][0] = SardanaAcquirableProxyModel # assign a copy because if just a key of this class memberwas modified, @@ -713,7 +714,7 @@ def onPlotsButtonToggled(self, checked): def demo(model=None, autoUpdate=False): """Experiment configuration""" - # w = main_ChannelEditor() + #w = main_ChannelEditor() w = ExpDescriptionEditor(autoUpdate=autoUpdate) if model is None: from sardana.taurus.qt.qtgui.extra_macroexecutor import \ From 1a5ca91d05e70e6a7aab9aa324469af802fb0029 Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 13 Jan 2020 14:48:21 +0100 Subject: [PATCH 357/830] fix some more pep8 --- src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 3 +-- test/HTMLTestRunner.py | 1 - test/poolunittest.py | 5 ++--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index e6877d879c..7bee7a3042 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -40,8 +40,7 @@ from sardana.sardanadefs import ElementType, TYPE_ACQUIRABLE_ELEMENTS from taurus.qt.qtgui.util.ui import UILoadable - -# Using a plain model and filtering and checking 'Acquirable' in item.itemData().interfaces is more elegant, but things don't get properly sorted... +#Using a plain model and filtering and checking 'Acquirable' in item.itemData().interfaces is more elegant, but things don't get properly sorted... #from taurus.qt.qtcore.tango.sardana.model import SardanaElementPlainModel diff --git a/test/HTMLTestRunner.py b/test/HTMLTestRunner.py index 1373f5255c..6adba0b80b 100644 --- a/test/HTMLTestRunner.py +++ b/test/HTMLTestRunner.py @@ -743,7 +743,6 @@ def _generate_ending(self): # build our own launcher to support more specific command line # parameters like test title, CSS, etc. class TestProgram(unittest.TestProgram): - """ A variation of the unittest.TestProgram. Please refer to the base class for command line parameters. diff --git a/test/poolunittest.py b/test/poolunittest.py index 75b46b3911..2ba7cf3dcb 100644 --- a/test/poolunittest.py +++ b/test/poolunittest.py @@ -201,7 +201,7 @@ def getCounterTimerSimulators(self): ret.append({"properties": {"Average": ['1.0'], "Sigma": ['250.0'], "MotorName": [mot_name]}, - }, ) + },) return ret # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- @@ -436,8 +436,7 @@ def prepareDevicePool(self): self.pool_exec = path break - self.assertFalse(self.pool_exec is None, - "Could not find Pool executable. Make sure it is in the PATH") + self.assertFalse(self.pool_exec is None, "Could not find Pool executable. Make sure it is in the PATH") self.pool_bin_dir = os.path.dirname(self.pool_exec) From 523e12f06e65d26298182db4109fbaa6790bda57 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 13 Jan 2020 14:53:17 +0100 Subject: [PATCH 358/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd02639f03..3ac4ba4805 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Use of env and hints in `macro` function decorator (#1239) * PMTV widget not updating the following attributes: limit switches, state and status (#1244) +* Reintroduce backwards compatibility for measurement groups' configurations (URIs) + created with Taurus 3 (#1266) ### Deprecated From 3e661aae3e4168fa2b490fe62bb50e4d39574377 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 13 Jan 2020 14:56:05 +0100 Subject: [PATCH 359/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ac4ba4805..cce12b90c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Documentation to Taurus Extensions of Sardana Devices: MacroServer part and the whole Sardana part of the Qt Taurus Extensions (#1228, #1233) * Advertise newfile macro in case no ScanDir or ScanFile is set (#1254, #1258) +* Improve scans to detect if a ScanFile od ScanDir are set but empty (#1262) ### Fixed From de2d717abadd5e59bd7a93bf949fd11728a579b1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 13 Jan 2020 15:13:55 +0100 Subject: [PATCH 360/830] Return from "showscan online" after showing ShowScanOnline windows --- src/sardana/spock/magic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index ca70429177..03882910fe 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -84,7 +84,6 @@ def showscan(self, parameter_s=''): online, scan_nb = False, None if len(params) > 0: if params[0].lower() == 'online': - online = True try: from sardana.taurus.qt.qtgui.extra_sardana import \ ShowScanOnline @@ -111,6 +110,7 @@ def showscan(self, parameter_s=''): fname = sys.modules[ShowScanOnline.__module__].__file__ args = ['python3', fname, doorname, '--taurus-log-level=error'] subprocess.Popen(args) + return # show the scan plot, ignoring the plot configuration elif params[0].lower() == 'online_raw': From 73e103e8a2d31b6d07dbd2b202633b363515f105 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 13 Jan 2020 15:20:37 +0100 Subject: [PATCH 361/830] Remove showscan online_raw New taurus trend (based on pyqtgraph) does not understand "scan://" models - it is not able to connect to the Door's RecordData events. --- src/sardana/spock/magic.py | 7 +------ src/sardana/spock/spockms.py | 18 +----------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index 03882910fe..d38c1f6ffe 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -111,14 +111,9 @@ def showscan(self, parameter_s=''): args = ['python3', fname, doorname, '--taurus-log-level=error'] subprocess.Popen(args) return - - # show the scan plot, ignoring the plot configuration - elif params[0].lower() == 'online_raw': - online = True else: scan_nb = int(params[0]) - - door.show_scan(scan_nb, online=online) + door.show_scan(scan_nb) def spsplot(self, parameter_s=''): diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index e9c36d2bd8..ccac187e07 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -67,19 +67,6 @@ def run(self): self.plot() def show_scan(self, scan_nb=None, scan_history_info=None, directory_map=None): - if scan_nb is None and scan_history_info is None: - #================================================================== - # Hack to avoid ipython-qt issues. See similar workaround in expconf magic command - # @todo: do this in a better way - #import taurus.qt.qtgui.plot - #w = taurus.qt.qtgui.plot.TaurusTrend() - #w.model = "scan://" + self._door.getNormalName() - # w.show() - import subprocess - args = ['taurustrend', 'scan://%s' % self._door.getNormalName()] - subprocess.Popen(args) - #================================================================== - return scan_dir, scan_file = None, None if scan_nb is None: import h5py @@ -421,10 +408,7 @@ def _getMacroResult(self, macro): def plot(self): self._plotter.run() - def show_scan(self, scan_nb=None, online=False): - if online: - self._plotter.show_scan() - return + def show_scan(self, scan_nb=None): env = self.getEnvironment() scan_history_info = env.get("ScanHistory") directory_map = env.get("DirectoryMap") From ad5b6fc3d6e689fd5c5146a59166ced2e6e24ae0 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 13 Jan 2020 15:48:58 +0100 Subject: [PATCH 362/830] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cce12b90c8..9712968eb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Use of env and hints in `macro` function decorator (#1239) * PMTV widget not updating the following attributes: limit switches, state and status (#1244) +* `showscan online` shows only the online trend and not erroneously online and offline + (#1260) * Reintroduce backwards compatibility for measurement groups' configurations (URIs) created with Taurus 3 (#1266) @@ -44,6 +46,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Support to Python < 3.5 (#1089, #1173, #1201, #1263) * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) +* `showscan online_raw` magic command in spock (#1260) +* `online` kwarg in `SpockBaseDoor` constructor (#1260) * `sardana.requirements` (#1185) ## [2.8.3] 2019-09-16 From 9977b165ef3b5aac594398ce469d3cac283f7c5d Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 13 Jan 2020 16:13:00 +0100 Subject: [PATCH 363/830] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9712968eb3..10ff93e79b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ This file follows the formats and conventions from [keepachangelog.com] and the whole Sardana part of the Qt Taurus Extensions (#1228, #1233) * Advertise newfile macro in case no ScanDir or ScanFile is set (#1254, #1258) * Improve scans to detect if a ScanFile od ScanDir are set but empty (#1262) +* Possibility to view debug messages in `DoorOutput` widget - enable/disable + using context menu option (#1242) ### Fixed @@ -36,6 +38,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `nr_points` attribute of scan macros e.g., aNscan family of scans, `fscan` etc. (#1218, #1220) +* `DoorDebug` widget - use `DoorOutput` with enabled debugging (#1242) ### Changed From 37ce72bd939da245f55e729156c8aff538a4e9be Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 14 Jan 2020 10:06:11 +0100 Subject: [PATCH 364/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ff93e79b..60d46f6602 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Use of env and hints in `macro` function decorator (#1239) * PMTV widget not updating the following attributes: limit switches, state and status (#1244) +* OutputBlock view option when macros produce outputs at high rate (#1245) * `showscan online` shows only the online trend and not erroneously online and offline (#1260) * Reintroduce backwards compatibility for measurement groups' configurations (URIs) From 5b6e938602babab4ad63ce90f9ac55e1c2b2af16 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 14 Jan 2020 10:34:45 +0100 Subject: [PATCH 365/830] Add hotfix 2.8.4 to CHANGELOG --- CHANGELOG.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60d46f6602..4f971ff7cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,6 @@ This file follows the formats and conventions from [keepachangelog.com] ### Fixed * Default macro parameter values in macroexecutor (#1153) -* fscan macro that was broken 2.6.0 (#1218, #1220) * Executing RunMacro Door's command with string parameters containing spaces (#1240) * Setting of environment variables in Python 3.7 (#1195) * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` @@ -37,8 +36,6 @@ This file follows the formats and conventions from [keepachangelog.com] ### Deprecated -* `nr_points` attribute of scan macros e.g., aNscan family of scans, `fscan` etc. - (#1218, #1220) * `DoorDebug` widget - use `DoorOutput` with enabled debugging (#1242) ### Changed @@ -54,6 +51,18 @@ This file follows the formats and conventions from [keepachangelog.com] * `online` kwarg in `SpockBaseDoor` constructor (#1260) * `sardana.requirements` (#1185) +## [2.8.4] 2019-11-13 + +### Fixed + +* fix compatibility with python 2.6 when overwritting macros +* fscan macro that was broken 2.6.0 (#1218, #1220) + +### Deprecated + +* `nr_points` attribute of scan macros e.g., aNscan family of scans, `fscan` etc. + (#1218, #1220) + ## [2.8.3] 2019-09-16 ### Fixed @@ -772,7 +781,8 @@ Main improvements since sardana 1.5.0 (aka Jan15): [keepachangelog.com]: http://keepachangelog.com -[Unreleased]: https://github.com/sardana-org/sardana/compare/2.8.3...HEAD +[Unreleased]: https://github.com/sardana-org/sardana/compare/2.8.4...HEAD +[2.8.4]: https://github.com/sardana-org/sardana/compare/2.8.4...2.8.3 [2.8.3]: https://github.com/sardana-org/sardana/compare/2.8.3...2.8.2 [2.8.2]: https://github.com/sardana-org/sardana/compare/2.8.2...2.8.1 [2.8.1]: https://github.com/sardana-org/sardana/compare/2.8.1...2.8.0 From e7619c5e95a7e9dbb147a75fe36d2f585dc7ec34 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 14 Jan 2020 18:51:49 +0100 Subject: [PATCH 366/830] Update SEP16.md --- doc/source/sep/SEP16.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/source/sep/SEP16.md b/doc/source/sep/SEP16.md index 429222915f..becdca9064 100644 --- a/doc/source/sep/SEP16.md +++ b/doc/source/sep/SEP16.md @@ -1,6 +1,6 @@ Title: Plugins (controllers, macros, etc.) catalogue SEP: 16 - State: CANDIDATE + State: ACCEPTED Reason: SEP15 migrated the Sardana repository from SourceForge to GitHub. The third-party repositories are still at SourceForge and a decision @@ -182,5 +182,7 @@ Changes - 2019-07-16 [reszelaz](https://github.com/reszelaz) Rename register to catalogue - 2019-07-16 [reszelaz](https://github.com/reszelaz) Move to CANDIDATE after -meeting the DESY, MAXIV and SOLARIS. +meeting with DESY, MAXIV and SOLARIS +- 2019-11-28 [reszelaz](https://github.com/reszelaz) Move to ACCEPTED after +meeting with DESY, MAXIV and SOLARIS From b0262d6708f609377272d3b0e4110350394f15b9 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 14 Jan 2020 18:52:30 +0100 Subject: [PATCH 367/830] Update index.md --- doc/source/sep/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/sep/index.md b/doc/source/sep/index.md index 288bca18ab..5749785cce 100644 --- a/doc/source/sep/index.md +++ b/doc/source/sep/index.md @@ -26,7 +26,7 @@ Proposals list [SEP13][] | REJECTED (moved to [TEP13][]) | Unified plugins support in Taurus & Sardana [SEP14][] | DRAFT | MSENV taurus schema [SEP15][] | ACCEPTED | Moving Sardana to Github - [SEP16][] | CANDIDATE | Plugins (controllers, macros, etc.) catalogue + [SEP16][] | ACCEPTED | Plugins (controllers, macros, etc.) catalogue [SEP17][] | DRAFT | Ongoing acquisition formalization and implementation [SEP18][] | ACCEPTED | Extend acquisition and synchronization concepts for SEP2 needs @@ -49,7 +49,7 @@ Proposals list [SEP13]: http://www.sardana-controls.org/sep/?SEP13.md [SEP14]: http://www.sardana-controls.org/sep/?SEP14.md [SEP15]: http://www.sardana-controls.org/sep/?SEP15.md -[SEP16]: https://github.com/reszelaz/sardana/blob/sep16/doc/source/sep/SEP16.md +[SEP16]: http://www.sardana-controls.org/sep/?SEP16.md [SEP17]: https://github.com/reszelaz/sardana/blob/sep17/doc/source/sep/SEP17.md [SEP18]: http://www.sardana-controls.org/sep/?SEP18.md From ae6c28a68a53c2e278a11a1e0ba7503432da05c5 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 15 Jan 2020 09:56:28 +0100 Subject: [PATCH 368/830] Revert "Console size py3" --- .../taurus/core/tango/sardana/macroserver.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 229f8f6366..563543a43c 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -66,6 +66,17 @@ CHANGE_EVT_TYPES = TaurusEventType.Change, TaurusEventType.Periodic + + + +def _get_console_width(): + try: + width = int(os.popen('stty size', 'r').read().split()[1]) + except Exception: + width = float('inf') + return width + + def _get_nb_lines(nb_chrs, max_chrs): return int(math.ceil(float(nb_chrs)/max_chrs)) @@ -680,7 +691,7 @@ def macroStatusReceived(self, s, t, v): return data def logReceived(self, log_name, output): - max_chrs = os.get_terminal_size().columns + max_chrs = _get_console_width() if not output or self._silent or self._ignore_logs: return From ef160f29ed5e0dd11e6f43e13ce79498e1b6e58c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 20 Jan 2020 10:09:36 +0100 Subject: [PATCH 369/830] MeasurementGroup's configurations created with Taurus3 were supposed to be backwards compatible when migrating to Taurus 4. Now this is broken (most probably after accepting SEP18). Fix it by converting measurement group's timer and monitor configurations to use full URIs (scheme + FQDN in authority). --- src/sardana/pool/poolmeasurementgroup.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 19f8e8e75b..ce58b2fc49 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -848,8 +848,11 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): elif master_timer_sw_start is not None: user_config['timer'] = master_timer_sw_start.full_name else: # Measurement Group with all channel synchronized by hardware - if 'timer' in cfg: - user_config['timer'] = cfg['timer'] + mnt_grp_timer = cfg.get('timer') + if mnt_grp_timer: + if to_fqdn: + mnt_grp_timer = _to_fqdn(mnt_grp_timer, self._parent) + user_config['timer'] = mnt_grp_timer else: # for backwards compatibility use a random monitor user_config['timer'] = user_config_ctrl['timer'] @@ -859,8 +862,11 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): elif master_monitor_sw_start is not None: user_config['monitor'] = master_monitor_sw_start.full_name else: # Measurement Group with all channel synchronized by hardware - if 'monitor' in cfg: - user_config['monitor'] = cfg['monitor'] + mnt_grp_monitor = cfg.get('monitor') + if mnt_grp_monitor: + if to_fqdn: + mnt_grp_monitor = _to_fqdn(mnt_grp_monitor, self._parent) + user_config['monitor'] = mnt_grp_monitor else: # for backwards compatibility use a random monitor user_config['monitor'] = user_config_ctrl['monitor'] From 29761eb09207257515f962e4b4ba4ac66c0e5604 Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 20 Jan 2020 17:15:17 +0100 Subject: [PATCH 370/830] New test for MGConfiguration class --- .../test_MeasurementGroupConfiguretion.py | 282 ++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 src/sardana/pool/test/test_MeasurementGroupConfiguretion.py diff --git a/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py b/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py new file mode 100644 index 0000000000..e60dd074c6 --- /dev/null +++ b/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py @@ -0,0 +1,282 @@ +from sardana.taurus.core.tango.sardana import * +import uuid +import numpy +from taurus import Device +from taurus.external.unittest import TestCase +from taurus.core.tango.tangovalidator import TangoDeviceNameValidator +from taurus.test.base import insertTest +from sardana.sardanautils import is_number, is_non_str_seq, is_pure_str +from sardana.taurus.core.tango.sardana.pool import registerExtensions +from sardana.tango.pool.test.base_sartest import SarTestTestCase + +def is_numerical(obj): + if is_number(obj): + return True + if is_non_str_seq(obj) or isinstance(obj, numpy.ndarray): + if is_number(obj[0]): + return True + elif is_non_str_seq(obj[0]) or isinstance(obj, numpy.ndarray): + if is_number(obj[0][0]): + return True + return False + + +class TestMeasurementGroupConfiguration(SarTestTestCase, TestCase): + + def setUp(self): + SarTestTestCase.setUp(self) + registerExtensions() + + def tearDown(self): + SarTestTestCase.tearDown(self) + + def _assertResult(self, result, channels, expected_value): + expected_channels = list(channels) + print(result) + for channel, value in result.items(): + msg = "unexpected key: {}".format(channel) + self.assertIn(channel, expected_channels, msg) + expected_channels.remove(channel) + self.assertEqual(value, expected_value) + msg = "{} are missing".format(expected_channels) + self.assertEqual(len(expected_channels), 0, msg) + + def test_enabled(self): + elements = ["_test_ct_1_1", "_test_ct_1_2"] + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + try: + mg = Device(mg_name) + enabled = mg.getEnabled(*elements) + self._assertResult(enabled, elements, True) + mg.setEnabled(False, *elements) + enabled = mg.getEnabled(*elements) + self._assertResult(enabled, elements, False) + enabled = mg.getEnabled("_test_ct_ctrl_1") + self._assertResult(enabled, elements, False) + mg.setEnabled(True, *elements) + enabled = mg.getEnabled(*elements) + self._assertResult(enabled, elements, True) + # enabled = mg.getEnabled(*elements, ret_full_name=True) + v = TangoDeviceNameValidator() + full_names = [v.getNames(element)[0] for element in elements] + enabled = mg.getEnabled(*full_names) + self._assertResult(enabled, elements, True) + # TODO Fix ret_full_name error and make a test + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + + def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + + try: + mg = Device(mg_name) + is_output = mg.getOutput(*elements) + self._assertResult(is_output, elements, True) + mg.setOutput(False, *elements) + is_output = mg.getOutput(*elements) + self._assertResult(is_output, elements, False) + is_output = mg.getOutput("_test_ct_ctrl_1") + self._assertResult(is_output, elements, False) + mg.setOutput(True, *elements) + is_output = mg.getOutput(*elements) + self._assertResult(is_output, elements, True) + # is_output = mg.getOutput(*elements, ret_full_name=True) + v = TangoDeviceNameValidator() + full_names = [] + for element in elements: + full_names.append(v.getNames(element)[0]) + print(full_names) + is_output = mg.getOutput(*full_names) + self._assertResult(is_output, elements, True) + + # mg = Device(mg_name) + # enabled = mg.getOutput(*elements) + # self._assertResult(enabled, elements, True) + # for key in elements: + # self.assertTrue(mg._getOutputChannels()[key]) + # mg._setOutputChannels(False, elements) + # for key in elements: + # self.assertFalse(mg._getOutputChannels()[key]) + # mg._setOutputChannels(True, elements) + # for key in elements: + # self.assertTrue(mg._getOutputChannels()[key]) + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + + def testPlotTypeChannels(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + + try: + mg = Device(mg_name) + print(mg._getPlotTypeChannels()) + mg._setPlotTypeChannels("Image", [elements[0]]) + mg._setPlotTypeChannels("Spectrum", [elements[1]]) + mg._setPlotTypeChannels("No", [elements[2]]) + print(mg._getPlotTypeChannels()) + try: + mg._setPlotTypeChannels("asdf", [elements[2]]) + error = 1 + except: + error = 0 + if error == 1: + raise ValueError("Plot type string values are not restricted") + print(mg._getPlotTypeChannels()) + + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + + def testPlotAxesChannels(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + + try: + mg = Device(mg_name) + mg._setPlotTypeChannels("Image", [elements[0]]) + mg._setPlotTypeChannels("Spectrum", [elements[1]]) + mg._setPlotTypeChannels("No", [elements[2]]) + print(mg._getPlotAxesChannels()) + mg._setPlotAxesChannels(["", ""], [elements[0]]) + mg._setPlotAxesChannels([""], [elements[1]]) + print(mg._getPlotAxesChannels()) + mg._setPlotAxesChannels(["", ""], [elements[0]]) + mg._setPlotAxesChannels([""], [elements[1]]) + print(mg._getPlotAxesChannels()) + mg._setPlotAxesChannels(["", ""], [elements[0]]) + print(mg._getPlotAxesChannels()) + + try: + mg._setPlotAxesChannels([""], [elements[2]]) + error = 1 + except: + error = 0 + if error == 1: + raise ValueError("Channel without PlotType shouldn't accept a value") + try: + mg._setPlotAxesChannels(["", ""], [elements[1]]) + error = 1 + except: + error = 0 + if error == 1: + raise ValueError("PlotType spectrum should only accept one axis") + try: + mg._setPlotAxesChannels([""], [elements[0]]) + error = 1 + except: + error = 0 + if error == 1: + raise ValueError("PlotType image should only accept two axis") + + print(mg._getPlotAxesChannels()) + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + + def testCtrlsTimer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + try: + mg = Device(mg_name) + previous = mg._getCtrlsTimer() + print(previous) + mg._setCtrlsTimer(['_test_ct_1_3']) + if mg._getCtrlsTimer() == previous: + raise RuntimeError("setter function failed aplying changes") + print(mg._getCtrlsTimer()) + + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + + def testCtrlsMonitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + # TODO set method is missing a parameter (doesn't work as the description says) + try: + mg = Device(mg_name) + previous = mg._getCtrlsMonitor() + print(previous) + mg._setCtrlsTimer(["_test_ct_1_2"]) + if mg._getCtrlsMonitor() == previous: + raise RuntimeError("setter function failed aplying changes") + print(mg._getCtrlsMonitor()) + + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + + def testCtrlsSynchronization(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + try: + mg = Device(mg_name) + previous = mg._getCtrlsSynchronization() + print(previous) + mg._setCtrlsSynchronization('Gate') + if mg._getCtrlsSynchronization() == previous: + raise RuntimeError("setter function failed aplying changes") + + previous = mg._getCtrlsSynchronization() + print(previous) + mg._setCtrlsSynchronization('Trigger') + if mg._getCtrlsSynchronization() == previous: + raise RuntimeError("setter function failed aplying changes") + + previous = mg._getCtrlsSynchronization() + print(previous) + mg._setCtrlsSynchronization('Start') + if mg._getCtrlsSynchronization() == previous: + raise RuntimeError("setter function failed aplying changes") + print(mg._getCtrlsSynchronization()) + + try: + mg._setCtrlsSynchronization('Software') + error = 1 + except: + error = 0 + if error == 1: + raise ValueError("CtrlsSynchronization should only admit Gate/Trigger/Start") + print(mg._getCtrlsSynchronization()) + + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + + def testCtrlsSynchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + # TODO ERROR function doesn't accept triggergate + try: + mg = Device(mg_name) + print(mg._getCtrlsSynchronizer()) + mg.setSynchronizer('software') + print(mg._getCtrlsSynchronizer()) + mg.setSynchronizer('triggergate') + print(mg._getCtrlsSynchronizer()) + + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + +if __name__ == '__main__': + + test = TestMeasurementGroupConfiguration() + test.setUp() + test.test_output() + test.tearDown() + # dev = taurus.Device("mntgrp/pool_test01_1/mntgrp03") + # print(dev) + # print(dev.getSynchronizer(ret_by_ctrl=True)) From 378541f9e616ba3500fbc9f2c87aff95ea686530 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 20 Jan 2020 17:37:06 +0100 Subject: [PATCH 371/830] Fix flake8 --- src/sardana/taurus/core/tango/sardana/macroserver.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 563543a43c..c2f4f20073 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -66,9 +66,6 @@ CHANGE_EVT_TYPES = TaurusEventType.Change, TaurusEventType.Periodic - - - def _get_console_width(): try: width = int(os.popen('stty size', 'r').read().split()[1]) From 10aaa9ed7fd5a4b446ef0086f56d1d394c947bfa Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 21 Jan 2020 17:33:45 +0100 Subject: [PATCH 372/830] Add deleted flag to PoolElement Deleted flag is used to mark element as deleted from the pool --- src/sardana/pool/poolelement.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sardana/pool/poolelement.py b/src/sardana/pool/poolelement.py index ebbd8b0de8..2574c9fe4a 100644 --- a/src/sardana/pool/poolelement.py +++ b/src/sardana/pool/poolelement.py @@ -46,6 +46,7 @@ def __init__(self, **kwargs): self._ctrl = weakref.ref(ctrl) self._axis = kwargs.pop('axis') self._ctrl_id = ctrl.get_id() + self._deleted = False try: instrument = kwargs.pop('instrument') self.set_instrument(instrument) @@ -74,6 +75,12 @@ def get_controller_id(self): def get_axis(self): return self._axis + def is_deleted(self): + return self._deleted + + def set_deleted(self, deleted): + self._deleted = deleted + def set_action_cache(self, action_cache): self._action_cache = action_cache action_cache.add_element(self) @@ -143,3 +150,5 @@ def set_extra_par(self, name, value): controller_id = property(get_controller_id, doc="element controller id") instrument = property(get_instrument, set_instrument, doc="element instrument") + deleted = property(is_deleted, set_deleted, + doc="element is deleted from pool (experimental API)") From 5d236dcfda3a0b54bd0509e66a3afbe19a7c2624 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 21 Jan 2020 17:35:40 +0100 Subject: [PATCH 373/830] Mark element as deleted in Pool.delete_device Information that element is deleted may be useful in other palces e.g. when shutting down the Pool to know if we still must call the DeleteDevice. --- src/sardana/pool/pool.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py index 11d2c799b7..0516c94933 100644 --- a/src/sardana/pool/pool.py +++ b/src/sardana/pool/pool.py @@ -576,6 +576,8 @@ def delete_element(self, name): self.remove_element(elem) self.fire_event(EventType("ElementDeleted"), elem) + if hasattr(elem, "get_controller"): + elem.set_deleted(True) def create_instrument(self, full_name, klass_name, id=None): is_root = full_name.count('/') == 1 From d01262922a239a3a316995368e7b7eff60379c3f Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 21 Jan 2020 17:37:41 +0100 Subject: [PATCH 374/830] Call controller's DeleteDevice when Tango calls delete_device. AddDevice is called on init_device. Call DeleteDevice on delete_device. This is done for all elements proceeding from a controller e.g. motor, counter/timer, pseudomotor, pseudocounter, etc. --- src/sardana/tango/pool/PoolDevice.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sardana/tango/pool/PoolDevice.py b/src/sardana/tango/pool/PoolDevice.py index e3247e8191..ca4ce9b944 100644 --- a/src/sardana/tango/pool/PoolDevice.py +++ b/src/sardana/tango/pool/PoolDevice.py @@ -593,6 +593,21 @@ def init_device(self): except ValueError: pass + def delete_device(self): + element = self.element + if not element.deleted: + pool_ctrl = self.ctrl + ctrl = pool_ctrl.ctrl + if ctrl is not None: + axis = element.axis + try: + ctrl.DeleteDevice(axis) + except Exception: + name = element.name + self.error("Unable to delete %s(%s)", name, axis, + exc_info=1) + PoolDevice.delete_device(self) + def read_Instrument(self, attr): """Read the value of the ``Instrument`` tango attribute. Returns the instrument full name or empty string if this element doesn't From 873e7f43f8f1e7b751fef2ac4063842de1a255c1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 22 Jan 2020 16:30:56 +0100 Subject: [PATCH 375/830] Further improve error stream when macro failed - Instead of printing description print just the name (macro id is not really interesting to the user) - Add hint info message to use www --- src/sardana/macroserver/msmacromanager.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 7f746079eb..d8e2493d75 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1618,8 +1618,11 @@ def runMacro(self, macro_obj): if isinstance(macro_exp, MacroServerException): if macro_obj.parent_macro is None: door.debug(macro_exp.traceback) - door.error("An error occurred while running %s:\n%r" % - (macro_obj.description, macro_exp)) + msg = ("An error occurred while running {}:\n" + "{!r}").format(macro_obj.getName(), macro_exp) + door.error(msg) + msg = "Hint: in Spock execute `www`to get more details" + door.info(msg) self._popMacro() raise macro_exp self.debug("[ END ] runMacro %s" % desc) From 0697a56da1a2d0c6385d9b02fbdad46f934e20ac Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 22 Jan 2020 17:04:00 +0100 Subject: [PATCH 376/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f971ff7cb..977ee43146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Improve scans to detect if a ScanFile od ScanDir are set but empty (#1262) * Possibility to view debug messages in `DoorOutput` widget - enable/disable using context menu option (#1242) +* Better macro exception message and hint to use `www` (#1191) ### Fixed From 4ee47f0bd31cba2a54f25bac15159c2cba952459 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 6 Oct 2019 11:09:42 -0400 Subject: [PATCH 377/830] MNT: use variable defined above instead of re-checking if we are in a no-database environment. --- src/sardana/tango/core/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 2d09e61152..29040f233e 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -726,7 +726,7 @@ def prepare_server(args, tango_args): else: inst_name = tango_args[1].lower() - if "-nodb" in tango_args: + if nodb: return log_messages db = Database() From f01139f24bd0c1624548a3a0ce3d0da76e87a526 Mon Sep 17 00:00:00 2001 From: Juliano Murari Date: Thu, 23 Jan 2020 11:24:04 +0100 Subject: [PATCH 378/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 977ee43146..2fe28be069 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Improve scans to detect if a ScanFile od ScanDir are set but empty (#1262) * Possibility to view debug messages in `DoorOutput` widget - enable/disable using context menu option (#1242) +* Improve documentation (#1241) * Better macro exception message and hint to use `www` (#1191) ### Fixed @@ -34,6 +35,7 @@ This file follows the formats and conventions from [keepachangelog.com] (#1260) * Reintroduce backwards compatibility for measurement groups' configurations (URIs) created with Taurus 3 (#1266) +* Measurement groups renaming (#951) ### Deprecated From 5bbb6da250b49fc78a10bd130d47c4b024a41ed6 Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 23 Jan 2020 11:35:13 +0100 Subject: [PATCH 379/830] Fix all flek8 travis problems --- src/sardana/pool/poolcontrollers/test/base.py | 4 +-- src/sardana/pool/test/test_synchronization.py | 4 +-- src/sardana/tango/pool/test/base.py | 6 ++-- .../extra_hkl/diffractometeralignment.py | 21 +++++++------ .../taurus/qt/qtgui/extra_hkl/hklscan.py | 11 ++++--- .../taurus/qt/qtgui/extra_hkl/ubmatrix.py | 15 +++++---- .../qtgui/extra_macroexecutor/macrobutton.py | 3 +- .../sequenceeditor/sequenceeditor.py | 13 +++++--- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 3 +- .../qt/qtgui/extra_sardana/expdescription.py | 7 +++-- .../qt/qtgui/extra_sardana/sardanaeditor.py | 4 ++- src/sardana/test/testsuite.py | 4 +-- test/poolunittest.py | 31 +++++++++++-------- 13 files changed, 73 insertions(+), 53 deletions(-) diff --git a/src/sardana/pool/poolcontrollers/test/base.py b/src/sardana/pool/poolcontrollers/test/base.py index 0d7e6609bf..03b0d21ba7 100644 --- a/src/sardana/pool/poolcontrollers/test/base.py +++ b/src/sardana/pool/poolcontrollers/test/base.py @@ -22,8 +22,6 @@ # along with Sardana. If not, see . ## ############################################################################## -__all__ = ['BaseControllerTestCase', 'TriggerGateControllerTestCase', - 'PositionGenerator', 'TriggerGateReceiver'] import time import threading @@ -37,6 +35,8 @@ from sardana.sardanaattribute import SardanaAttribute from taurus.core.util.log import Logger +__all__ = ['BaseControllerTestCase', 'TriggerGateControllerTestCase', + 'PositionGenerator', 'TriggerGateReceiver'] class BaseControllerTestCase(object): """ Base test case for unit testing arbitrary controllers. diff --git a/src/sardana/pool/test/test_synchronization.py b/src/sardana/pool/test/test_synchronization.py index df9d296fc4..32295f0413 100644 --- a/src/sardana/pool/test/test_synchronization.py +++ b/src/sardana/pool/test/test_synchronization.py @@ -26,8 +26,6 @@ """This module contains tests for trigger gate generation using a given controller""" -__docformat__ = "restructuredtext" - import threading from taurus.test import insertTest @@ -41,6 +39,8 @@ createPoolController, createPoolTriggerGate, \ createControllerConfiguration +__docformat__ = "restructuredtext" + class SynchronizationTestCase(object): """Base class for integration tests of PoolSynchronization class and any diff --git a/src/sardana/tango/pool/test/base.py b/src/sardana/tango/pool/test/base.py index 31c34466e0..8027e34ae2 100644 --- a/src/sardana/tango/pool/test/base.py +++ b/src/sardana/tango/pool/test/base.py @@ -25,9 +25,6 @@ """Base classes for the controller tests""" -__all__ = ['BasePoolTestCase', 'ControllerLoadsTestCase', - 'ControllerCreationTestCase', 'ElementCreationTestCase'] - import PyTango import unittest from taurus.core.tango.starter import ProcessStarter @@ -36,6 +33,9 @@ get_free_alias) from taurus.core.util import whichexecutable +__all__ = ['BasePoolTestCase', 'ControllerLoadsTestCase', + 'ControllerCreationTestCase', 'ElementCreationTestCase'] + class BasePoolTestCase(object): """Abstract class for pool DS testing. diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py index fe774df6cb..b5e0d7ab73 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py @@ -23,9 +23,6 @@ ## ############################################################################## - -__docformat__ = 'restructuredtext' - import sys import time from taurus.external.qt import Qt @@ -50,6 +47,8 @@ from .selectsignal import SelectSignal +__docformat__ = 'restructuredtext' + class EngineModesComboBox(Qt.QComboBox, TaurusBaseWidget): """ComboBox representing list of engine modes""" @@ -275,7 +274,7 @@ def exec_scan(self, imot): macro_command.append(str(self.selectsignal._ui.SignallineEdit.text())) self.door_device.RunMacro(macro_command) - while(self.door_device.State()) == PyTango.DevState.RUNNING: + while (self.door_device.State()) == PyTango.DevState.RUNNING: time.sleep(0.01) # TODO: the string parsing should be eliminated and the sardana # generic "goto_peak" feature should be used instead - when available @@ -351,12 +350,14 @@ def open_selectsignal_panel(self): def main(): - parser = argparse.ArgumentParser(usage="%prog [door_name]", description="a taurus application for " - "diffractometer alignment: h, k, l movements and scans, go to maximum, ...") - - - app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser, - app_version=sardana.Release.version) + parser = argparse.ArgumentParser(usage="%prog [door_name]", + description="a taurus application for " + "diffractometer alignment: " + "h, k, l movements and " + "scans, go to maximum, ...") + + app = taurus.qt.qtgui.application.TaurusApplication( + cmd_line_parser=parser, app_version=sardana.Release.version) app.setApplicationName("diffractometeralignment") args = app.get_command_line_args() if len(args) < 1: diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py index 4b98d25186..3bd5df0faf 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py @@ -23,8 +23,6 @@ ## ############################################################################## -__docformat__ = 'restructuredtext' - import sys import sardana @@ -50,6 +48,8 @@ TaurusMacroConfigurationDialog, \ TaurusMacroDescriptionViewer, DoorOutput, DoorDebug, DoorResult +__docformat__ = 'restructuredtext' + class EngineModesComboBox(Qt.QComboBox, TaurusBaseWidget): """ComboBox representing list of engine modes""" @@ -357,11 +357,12 @@ def onDoorChanged(self, doorName): def main(): - parser = argparse.ArgumentParser(description="a taurus application for performing hkl scans", + parser = argparse.ArgumentParser(description="a taurus application " + "for performing hkl scans", usage="%prog [door_name]") - app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser, - app_version=sardana.Release.version) + app = taurus.qt.qtgui.application.TaurusApplication( + cmd_line_parser=parser, app_version=sardana.Release.version) app.setApplicationName("hklscan") args = app.get_command_line_args() if len(args) < 1: diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py index e73ac8e55c..a4032beede 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py @@ -23,8 +23,6 @@ ## ############################################################################## -__docformat__ = 'restructuredtext' - import sys import sardana @@ -47,6 +45,8 @@ global flag_update flag_update = 0 +__docformat__ = 'restructuredtext' + class PrivateComboBox(Qt.QComboBox, TaurusBaseWidget): """ComboBox""" @@ -464,11 +464,14 @@ def affine(self): def main(): - parser = argparse.ArgumentParser(description="a taurus application for setting diffractometer parameters: ubmatrix," - + " lattice, reflections, ...", usage="%prog ") + parser = argparse.ArgumentParser(description="a taurus application for " + "setting diffractometer " + "parameters: ubmatrix, " + "lattice, reflections, ...", + usage="%prog ") - app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser, - app_version=sardana.Release.version) + app = taurus.qt.qtgui.application.TaurusApplication( + cmd_line_parser=parser, app_version=sardana.Release.version) app.setApplicationName("ubmatrix") args = app.get_command_line_args() if len(args) < 1: diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 9e37f56cd3..85dbe42f62 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -376,7 +376,8 @@ def abort(self): registerExtensions registerExtensions() - parser = ArgumentParser(usage="python macrobutton.py [door_name] [macro_name]", + parser = ArgumentParser(usage="python macrobutton.py " + "[door_name] [macro_name]", description="Macro button for macro execution") app = TaurusApplication(app_name="macrobutton", diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index eddf1930d7..c1e10f81f2 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -991,11 +991,14 @@ def main(): parser = argparse.ArgumentParser(usage="%prog [options]", description="Sardana macro sequencer.\n" - "It allows the creation of sequences of " - "macros, executed one after the other.\n" - "The sequences can be stored under xml files") - parser.add_argument("-f", "--file", dest="file", default=None, help="load macro sequence from a file(XML or spock " - "syntax)") + "It allows the creation of " + "sequences of macros, " + "executed one after the " + "other.\n The sequences can " + "be stored under xml files") + parser.add_argument("-f", "--file", dest="file", default=None, + help="load macro sequence from a " + "file(XML or spock syntax)") app = TaurusApplication(cmd_line_parser=parser, app_name="sequencer", diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 482bc7a786..79c90984bf 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1689,7 +1689,8 @@ def main(): from taurus.qt.qtgui.panel import TaurusForm import taurus.core.util.argparse parser = taurus.core.util.argparse.get_taurus_parser() - parser = argparse.ArgumentParser(usage="%prog [options] [ [] ...]") + parser = argparse.ArgumentParser( + usage="%prog [options] [ [] ...]") app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser) args = app.get_command_line_args() diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index 7bee7a3042..ecfc595c6c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -40,8 +40,11 @@ from sardana.sardanadefs import ElementType, TYPE_ACQUIRABLE_ELEMENTS from taurus.qt.qtgui.util.ui import UILoadable -#Using a plain model and filtering and checking 'Acquirable' in item.itemData().interfaces is more elegant, but things don't get properly sorted... -#from taurus.qt.qtcore.tango.sardana.model import SardanaElementPlainModel +# Using a plain model and filtering and checking +# 'Acquirable' in item.itemData().interfaces is more elegant, +# but things don't get properly sorted... + +# from taurus.qt.qtcore.tango.sardana.model import SardanaElementPlainModel def _to_fqdn(name, logger=None): diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py index 801dad53f4..2144847daf 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py @@ -565,7 +565,9 @@ def main(): if owns_app: import argparse - parser = argparse.ArgumentParser(usage="%prog [options] ") + parser = argparse.ArgumentParser( + usage="%prog [options] ") + app = Application(sys.argv, cmd_line_parser=parser, app_name="Macro editor demo", app_version="1.0", org_domain="Sardana", org_name="Tango community") diff --git a/src/sardana/test/testsuite.py b/src/sardana/test/testsuite.py index 1924164076..193d1d71b2 100644 --- a/src/sardana/test/testsuite.py +++ b/src/sardana/test/testsuite.py @@ -32,13 +32,13 @@ """ -__docformat__ = 'restructuredtext' - import os import re import unittest import sardana +__docformat__ = 'restructuredtext' + def _filter_suite(suite, exclude_pattern, ret=None): """removes TestCases from a suite based on regexp matching on the Test id""" diff --git a/test/poolunittest.py b/test/poolunittest.py index 2ba7cf3dcb..11920c5db3 100644 --- a/test/poolunittest.py +++ b/test/poolunittest.py @@ -14,7 +14,8 @@ try: import pexpect except: - print("The Pool Unit test requires pexpect python module which was not found.") + print("The Pool Unit test requires pexpect python module " + "which was not found.") print("This module can be found at http://www.noah.org/wiki/Pexpect") sys.exit(-1) @@ -139,7 +140,8 @@ def wrong_argument(self, dev, cmd_name, arg_list, err, pr=False): try: dev.command_inout(cmd_name, arg_list) self.assert_( - False, "The %s command succeed with wrong arguments!!" % (cmd_name)) + False, "The %s command succeed " + "with wrong arguments!!" % cmd_name) except PyTango.DevFailed as e: except_value = sys.exc_info()[1] if pr: @@ -163,9 +165,10 @@ def _printException(self, except_value): print("desc =", except_value[0]["desc"]) print("origin =", except_value[0]['origin']) - # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - # Default setup. Overwrite this methods in each test scenario when necessary - # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ + # Default setup. Overwrite this methods in each + # test scenario when necessary + # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ def getPoolPath(self): """getPoolPath() -> list """ @@ -196,7 +199,7 @@ def getMotorSimulators(self): def getCounterTimerSimulators(self): ret = [] - for i in xrange(10): + for i in range(10): mot_name = "%s/simmot/test%03d" % (self.username, i + 1) ret.append({"properties": {"Average": ['1.0'], "Sigma": ['250.0'], @@ -300,10 +303,10 @@ def prepareMotorSimulator(self): f.close() except ImportError as e: self.assertTrue(False, e) - logging.log_exception(e) + logging.log_exception(e) # Include the name and path attributes in output. - logging.log(f'error.name: {e.name}') - logging.log(f'error.path: {e.path}') + logging.log('error.name: {e.name}') + logging.log('error.path: {e.path}') # Cleanup the database self.deleteMotorSimulatorFromDB() @@ -370,10 +373,10 @@ def prepareCounterTimerSimulator(self): f.close() except ImportError as e: self.assert_(False, e) - logging.log_exception(e) + logging.log_exception(e) # Include the name and path attributes in output. - logging.log(f'error.name: {e.name}') - logging.log(f'error.path: {e.path}') + logging.log('error.name: {e.name}') + logging.log('error.path: {e.path}') self.ctsim_exec = self.ctsim_exec @@ -436,7 +439,9 @@ def prepareDevicePool(self): self.pool_exec = path break - self.assertFalse(self.pool_exec is None, "Could not find Pool executable. Make sure it is in the PATH") + self.assertFalse(self.pool_exec is None, + "Could not find Pool executable. " + "Make sure it is in the PATH") self.pool_bin_dir = os.path.dirname(self.pool_exec) From 8c328097f81f9f45547dbc1be7f2fe4c3ee4fc32 Mon Sep 17 00:00:00 2001 From: aalonso Date: Fri, 24 Jan 2020 10:58:10 +0100 Subject: [PATCH 380/830] MacroExecutor was connecting to offline door but MacroExecutorWidged wasn't, so I connected directly Widget to door. --- .../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index be2f222a07..9ebf8e5875 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -1020,7 +1020,12 @@ def __init__(self, parent=None, designMode=False): def initComponents(self): self.taurusMacroExecutorWidget = TaurusMacroExecutorWidget(self) self.registerConfigDelegate(self.taurusMacroExecutorWidget) - self.taurusMacroExecutorWidget.setUseParentModel(True) + + # self.taurusMacroExecutorWidget.setUseParentModel(True) + self.taurusMacroExecutorWidget.setModelInConfig(True) + self.taurusMacroExecutorWidget.doorChanged.connect( + self.taurusMacroExecutorWidget.onDoorChanged) + self.setCentralWidget(self.taurusMacroExecutorWidget) self.taurusMacroExecutorWidget.shortMessageEmitted.connect( self.onShortMessage) @@ -1070,6 +1075,8 @@ def createMacroExecutor(args): if len(args) == 2: macroExecutor.setModel(args[0]) macroExecutor.doorChanged.emit(args[1]) + macroExecutor.taurusMacroExecutorWidget.setModel(args[0]) + macroExecutor.taurusMacroExecutorWidget.doorChanged.emit(args[1]) macroExecutor.loadSettings() return macroExecutor From fbca27227372ec9cd64577a678e6cf0fdfc3f624 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 24 Jan 2020 14:44:33 +0100 Subject: [PATCH 381/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fe28be069..8c1ea4759a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `showscan online` shows only the online trend and not erroneously online and offline (#1260) * Reintroduce backwards compatibility for measurement groups' configurations (URIs) - created with Taurus 3 (#1266) + created with Taurus 3 (#1266, #1271) * Measurement groups renaming (#951) ### Deprecated From 3c1a3f66f0c01fd5344004b82dfb597107de8909 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 24 Jan 2020 14:47:47 +0100 Subject: [PATCH 382/830] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c1ea4759a..68775e8870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) * Remove Taurus deprecated code what reduces deprecation warnings (#1206, #1252) +* Macro functions which define results now really report the result (#1238) * Use of env and hints in `macro` function decorator (#1239) * PMTV widget not updating the following attributes: limit switches, state and status (#1244) @@ -35,7 +36,7 @@ This file follows the formats and conventions from [keepachangelog.com] (#1260) * Reintroduce backwards compatibility for measurement groups' configurations (URIs) created with Taurus 3 (#1266, #1271) -* Measurement groups renaming (#951) +* Measurement groups renaming with `renameelem` macro(#951) ### Deprecated From e00bb0f111c50db30e9886ee96d7b553ae77ded6 Mon Sep 17 00:00:00 2001 From: aalonso Date: Fri, 24 Jan 2020 15:27:25 +0100 Subject: [PATCH 383/830] Change code to work on MacroExecutor setModel new function --- .../qt/qtgui/extra_macroexecutor/macroexecutor.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 9ebf8e5875..0caa162396 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -1053,6 +1053,15 @@ def onDoorChanged(self, doorName): self.taurusMacroExecutorWidget.onMacroStatusUpdated) self.taurusMacroExecutorWidget.onDoorChanged(doorName) + def setModel(self, model): + """Reimplemented from :meth:`TaurusWidget.setModel`""" + TaurusWidget.setModel(self, model) + self.setWindowTitle(Qt.QApplication.applicationName() + ": " + model) + # model_obj = self.getModelObj() + # if model_obj is not None: + # print(model_obj) + self.taurusMacroExecutorWidget.setModel(model) + @classmethod def getQtDesignerPluginInfo(cls): return None @@ -1075,8 +1084,8 @@ def createMacroExecutor(args): if len(args) == 2: macroExecutor.setModel(args[0]) macroExecutor.doorChanged.emit(args[1]) - macroExecutor.taurusMacroExecutorWidget.setModel(args[0]) - macroExecutor.taurusMacroExecutorWidget.doorChanged.emit(args[1]) + # macroExecutor.taurusMacroExecutorWidget.setModel(args[0]) + # macroExecutor.taurusMacroExecutorWidget.doorChanged.emit(args[1]) macroExecutor.loadSettings() return macroExecutor From c31abf3a7357e7e6f3fae8e4b91889baebacd69e Mon Sep 17 00:00:00 2001 From: aalonso Date: Fri, 24 Jan 2020 16:42:58 +0100 Subject: [PATCH 384/830] Found real problem. MacroComboBox function that loads macros list wasn't connected to parent model. --- .../qtgui/extra_macroexecutor/macroexecutor.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 0caa162396..9911f4b217 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -681,7 +681,7 @@ def __init__(self, parent=None, designMode=False): self._favouritesBuffer = None self.favouritesMacrosEditor = FavouritesMacrosEditor(self) self.registerConfigDelegate(self.favouritesMacrosEditor) - self.favouritesMacrosEditor.setUseParentModel(True) + self.favouritesMacrosEditor.setUseParentModel(True) # TODO aalonso self.favouritesMacrosEditor.setFocusPolicy(Qt.Qt.NoFocus) self._historyBuffer = None @@ -1021,10 +1021,10 @@ def initComponents(self): self.taurusMacroExecutorWidget = TaurusMacroExecutorWidget(self) self.registerConfigDelegate(self.taurusMacroExecutorWidget) - # self.taurusMacroExecutorWidget.setUseParentModel(True) - self.taurusMacroExecutorWidget.setModelInConfig(True) - self.taurusMacroExecutorWidget.doorChanged.connect( - self.taurusMacroExecutorWidget.onDoorChanged) + self.taurusMacroExecutorWidget.setUseParentModel(True) + # self.taurusMacroExecutorWidget.setModelInConfig(True) + # self.taurusMacroExecutorWidget.doorChanged.connect( + # self.taurusMacroExecutorWidget.onDoorChanged) self.setCentralWidget(self.taurusMacroExecutorWidget) self.taurusMacroExecutorWidget.shortMessageEmitted.connect( @@ -1057,10 +1057,8 @@ def setModel(self, model): """Reimplemented from :meth:`TaurusWidget.setModel`""" TaurusWidget.setModel(self, model) self.setWindowTitle(Qt.QApplication.applicationName() + ": " + model) - # model_obj = self.getModelObj() - # if model_obj is not None: - # print(model_obj) - self.taurusMacroExecutorWidget.setModel(model) + self.taurusMacroExecutorWidget.getModelObj().macrosUpdated.connect( + self.taurusMacroExecutorWidget.macroComboBox.onMacrosUpdated) # TODO aalonso @classmethod def getQtDesignerPluginInfo(cls): From d5fefae3bd198e54d59d02e6463afcb7c3a3399d Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 27 Jan 2020 14:30:53 +0100 Subject: [PATCH 385/830] Add singleton to glossary --- doc/source/glossary.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/source/glossary.rst b/doc/source/glossary.rst index c0d9e37289..3e34bf868b 100644 --- a/doc/source/glossary.rst +++ b/doc/source/glossary.rst @@ -264,6 +264,11 @@ Glossary mapping rather than a sequence because the lookups use arbitrary :term:`immutable` keys rather than integers. + singleton pattern + Singleton pattern is a software design pattern that restricts the + instantiation of a class to one "single" instance. This is useful when + exactly one object is needed to coordinate actions across the system. + slice An object usually containing a portion of a :term:`sequence`. A slice is created using the subscript notation, ``[]`` with colons between numbers From 14a7c69a566e0afde806c876fe2376140f1557ba Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 27 Jan 2020 14:31:14 +0100 Subject: [PATCH 386/830] Add "how to write custom recorder" --- doc/source/devel/howto_recorders.rst | 29 +++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/doc/source/devel/howto_recorders.rst b/doc/source/devel/howto_recorders.rst index f1c7ec33e6..73e8741eef 100644 --- a/doc/source/devel/howto_recorders.rst +++ b/doc/source/devel/howto_recorders.rst @@ -48,7 +48,34 @@ the environment variables. Writing a custom recorder ------------------------- -.. todo:: document how to write custom recorders +This chapter (still under development) provides the necessary information to +write recorders in Sardana. + +Before writing a new recorder you should check the `Sardana plugins +catalogue `_. +There's a chance that somebody already wrote one that fits to your needs. + +If finally you decide to write a new one below you can find some useful +information: + +* Your recorder class would need to inherit from ``BaseFileRecorder`` and + implement minimum the following methods: + + * ``_startRecordList`` + * ``_writeRecord`` + * ``_endRecordList`` + + You can find some examples in ``sardana.macroserver.recorders`` module. +* Recorder classes are used to instantiate recorder objects in a scan. + Every time a scan starts, in its preparation phase, a new recorder object + gets created. Reversely, at the end of the scan the recerder object gets + destroyed. If you need to keep a long lived objects in your recorder + you may consider using the :term:`singleton pattern` + + + + + Configuration ------------- From 53721e7187b5d68549846071d2e074772d81079c Mon Sep 17 00:00:00 2001 From: cfalcon Date: Mon, 27 Jan 2020 17:23:49 +0100 Subject: [PATCH 387/830] Protect against exceptions in MeasurementGroup.count_continuous The count_continuous method is not protected against possible exceptions in the count_raw call (e.g. TimeOuts). Due to this the subscription of all value buffers are kept. This cause that the online recorder still reports the output even the door is in alarm. --- src/sardana/taurus/core/tango/sardana/pool.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index d70b626b24..1fe4bd1c7d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2149,8 +2149,10 @@ class on a provisional basis. Backwards incompatible changes cfg.prepare() self.setSynchronization(synchronization) self.subscribeValueBuffer(value_buffer_cb) - self.count_raw(start_time) - self.unsubscribeValueBuffer(value_buffer_cb) + try: + self.count_raw(start_time) + finally: + self.unsubscribeValueBuffer(value_buffer_cb) state = self.getStateEG().readValue() if state == Fault: msg = "Measurement group ended acquisition with Fault state" From 8eeee25ee4fe0c40c44215f6762ec4ae32be30c9 Mon Sep 17 00:00:00 2001 From: aalonso Date: Tue, 28 Jan 2020 10:01:05 +0100 Subject: [PATCH 388/830] Final solution: remove deprecated "setParentModel()" and directly call all the widgets "setModel()" inside the parent "setModel()" --- .../extra_macroexecutor/macroexecutor.py | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 9911f4b217..8d0b4a16e4 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -649,7 +649,7 @@ def __init__(self, parent=None, designMode=False): actionsLayout.addWidget(addToFavouritsButton) self.macroComboBox = MacroComboBox(self) - self.macroComboBox.setUseParentModel(True) + self.macroComboBox.setModelInConfig(True) self.macroComboBox.setModelColumn(0) actionsLayout.addWidget(self.macroComboBox) stopMacroButton = Qt.QToolButton() @@ -681,13 +681,13 @@ def __init__(self, parent=None, designMode=False): self._favouritesBuffer = None self.favouritesMacrosEditor = FavouritesMacrosEditor(self) self.registerConfigDelegate(self.favouritesMacrosEditor) - self.favouritesMacrosEditor.setUseParentModel(True) # TODO aalonso + self.favouritesMacrosEditor.setModelInConfig(True) self.favouritesMacrosEditor.setFocusPolicy(Qt.Qt.NoFocus) self._historyBuffer = None self.historyMacrosViewer = HistoryMacrosViewer(self) self.registerConfigDelegate(self.historyMacrosViewer) - self.historyMacrosViewer.setUseParentModel(True) + self.historyMacrosViewer.setModelInConfig(True) self.historyMacrosViewer.setFocusPolicy(Qt.Qt.NoFocus) self.tabMacroListsWidget = Qt.QTabWidget(self) @@ -712,7 +712,7 @@ def __init__(self, parent=None, designMode=False): self.spockCommand = SpockCommandWidget("Spock", self) self.spockCommand.setSizePolicy( Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum) - self.spockCommand.setUseParentModel(True) + self.spockCommand.setModelInConfig(True) spockCommandLayout = Qt.QHBoxLayout() spockCommandLayout.setContentsMargins(0, 0, 0, 0) # spockCommandLayout.addWidget(spockCommandLabel) @@ -1003,6 +1003,10 @@ def setModel(self, model): newModelObj = self.getModelObj() newModelObj.macrosUpdated.connect( self.macroComboBox.onMacrosUpdated) + self.macroComboBox.setModel(model) + self.favouritesMacrosEditor.setModelCheck(model) + self.historyMacrosViewer.setModelCheck(model) + self.spockCommand.setModelCheck(model) @classmethod def getQtDesignerPluginInfo(cls): @@ -1020,12 +1024,9 @@ def __init__(self, parent=None, designMode=False): def initComponents(self): self.taurusMacroExecutorWidget = TaurusMacroExecutorWidget(self) self.registerConfigDelegate(self.taurusMacroExecutorWidget) - - self.taurusMacroExecutorWidget.setUseParentModel(True) - # self.taurusMacroExecutorWidget.setModelInConfig(True) - # self.taurusMacroExecutorWidget.doorChanged.connect( - # self.taurusMacroExecutorWidget.onDoorChanged) - + self.taurusMacroExecutorWidget.setModelInConfig(True) + self.taurusMacroExecutorWidget.doorChanged.connect( + self.taurusMacroExecutorWidget.onDoorChanged) self.setCentralWidget(self.taurusMacroExecutorWidget) self.taurusMacroExecutorWidget.shortMessageEmitted.connect( self.onShortMessage) @@ -1057,8 +1058,7 @@ def setModel(self, model): """Reimplemented from :meth:`TaurusWidget.setModel`""" TaurusWidget.setModel(self, model) self.setWindowTitle(Qt.QApplication.applicationName() + ": " + model) - self.taurusMacroExecutorWidget.getModelObj().macrosUpdated.connect( - self.taurusMacroExecutorWidget.macroComboBox.onMacrosUpdated) # TODO aalonso + self.taurusMacroExecutorWidget.setModel(model) @classmethod def getQtDesignerPluginInfo(cls): @@ -1082,9 +1082,8 @@ def createMacroExecutor(args): if len(args) == 2: macroExecutor.setModel(args[0]) macroExecutor.doorChanged.emit(args[1]) - # macroExecutor.taurusMacroExecutorWidget.setModel(args[0]) - # macroExecutor.taurusMacroExecutorWidget.doorChanged.emit(args[1]) - macroExecutor.loadSettings() + else: + macroExecutor.loadSettings() return macroExecutor From 54f481fa36bc84d11deb7be80a4ba3ff001074aa Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 28 Jan 2020 10:59:42 +0100 Subject: [PATCH 389/830] Move recorder configuration to dedicated chapters Move recorder configuration to: * MacroServer configuration * sardanacustomsettings --- doc/source/devel/howto_recorders.rst | 23 +++++++++---------- .../users/configuration/macroserver.rst | 5 +++- src/sardana/sardanacustomsettings.py | 11 ++++++--- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/doc/source/devel/howto_recorders.rst b/doc/source/devel/howto_recorders.rst index 73e8741eef..3350c068a3 100644 --- a/doc/source/devel/howto_recorders.rst +++ b/doc/source/devel/howto_recorders.rst @@ -48,8 +48,9 @@ the environment variables. Writing a custom recorder ------------------------- -This chapter (still under development) provides the necessary information to -write recorders in Sardana. +.. todo:: finish chapter based on user's input + +This chapter provides the necessary information to write recorders in Sardana. Before writing a new recorder you should check the `Sardana plugins catalogue `_. @@ -82,8 +83,8 @@ Configuration Custom recorders may be added to the Sardana system by placing the recorder library module in a directory which is specified by the MacroServer -*RecorderPath* property. RecorderPath property may contain an ordered, -colon-separated list of directories. +:ref:`RecorderPath ` property. + In case of overriding recorders by name or by file extension (in case of the file recorders), recorders located in the first paths are of higher priority than the ones from the last paths. @@ -108,12 +109,10 @@ Three types of overriding may occur: same directory, the system will assign a list of recorders to a given extension. Then the application is in charge of deciding which one to use. -As previously mentioned recorders are selectable by either the recorder name or -the extension. During the MacroServer startup the extension to recorder map is +As previously mentioned recorders are selectable by either the extension +(using the :ref:`ScanFile ` environment variable) or the recorder name +(using the :ref:`ScanRecorder ` environment variable). + +During the MacroServer startup the extension to recorder map is generated while loading the recorder libraries. This dynamically created map -may be overridden by the custom map defined in the *sardanacustomsettings* -module (SCAN_RECORDER_MAP variable with a dictionary where key is the scan file -extension e.g. ".h5" and value is the recorder name e.g. "MyCustomRecorder", -where both keys and values are of type string). The SCAN_RECORDER_MAP will make -an union with the dynamically created map taking precedence in case the -extensions repeats in both of them. +may be overridden by editing the :data:`~sardana.sardanacustomsettings.SCAN_RECORDER_MAP`. diff --git a/doc/source/users/configuration/macroserver.rst b/doc/source/users/configuration/macroserver.rst index a7b25979c3..415929d600 100644 --- a/doc/source/users/configuration/macroserver.rst +++ b/doc/source/users/configuration/macroserver.rst @@ -22,13 +22,16 @@ configurable via :ref:`sardana-macroserver-api-macropath` and :ref:`sardana-macroserver-api-recorderpath` attributes. In case Sardana is used with Tango this configuration is accessible via the ``MacroPath`` and ``RecorderPath`` :class:`~sardana.tango.macroserver.MacroServer.MacroServer` -device properties. +device properties. Both ``MacroPath`` and ``RecorderPath`` properties may +contain an ordered, colon-separated list of directories. Your plugins may need to access to third party Python modules. One can configure the directory where to look for them via :ref:`sardana-macroserver-api-pythonpath` attribute. In case Sardana is used with Tango this configuration is accessible via the ``PythonPath`` :class:`~sardana.tango.macroserver.MacroServer.MacroServer` device property. +``PythonPath`` property may contain an ordered, colon-separated list of +directories. Multiple macros can run concurrently in the MacroServer and the maximum number of these threads is configurable via diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py index 8714c976c0..5b5989ed6a 100644 --- a/src/sardana/sardanacustomsettings.py +++ b/src/sardana/sardanacustomsettings.py @@ -51,9 +51,14 @@ #: Use this map in order to avoid ambiguity with scan recorders (file) if #: extension is intended to be the recorder selector. -#: dict -#: key - scan file extension e.g. ".h5" -#: value - recorder name +#: Set it to a dict where: +#: +#: - key - scan file extension e.g. ".h5" +#: - value - recorder name +#: +#: The SCAN_RECORDER_MAP will make an union with the dynamically (created map +#: at the MacroServer startup) taking precedence in case the extensions repeats +#: in both of them. SCAN_RECORDER_MAP = None #: Filter for macro logging: name of the class to be used as filter From 0f271c3a354600626206b41081ad3b6714930a2f Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 28 Jan 2020 11:01:13 +0100 Subject: [PATCH 390/830] Apply comments from PR review --- doc/source/devel/howto_recorders.rst | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/doc/source/devel/howto_recorders.rst b/doc/source/devel/howto_recorders.rst index 3350c068a3..0b7c2d3748 100644 --- a/doc/source/devel/howto_recorders.rst +++ b/doc/source/devel/howto_recorders.rst @@ -54,12 +54,13 @@ This chapter provides the necessary information to write recorders in Sardana. Before writing a new recorder you should check the `Sardana plugins catalogue `_. -There's a chance that somebody already wrote one that fits to your needs. +There's a chance that somebody already wrote one that fits your needs. -If finally you decide to write a new one below you can find some useful +If finally you decide to write a new one, below you can find some useful information: -* Your recorder class would need to inherit from ``BaseFileRecorder`` and +* Your recorder class would need to inherit from ``DataRecorder`` + (or ``BaseFileRecorder`` if you write data to a file) and implement minimum the following methods: * ``_startRecordList`` @@ -69,15 +70,10 @@ information: You can find some examples in ``sardana.macroserver.recorders`` module. * Recorder classes are used to instantiate recorder objects in a scan. Every time a scan starts, in its preparation phase, a new recorder object - gets created. Reversely, at the end of the scan the recerder object gets + gets created. Reversely, at the end of the scan the recorder object gets destroyed. If you need to keep a long lived objects in your recorder you may consider using the :term:`singleton pattern` - - - - - Configuration ------------- From b2ca988481b651e1cf69064944d5197d3aeff272 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 28 Jan 2020 14:58:00 +0100 Subject: [PATCH 391/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68775e8870..b0ba094f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ This file follows the formats and conventions from [keepachangelog.com] using context menu option (#1242) * Improve documentation (#1241) * Better macro exception message and hint to use `www` (#1191) +* Add basic information to "how to write custom recorder" to + the documentation (#1275) ### Fixed From 69b715c5994efe85bc2c706865346589bfa5d0b7 Mon Sep 17 00:00:00 2001 From: aalonso Date: Tue, 28 Jan 2020 15:51:43 +0100 Subject: [PATCH 392/830] Created new spock setModel() to be able to use macroserver model and Qtmodel at the same time. Fixed conflict problem between command line model and ini file model. --- .../extra_macroexecutor/macroexecutor.py | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 8d0b4a16e4..ba2a50cc78 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -28,6 +28,7 @@ """ import sys +import pickle from copy import deepcopy import PyTango @@ -136,12 +137,16 @@ def onDataChanged(self, idx): self.setCommand() def setModel(self, model): - enable = bool(model) - self.disableEditMode = not enable - self.setEnabled(enable) - self._model = model - self._model.dataChanged.connect(self.onDataChanged) - self._model.modelReset.connect(self.setCommand) + if isinstance(model, Qt.QAbstractItemModel): + enable = bool(model) + self.disableEditMode = not enable + self.setEnabled(enable) + self._model = model + self._model.dataChanged.connect(self.onDataChanged) + self._model.modelReset.connect(self.setCommand) + else: + TaurusBaseContainer.setModel(self, model) + def model(self): return self._model @@ -712,7 +717,6 @@ def __init__(self, parent=None, designMode=False): self.spockCommand = SpockCommandWidget("Spock", self) self.spockCommand.setSizePolicy( Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum) - self.spockCommand.setModelInConfig(True) spockCommandLayout = Qt.QHBoxLayout() spockCommandLayout.setContentsMargins(0, 0, 0, 0) # spockCommandLayout.addWidget(spockCommandLabel) @@ -1004,9 +1008,9 @@ def setModel(self, model): newModelObj.macrosUpdated.connect( self.macroComboBox.onMacrosUpdated) self.macroComboBox.setModel(model) - self.favouritesMacrosEditor.setModelCheck(model) - self.historyMacrosViewer.setModelCheck(model) - self.spockCommand.setModelCheck(model) + self.favouritesMacrosEditor.setModel(model) + self.historyMacrosViewer.setModel(model) + self.spockCommand.setModel(model) @classmethod def getQtDesignerPluginInfo(cls): @@ -1079,10 +1083,14 @@ def createMacroExecutor(args): macroExecutor = TaurusMacroExecutor() macroExecutor.setModelInConfig(True) macroExecutor.doorChanged.connect(macroExecutor.onDoorChanged) + settings = macroExecutor.getQSettings() + taurus_config_raw = settings.value("TaurusConfig") + taurus_config = pickle.loads(taurus_config_raw.data()) + oldmodel = taurus_config['__itemConfigurations__']['model'] if len(args) == 2: macroExecutor.setModel(args[0]) macroExecutor.doorChanged.emit(args[1]) - else: + if args[0] == oldmodel: macroExecutor.loadSettings() return macroExecutor From d8689755d7e11d60138f1e601a4d177cd412fdb1 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 10 Oct 2019 12:13:37 +0200 Subject: [PATCH 393/830] Fix Tango inheritance --- src/sardana/tango/core/SardanaDevice.py | 8 ++++---- src/sardana/tango/pool/Pool.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sardana/tango/core/SardanaDevice.py b/src/sardana/tango/core/SardanaDevice.py index 3e496be7a2..7d8d9ca70c 100644 --- a/src/sardana/tango/core/SardanaDevice.py +++ b/src/sardana/tango/core/SardanaDevice.py @@ -35,7 +35,7 @@ import threading import PyTango.constants -from PyTango import Device_4Impl, DeviceClass, Util, DevState, \ +from PyTango import LatestDeviceImpl, DeviceClass, Util, DevState, \ AttrQuality, TimeVal, ArgType, ApiUtil, DevFailed, WAttribute from taurus.core.util.threadpool import ThreadPool @@ -66,7 +66,7 @@ def get_thread_pool(): return __thread_pool -class SardanaDevice(Device_4Impl, Logger): +class SardanaDevice(LatestDeviceImpl, Logger): """SardanaDevice represents the base class for all Sardana :class:`PyTango.DeviceImpl` classes""" @@ -74,7 +74,7 @@ def __init__(self, dclass, name): """Constructor""" self.in_constructor = True try: - Device_4Impl.__init__(self, dclass, name) + LatestDeviceImpl.__init__(self, dclass, name) self.init(name) Logger.__init__(self, name) @@ -512,7 +512,7 @@ def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Generic description", doc_url="http://sardana-controls.org/", __icon=self.get_name().lower() + ".png", - InheritedFrom=["Device_4Impl"]) + InheritedFrom=["Device_5Impl"]) def write_class_property(self): """Write class properties ``ProjectTitle``, ``Description``, diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index e073abf4f4..54c0e29934 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -48,12 +48,12 @@ import collections -class Pool(PyTango.Device_4Impl, Logger): +class Pool(PyTango.LatestDeviceImpl, Logger): ElementsCache = None def __init__(self, cl, name): - PyTango.Device_4Impl.__init__(self, cl, name) + PyTango.LatestDeviceImpl.__init__(self, cl, name) Logger.__init__(self, name) self.init(name) self.init_device() @@ -1576,7 +1576,7 @@ def __init__(self, name): def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Device Pool management class", doc_url="http://sardana-controls.org/", - InheritedFrom="Device_4Impl") + InheritedFrom="Device_5Impl") def write_class_property(self): util = PyTango.Util.instance() From 2ca04b880bf291cd56aef2c215de674a40fbf212 Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 30 Jan 2020 09:44:27 +0100 Subject: [PATCH 394/830] New test for MeasurementGroupAPI functions. --- .../test_MeasurementGroupConfiguretion.py | 240 ++++++++---------- 1 file changed, 107 insertions(+), 133 deletions(-) diff --git a/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py b/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py index e60dd074c6..c447ebeb70 100644 --- a/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py +++ b/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py @@ -1,25 +1,11 @@ from sardana.taurus.core.tango.sardana import * import uuid -import numpy from taurus import Device from taurus.external.unittest import TestCase from taurus.core.tango.tangovalidator import TangoDeviceNameValidator -from taurus.test.base import insertTest -from sardana.sardanautils import is_number, is_non_str_seq, is_pure_str from sardana.taurus.core.tango.sardana.pool import registerExtensions from sardana.tango.pool.test.base_sartest import SarTestTestCase -def is_numerical(obj): - if is_number(obj): - return True - if is_non_str_seq(obj) or isinstance(obj, numpy.ndarray): - if is_number(obj[0]): - return True - elif is_non_str_seq(obj[0]) or isinstance(obj, numpy.ndarray): - if is_number(obj[0][0]): - return True - return False - class TestMeasurementGroupConfiguration(SarTestTestCase, TestCase): @@ -41,8 +27,18 @@ def _assertResult(self, result, channels, expected_value): msg = "{} are missing".format(expected_channels) self.assertEqual(len(expected_channels), 0, msg) - def test_enabled(self): - elements = ["_test_ct_1_1", "_test_ct_1_2"] + def _assertMultipleResults(self, result, channels, expected_values): + expected_channels = list(channels) + print(result) + for (channel, value), expected_value in zip(result.items(), expected_values): + msg = "unexpected key: {}".format(channel) + self.assertIn(channel, expected_channels, msg) + expected_channels.remove(channel) + self.assertEqual(value, expected_value) + msg = "{} are missing".format(expected_channels) + self.assertEqual(len(expected_channels), 0, msg) + + def test_enabled(self, elements = ["_test_ct_1_1", "_test_ct_1_2"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -93,190 +89,168 @@ def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2"]): print(full_names) is_output = mg.getOutput(*full_names) self._assertResult(is_output, elements, True) - - # mg = Device(mg_name) - # enabled = mg.getOutput(*elements) - # self._assertResult(enabled, elements, True) - # for key in elements: - # self.assertTrue(mg._getOutputChannels()[key]) - # mg._setOutputChannels(False, elements) - # for key in elements: - # self.assertFalse(mg._getOutputChannels()[key]) - # mg._setOutputChannels(True, elements) - # for key in elements: - # self.assertTrue(mg._getOutputChannels()[key]) + # TODO Fix ret_full_name error and make a test finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def testPlotTypeChannels(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) - print(mg._getPlotTypeChannels()) - mg._setPlotTypeChannels("Image", [elements[0]]) - mg._setPlotTypeChannels("Spectrum", [elements[1]]) - mg._setPlotTypeChannels("No", [elements[2]]) - print(mg._getPlotTypeChannels()) - try: - mg._setPlotTypeChannels("asdf", [elements[2]]) - error = 1 - except: - error = 0 - if error == 1: - raise ValueError("Plot type string values are not restricted") - print(mg._getPlotTypeChannels()) + plottype = mg.getPlotType() + self._assertResult(plottype, elements, 0) + mg.setPlotType("Image", elements[0]) + mg.setPlotType("Spectrum", elements[1]) + mg.setPlotType("No", elements[2]) + plottype = mg.getPlotType() + expected_values = [2, 1, 0] + self._assertMultipleResults(plottype, elements, expected_values) + with self.assertRaises(ValueError): + mg.setPlotType("asdf", elements[2]) + print(mg.getPlotType()) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def testPlotAxesChannels(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) - mg._setPlotTypeChannels("Image", [elements[0]]) - mg._setPlotTypeChannels("Spectrum", [elements[1]]) - mg._setPlotTypeChannels("No", [elements[2]]) - print(mg._getPlotAxesChannels()) - mg._setPlotAxesChannels(["", ""], [elements[0]]) - mg._setPlotAxesChannels([""], [elements[1]]) - print(mg._getPlotAxesChannels()) - mg._setPlotAxesChannels(["", ""], [elements[0]]) - mg._setPlotAxesChannels([""], [elements[1]]) - print(mg._getPlotAxesChannels()) - mg._setPlotAxesChannels(["", ""], [elements[0]]) - print(mg._getPlotAxesChannels()) + mg.setPlotType("Image", elements[0]) + mg.setPlotType("Spectrum", elements[1]) + mg.setPlotType("No", elements[2]) + result = mg.getPlotAxes() + self._assertResult(result, elements, []) + mg.setPlotAxes(["", ""], elements[0]) + mg.setPlotAxes([""], elements[1]) + result = mg.getPlotAxes() + expected_result = [['', ''], [''], []] + self._assertMultipleResults(result, elements, expected_result) + mg.setPlotAxes(["", ""], elements[0]) + mg.setPlotAxes([""], elements[1]) + result = mg.getPlotAxes() + expected_result = [['', ''], [''], []] + self._assertMultipleResults(result, elements, expected_result) + mg.setPlotAxes(["", ""], elements[0]) + result = mg.getPlotAxes() + expected_result = [['', ''], [''], []] + self._assertMultipleResults(result, elements, expected_result) - try: - mg._setPlotAxesChannels([""], [elements[2]]) - error = 1 - except: - error = 0 - if error == 1: - raise ValueError("Channel without PlotType shouldn't accept a value") - try: - mg._setPlotAxesChannels(["", ""], [elements[1]]) - error = 1 - except: - error = 0 - if error == 1: - raise ValueError("PlotType spectrum should only accept one axis") - try: - mg._setPlotAxesChannels([""], [elements[0]]) - error = 1 - except: - error = 0 - if error == 1: - raise ValueError("PlotType image should only accept two axis") - - print(mg._getPlotAxesChannels()) + with self.assertRaises(RuntimeError): + mg.setPlotAxes([""], elements[2]) + with self.assertRaises(ValueError): + mg.setPlotAxes(["", ""], elements[1]) + with self.assertRaises(ValueError): + mg.setPlotAxes([""], elements[0]) + print(mg.getPlotAxes()) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def testCtrlsTimer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_Timer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) - previous = mg._getCtrlsTimer() + previous = mg.getTimer() print(previous) - mg._setCtrlsTimer(['_test_ct_1_3']) - if mg._getCtrlsTimer() == previous: - raise RuntimeError("setter function failed aplying changes") - print(mg._getCtrlsTimer()) + mg.setTimer('_test_ct_1_3') + self.assertNotEqual(mg.getTimer(), previous) + self._assertResult(mg.getTimer(), elements, '_test_ct_1_3') + self._assertResult(mg.getTimer(ret_by_ctrl=True), ['_test_ct_ctrl_1'], '_test_ct_1_3') finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def testCtrlsMonitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) - # TODO set method is missing a parameter (doesn't work as the description says) try: mg = Device(mg_name) - previous = mg._getCtrlsMonitor() + previous = mg.getMonitor() print(previous) - mg._setCtrlsTimer(["_test_ct_1_2"]) - if mg._getCtrlsMonitor() == previous: - raise RuntimeError("setter function failed aplying changes") - print(mg._getCtrlsMonitor()) + mg.setMonitor("_test_ct_1_2") + self.assertNotEqual(mg.getMonitor(), previous) + self._assertResult(mg.getMonitor(), elements, '_test_ct_1_2') + self._assertResult(mg.getMonitor(ret_by_ctrl=True), ['_test_ct_ctrl_1'], '_test_ct_1_2') finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def testCtrlsSynchronization(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) - previous = mg._getCtrlsSynchronization() - print(previous) - mg._setCtrlsSynchronization('Gate') - if mg._getCtrlsSynchronization() == previous: - raise RuntimeError("setter function failed aplying changes") - - previous = mg._getCtrlsSynchronization() - print(previous) - mg._setCtrlsSynchronization('Trigger') - if mg._getCtrlsSynchronization() == previous: - raise RuntimeError("setter function failed aplying changes") + result = mg.getSynchronizer() + self._assertResult(result, elements, 'software') + mg.setSynchronizer('_test_tg_1_2') + result = mg.getSynchronizer() + self._assertResult(result, elements, '_test_tg_1_2') + mg.setSynchronizer('software') + result = mg.getSynchronizer() + self._assertResult(result, elements, 'software') + result = mg.getSynchronizer(ret_by_ctrl=True) + self._assertResult(result, ['_test_ct_ctrl_1'], 'software') + with self.assertRaises(Exception): + mg.setSynchronizer('asdf') + self._assertResult(result, ['_test_ct_ctrl_1'], 'software') - previous = mg._getCtrlsSynchronization() - print(previous) - mg._setCtrlsSynchronization('Start') - if mg._getCtrlsSynchronization() == previous: - raise RuntimeError("setter function failed aplying changes") - print(mg._getCtrlsSynchronization()) + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) - try: - mg._setCtrlsSynchronization('Software') - error = 1 - except: - error = 0 - if error == 1: - raise ValueError("CtrlsSynchronization should only admit Gate/Trigger/Start") - print(mg._getCtrlsSynchronization()) + def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", "_test_ct_1_1", "_test_ct_1_2"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + try: + mg = Device(mg_name) + enabled = mg.getValueRefEnabled(*elements) + self._assertResult(enabled, elements, False) + mg.setValueRefEnabled(False, *elements) + enabled = mg.getValueRefEnabled(*elements) + self._assertResult(enabled, elements, False) + enabled = mg.getValueRefEnabled("_test_2d_ctrl_1") + self._assertResult(enabled, elements[:2], False) + enabled = mg.getValueRefEnabled("_test_ct_ctrl_1") + self._assertResult(enabled, elements[-2:], False) + mg.setValueRefEnabled(True, *elements) + enabled = mg.getValueRefEnabled(*elements) + self._assertResult(enabled, elements, True) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def testCtrlsSynchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) - # TODO ERROR function doesn't accept triggergate try: mg = Device(mg_name) - print(mg._getCtrlsSynchronizer()) - mg.setSynchronizer('software') - print(mg._getCtrlsSynchronizer()) - mg.setSynchronizer('triggergate') - print(mg._getCtrlsSynchronizer()) + pattern = mg.getValueRefEnabled(*elements) + self._assertResult(pattern, elements, False) + mg.setValueRefEnabled('/tmp/test_foo.txt', *elements) + pattern = mg.getValueRefEnabled(*elements) + self._assertResult(pattern, elements, '/tmp/test_foo.txt') + pattern = mg.getValueRefEnabled("_test_2d_ctrl_1") + self._assertResult(pattern, elements, '/tmp/test_foo.txt') finally: mg.cleanUp() self.pool.DeleteElement(mg_name) -if __name__ == '__main__': - - test = TestMeasurementGroupConfiguration() - test.setUp() - test.test_output() - test.tearDown() - # dev = taurus.Device("mntgrp/pool_test01_1/mntgrp03") - # print(dev) - # print(dev.getSynchronizer(ret_by_ctrl=True)) From 816e24e36b518d000f660bfe2e2334804b0747ac Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 30 Jan 2020 11:02:16 +0100 Subject: [PATCH 395/830] Removal of unused code --- .../pool/poolcontrollers/TangoController.py | 210 ------------------ 1 file changed, 210 deletions(-) delete mode 100644 src/sardana/pool/poolcontrollers/TangoController.py diff --git a/src/sardana/pool/poolcontrollers/TangoController.py b/src/sardana/pool/poolcontrollers/TangoController.py deleted file mode 100644 index b85cbe1b66..0000000000 --- a/src/sardana/pool/poolcontrollers/TangoController.py +++ /dev/null @@ -1,210 +0,0 @@ -############################################################################## -## -# This file is part of Sardana -## -# http://www.sardana-controls.org/ -## -# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -# Sardana 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. -## -# Sardana 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 Sardana. If not, see . -## -############################################################################## - -import math - -import PyTango - -from taurus.core.util.containers import CaselessDict - -from sardana import State, DataAccess -from sardana.pool.controller import MotorController, CounterTimerController, \ - ZeroDController, \ - Type, Access, Description, DefaultValue - -TangoAttribute = "TangoAttribute" -Formula = "Formula" - - -class ReadTangoAttributes(object): - """ Generic class that has as many devices as the user wants. - Each device has a tango attribute and a formula and the 'hardware' tango calls - are optimized in the sense that only one call per tango device is issued. - """ - axis_attributes = { - TangoAttribute: {Type: str, Access: DataAccess.ReadWrite, - Description: 'Attribute to read (e.g. a/b/c/attr)'}, - Formula: {Type: str, Access: DataAccess.ReadWrite, - DefaultValue: "VALUE", - Description: 'The Formula to get the desired value.\n' - 'e.g. "math.sqrt(VALUE)"'}, - } - - def __init__(self): - #: dict - self._pending = {} - - #: dict>> - self._devices = CaselessDict() - - #: dict, str, DeviceProxy>> - self._axis_tango_attributes = {} - - #: dict> - self._axis_formulas = {} - - def add_device(self, axis): - self._pending[ - axis] = "No tango attribute associated to this device yet" - self._axis_formulas[axis] = self.axis_attribute[Formula][DefaultValue] - - def delete_device(self, axis): - if axis in self._pending: - del self._pending[axis] - else: - del self._axis_tango_attributes[axis] - del self._axis_formulas[axis] - - def state_one(self, axis): - pending_info = self._pending.get(axis) - if pending_info is not None: - return State.Fault, pending_info - return State.On, 'Always ON, just reading tango attribute' - - def pre_read_all(self): - self._devices_read = {} - - def pre_read_one(self, axis): - attr_name, dev = self._axis_tango_attributes[axis][1:] - dev_attrs = self._devices_read.get(dev) - if dev_attrs is None: - self._ - self._devices_read[dev] = dev_attrs = [] - dev_attrs.append(attr_name) - - def read_all(self): - pass - - def read_one(self, axis): - pass - - def get_extra_attribute_par(self, axis, name): - if name == TangoAttribute: - return self._axis_tango_attributes[axis][0] - elif name == Formula: - return self._axis_formulas[axis] - - def set_extra_attribute_par(self, axis, name, value): - if name == TangoAttribute: - value = value.lower() - self._axis_tango_attributes[axis] = data = value, None, None - try: - dev_name, attr_name = value.rsplit("/", 1) - data[1] = attr_name - except: - self._pending[axis] = "invalid device name " + value - raise Exception(self._pending[axis]) - dev_info = self._devices.get(dev_name) - if dev_info is None: - try: - proxy = PyTango.DeviceProxy(dev_name) - except PyTango.DevFailed as df: - if len(df.args): - self._pending[axis] = df.args[0].reason + ": " + \ - df.args[0].desc - else: - self._pending[ - axis] = "Unknwon PyTango Error: " + str(df) - raise - self._devices[dev_name] = dev_info = proxy, [] - data[2] = dev_info[0] - dev_info[1].append(attr_name) - - elif name == Formula: - self._axis_formulas[axis] = value - - -class TangoCounterTimerController(ReadTangoAttributes, CounterTimerController): - """This controller offers as many channels as the user wants. - Each channel has two _MUST_HAVE_ extra attributes: - +) TangoAttribute - Tango attribute to retrieve the value of the counter - +) Formula - Formula to evaluate using 'VALUE' as the tango attribute value - As examples you could have: - ch1.TangoExtraAttribute = 'my/tango/device/attribute1' - ch1.Formula = '-1 * VALUE' - ch2.TangoExtraAttribute = 'my/tango/device/attribute2' - ch2.Formula = 'math.sqrt(VALUE)' - ch3.TangoExtraAttribute = 'my_other/tango/device/attribute1' - ch3.Formula = 'math.cos(VALUE)' - - ..warning:: As pointed in sardana-org/sardana#722 this controller is - buggy. A working version of this and other Tango controllers can be found - in the third-party repository: http://sf.net/p/sardana/controllers.git. - As part of the sardana-org/sardana#181 this controller will be fixed or - removed from Sardana. - """ - - gender = "" - model = "" - organization = "Sardana team" - - MaxDevice = 1024 - - def __init__(self, inst, props, *args, **kwargs): - ReadTangoAttributes.__init__(self) - CounterTimerController.__init__(self, inst, props, *args, **kwargs) - - def AddDevice(self, axis): - self.add_device(axis) - - def DeleteDevice(self, axis): - self.delete_device(axis) - - def StateOne(self, axis): - return self.state_one(axis) - - def PreReadAll(self): - self.pre_read_all() - - def PreReadOne(self, axis): - self.pre_read_one(axis) - - def ReadAll(self): - self.read_all() - - def ReadOne(self, axis): - return self.read_one(axis) - - def GetExtraAttributePar(self, axis, name): - return self.get_extra_attribute_par(axis, name) - - def SetExtraAttributePar(self, axis, name, value): - self.set_extra_attribute_par(axis, name, value) - - def SendToCtrl(self, in_data): - return "" - - def AbortOne(self, axis): - pass - - def PreStartAllCT(self): - pass - - def StartOneCT(self, axis): - pass - - def StartAllCT(self): - pass - - def LoadOne(self, axis, value): - pass From b54e3df43d0676ed1487823943fe52b5f7425da1 Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 30 Jan 2020 12:18:45 +0100 Subject: [PATCH 396/830] Update documentation to match the current behaviour of the macroexecutor --- doc/source/users/taurus/macroexecutor.rst | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/doc/source/users/taurus/macroexecutor.rst b/doc/source/users/taurus/macroexecutor.rst index b3675dfa6c..de246737e1 100644 --- a/doc/source/users/taurus/macroexecutor.rst +++ b/doc/source/users/taurus/macroexecutor.rst @@ -53,8 +53,11 @@ Options:: The model list is optional and is a space-separated list of two device names: macro server and door. -If not provided at the application startup, models can be later on changed in configuration dialog. - +If not provided at the application startup, models can be later on changed in configuration dialog. + +If the macro server and door are changed in the configuration dialog, the favourites list and the history will +be discarded. + Extra functionalities: - Changing macro configuration @@ -143,10 +146,21 @@ with current macro and its current settings. To restore macro from favourites list just select it in the list and macro parameters editor will immediately populate with stored settings. -- modifying favouites list +- modifying favourites list First select favourite macro and buttons with arrows becomes enable (if it is feasible to change order) - removing a favourite -First select favourite macro, button with '-' sign appears enabled. After pressing this button, previously selected macro disappears from the list. \ No newline at end of file +First select favourite macro, button with '-' sign appears enabled. After pressing this button, previously selected macro disappears from the list. + +.. _using_history_viewer: + +Using history viewer +-------------------- + +Once a macreo is used, it gets registered in the history viewer with the same values it has been executed. + +To load a macro from the history viewer, doubleclick on the macro of the history viewer. + +To remove all history, click on the bin button. \ No newline at end of file From 981ff45f31feffc3cf98909c3eba471ce85676bc Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 31 Jan 2020 15:45:53 +0100 Subject: [PATCH 397/830] Avoid unnecessary creations of DeviceProxies in ascanct To determine if a channel is proceeding from Sardana or is an arbitrary Tango attribute it is enough to use TangoDeviceNameValidator. --- src/sardana/macroserver/scan/gscan.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index c6b9d25b48..45218bf8d9 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -51,6 +51,7 @@ from taurus.core.util.enumeration import Enumeration from taurus.core.util.threadpool import ThreadPool from taurus.core.util.event import CallableRef +from taurus.core.tango.tangovalidator import TangoDeviceNameValidator from sardana.util.tree import BranchNode, LeafNode, Tree from sardana.util.motion import Motor as VMotor @@ -2039,18 +2040,12 @@ def is_measurement_group_compatible(measurement_group): .. todo:: add validation for psuedo counters """ non_compatible_channels = [] + validator = TangoDeviceNameValidator() for channel_info in measurement_group.getChannels(): full_name = channel_info["full_name"] name = channel_info["name"] - try: - # Use DeviceProxy instead of taurus to avoid crashes in Py3 - # See: tango-controls/pytango#292 - # taurus.Device(full_name) - PyTango.DeviceProxy(full_name) - except Exception: - # external channels are attributes so Device constructor fails + if not validator.isValid(full_name): non_compatible_channels.append(name) - continue is_compatible = len(non_compatible_channels) == 0 return is_compatible, non_compatible_channels From 655308f01b8c258f192f551c69dd5207baeef416 Mon Sep 17 00:00:00 2001 From: aalonso Date: Fri, 31 Jan 2020 16:55:49 +0100 Subject: [PATCH 398/830] Fix problem that caused conflict between ini file and arguments when changing macroserver and door on runtime. --- .../extra_macroexecutor/sequenceeditor/sequenceeditor.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 97becda18d..8ed52aeba1 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -28,7 +28,7 @@ """ import os import sys - +import pickle from lxml import etree import PyTango @@ -976,10 +976,15 @@ def createSequencer(args, options): sequencer = TaurusSequencer() sequencer.setModelInConfig(True) sequencer.doorChanged.connect(sequencer.onDoorChanged) + settings = sequencer.getQSettings() + taurus_config_raw = settings.value("TaurusConfig") + taurus_config = pickle.loads(taurus_config_raw.data()) + oldmodel = taurus_config['__itemConfigurations__']['model'] if len(args) == 2: sequencer.setModel(args[0]) sequencer.doorChanged.emit(args[1]) - sequencer.loadSettings() + if args[0] == oldmodel: + sequencer.loadSettings() if options.file is not None: sequencer.taurusSequencerWidget.loadFile(options.file) return sequencer From ace0c567751898dc733876afe89f41401d131056 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 31 Jan 2020 17:04:53 +0100 Subject: [PATCH 399/830] Make sure plotting is updated on client side --- doc/source/devel/howto_macros/macros_general.rst | 10 ++++++++++ src/sardana/macroserver/macros/examples/plotting.py | 3 +++ src/sardana/macroserver/macros/examples/scans.py | 1 + 3 files changed, 14 insertions(+) diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst index c88f59063e..2f0b2bc8f6 100644 --- a/doc/source/devel/howto_macros/macros_general.rst +++ b/doc/source/devel/howto_macros/macros_general.rst @@ -983,6 +983,10 @@ example:: self.pyplot.title(r'Verify $J_0(x)=\frac{1}{\pi}\int_0^{\pi}\cos(x \sin\phi)\,d\phi$') self.pyplot.xlabel('$x$') self.pyplot.legend() + self.pyplot.draw() + +The last call to `pyplot.draw()` is important to ensure the client updates +the figure properly. Running this macro from spock will result in something like: @@ -1014,6 +1018,7 @@ Just for fun, the following macro computes a fractal and plots it as an image:: mask = (fractal == 255) & (abs(z) > 10) fractal[mask] = 254 * n / finteractions self.pyplot.imshow(fractal) + self.pyplot.draw() And the resulting image (interactions=20, density=512): @@ -1046,6 +1051,11 @@ Also consider that each time you plot the complete data to be plotted is sent from the server to the client... so please avoid plotting arrays of 10,000,000 points! +Clients like spock receive the requests to plot in a separate thread. +Matplotlib has a long history of issues concerning plot updates using different +threads. To mitigate these effects please be sure to call `self.pyplot.draw()` +on your macro every time you needto be sure the matplotlib figure is up to date. + .. _sardana-macro-input: Asking for user input diff --git a/src/sardana/macroserver/macros/examples/plotting.py b/src/sardana/macroserver/macros/examples/plotting.py index 103dcc34d8..de593146dc 100644 --- a/src/sardana/macroserver/macros/examples/plotting.py +++ b/src/sardana/macroserver/macros/examples/plotting.py @@ -26,6 +26,7 @@ def J0_plot(self): r'Verify $J_0(x)=\frac{1}{\pi}\int_0^{\pi}\cos(x \sin\phi)\,d\phi$') self.pyplot.xlabel('$x$') self.pyplot.legend() + self.pyplot.draw() from numpy import random @@ -36,6 +37,7 @@ def random_image(self): """Shows a random image 32x32""" img = random.random((32, 32)) self.pyplot.matshow(img) + self.pyplot.draw() import numpy @@ -61,3 +63,4 @@ def mandelbrot(self, interactions, density): mask = (fractal == 255) & (abs(z) > 10) fractal[mask] = 254 * n / interactions self.pyplot.imshow(fractal) + self.pyplot.draw() diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index 829d809902..95f9317654 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -656,6 +656,7 @@ def run(self, motor, start_pos, final_pos, nr_interv, integ_time, **opts): # as a bonus, plot the fit self.pyplot.plot(x, y, 'ro') self.pyplot.plot(x, fitted_y, 'b-') + self.pyplot.draw() class ascanct_midtrigger(Macro): From faa17eac20031caec477ca27c4dd4b5db31cc2ec Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 31 Jan 2020 17:31:51 +0100 Subject: [PATCH 400/830] Minor corrections --- doc/source/devel/howto_macros/macros_general.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst index 2f0b2bc8f6..d0ac20c412 100644 --- a/doc/source/devel/howto_macros/macros_general.rst +++ b/doc/source/devel/howto_macros/macros_general.rst @@ -983,9 +983,9 @@ example:: self.pyplot.title(r'Verify $J_0(x)=\frac{1}{\pi}\int_0^{\pi}\cos(x \sin\phi)\,d\phi$') self.pyplot.xlabel('$x$') self.pyplot.legend() - self.pyplot.draw() + self.pyplot.draw() -The last call to `pyplot.draw()` is important to ensure the client updates +The last call to ``pyplot.draw()`` is important to ensure the client updates the figure properly. Running this macro from spock will result in something like: @@ -1053,8 +1053,8 @@ points! Clients like spock receive the requests to plot in a separate thread. Matplotlib has a long history of issues concerning plot updates using different -threads. To mitigate these effects please be sure to call `self.pyplot.draw()` -on your macro every time you needto be sure the matplotlib figure is up to date. +threads. To mitigate these effects please be sure to call ``self.pyplot.draw()`` +on your macro every time you need to be sure the matplotlib figure is up to date. .. _sardana-macro-input: From a5c96fc1e1375a509d68998df7a39d5765cb95d7 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 31 Jan 2020 18:09:14 +0100 Subject: [PATCH 401/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0ba094f3e..ea40c692dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Reintroduce backwards compatibility for measurement groups' configurations (URIs) created with Taurus 3 (#1266, #1271) * Measurement groups renaming with `renameelem` macro(#951) +* Macro plotting in new versions of ipython and matplotlib require extra call to + pyplot.draw() to make sure that the plot is refreshed (#1280) ### Deprecated From 6238d28a3f59f9b1cf121a45b520a06fb2a4c71b Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 3 Feb 2020 12:32:13 +0100 Subject: [PATCH 402/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea40c692dd..10be64161c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Measurement groups renaming with `renameelem` macro(#951) * Macro plotting in new versions of ipython and matplotlib require extra call to pyplot.draw() to make sure that the plot is refreshed (#1280) +* Remove TangoAttribute controllers from Sardana (#181, #1279) ### Deprecated From ef1716462a3517f999d0241b2e941c70fd639c4e Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 3 Feb 2020 13:06:33 +0100 Subject: [PATCH 403/830] setParentModel removed from sequencer and manual test are ok --- .../sequenceeditor/sequenceeditor.py | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 8ed52aeba1..b1768d47c3 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -324,6 +324,7 @@ def dropEvent(self, event): class TaurusSequencerWidget(TaurusWidget): + doorChanged = Qt.pyqtSignal('QString') macroStarted = Qt.pyqtSignal('QString') plotablesFilterChanged = Qt.pyqtSignal(compat.PY_OBJECT) currentMacroChanged = Qt.pyqtSignal(compat.PY_OBJECT) @@ -350,13 +351,13 @@ def __init__(self, parent=None, designMode=False): self.layout().addWidget(splitter) splitter.setOrientation(Qt.Qt.Vertical) - sequenceEditor = TaurusWidget() - splitter.addWidget(sequenceEditor) - sequenceEditor.setUseParentModel(True) - sequenceEditor.setLayout(Qt.QVBoxLayout()) - sequenceEditor.layout().setContentsMargins(0, 0, 0, 0) + self.sequenceEditor = TaurusWidget() + splitter.addWidget(self.sequenceEditor) + self.sequenceEditor.setModelInConfig(True) + self.sequenceEditor.setLayout(Qt.QVBoxLayout()) + self.sequenceEditor.layout().setContentsMargins(0, 0, 0, 0) - self.tree = MacroSequenceTree(sequenceEditor) + self.tree = MacroSequenceTree(self.sequenceEditor) self.sequenceProxyModel = MacroSequenceProxyModel() self.sequenceProxyModel.setSourceModel(self._sequenceModel) self.tree.setModel(self.sequenceProxyModel) @@ -429,14 +430,14 @@ def __init__(self, parent=None, designMode=False): 0, 0, Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed) actionsLayout.addItem(spacerItem) - sequenceEditor.layout().addLayout(actionsLayout) + self.sequenceEditor.layout().addLayout(actionsLayout) macroLayout = Qt.QHBoxLayout() macroLayout.setContentsMargins(0, 0, 0, 0) macroLabel = Qt.QLabel("Macro:") macroLayout.addWidget(macroLabel) self.macroComboBox = MacroComboBox(self) - self.macroComboBox.setUseParentModel(True) + self.macroComboBox.setModelInConfig(True) self.macroComboBox.setModelColumn(0) self.macroComboBox.setSizePolicy( Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum) @@ -452,7 +453,7 @@ def __init__(self, parent=None, designMode=False): addButton.setDefaultAction(self.addMacroAction) macroLayout.addWidget(addButton) - sequenceEditor.layout().addLayout(macroLayout) + self.sequenceEditor.layout().addLayout(macroLayout) sequenceLayout = Qt.QHBoxLayout() sequenceLayout.addWidget(self.tree) @@ -482,7 +483,7 @@ def __init__(self, parent=None, designMode=False): 0, 40, Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Expanding) layout.addItem(spacerItem) sequenceLayout.addLayout(layout) - sequenceEditor.layout().addLayout(sequenceLayout) + self.sequenceEditor.layout().addLayout(sequenceLayout) self.parametersProxyModel = MacroParametersProxyModel() self.parametersProxyModel.setSourceModel(self._sequenceModel) @@ -906,6 +907,8 @@ def setModel(self, model): TaurusWidget.setModel(self, model) newModelObj = self.getModelObj() newModelObj.macrosUpdated.connect(self.macroComboBox.onMacrosUpdated) + self.sequenceEditor.setModel(model) + self.macroComboBox.setModel(model) @classmethod def getQtDesignerPluginInfo(cls): @@ -925,7 +928,9 @@ def initComponents(self): #@todo: take care about storing model self.setModelInConfig(True) self.taurusSequencerWidget = TaurusSequencerWidget(self) - self.taurusSequencerWidget.setUseParentModel(True) + self.taurusSequencerWidget.setModelInConfig(True) + self.taurusSequencerWidget.doorChanged.connect( + self.taurusSequencerWidget.onDoorChanged) self.registerConfigDelegate(self.taurusSequencerWidget) self.setCentralWidget(self.taurusSequencerWidget) self.taurusSequencerWidget.shortMessageEmitted.connect( @@ -956,6 +961,12 @@ def onDoorChanged(self, doorName): self.taurusSequencerWidget.onMacroStatusUpdated) self.taurusSequencerWidget.onDoorChanged(doorName) + def setModel(self, model): + """Reimplemented from :meth:`TaurusWidget.setModel`""" + TaurusWidget.setModel(self, model) + self.setWindowTitle(Qt.QApplication.applicationName() + ": " + model) + self.taurusSequencerWidget.setModel(model) + @classmethod def getQtDesignerPluginInfo(cls): return None From 658d7a1b8a66ada000b23b2a80b3daf6338c4239 Mon Sep 17 00:00:00 2001 From: aalonso Date: Tue, 4 Feb 2020 10:40:14 +0100 Subject: [PATCH 404/830] setParentModel removed from senv, and delegate. Tests on sequencer are ok. scanplotter set default value so there shouldn't be any difference --- .../macroparameterseditor/customeditors/senv.py | 8 +++++--- .../taurus/qt/qtgui/extra_macroexecutor/scanplotter.py | 1 - .../qtgui/extra_macroexecutor/sequenceeditor/delegate.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py index 3af96a985f..13cb0715b2 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py @@ -88,6 +88,9 @@ def onNameComboBoxChanged(self, index): self.valueWidget = None self.valueWidget, label = getSenvValueEditor(text, self) + if text == "ActiveMntGrp": + self.valueWidget.setModel(self.model()) + self.valueWidget.setModel("/MeasurementGroupList") paramRepeatIndex = self.model().index(1, 0, self.rootIndex()) repeatIndex = paramRepeatIndex.child(0, 0) @@ -108,8 +111,6 @@ def getSenvValueEditor(envName, parent): label = "value:" if envName == "ActiveMntGrp": editor = MSAttrListComboBoxParam(parent) - editor.setUseParentModel(True) - editor.setModel("/MeasurementGroupList") elif envName == "ExtraColumns": editor = ExtraColumnsEditor(parent) label = None @@ -221,7 +222,7 @@ def createEditor(self, parent, option, index): editor.setView(treeView) elif index.column() == 2: editor = MSAttrListComboBox(parent) - editor.setUseParentModel(True) + editor.setModel(index.model()) editor.setModel("/InstrumentList") else: editor = Qt.QItemDelegate.createEditor(self, parent, option, index) @@ -384,6 +385,7 @@ def removeRow(self, row, parentIndex=None): import sys import taurus from taurus.qt.qtgui.application import TaurusApplication + from sardana.taurus.core.tango.sardana.macro import MacroNode app = TaurusApplication(sys.argv) args = app.get_command_line_args() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/scanplotter.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/scanplotter.py index 778cfb5a5c..450150a73a 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/scanplotter.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/scanplotter.py @@ -45,7 +45,6 @@ class ScanPlotter(TaurusTrend): def __init__(self, parent=None, designMode=False): TaurusTrend.__init__(self, parent, designMode) - self.setUseParentModel(False) self._plotables = CaselessDict() self._movingMotors = [] self._macroNames = [] diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py index 6880180b01..3210756773 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py @@ -90,7 +90,7 @@ def createEditor(self, parent, option, index): paramType = node.type() if paramType in globals.EDITOR_COMBOBOX_PARAMS: comboBox = AttrListComboBoxParam(parent, node) - comboBox.setUseParentModel(True) + comboBox.setModel(index.model()) return comboBox elif paramType in globals.EDITOR_SPINBOX_PARAMS: return SpinBoxParam(parent, node) From 2db466ae439e96263d18c713553ced559140f8d2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 4 Feb 2020 14:05:05 +0100 Subject: [PATCH 405/830] Fix Tauru4 quantities issue Sardana is not quantities aware, so for the moment extract magnitude to maintain backwards comp. --- src/sardana/macroserver/macros/standard.py | 17 +++++++++-------- src/sardana/taurus/core/tango/sardana/pool.py | 9 +++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 9cde33f391..e4db5736c4 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -309,7 +309,7 @@ def run(self, motor, pos): name = motor.getName() old_pos = motor.getPosition(force=True) offset_attr = motor.getAttribute('Offset') - old_offset = offset_attr.read().rvalue + old_offset = offset_attr.read().rvalue.magnitude new_offset = pos - (old_pos - old_offset) offset_attr.write(new_offset) msg = "%s reset from %.4f (offset %.4f) to %.4f (offset %.4f)" % ( @@ -368,8 +368,8 @@ def run(self, motor_list): except: val1 = str_fmt % motor.getPosition(force=True) - val2 = str_fmt % posObj.getMaxRange() - val3 = str_fmt % posObj.getMinRange() + val2 = str_fmt % posObj.getMaxRange().magnitude + val3 = str_fmt % posObj.getMinRange().magnitude if show_ctrlaxis: valctrl = str_fmt % (ctrl_name) @@ -387,8 +387,8 @@ def run(self, motor_list): val1 = str_fmt % motor.getDialPosition(force=True) dPosObj = motor.getDialPositionObj() - val2 = str_fmt % dPosObj.getMaxRange() - val3 = str_fmt % dPosObj.getMinRange() + val2 = str_fmt % dPosObj.getMaxRange().magnitude + val3 = str_fmt % dPosObj.getMinRange().magnitude dpos = list(map(str, [val2, val1, val3])) pos_data += [''] + dpos @@ -431,8 +431,9 @@ def run(self, motor_list): name = motor.getName() motor_names.append([name]) posObj = motor.getPositionObj() - upos = list(map(str, [posObj.getMaxRange(), motor.getPosition( - force=True), posObj.getMinRange()])) + upos = list(map(str, [posObj.getMaxRange().magnitude, + motor.getPosition(force=True), + posObj.getMinRange().magnitude])) pos_data = [''] + upos motor_pos.append(pos_data) @@ -491,7 +492,7 @@ class mstate(Macro): param_def = [['motor', Type.Moveable, None, 'Motor to check state']] def run(self, motor): - self.info("Motor %s" % str(motor.stateObj.read())) + self.info("Motor %s" % str(motor.stateObj.read().rvalue)) class umv(Macro): diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index aa38291ae3..52b3f200ea 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -227,11 +227,16 @@ def eventReceived(self, evt_src, evt_type, evt_value): v = None else: v = evt_value.rvalue + if hasattr(v, "magnitude"): + v = v.magnitude EventGenerator.fireEvent(self, v) def read(self, force=False): try: - self.last_val = self._attr.read(cache=not force).rvalue + last_val = self._attr.read(cache=not force).rvalue + if hasattr(last_val, "magnitude"): + last_val = last_val.magnitude + self.last_val = last_val except: self.error("Read error") self.debug("Details:", exc_info=1) @@ -549,7 +554,7 @@ def _information(self, tab=' '): indent = "\n" + tab + 10 * ' ' msg = [self.getName() + ":"] try: - state_value = self.stateObj.read().value + state_value = self.stateObj.read().rvalue # state_value is DevState enumeration (IntEnum) state = state_value.name.capitalize() except DevFailed as df: From 3fc3301cc4e5acefdb794b9f2d60042eab3e5f13 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 4 Feb 2020 14:06:27 +0100 Subject: [PATCH 406/830] Revert to_fqdn change to_fqdn for the test was set to False on purpose. Don't change it cause it does not raise deprecation warnings. --- src/sardana/pool/test/test_measurementgroup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index 9f72257cd8..fe9170e727 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -55,7 +55,7 @@ def prepare_meas(self, config): conf["user_elements"] = channel_ids self.pmg = createPoolMeasurementGroup(pool, conf) pool.add_element(self.pmg) - self.pmg.set_configuration_from_user(mg_conf, to_fqdn=True) + self.pmg.set_configuration_from_user(mg_conf, to_fqdn=False) return channel_names def prepare_attribute_listener(self): @@ -151,7 +151,7 @@ def consecutive_acquisitions(self, pool, config, synchronization): pool, config) # setting mg configuration - this cleans the action cache! - self.pmg.set_configuration_from_user(mg_conf, to_fqdn=True) + self.pmg.set_configuration_from_user(mg_conf, to_fqdn=False) repetitions = 0 for group in synchronization: repetitions += group[SynchParam.Repeats] From 287ac9e483ba9baa7f32b223f1affb80c5fbd56f Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 4 Feb 2020 14:08:41 +0100 Subject: [PATCH 407/830] Revert prepare calls in Tango devices. Timerable experimental channels already call prepare in the start_acquisition. IOR and 0D do not need prepare. MeasurementGroup has a dedicated command to call prepare. --- src/sardana/tango/pool/CTExpChannel.py | 1 - src/sardana/tango/pool/IORegister.py | 1 - src/sardana/tango/pool/MeasurementGroup.py | 2 -- src/sardana/tango/pool/OneDExpChannel.py | 1 - src/sardana/tango/pool/TwoDExpChannel.py | 1 - src/sardana/tango/pool/ZeroDExpChannel.py | 1 - 6 files changed, 7 deletions(-) diff --git a/src/sardana/tango/pool/CTExpChannel.py b/src/sardana/tango/pool/CTExpChannel.py index e6d9609c81..8c193abc58 100644 --- a/src/sardana/tango/pool/CTExpChannel.py +++ b/src/sardana/tango/pool/CTExpChannel.py @@ -214,7 +214,6 @@ def is_Value_allowed(self, req_type): return True def Start(self): - self.ct.prepare() self.ct.start_acquisition() diff --git a/src/sardana/tango/pool/IORegister.py b/src/sardana/tango/pool/IORegister.py index 5f239a1322..b6f4bd2378 100644 --- a/src/sardana/tango/pool/IORegister.py +++ b/src/sardana/tango/pool/IORegister.py @@ -234,7 +234,6 @@ def is_Value_allowed(self, req_type): return True def Start(self): - self.ior.prepare() self.ior.start_acquisition() diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 16f40fb8ec..3a9e4d219e 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -298,7 +298,6 @@ def Start(self): self.wait_for_operation() except: raise Exception("Cannot acquire: already involved in an operation") - self.measurement_group.prepare() self.measurement_group.start_acquisition() def Stop(self): @@ -309,7 +308,6 @@ def StartMultiple(self, n): self.wait_for_operation() except: raise Exception("Cannot acquire: already involved in an operation") - self.measurement_group.prepare(n) self.measurement_group.start_acquisition(multiple=n) diff --git a/src/sardana/tango/pool/OneDExpChannel.py b/src/sardana/tango/pool/OneDExpChannel.py index a8fe1aba48..db3ffc917c 100644 --- a/src/sardana/tango/pool/OneDExpChannel.py +++ b/src/sardana/tango/pool/OneDExpChannel.py @@ -223,7 +223,6 @@ def read_DataSource(self, attr): attr.set_value(data_source) def Start(self): - self.oned.prepare() self.oned.start_acquisition() diff --git a/src/sardana/tango/pool/TwoDExpChannel.py b/src/sardana/tango/pool/TwoDExpChannel.py index aaae989fdd..2ae70a102d 100644 --- a/src/sardana/tango/pool/TwoDExpChannel.py +++ b/src/sardana/tango/pool/TwoDExpChannel.py @@ -275,7 +275,6 @@ def read_DataSource(self, attr): attr.set_value(data_source) def Start(self): - self.twod.prepare() self.twod.start_acquisition() diff --git a/src/sardana/tango/pool/ZeroDExpChannel.py b/src/sardana/tango/pool/ZeroDExpChannel.py index 4dc159e174..cd9bcb781f 100644 --- a/src/sardana/tango/pool/ZeroDExpChannel.py +++ b/src/sardana/tango/pool/ZeroDExpChannel.py @@ -194,7 +194,6 @@ def read_CurrentValue(self, attr): priority=0, timestamp=value.timestamp) def Start(self): - self.zerod.prepare() self.zerod.start_acquisition() def read_AccumulationBuffer(self, attr): From c642c83d787d8ce0b6d4d2c8b611744545e13ea5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 4 Feb 2020 14:09:36 +0100 Subject: [PATCH 408/830] Revert move of __docformat__ __docformat__ should not cause flake8 problems. Move it back. --- .../taurus/qt/qtgui/extra_hkl/diffractometeralignment.py | 4 ++-- src/sardana/test/testsuite.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py index b5e0d7ab73..3d3ba647e2 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py @@ -23,6 +23,8 @@ ## ############################################################################## +__docformat__ = 'restructuredtext' + import sys import time from taurus.external.qt import Qt @@ -47,8 +49,6 @@ from .selectsignal import SelectSignal -__docformat__ = 'restructuredtext' - class EngineModesComboBox(Qt.QComboBox, TaurusBaseWidget): """ComboBox representing list of engine modes""" diff --git a/src/sardana/test/testsuite.py b/src/sardana/test/testsuite.py index 193d1d71b2..1924164076 100644 --- a/src/sardana/test/testsuite.py +++ b/src/sardana/test/testsuite.py @@ -32,13 +32,13 @@ """ +__docformat__ = 'restructuredtext' + import os import re import unittest import sardana -__docformat__ = 'restructuredtext' - def _filter_suite(suite, exclude_pattern, ret=None): """removes TestCases from a suite based on regexp matching on the Test id""" From 09fc12ac2b10f5613923c8d6d370f53ce769b8fa Mon Sep 17 00:00:00 2001 From: aalonso Date: Tue, 4 Feb 2020 16:38:16 +0100 Subject: [PATCH 409/830] fix timescan deprecated warning, wasn't using prepare() --- src/sardana/macroserver/scan/gscan.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index a2602c1052..72ce7c57c7 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2765,6 +2765,8 @@ def scan_loop(self): hook() yield 0 + measurement_group.setNbStarts(1) + measurement_group.prepare() measurement_group.count_continuous(synchronization, self.value_buffer_changed) self.debug("Waiting for value buffer events to be processed") From 3636977361ff83a7ab5c53ee75bbaa37749a36ff Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 10 Feb 2020 12:21:59 +0100 Subject: [PATCH 410/830] Added feature that saves expert view and write widged settings on perspectives and also on closing and reopening GUI. --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index b7b342396e..591674fcef 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1166,7 +1166,7 @@ def __init__(self, parent=None, designMode=False): self.cbAbsoluteRelativeChanged) self.cbAbsoluteRelative.addItems(['Abs', 'Rel']) self.layout().addWidget(self.cbAbsoluteRelative, 0, 1) - + self.registerConfigProperty(self.cbAbsoluteRelative.currentIndex, self.cbAbsoluteRelative.setCurrentIndex, 'AbsRelindex') # WITH THE COMPACCT VIEW FEATURE, BETTER TO HAVE IT IN THE READ WIDGET # WOULD BE BETTER AS AN 'EXTRA WIDGET' (SOME DAY...) #self.btn_stop = Qt.QPushButton() @@ -1421,6 +1421,7 @@ def __init__(self, parent=None, designMode=False): self.setUnitsWidgetClass(PoolMotorTVUnitsWidget) self.motor_dev = None self._expertView = False + self.registerConfigProperty(self.getExpertView, self.setExpertView, '_expertView') self.limits_listener = None self.poweron_listener = None self.status_listener = None @@ -1431,6 +1432,9 @@ def setExpertView(self, expertView): self._expertView = expertView self.expertViewChanged.emit(expertView) + def getExpertView(self): + return self._expertView + def minimumHeight(self): return None # @todo: UGLY HACK to avoid subwidgets being forced to minimumheight=20 From 54e3eb923d54551cf057c73a50b1f44d30436152 Mon Sep 17 00:00:00 2001 From: aalonso Date: Tue, 11 Feb 2020 10:35:13 +0100 Subject: [PATCH 411/830] revert argparse changes --- sandbox/pool_gui | 5 +++-- .../extra_hkl/diffractometeralignment.py | 14 ++++++------ .../taurus/qt/qtgui/extra_hkl/hklscan.py | 8 +++---- .../taurus/qt/qtgui/extra_hkl/ubmatrix.py | 13 +++++------ .../qtgui/extra_macroexecutor/macrobutton.py | 8 +++---- .../sequenceeditor/sequenceeditor.py | 22 +++++++++---------- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 6 ++--- .../taurus/qt/qtgui/extra_sardana/cmdline.py | 4 ++-- .../qt/qtgui/extra_sardana/expdescription.py | 11 +++++----- .../qt/qtgui/extra_sardana/sardanaeditor.py | 6 ++--- .../qt/qtgui/extra_sardana/showscanonline.py | 5 +++-- 11 files changed, 51 insertions(+), 51 deletions(-) diff --git a/sandbox/pool_gui b/sandbox/pool_gui index bb9c47dd72..b0ac2c2268 100644 --- a/sandbox/pool_gui +++ b/sandbox/pool_gui @@ -4,7 +4,7 @@ import sys import datetime import taurus -from argparse import ArgumentParser +from taurus.core.util.argparse import get_taurus_parser from taurus.core.util import CodecFactory from taurus.qt import Qt from taurus.qt.qtgui.application import TaurusApplication @@ -277,7 +277,8 @@ def gui(model): def main(): - parser = ArgumentParser(usage="%prog [options] ") + parser = get_taurus_parser() + parser.usage = "%prog [options] " app = TaurusApplication(cmd_line_parser=parser) app.setTaurusStyle("nebula") diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py index 3d3ba647e2..6b704a0bbe 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py @@ -41,7 +41,7 @@ from taurus.qt.qtcore.communication import SharedDataManager from taurus.qt.qtgui.input import TaurusValueLineEdit -import argparse +import taurus.core.util.argparse import taurus.qt.qtgui.application from taurus.qt.qtgui.util.ui import UILoadable @@ -274,7 +274,7 @@ def exec_scan(self, imot): macro_command.append(str(self.selectsignal._ui.SignallineEdit.text())) self.door_device.RunMacro(macro_command) - while (self.door_device.State()) == PyTango.DevState.RUNNING: + while(self.door_device.State()) == PyTango.DevState.RUNNING: time.sleep(0.01) # TODO: the string parsing should be eliminated and the sardana # generic "goto_peak" feature should be used instead - when available @@ -350,11 +350,11 @@ def open_selectsignal_panel(self): def main(): - parser = argparse.ArgumentParser(usage="%prog [door_name]", - description="a taurus application for " - "diffractometer alignment: " - "h, k, l movements and " - "scans, go to maximum, ...") + parser = taurus.core.util.argparse.get_taurus_parser() + parser.usage = "%prog [door_name]" + desc = ("a taurus application for diffractometer alignment: h, k, l " + + "movements and scans, go to maximum, ...") + parser.set_description(desc) app = taurus.qt.qtgui.application.TaurusApplication( cmd_line_parser=parser, app_version=sardana.Release.version) diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py index 3bd5df0faf..b9d9230354 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py @@ -39,7 +39,7 @@ from .displayscanangles import DisplayScanAngles -import argparse +import taurus.core.util.argparse import taurus.qt.qtgui.application from taurus.qt.qtgui.util.ui import UILoadable @@ -357,9 +357,9 @@ def onDoorChanged(self, doorName): def main(): - parser = argparse.ArgumentParser(description="a taurus application " - "for performing hkl scans", - usage="%prog [door_name]") + parser = taurus.core.util.argparse.get_taurus_parser() + parser.usage = "%prog [door_name]" + parser.set_description("a taurus application for performing hkl scans") app = taurus.qt.qtgui.application.TaurusApplication( cmd_line_parser=parser, app_version=sardana.Release.version) diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py index a4032beede..be0d6a4fbe 100644 --- a/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py +++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py @@ -38,7 +38,7 @@ from taurus.qt.qtgui.input import TaurusValueLineEdit import taurus.core -import argparse +import taurus.core.util.argparse import taurus.qt.qtgui.application from taurus.qt.qtgui.util.ui import UILoadable @@ -463,12 +463,11 @@ def affine(self): def main(): - - parser = argparse.ArgumentParser(description="a taurus application for " - "setting diffractometer " - "parameters: ubmatrix, " - "lattice, reflections, ...", - usage="%prog ") + parser = taurus.core.util.argparse.get_taurus_parser() + parser.usage = "%prog " + parser.set_description( + "a taurus application for setting diffractometer parameters: " + "ubmatrix, lattice, reflections, ...") app = taurus.qt.qtgui.application.TaurusApplication( cmd_line_parser=parser, app_version=sardana.Release.version) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 85dbe42f62..9542fbdbc9 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -371,14 +371,14 @@ def abort(self): if __name__ == '__main__': import sys from taurus.qt.qtgui.application import TaurusApplication - from argparse import ArgumentParser + from taurus.core.util.argparse import get_taurus_parser from sardana.taurus.qt.qtcore.tango.sardana.macroserver import \ registerExtensions registerExtensions() - parser = ArgumentParser(usage="python macrobutton.py " - "[door_name] [macro_name]", - description="Macro button for macro execution") + parser = get_taurus_parser() + parser.set_usage("python macrobutton.py [door_name] [macro_name]") + parser.set_description("Macro button for macro execution") app = TaurusApplication(app_name="macrobutton", app_version=taurus.Release.version) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index c1e10f81f2..97becda18d 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -986,19 +986,19 @@ def createSequencer(args, options): def main(): - import argparse + from taurus.core.util import argparse from taurus.qt.qtgui.application import TaurusApplication - parser = argparse.ArgumentParser(usage="%prog [options]", - description="Sardana macro sequencer.\n" - "It allows the creation of " - "sequences of macros, " - "executed one after the " - "other.\n The sequences can " - "be stored under xml files") - parser.add_argument("-f", "--file", dest="file", default=None, - help="load macro sequence from a " - "file(XML or spock syntax)") + parser = argparse.get_taurus_parser() + parser.set_usage("%prog [options]") + parser.set_description("Sardana macro sequencer.\n" + "It allows the creation of sequences of " + "macros, executed one after the other.\n" + "The sequences can be stored under xml files") + parser.add_option("-f", "--file", + dest="file", default=None, + help="load macro sequence from a file(XML or spock " + "syntax)") app = TaurusApplication(cmd_line_parser=parser, app_name="sequencer", diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 79c90984bf..3b943a3bc7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1685,12 +1685,10 @@ def main(): import sys import taurus.qt.qtgui.application - import argparse - from taurus.qt.qtgui.panel import TaurusForm import taurus.core.util.argparse + from taurus.qt.qtgui.panel import TaurusForm parser = taurus.core.util.argparse.get_taurus_parser() - parser = argparse.ArgumentParser( - usage="%prog [options] [ [] ...]") + parser.usage = "%prog [options] [ [] ...]" app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser) args = app.get_command_line_args() diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py index ec0ab46f70..336b4235e1 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/cmdline.py @@ -93,8 +93,8 @@ def main(): owns_app = app is None if owns_app: - import argparse - parser = argparse.ArgumentParser() + import taurus.core.util.argparse + parser = taurus.core.util.argparse.get_taurus_parser() app = Application(sys.argv, cmd_line_parser=parser, app_name="Taurus command line demo", app_version="1.0", org_domain="Taurus", org_name="Tango community") diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py index ecfc595c6c..1e84f87670 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py @@ -738,11 +738,12 @@ def main(): app = Application.instance() owns_app = app is None if owns_app: - import argparse - parser = argparse.ArgumentParser(usage="%prog [options] ") - parser.add_argument('--auto-update', dest='auto_update', - action='store_true', - help='Set auto update of experiment configuration') + import taurus.core.util.argparse + parser = taurus.core.util.argparse.get_taurus_parser() + parser.usage = "%prog [options] " + parser.add_option('--auto-update', dest='auto_update', + action='store_true', + help='Set auto update of experiment configuration') app = Application(app_name="Exp. Description demo", app_version="1.0", org_domain="Sardana", org_name="Tango community", diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py index 2144847daf..25c6593fa8 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/sardanaeditor.py @@ -564,9 +564,9 @@ def main(): owns_app = app is None if owns_app: - import argparse - parser = argparse.ArgumentParser( - usage="%prog [options] ") + import taurus.core.util.argparse + parser = taurus.core.util.argparse.get_taurus_parser() + parser.usage = "%prog [options] " app = Application(sys.argv, cmd_line_parser=parser, app_name="Macro editor demo", app_version="1.0", diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index 5ea9a466e5..3a39c1e606 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -88,9 +88,10 @@ def main(): from taurus.qt.qtgui.application import TaurusApplication import sys - from argparse import ArgumentParser + from taurus.core.util.argparse import get_taurus_parser - parser = ArgumentParser(usage="python showscanonline.py [door_name]") + parser = get_taurus_parser() + parser.set_usage("python showscanonline.py [door_name]") app = TaurusApplication(app_name='Showscan Online', org_domain="Sardana", org_name="Tango communinity", cmd_line_parser=parser) From 049b4bfb645c6027f3fd0a5461019911d15b9a63 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 11 Feb 2020 13:13:13 +0100 Subject: [PATCH 412/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10be64161c..0fb73ea439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Macro plotting in new versions of ipython and matplotlib require extra call to pyplot.draw() to make sure that the plot is refreshed (#1280) * Remove TangoAttribute controllers from Sardana (#181, #1279) +* Remove deprecation warning revealed when running test suite (#1267) ### Deprecated From 224df40de89588269756af96384ae3995aab2c8b Mon Sep 17 00:00:00 2001 From: aalonso Date: Wed, 12 Feb 2020 12:09:07 +0100 Subject: [PATCH 413/830] added if that checks if the motor has an encoder before creating a model with encoder. --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 82489b0739..23d8ae9678 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1363,7 +1363,9 @@ def setModel(self, model): return TaurusWidget.setModel(self, model + '/Position') self.le_write_absolute.setModel(model + '/Position#wvalue.magnitude') - + if self.taurusValueBuddy().motor_dev is not None and \ + self.taurusValueBuddy().hasEncoder(): + self.lbl_enc_read.setModel(model + '/Encoder') # Handle User/Expert View self.setExpertView(self.taurusValueBuddy()._expertView) self.taurusValueBuddy().expertViewChanged.connect( From bd1d8c0313c049f3273fc0a91d128fc7f3bd4e9d Mon Sep 17 00:00:00 2001 From: aalonso Date: Wed, 12 Feb 2020 12:12:39 +0100 Subject: [PATCH 414/830] added if that checks if the motor has an encoder before creating a model with encoder. --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 23d8ae9678..70c26090dc 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1099,7 +1099,9 @@ def setModel(self, model): return TaurusWidget.setModel(self, model + '/Position') self.lbl_read.setModel(model + '/Position') - self.lbl_enc_read.setModel(model + '/Encoder') + if self.taurusValueBuddy().motor_dev is not None and \ + self.taurusValueBuddy().hasEncoder(): + self.lbl_enc_read.setModel(model + '/Encoder') # Handle User/Expert view self.setExpertView(self.taurusValueBuddy()._expertView) self.taurusValueBuddy().expertViewChanged.connect( @@ -1363,9 +1365,7 @@ def setModel(self, model): return TaurusWidget.setModel(self, model + '/Position') self.le_write_absolute.setModel(model + '/Position#wvalue.magnitude') - if self.taurusValueBuddy().motor_dev is not None and \ - self.taurusValueBuddy().hasEncoder(): - self.lbl_enc_read.setModel(model + '/Encoder') + # Handle User/Expert View self.setExpertView(self.taurusValueBuddy()._expertView) self.taurusValueBuddy().expertViewChanged.connect( From 72b14dc232927d59a9f5fa9dbd3a00bb361a046d Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 13 Feb 2020 13:07:47 +0100 Subject: [PATCH 415/830] Initialise encoder only if motor has. The initialization code has been moved to a function because "taurusValueBuddy" is not accessible from the constructor. --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 70c26090dc..d53fa5860b 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1022,14 +1022,8 @@ def __init__(self, parent=None, designMode=False): # self.cb_expertRead.addItems(['Enc']) #self.layout().addWidget(self.cb_expertRead, 1, 0) - self.lbl_enc = Qt.QLabel('Encoder') - self.layout().addWidget(self.lbl_enc, 1, 0) - - self.lbl_enc_read = TaurusLabel() - self.lbl_enc_read.setBgRole('none') - self.lbl_enc_read.setSizePolicy(Qt.QSizePolicy( - Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) - self.layout().addWidget(self.lbl_enc_read, 1, 1) + self.lbl_enc = None + self.lbl_enc_read = None # Align everything on top self.layout().addItem(Qt.QSpacerItem( @@ -1040,8 +1034,6 @@ def __init__(self, parent=None, designMode=False): # SO WE ASSUME 'expertview is FALSE' AND WE HAVE TO AVOID self.setExpertView :-( # WOULD BE NICE THAT THE taurusValueBuddy COULD EMIT THE PROPER # SIGNAL... - self.lbl_enc.setVisible(False) - self.lbl_enc_read.setVisible(False) def eventFilter(self, obj, event): if event.type() == Qt.QEvent.MouseButtonDblClick: @@ -1064,17 +1056,17 @@ def abort(self): motor_dev.abort() def setExpertView(self, expertView): - self.lbl_enc.setVisible(False) - self.lbl_enc_read.setVisible(False) if self.taurusValueBuddy().motor_dev is not None: hw_limits = self.taurusValueBuddy().hasHwLimits() self.btn_lim_neg.setEnabled(hw_limits) self.btn_lim_pos.setEnabled(hw_limits) - - if expertView and self.taurusValueBuddy().motor_dev is not None: - encoder = self.taurusValueBuddy().hasEncoder() - self.lbl_enc.setVisible(encoder) - self.lbl_enc_read.setVisible(encoder) + if self.lbl_enc_read and self.lbl_enc is not None: + self.lbl_enc.setVisible(False) + self.lbl_enc_read.setVisible(False) + if expertView and self.taurusValueBuddy().motor_dev is not None: + encoder = self.taurusValueBuddy().hasEncoder() + self.lbl_enc.setVisible(encoder) + self.lbl_enc_read.setVisible(encoder) def prepare_button(self, btn): btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) @@ -1085,8 +1077,27 @@ def prepare_button(self, btn): btn.setMaximumSize(25, 25) btn.setText('') + def create_encoder(self): + if self.taurusValueBuddy().hasEncoder() and self.lbl_enc_read is None: + self.lbl_enc = Qt.QLabel('Encoder') + self.layout().addWidget(self.lbl_enc, 1, 0) + + self.lbl_enc_read = TaurusLabel() + self.lbl_enc_read.setBgRole('none') + self.lbl_enc_read.setSizePolicy(Qt.QSizePolicy( + Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed)) + self.layout().addWidget(self.lbl_enc_read, 1, 1) + + # Align everything on top + self.layout().addItem(Qt.QSpacerItem( + 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 2) + + self.lbl_enc.setVisible(False) + self.lbl_enc_read.setVisible(False) + def setModel(self, model): if hasattr(self, 'taurusValueBuddy'): + self.create_encoder() try: self.taurusValueBuddy().expertViewChanged.disconnect( self.setExpertView) @@ -1095,12 +1106,12 @@ def setModel(self, model): if model in (None, ''): TaurusWidget.setModel(self, model) self.lbl_read.setModel(model) - self.lbl_enc_read.setModel(model) + if self.lbl_enc_read is not None: + self.lbl_enc_read.setModel(model) return TaurusWidget.setModel(self, model + '/Position') self.lbl_read.setModel(model + '/Position') - if self.taurusValueBuddy().motor_dev is not None and \ - self.taurusValueBuddy().hasEncoder(): + if self.lbl_enc_read and self.taurusValueBuddy().motor_dev is not None: self.lbl_enc_read.setModel(model + '/Encoder') # Handle User/Expert view self.setExpertView(self.taurusValueBuddy()._expertView) From 62b1a62b43ee5abed3f2d660956c926b327cc516 Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 13 Feb 2020 14:52:14 +0100 Subject: [PATCH 416/830] Fix flake8 --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index d53fa5860b..26bba3f34a 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1090,7 +1090,8 @@ def create_encoder(self): # Align everything on top self.layout().addItem(Qt.QSpacerItem( - 1, 1, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.Expanding), 2, 0, 1, 2) + 1, 1, Qt.QSizePolicy.Minimum, + Qt.QSizePolicy.Expanding), 2, 0, 1, 2) self.lbl_enc.setVisible(False) self.lbl_enc_read.setVisible(False) From 3d8fcd78964151ccb32ac93261b068d6d4d39620 Mon Sep 17 00:00:00 2001 From: aalonso Date: Thu, 13 Feb 2020 15:00:41 +0100 Subject: [PATCH 417/830] Fix flake8 --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 591674fcef..4fc86c88db 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1166,7 +1166,9 @@ def __init__(self, parent=None, designMode=False): self.cbAbsoluteRelativeChanged) self.cbAbsoluteRelative.addItems(['Abs', 'Rel']) self.layout().addWidget(self.cbAbsoluteRelative, 0, 1) - self.registerConfigProperty(self.cbAbsoluteRelative.currentIndex, self.cbAbsoluteRelative.setCurrentIndex, 'AbsRelindex') + self.registerConfigProperty( + self.cbAbsoluteRelative.currentIndex, + self.cbAbsoluteRelative.setCurrentIndex, 'AbsRelindex') # WITH THE COMPACCT VIEW FEATURE, BETTER TO HAVE IT IN THE READ WIDGET # WOULD BE BETTER AS AN 'EXTRA WIDGET' (SOME DAY...) #self.btn_stop = Qt.QPushButton() @@ -1421,7 +1423,8 @@ def __init__(self, parent=None, designMode=False): self.setUnitsWidgetClass(PoolMotorTVUnitsWidget) self.motor_dev = None self._expertView = False - self.registerConfigProperty(self.getExpertView, self.setExpertView, '_expertView') + self.registerConfigProperty( + self.getExpertView, self.setExpertView, '_expertView') self.limits_listener = None self.poweron_listener = None self.status_listener = None From 352cccae0598c9882bd8893f31704c837543a6b3 Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 17 Feb 2020 16:52:13 +0100 Subject: [PATCH 418/830] New style and text information for limit icons. Icons now are labels instead of buttons. They have a more intuitive icon and reflect correctly all the limit states. Indicating with the text information if the active limit is software or hardware. --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 120 ++++++++++++++---- 1 file changed, 95 insertions(+), 25 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 82489b0739..f728232231 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -983,19 +983,13 @@ def __init__(self, parent=None, designMode=False): limits_layout.setContentsMargins(0, 0, 0, 0) limits_layout.setSpacing(0) - self.btn_lim_neg = Qt.QPushButton() - self.btn_lim_neg.setToolTip('Negative Limit') - # self.btn_lim_neg.setEnabled(False) - self.prepare_button(self.btn_lim_neg) - self.btn_lim_neg.setIcon(Qt.QIcon("actions:list-remove.svg")) - limits_layout.addWidget(self.btn_lim_neg) - - self.btn_lim_pos = Qt.QPushButton() - self.btn_lim_pos.setToolTip('Positive Limit') - # self.btn_lim_pos.setEnabled(False) - self.prepare_button(self.btn_lim_pos) - self.btn_lim_pos.setIcon(Qt.QIcon("actions:list-add.svg")) - limits_layout.addWidget(self.btn_lim_pos) + self.lim_neg = Qt.QLabel() + limits_layout.addWidget(self.lim_neg) + self.config_limit(self.lim_neg, "negative", "Disabled") + + self.lim_pos = Qt.QLabel() + limits_layout.addWidget(self.lim_pos) + self.config_limit(self.lim_pos, "positive", "Disabled") self.layout().addLayout(limits_layout, 0, 0) @@ -1067,9 +1061,12 @@ def setExpertView(self, expertView): self.lbl_enc.setVisible(False) self.lbl_enc_read.setVisible(False) if self.taurusValueBuddy().motor_dev is not None: - hw_limits = self.taurusValueBuddy().hasHwLimits() - self.btn_lim_neg.setEnabled(hw_limits) - self.btn_lim_pos.setEnabled(hw_limits) + if self.taurusValueBuddy().hasHwLimits(): + self.config_limit(self.lim_neg, "negative", "Enabled") + self.config_limit(self.lim_pos, "positive", "Enabled") + else: + self.config_limit(self.lim_neg, "negative", "Disabled") + self.config_limit(self.lim_pos, "positive", "Disabled") if expertView and self.taurusValueBuddy().motor_dev is not None: encoder = self.taurusValueBuddy().hasEncoder() @@ -1085,6 +1082,54 @@ def prepare_button(self, btn): btn.setMaximumSize(25, 25) btn.setText('') + def prepare_limit(self, btn): + btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) + btn_policy.setHorizontalStretch(0) + btn_policy.setVerticalStretch(0) + btn.setSizePolicy(btn_policy) + btn.setText('') + + def config_limit(self, limit, polarity, state): + """ + :param limit: Qt.Label + :param polarity: {negative, positive} + :param state: {Disabled, Enabled, SwActive, HwActive} + """ + self.prepare_limit(limit) + + if polarity == "negative": + tooltip = 'Negative Limit' + icon = Qt.QIcon("designer:minus.png") + elif polarity == "positive": + tooltip = 'Positive Limit' + icon = Qt.QIcon("designer:plus.png") + else: + raise ValueError("Wrong polarity {}".format(polarity)) + limit.setStyleSheet('') + if state == "Disabled": + pixmap = Qt.QPixmap(icon.pixmap(Qt.QSize(32, 32), + Qt.QIcon.Disabled)) + else: + pixmap = Qt.QPixmap(icon.pixmap(Qt.QSize(32, 32), + Qt.QIcon.Active)) + if state == "SwActive": + colour = DEVICE_STATE_PALETTE.qtStyleSheet( + PyTango.DevState.ALARM) + limit.setStyleSheet(colour) + tooltip = tooltip + " - Software limit reached" + elif state == "HwActive": + colour = DEVICE_STATE_PALETTE.qtStyleSheet( + PyTango.DevState.ALARM) + limit.setStyleSheet(colour) + tooltip = tooltip + " - Hardware limit reached" + elif state == "Enabled": + pass + else: + raise ValueError("Wrong state {}".format(state)) + + limit.setToolTip(tooltip) + limit.setPixmap(pixmap) + def setModel(self, model): if hasattr(self, 'taurusValueBuddy'): try: @@ -1553,25 +1598,41 @@ def updateLimits(self, limits, position=None): min_value_str = position_attribute.min_value try: max_value = float(max_value_str) - limits[POS] = limits[POS] or (position >= max_value) + if position >= max_value: + limits[POS] = True + pos_lim_status = "HwActive" + else: + pos_lim_status = "SwActive" except: - pass + pos_lim_status = "SwActive" try: min_value = float(min_value_str) - limits[NEG] = limits[NEG] or (position <= min_value) + if position <= min_value: + limits[NEG] = True + neg_lim_status = "HwActive" + else: + neg_lim_status = "SwActive" except: - pass + neg_lim_status = "SwActive" pos_lim = limits[POS] - pos_btnstylesheet = '' enabled = True if pos_lim: pos_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( PyTango.DevState.ALARM) enabled = False - self.readWidget(followCompact=True).btn_lim_pos.setStyleSheet( - pos_btnstylesheet) + self.readWidget(followCompact=True).config_limit( + self.readWidget(followCompact=True).lim_pos, + "positive", pos_lim_status) + elif self.hasHwLimits(): + self.readWidget(followCompact=True).config_limit( + self.readWidget(followCompact=True).lim_pos, + "positive", "Enabled") + else: + self.readWidget(followCompact=True).config_limit( + self.readWidget(followCompact=True).lim_pos, + "positive", "Disabled") self.writeWidget(followCompact=True).btn_step_up.setEnabled(enabled) self.writeWidget(followCompact=True).btn_step_up.setStyleSheet( @@ -1589,8 +1650,17 @@ def updateLimits(self, limits, position=None): neg_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( PyTango.DevState.ALARM) enabled = False - self.readWidget(followCompact=True).btn_lim_neg.setStyleSheet( - neg_btnstylesheet) + self.readWidget(followCompact=True).config_limit( + self.readWidget(followCompact=True).lim_neg, + "negative", neg_lim_status) + elif self.hasHwLimits(): + self.readWidget(followCompact=True).config_limit( + self.readWidget(followCompact=True).lim_neg, + "negative", "Enabled") + else: + self.readWidget(followCompact=True).config_limit( + self.readWidget(followCompact=True).lim_neg, + "negative", "Disabled") self.writeWidget(followCompact=True).btn_step_down.setEnabled(enabled) self.writeWidget(followCompact=True).btn_step_down.setStyleSheet( From 3c26a23ed2a4ffddb3d7768c0e9021c34b45edc3 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 18 Feb 2020 18:05:06 +0100 Subject: [PATCH 419/830] Change config properties names to CamelCase --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 4fc86c88db..3d9edf2b65 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1168,7 +1168,7 @@ def __init__(self, parent=None, designMode=False): self.layout().addWidget(self.cbAbsoluteRelative, 0, 1) self.registerConfigProperty( self.cbAbsoluteRelative.currentIndex, - self.cbAbsoluteRelative.setCurrentIndex, 'AbsRelindex') + self.cbAbsoluteRelative.setCurrentIndex, 'AbsRelIndex') # WITH THE COMPACCT VIEW FEATURE, BETTER TO HAVE IT IN THE READ WIDGET # WOULD BE BETTER AS AN 'EXTRA WIDGET' (SOME DAY...) #self.btn_stop = Qt.QPushButton() @@ -1424,7 +1424,7 @@ def __init__(self, parent=None, designMode=False): self.motor_dev = None self._expertView = False self.registerConfigProperty( - self.getExpertView, self.setExpertView, '_expertView') + self.getExpertView, self.setExpertView, 'ExpertView') self.limits_listener = None self.poweron_listener = None self.status_listener = None From c334cd788c44daa525217ce4b6f125929d101c80 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 18 Feb 2020 18:13:13 +0100 Subject: [PATCH 420/830] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fb73ea439..d025855b91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,9 @@ This file follows the formats and conventions from [keepachangelog.com] * Improve scans to detect if a ScanFile od ScanDir are set but empty (#1262) * Possibility to view debug messages in `DoorOutput` widget - enable/disable using context menu option (#1242) -* Improve documentation (#1241) +* Store PMTV (motor widget) configurations: *expert view* and *write mode* + (relative or absolute) permanently as TaurusGUI settings. +* Improve documentation (#1241) * Better macro exception message and hint to use `www` (#1191) * Add basic information to "how to write custom recorder" to the documentation (#1275) From 06d3c1af80df27f8b9c9253bf5cbbc480a02f068 Mon Sep 17 00:00:00 2001 From: aalonso Date: Wed, 19 Feb 2020 11:19:45 +0100 Subject: [PATCH 421/830] Reset pending operations on movement relative PMTV was storing pending operations from absolute widget when using relative widget, this causes the motor to move to the absolute position when pressing apply button on the relative mode when it shouldn't. Now the pending operations are reset on changing the widget and after moving on relative. --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 45ff36d522..b2d8420bf9 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1281,6 +1281,8 @@ def eventFilter(self, obj, event): def cbAbsoluteRelativeChanged(self, abs_rel_option): abs_visible = abs_rel_option == 'Abs' rel_visible = abs_rel_option == 'Rel' + if rel_visible: + self.resetPendingOperations() self.le_write_absolute.setVisible(abs_visible) self.qw_write_relative.setVisible(rel_visible) @@ -1380,6 +1382,8 @@ def keyPressEvent(self, key_event): @Qt.pyqtSlot() def emitEditingFinished(self): self.applied.emit() + if self.cbAbsoluteRelative.currentIndex() == 1: + self.resetPendingOperations() ################################################## From 5fb4bd327979a2b3e201273cba90ca7ae667ee68 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 19 Feb 2020 15:32:32 +0100 Subject: [PATCH 422/830] Change order of meas grp prepare in timescan When removing deprecation warnings the explicit call to meas grp prepare was added at wrong place - before the synchronization was set. Move prepare to count_continuous. This gives the following benefits: * it is called after setting synchronization * it is done for all uses of count_continuous and not only for timescan --- src/sardana/macroserver/scan/gscan.py | 1 - src/sardana/taurus/core/tango/sardana/pool.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index ba0eda28c7..29f28b11d8 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2770,7 +2770,6 @@ def scan_loop(self): yield 0 measurement_group.setNbStarts(1) - measurement_group.prepare() measurement_group.count_continuous(synchronization, self.value_buffer_changed) self.debug("Waiting for value buffer events to be processed") diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index bf16ec261c..bc59c81148 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2153,6 +2153,7 @@ class on a provisional basis. Backwards incompatible changes cfg = self.getConfiguration() cfg.prepare() self.setSynchronization(synchronization) + self.prepare() self.subscribeValueBuffer(value_buffer_cb) try: self.count_raw(start_time) From 07c183039303739b449148c5912dfad748800428 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Tue, 14 Jan 2020 14:28:50 +0100 Subject: [PATCH 423/830] Protect get_terminal_size against non interactive terminal clients --- .../taurus/core/tango/sardana/macroserver.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index c2f4f20073..19b5ce6d08 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -66,12 +66,15 @@ CHANGE_EVT_TYPES = TaurusEventType.Change, TaurusEventType.Periodic -def _get_console_width(): +def get_terminal_size(fileno=None): try: - width = int(os.popen('stty size', 'r').read().split()[1]) + if fileno is None: + fileno = sys.stdout.fileno() + if not os.isatty(fileno): + return None + return os.get_terminal_size(fileno) except Exception: - width = float('inf') - return width + return None def _get_nb_lines(nb_chrs, max_chrs): @@ -688,7 +691,8 @@ def macroStatusReceived(self, s, t, v): return data def logReceived(self, log_name, output): - max_chrs = _get_console_width() + term_size = os.get_terminal_size() + max_chrs = term_size.columns if term_size else None if not output or self._silent or self._ignore_logs: return @@ -701,7 +705,7 @@ def logReceived(self, log_name, output): if line == self.BlockStart: self._in_block = True for i in range(self._block_lines): - if max_chrs == float('inf'): + if max_chrs is None: nb_lines = 1 else: nb_lines = _get_nb_lines( From 717d60b8ddea21cc8e558f01f5bd28310901a89b Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 20 Feb 2020 15:41:53 +0100 Subject: [PATCH 424/830] Use protected method get_terminal_size instead of the os equivalent --- src/sardana/taurus/core/tango/sardana/macroserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 19b5ce6d08..4b4aa4171c 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -691,7 +691,7 @@ def macroStatusReceived(self, s, t, v): return data def logReceived(self, log_name, output): - term_size = os.get_terminal_size() + term_size = get_terminal_size() max_chrs = term_size.columns if term_size else None if not output or self._silent or self._ignore_logs: return From 689f878880653248c07f4db1905a913c0f3db4dd Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 20 Feb 2020 16:14:36 +0100 Subject: [PATCH 425/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d025855b91..0f72c5c813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ This file follows the formats and conventions from [keepachangelog.com] * OutputBlock view option when macros produce outputs at high rate (#1245) * `showscan online` shows only the online trend and not erroneously online and offline (#1260) +* Use more efficient way to get terminal size for better printing spock output (#1245, #1268) * Reintroduce backwards compatibility for measurement groups' configurations (URIs) created with Taurus 3 (#1266, #1271) * Measurement groups renaming with `renameelem` macro(#951) From 76c53f908c7705cd0eabd22df385f191328687f6 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 25 Feb 2020 11:28:29 +0100 Subject: [PATCH 426/830] Remove unused import --- src/sardana/pool/test/test_MeasurementGroupConfiguretion.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py b/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py index c447ebeb70..47028d647c 100644 --- a/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py +++ b/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py @@ -1,4 +1,3 @@ -from sardana.taurus.core.tango.sardana import * import uuid from taurus import Device from taurus.external.unittest import TestCase From 097a8c109554443fc8dff722f6b5e1bd13acdddb Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 25 Feb 2020 11:30:05 +0100 Subject: [PATCH 427/830] Fix flake8 errors --- .../test_MeasurementGroupConfiguretion.py | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py b/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py index 47028d647c..b9e99bc438 100644 --- a/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py +++ b/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py @@ -29,7 +29,8 @@ def _assertResult(self, result, channels, expected_value): def _assertMultipleResults(self, result, channels, expected_values): expected_channels = list(channels) print(result) - for (channel, value), expected_value in zip(result.items(), expected_values): + for (channel, value), expected_value in zip(result.items(), + expected_values): msg = "unexpected key: {}".format(channel) self.assertIn(channel, expected_channels, msg) expected_channels.remove(channel) @@ -37,7 +38,7 @@ def _assertMultipleResults(self, result, channels, expected_values): msg = "{} are missing".format(expected_channels) self.assertEqual(len(expected_channels), 0, msg) - def test_enabled(self, elements = ["_test_ct_1_1", "_test_ct_1_2"]): + def test_enabled(self, elements=["_test_ct_1_1", "_test_ct_1_2"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -93,7 +94,8 @@ def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2"]): mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", + "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -116,7 +118,8 @@ def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3" mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", + "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -154,7 +157,8 @@ def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3" mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_Timer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_Timer(self, elements=["_test_ct_1_1", "_test_ct_1_2", + "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -165,13 +169,15 @@ def test_Timer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): mg.setTimer('_test_ct_1_3') self.assertNotEqual(mg.getTimer(), previous) self._assertResult(mg.getTimer(), elements, '_test_ct_1_3') - self._assertResult(mg.getTimer(ret_by_ctrl=True), ['_test_ct_ctrl_1'], '_test_ct_1_3') + self._assertResult(mg.getTimer(ret_by_ctrl=True), + ['_test_ct_ctrl_1'], '_test_ct_1_3') finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", + "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -182,13 +188,15 @@ def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] mg.setMonitor("_test_ct_1_2") self.assertNotEqual(mg.getMonitor(), previous) self._assertResult(mg.getMonitor(), elements, '_test_ct_1_2') - self._assertResult(mg.getMonitor(ret_by_ctrl=True), ['_test_ct_ctrl_1'], '_test_ct_1_2') + self._assertResult(mg.getMonitor(ret_by_ctrl=True), + ['_test_ct_ctrl_1'], '_test_ct_1_2') finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"]): + def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", + "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -212,7 +220,8 @@ def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_ mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", "_test_ct_1_1", "_test_ct_1_2"]): + def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", + "_test_ct_1_1", "_test_ct_1_2"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -235,7 +244,8 @@ def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", "_test_ mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", "_test_ct_1_3"]): + def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", + "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -252,4 +262,3 @@ def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", "_test_ finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - From 7d215ba9e990c35e9da10c56a8e71e1884621cef Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 25 Feb 2020 11:30:36 +0100 Subject: [PATCH 428/830] Rename module --- ...roupConfiguretion.py => test_measurementgroupconfiguretion.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/sardana/pool/test/{test_MeasurementGroupConfiguretion.py => test_measurementgroupconfiguretion.py} (100%) diff --git a/src/sardana/pool/test/test_MeasurementGroupConfiguretion.py b/src/sardana/pool/test/test_measurementgroupconfiguretion.py similarity index 100% rename from src/sardana/pool/test/test_MeasurementGroupConfiguretion.py rename to src/sardana/pool/test/test_measurementgroupconfiguretion.py From 5ee43560d4f6d77cc018216b5dcc2cbb5719012e Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 25 Feb 2020 14:55:47 +0100 Subject: [PATCH 429/830] Move module to more appropriated site --- .../tango/sardana}/test/test_measurementgroupconfiguretion.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/sardana/{pool => taurus/core/tango/sardana}/test/test_measurementgroupconfiguretion.py (100%) diff --git a/src/sardana/pool/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py similarity index 100% rename from src/sardana/pool/test/test_measurementgroupconfiguretion.py rename to src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py From 4c82d7931cc41d3a3210908ca9932fc2909ce03c Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 25 Feb 2020 18:00:10 +0100 Subject: [PATCH 430/830] Protect Worker threads with EnsureOmniThread Tango is not thread safe when using threading.Thread. One must use omni threads instead. This was confirmed for parallel event subscriptions in tango-controls/pytango#307. Customize taurus ThreadPool worker threads (taurus#1081) to use EnsureOmniThread (PyTango#327). Do it for: * sardanathreadpool.get_thread_pool (global thread pool used in actions and dummy TG) * CAcquisition._thread_pool (used to execute value_buffer callbacks - these employ recorders which could potentially subscribe to Tango events) For the momento don't protect: * ThreadPool FuncGeneratorTestCase.thread_pool (tests) * threading.Thread used by PoolMonitor * threading.Thread used by server execution task * threading.Thread used by PositionGenerator (tests) * threading.Thread used by DummyEventSource (tests) --- src/sardana/macroserver/scan/gscan.py | 3 ++- src/sardana/sardanathreadpool.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 29f28b11d8..7c08fc377d 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -52,6 +52,7 @@ from taurus.core.util.threadpool import ThreadPool from taurus.core.util.event import CallableRef +from sardana.sardanathreadpool import OmniWorker from sardana.util.tree import BranchNode, LeafNode, Tree from sardana.util.motion import Motor as VMotor from sardana.util.motion import MotionPath @@ -1962,7 +1963,7 @@ class CAcquisition(object): def __init__(self): self._thread_pool = ThreadPool(name="ValueBufferTH", Psize=1, - Qsize=100000) + Qsize=100000, worker_cls=OmniWorker) self._countdown_latch = CountLatch() self._index_offset = 0 diff --git a/src/sardana/sardanathreadpool.py b/src/sardana/sardanathreadpool.py index 4950d00550..95c883cb19 100644 --- a/src/sardana/sardanathreadpool.py +++ b/src/sardana/sardanathreadpool.py @@ -34,12 +34,21 @@ import threading -from taurus.core.util.threadpool import ThreadPool +from taurus.core.util.threadpool import ThreadPool, Worker + __thread_pool_lock = threading.Lock() __thread_pool = None +class OmniWorker(Worker): + + def run(self): + import tango + with tango.EnsureOmniThread(): + Worker.run(self) + + def get_thread_pool(): """Returns the global pool of threads for Sardana @@ -50,5 +59,6 @@ def get_thread_pool(): global __thread_pool_lock with __thread_pool_lock: if __thread_pool is None: - __thread_pool = ThreadPool(name="SardanaTP", Psize=10) + __thread_pool = ThreadPool(name="SardanaTP", Psize=10, + worker_cls=OmniWorker) return __thread_pool From 823a23228c2d2f90ce565f18cef2e9127f17e4c5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 26 Feb 2020 10:45:13 +0100 Subject: [PATCH 431/830] Protect older versions of PyTango without EnsureOmniThread --- src/sardana/sardanathreadpool.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sardana/sardanathreadpool.py b/src/sardana/sardanathreadpool.py index 95c883cb19..f54f2fd19f 100644 --- a/src/sardana/sardanathreadpool.py +++ b/src/sardana/sardanathreadpool.py @@ -44,8 +44,14 @@ class OmniWorker(Worker): def run(self): + # There is no release of PyTango yet to bump the requirement + # so protect the older versions of PyTango. import tango - with tango.EnsureOmniThread(): + try: + EnsureOmniThread = getattr(tango, "EnsureOmniThread") + with EnsureOmniThread(): + Worker.run(self) + except AttributeError: Worker.run(self) From 4062da101a62f915d5180c59e41f9f5c4e802ec6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 26 Feb 2020 11:04:21 +0100 Subject: [PATCH 432/830] Protect Tango independent installations. --- src/sardana/sardanathreadpool.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/sardana/sardanathreadpool.py b/src/sardana/sardanathreadpool.py index f54f2fd19f..4543d825c3 100644 --- a/src/sardana/sardanathreadpool.py +++ b/src/sardana/sardanathreadpool.py @@ -44,15 +44,20 @@ class OmniWorker(Worker): def run(self): - # There is no release of PyTango yet to bump the requirement - # so protect the older versions of PyTango. - import tango try: - EnsureOmniThread = getattr(tango, "EnsureOmniThread") - with EnsureOmniThread(): - Worker.run(self) - except AttributeError: + import tango + except ImportError: Worker.run(self) + # Tango is not thread safe when using threading.Thread. One must + # use omni threads instead. This was confirmed for parallel + # event subscriptions in PyTango#307. Use EnsureOmniThread introduced + # in PyTango#327 whenever available. + else: + if hasattr(tango, "EnsureOmniThread"): + with tango.EnsureOmniThread(): + Worker.run(self) + else: + Worker.run(self) def get_thread_pool(): From c116a383f28ae09a20d0977f1508bf4effb9dbcb Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 26 Feb 2020 11:11:36 +0100 Subject: [PATCH 433/830] Fix flake8 --- src/sardana/sardanathreadpool.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sardana/sardanathreadpool.py b/src/sardana/sardanathreadpool.py index 4543d825c3..ef54e63084 100644 --- a/src/sardana/sardanathreadpool.py +++ b/src/sardana/sardanathreadpool.py @@ -53,10 +53,10 @@ def run(self): # event subscriptions in PyTango#307. Use EnsureOmniThread introduced # in PyTango#327 whenever available. else: - if hasattr(tango, "EnsureOmniThread"): - with tango.EnsureOmniThread(): - Worker.run(self) - else: + if hasattr(tango, "EnsureOmniThread"): + with tango.EnsureOmniThread(): + Worker.run(self) + else: Worker.run(self) From b4200bd23a6d8b01c31241cefc8a5e8de8d47e80 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 30 Jan 2020 15:02:43 +0100 Subject: [PATCH 434/830] Add showscan options to group axis and grid display --- .../qt/qtgui/macrolistener/macrolistener.py | 238 ++++++++++-------- 1 file changed, 135 insertions(+), 103 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 3cf7b98a96..53c40192df 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -82,60 +82,55 @@ def assertPlotAvailability(exit_on_error=True): exit(1) -class ScanPlot(Qt.QWidget): +def empty_data(nb_points): + return ArrayBuffer(numpy.full(nb_points, numpy.nan)) - def __init__(self, x_axis, parent=None): - super().__init__(parent) - layout = Qt.QVBoxLayout(self) -# layout.setContentsMargins(0, 0, 0, 0) - self.plot_widget = self._buildPlotWidget(x_axis) - layout.addWidget(self.plot_widget) - self.x_axis = dict(x_axis, data=[]) - self.channels = [] - - def _buildPlotWidget(self, x_axis): - available = pyqtgraph is not None - if available: - widget = pyqtgraph.PlotWidget(labels=dict(bottom=x_axis['label'])) - widget.showGrid(x=True, y=True) - widget.scan_legend = widget.addLegend() - else: - widget = Qt.QLabel(NO_PLOT_MESSAGE) - widget.plot_available = available - return widget - def prepare(self, channels, nb_points=None): - widget = self.plot_widget - if not widget.plot_available: - return - widget.clear() - # legend is not properly updated when we clear the plot - widget.scan_legend.scene().removeItem(widget.scan_legend) - widget.scan_legend = widget.addLegend() +class MultiPlotWidget(Qt.QWidget): + + # plots: a list of plots + # each plot is: + # dict: { x_axis: { name: axis-name, label: axis-label + # curves: [{ name: curve_name, label: curve_label }] } + def __init__(self, plots, nb_points=None, parent=None): + super().__init__(parent) + layout = Qt.QVBoxLayout(self) + self.win = pyqtgraph.GraphicsLayoutWidget(title='Widget') + layout.addWidget(self.win) + plot_widgets = {} nb_points = 2**16 if nb_points is None else nb_points - self.x_axis['data'] = ArrayBuffer(numpy.full(nb_points, numpy.nan)) - self.channels = [] - styles = LoopList(CURVE_STYLES) - for channel in channels: - # don't use symbol: slows down plotting - pen, _ = styles.next() - item = widget.plot(name=channel['label'], pen=pen) - channel = dict(channel, plot_item=item, - data=ArrayBuffer(numpy.full(nb_points, numpy.nan))) - self.channels.append(channel) + plots_per_row = int(len(plots)**0.5) + for idx, plot in enumerate(plots): + plot_curves = {} + x_axis, curves = plot['x_axis'], plot['curves'] + if idx % plots_per_row == 0: + self.win.nextRow() + plot_widget = self.win.addPlot(labels=dict(bottom=x_axis['label'])) + plot_widget.showGrid(x=True, y=True) + plot_widget.addLegend() + plot_widget.x_axis = dict(x_axis, data=empty_data(nb_points)) + styles = LoopList(CURVE_STYLES) + for curve in curves: + pen, symbol = styles.current() + styles.next() + style = dict(pen=pen, symbol=symbol, + symbolSize=5, symbolBrush=pen) + curve_item = plot_widget.plot(name=curve['label'], **style) + curve_item.curve_data = empty_data(nb_points) + plot_curves[curve['name']] = curve_item + plot_widgets[plot_widget] = plot_curves + self.plots = plot_widgets def onNewPoint(self, data): - if not self.plot_widget.plot_available: - return - x_data = self.x_axis['data'] - x_data.append(data[self.x_axis['name']]) - for channel in self.channels: - name = channel['name'] - y_data = channel['data'] - plot_item = channel['plot_item'] - y_data.append(data[name]) - plot_item.setData(x_data.contents(), y_data.contents()) + for plot_widget, curves in self.plots.items(): + x_axis = plot_widget.x_axis + x_data = x_axis['data'] + x_data.append(data[x_axis['name']]) + for curve_name, curve_item in curves.items(): + y_data = curve_item.curve_data + y_data.append(data[curve_name]) + curve_item.setData(x_data.contents(), y_data.contents()) class DynamicPlotManager(Qt.QObject, TaurusBaseComponent): @@ -154,16 +149,36 @@ class DynamicPlotManager(Qt.QObject, TaurusBaseComponent): newShortMessage = Qt.pyqtSignal('QString') + Single = 'single' # each curve has its own plot + XAxis = 'x-axis' # group curves with same X-Axis + + Grid = 'grid' # layout in a square grid of plots + Panel = 'panel' # layout in a panel per plot + def __init__(self, parent=None): Qt.QObject.__init__(self, parent) TaurusBaseComponent.__init__(self, self.__class__.__name__) self.__panels = {} - - self._trends1d = {} - self._trends2d = {} + self.__plots = [] + self._layout_mode = self.Grid + self._group_mode = self.XAxis Qt.qApp.SDM.connectWriter("shortMessage", self, 'newShortMessage') + def setLayoutMode(self, layout): + assert layout in (self.Grid, self.Panel) + self._layout_mode = layout + + def layoutMode(self): + return self._layout_mode + + def setGroupMode(self, group): + assert group in (self.Single, self.XAxis) + self._group_mode = group + + def groupMode(self): + return self._group_mode + def setModel(self, doorname): '''reimplemented from :meth:`TaurusBaseComponent` @@ -236,55 +251,81 @@ def prepare(self, data_desc): Prepare UI for a new scan. Rebuilds plots as necessary to adapt to the new scan channels and moveables """ + self.removeAllPanels() data = data_desc['data'] - # dict< axis: list > - trends1d = collections.defaultdict(list) - column_map = {col['name']: col for col in data['column_desc']} + curves = [] + col_map = {column['name']: column for column in data['column_desc']} - # build a map of axis and corresponding channels for column in data['column_desc']: ptype = column.get('plot_type', PlotType.No) if ptype == PlotType.No: continue - ch_name = column['name'] - axes = [] - for axis in column.get('plot_axes', ()): - if axis == '': - axis = 'point_nb' - axes.append(axis) + elif ptype == PlotType.Image: + self.warning('Unsupported image plot for %s', ch_name) + continue + x_axes = ['point_nb' if axis == '' else axis + for axis in column.get('plot_axes', ())] + ndim = column.get('ndim', 0) or 0 if ptype == PlotType.Spectrum: - ndim = column.get('ndim', 0) or 0 if ndim == 0: # this is a trend - for axis in axes: - trends1d[axis].append(column) + for x_axis in x_axes: + x_label = col_map[x_axis]['label'] + curve = dict(column, x_axis=x_axis, + x_axis_label=x_label) + curves.append(curve) else: self.warning('Cannot create spectrum plot for %d dims ' 'channel %r', ndim, ch_name) - elif ptype == PlotType.Image: - self.warning('Unsupported image plot for %s', ch_name) - # build list of widgets: one plot for each axis. Widgets are recycled - # from the previous scans if possible to avoid rearranging the GUI - for axis in trends1d: - if axis not in self._trends1d: - x_axis = column_map[axis] - w = ScanPlot(x_axis) - title = 'Trend1D - ' + x_axis['label'] - self.createPanel(w, title, registerconfig=False, - permanent=False) - self._trends1d[axis] = title - - # remove widgets from previous scans which are not used in current scan - for axis in tuple(self._trends1d): - if axis not in trends1d: - self.removePanel(self._trends1d[axis]) - del self._trends1d[axis] - - # prepare each plot widget with list of channels - nb_points = data.get('total_scan_intervals', 2**16) + 1 - for axis, panel_name in self._trends1d.items(): - widget = self.getPanelWidget(panel_name) - widget.prepare(trends1d[axis], nb_points) + nb_points = data.get('total_scan_intervals', 2**16 - 1) + 1 + panels = [] + if self._group_mode == 'single': + if self._layout_mode == 'panel': + for curve in curves: + plot = dict(x_axis=dict(name=curve['x_axis'], + label=curve['x_axis_label']), + curves=[curve]) + plot_widget = MultiPlotWidget([plot], nb_points=nb_points) + panels.append((curve['label'], plot_widget)) + elif self._layout_mode == 'grid': + plots = [] + for curve in curves: + plot = dict(x_axis=dict(name=curve['x_axis'], + label=curve['x_axis_label']), + curves=[curve]) + plots.append(plot) + plot_widget = MultiPlotWidget(plots, nb_points=nb_points) + panels.append(('Plot scan', plot_widget)) + else: + raise NotImplementedError + elif self._group_mode == 'x-axis': + plot_map = {} + for curve in curves: + x_axis = curve['x_axis'] + plot = plot_map.get(x_axis) + if plot is None: + plot = dict(x_axis=dict(name=curve['x_axis'], + label=curve['x_axis_label']), + curves=[]) + plot_map[x_axis] = plot + plot['curves'].append(curve) + if self._layout_mode == 'panel': + for plot in plot_map.values(): + plot_widget = MultiPlotWidget([plot], nb_points=nb_points) + panel_name = ', '.join(curve['label'] for curve in plot['curves']) + panels.append((panel_name, plot_widget)) + elif self._layout_mode == 'grid': + plots = tuple(plot_map.values()) + plot_widget = MultiPlotWidget(plots, nb_points=nb_points) + panels.append(('Plot scan', plot_widget)) + else: + raise NotImplementedError + else: + raise NotImplementedError + + for name, panel in panels: + self.createPanel(panel, name, registerconfig=False, permanent=False) + self.__plots = panels # build status message serialno = 'Scan #{}'.format(data.get('serialno', '?')) @@ -305,9 +346,8 @@ def prepare(self, data_desc): def newPoint(self, point): data = point['data'] - for _, panel_name in self._trends1d.items(): - widget = self.getPanelWidget(panel_name) - widget.onNewPoint(data) + for _, plot in self.__plots: + plot.onNewPoint(data) point_nb = 'Point #{}'.format(data['point_nb']) msg = self.message_template.format(progress=point_nb) self.newShortMessage.emit(msg) @@ -351,18 +391,10 @@ def removePanel(self, name): widget.setParent(None) widget.close() - def removePanels(self, names=None): - '''removes panels. - - :param names: (seq) names of the panels to be removed. If None is - given (default), all the panels are removed. - ''' - if names is None: - names = (list(self._trends1d.values()) - + list(self._trends2d.values())) - # TODO: do the same for other temporary panels - for pname in names: - self.removePanel(pname) + def removeAllPanels(self): + '''removes all panels.''' + for name, _ in self.__plots: + self.removePanel(name) class MacroBroker(DynamicPlotManager): From da5af7dfc6401139b84e72b9586002f8c187dd71 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 30 Jan 2020 15:04:53 +0100 Subject: [PATCH 435/830] Add showscan gui script --- setup.py | 6 ++-- .../qt/qtgui/extra_sardana/showscanonline.py | 29 ++++++++----------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/setup.py b/setup.py index d69916f532..217c544c37 100644 --- a/setup.py +++ b/setup.py @@ -61,7 +61,8 @@ def get_release_info(): 'PyTango>=9.2.5', 'itango>=0.1.6', 'taurus>4.5.4', - 'lxml>=2.3' + 'lxml>=2.3', + 'click', ] @@ -78,7 +79,8 @@ def get_release_info(): "hklscan = sardana.taurus.qt.qtgui.extra_hkl.hklscan:main", "macroexecutor = sardana.taurus.qt.qtgui.extra_macroexecutor.macroexecutor:main", "sequencer = sardana.taurus.qt.qtgui.extra_macroexecutor.sequenceeditor:main", - "ubmatrix = sardana.taurus.qt.qtgui.extra_hkl.ubmatrix:main" + "ubmatrix = sardana.taurus.qt.qtgui.extra_hkl.ubmatrix:main", + "showscan = sardana.taurus.qt.qtgui.extra_sardana.showscanonline:main" ] entry_points = {'console_scripts': console_scripts, diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index 3a39c1e606..1930682cd4 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -27,6 +27,8 @@ __all__ = ["ShowScanOnline"] +import click + from taurus.qt.qtgui.taurusgui import TaurusGui from sardana.taurus.qt.qtgui.macrolistener import (DynamicPlotManager, assertPlotAvailability) @@ -83,33 +85,26 @@ class TaurusGuiLite(TaurusGui): SPLASH_LOGO_NAME = None -def main(): - +@click.command() +@click.option('--group', default='x-axis', + type=click.Choice(['single', 'x-axis']), + help='group curves') +@click.argument('door') +def main(group, door): from taurus.qt.qtgui.application import TaurusApplication - import sys - from taurus.core.util.argparse import get_taurus_parser - - parser = get_taurus_parser() - parser.set_usage("python showscanonline.py [door_name]") app = TaurusApplication(app_name='Showscan Online', org_domain="Sardana", - org_name="Tango communinity", - cmd_line_parser=parser) + org_name="Tango communinity", parser=None) assertPlotAvailability() gui = TaurusGuiLite() - args = app.get_command_line_args() - - if len(args) < 1: - parser.print_help(sys.stderr) - sys.exit(1) - door_name = args[0] widget = ShowScanOnline(gui) - widget.setModel(door_name) + widget.setModel(door) + widget.setGroupMode(group) gui.show() - sys.exit(app.exec_()) + return app.exec_() if __name__ == "__main__": From 03f3b196038097b0cb3b50fb7da30180ba63c632 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 30 Jan 2020 15:07:09 +0100 Subject: [PATCH 436/830] Remove layout mode from showscan --- .../qt/qtgui/macrolistener/macrolistener.py | 55 +++++-------------- 1 file changed, 13 insertions(+), 42 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 53c40192df..c6e5cd94b1 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -152,26 +152,15 @@ class DynamicPlotManager(Qt.QObject, TaurusBaseComponent): Single = 'single' # each curve has its own plot XAxis = 'x-axis' # group curves with same X-Axis - Grid = 'grid' # layout in a square grid of plots - Panel = 'panel' # layout in a panel per plot - def __init__(self, parent=None): Qt.QObject.__init__(self, parent) TaurusBaseComponent.__init__(self, self.__class__.__name__) self.__panels = {} self.__plots = [] - self._layout_mode = self.Grid self._group_mode = self.XAxis Qt.qApp.SDM.connectWriter("shortMessage", self, 'newShortMessage') - def setLayoutMode(self, layout): - assert layout in (self.Grid, self.Panel) - self._layout_mode = layout - - def layoutMode(self): - return self._layout_mode - def setGroupMode(self, group): assert group in (self.Single, self.XAxis) self._group_mode = group @@ -279,26 +268,16 @@ def prepare(self, data_desc): nb_points = data.get('total_scan_intervals', 2**16 - 1) + 1 panels = [] - if self._group_mode == 'single': - if self._layout_mode == 'panel': - for curve in curves: - plot = dict(x_axis=dict(name=curve['x_axis'], - label=curve['x_axis_label']), - curves=[curve]) - plot_widget = MultiPlotWidget([plot], nb_points=nb_points) - panels.append((curve['label'], plot_widget)) - elif self._layout_mode == 'grid': - plots = [] - for curve in curves: - plot = dict(x_axis=dict(name=curve['x_axis'], - label=curve['x_axis_label']), - curves=[curve]) - plots.append(plot) - plot_widget = MultiPlotWidget(plots, nb_points=nb_points) - panels.append(('Plot scan', plot_widget)) - else: - raise NotImplementedError - elif self._group_mode == 'x-axis': + if self._group_mode == self.Single: + plots = [] + for curve in curves: + plot = dict(x_axis=dict(name=curve['x_axis'], + label=curve['x_axis_label']), + curves=[curve]) + plots.append(plot) + plot_widget = MultiPlotWidget(plots, nb_points=nb_points) + panels.append(('Plot scan', plot_widget)) + elif self._group_mode == self.XAxis: plot_map = {} for curve in curves: x_axis = curve['x_axis'] @@ -309,17 +288,9 @@ def prepare(self, data_desc): curves=[]) plot_map[x_axis] = plot plot['curves'].append(curve) - if self._layout_mode == 'panel': - for plot in plot_map.values(): - plot_widget = MultiPlotWidget([plot], nb_points=nb_points) - panel_name = ', '.join(curve['label'] for curve in plot['curves']) - panels.append((panel_name, plot_widget)) - elif self._layout_mode == 'grid': - plots = tuple(plot_map.values()) - plot_widget = MultiPlotWidget(plots, nb_points=nb_points) - panels.append(('Plot scan', plot_widget)) - else: - raise NotImplementedError + plots = tuple(plot_map.values()) + plot_widget = MultiPlotWidget(plots, nb_points=nb_points) + panels.append(('Plot scan', plot_widget)) else: raise NotImplementedError From bd4d923931a0120ebde3363befadfdac758057d1 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 30 Jan 2020 17:41:27 +0100 Subject: [PATCH 437/830] Fix showscan options by preventing taurus from intercepting command line options --- src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index 1930682cd4..22b9cefd1a 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -94,7 +94,7 @@ def main(group, door): from taurus.qt.qtgui.application import TaurusApplication app = TaurusApplication(app_name='Showscan Online', org_domain="Sardana", - org_name="Tango communinity", parser=None) + org_name="Tango communinity", cmd_line_parser=None) assertPlotAvailability() From c0b5d920e78a520819cf54b870d7b0ed7c581017 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 30 Jan 2020 17:44:11 +0100 Subject: [PATCH 438/830] Recycle showscan plot widget and update curves at fixed rate (5Hz) --- .../qt/qtgui/macrolistener/macrolistener.py | 94 +++++++++++++------ 1 file changed, 64 insertions(+), 30 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index c6e5cd94b1..9fc78582c5 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -88,16 +88,23 @@ def empty_data(nb_points): class MultiPlotWidget(Qt.QWidget): + def __init__(self, parent=None): + super().__init__(parent) + layout = Qt.QVBoxLayout(self) + self.win = pyqtgraph.GraphicsLayoutWidget(title='Widget') + layout.addWidget(self.win) + self._plots = {} + self._timer = None + self._event_nb = 0 + self._last_event_nb = 0 + # plots: a list of plots # each plot is: # dict: { x_axis: { name: axis-name, label: axis-label # curves: [{ name: curve_name, label: curve_label }] } - def __init__(self, plots, nb_points=None, parent=None): - super().__init__(parent) - layout = Qt.QVBoxLayout(self) - self.win = pyqtgraph.GraphicsLayoutWidget(title='Widget') - layout.addWidget(self.win) + def prepare(self, plots, nb_points=None): + self.win.clear() plot_widgets = {} nb_points = 2**16 if nb_points is None else nb_points plots_per_row = int(len(plots)**0.5) @@ -106,32 +113,69 @@ def __init__(self, plots, nb_points=None, parent=None): x_axis, curves = plot['x_axis'], plot['curves'] if idx % plots_per_row == 0: self.win.nextRow() - plot_widget = self.win.addPlot(labels=dict(bottom=x_axis['label'])) + nb_curves = len(curves) + labels = dict(bottom=x_axis['label']) + if nb_curves == 1: + labels['left'] = curves[0]['label'] + plot_widget = self.win.addPlot(labels=labels) plot_widget.showGrid(x=True, y=True) - plot_widget.addLegend() + if nb_curves > 1: + plot_widget.addLegend() plot_widget.x_axis = dict(x_axis, data=empty_data(nb_points)) styles = LoopList(CURVE_STYLES) for curve in curves: pen, symbol = styles.current() styles.next() style = dict(pen=pen, symbol=symbol, - symbolSize=5, symbolBrush=pen) + symbolSize=5, symbolPen=pen, symbolBrush=pen) curve_item = plot_widget.plot(name=curve['label'], **style) curve_item.curve_data = empty_data(nb_points) plot_curves[curve['name']] = curve_item plot_widgets[plot_widget] = plot_curves - self.plots = plot_widgets + self._plots = plot_widgets + self._event_nb = 0 + self._last_event_nb = 0 + self._start_update() def onNewPoint(self, data): - for plot_widget, curves in self.plots.items(): + # update internal data and mark the new event + for plot_widget, curves in self._plots.items(): x_axis = plot_widget.x_axis x_data = x_axis['data'] x_data.append(data[x_axis['name']]) for curve_name, curve_item in curves.items(): y_data = curve_item.curve_data y_data.append(data[curve_name]) + self._event_nb += 1 + + def onEnd(self, data): + self.do_update() + self._end_update() + + def do_update(self): + if self._event_nb == self._last_event_nb: + return + self._last_event_nb = self._event_nb + for plot_widget, curves in self._plots.items(): + x_axis = plot_widget.x_axis + x_data = x_axis['data'] + for curve_name, curve_item in curves.items(): + y_data = curve_item.curve_data curve_item.setData(x_data.contents(), y_data.contents()) + def _start_update(self): + self._end_update() + timer = Qt.QTimer() + timer.timeout.connect(self.do_update) + # refresh curves at a fix rate of ~5Hz + timer.start(200) + self._timer = timer + + def _end_update(self): + if self._timer: + self._timer.stop() + self._timer = None + class DynamicPlotManager(Qt.QObject, TaurusBaseComponent): '''This is a manager of plots related to the execution of macros. @@ -156,11 +200,12 @@ def __init__(self, parent=None): Qt.QObject.__init__(self, parent) TaurusBaseComponent.__init__(self, self.__class__.__name__) - self.__panels = {} - self.__plots = [] self._group_mode = self.XAxis Qt.qApp.SDM.connectWriter("shortMessage", self, 'newShortMessage') + self._plot = MultiPlotWidget() + self.createPanel(self._plot, 'Scan plot', registerconfig=False, permanent=False) + def setGroupMode(self, group): assert group in (self.Single, self.XAxis) self._group_mode = group @@ -240,11 +285,12 @@ def prepare(self, data_desc): Prepare UI for a new scan. Rebuilds plots as necessary to adapt to the new scan channels and moveables """ - self.removeAllPanels() data = data_desc['data'] - curves = [] col_map = {column['name']: column for column in data['column_desc']} + # Build list of curves. Each curve has the same column info from the + # event and additionally the x-axis it should be plot against + curves = [] for column in data['column_desc']: ptype = column.get('plot_type', PlotType.No) if ptype == PlotType.No: @@ -266,8 +312,6 @@ def prepare(self, data_desc): self.warning('Cannot create spectrum plot for %d dims ' 'channel %r', ndim, ch_name) - nb_points = data.get('total_scan_intervals', 2**16 - 1) + 1 - panels = [] if self._group_mode == self.Single: plots = [] for curve in curves: @@ -275,8 +319,6 @@ def prepare(self, data_desc): label=curve['x_axis_label']), curves=[curve]) plots.append(plot) - plot_widget = MultiPlotWidget(plots, nb_points=nb_points) - panels.append(('Plot scan', plot_widget)) elif self._group_mode == self.XAxis: plot_map = {} for curve in curves: @@ -289,14 +331,11 @@ def prepare(self, data_desc): plot_map[x_axis] = plot plot['curves'].append(curve) plots = tuple(plot_map.values()) - plot_widget = MultiPlotWidget(plots, nb_points=nb_points) - panels.append(('Plot scan', plot_widget)) else: raise NotImplementedError - for name, panel in panels: - self.createPanel(panel, name, registerconfig=False, permanent=False) - self.__plots = panels + nb_points = data.get('total_scan_intervals', 2**16 - 1) + 1 + self._plot.prepare(plots, nb_points=nb_points) # build status message serialno = 'Scan #{}'.format(data.get('serialno', '?')) @@ -317,14 +356,14 @@ def prepare(self, data_desc): def newPoint(self, point): data = point['data'] - for _, plot in self.__plots: - plot.onNewPoint(data) + self._plot.onNewPoint(data) point_nb = 'Point #{}'.format(data['point_nb']) msg = self.message_template.format(progress=point_nb) self.newShortMessage.emit(msg) def end(self, end_data): data = end_data['data'] + self._plot.onEnd(data) progress = 'Ended {}'.format(data['endtime']) msg = self.message_template.format(progress=progress) self.newShortMessage.emit(msg) @@ -362,11 +401,6 @@ def removePanel(self, name): widget.setParent(None) widget.close() - def removeAllPanels(self): - '''removes all panels.''' - for name, _ in self.__plots: - self.removePanel(name) - class MacroBroker(DynamicPlotManager): '''A manager of all macro-related panels of a TaurusGui. From f08c4dfee42f9130e2a86612f24e9a0332886e36 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 30 Jan 2020 17:44:34 +0100 Subject: [PATCH 439/830] Better showscan curve colors and symbols --- src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 9fc78582c5..6b3c28ac29 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -61,14 +61,14 @@ __docformat__ = 'restructuredtext' COLORS = [Qt.QColor(Qt.Qt.red), - Qt.QColor(Qt.Qt.blue), Qt.QColor(Qt.Qt.green), Qt.QColor(Qt.Qt.magenta), + Qt.QColor(Qt.Qt.blue), Qt.QColor(Qt.Qt.cyan), Qt.QColor(Qt.Qt.yellow), Qt.QColor(Qt.Qt.white)] -SYMBOLS = ['o', 't', 't1', 't2', 't3', 's', 'p'] +SYMBOLS = ['x', 'o', 't', 't1', 't2', 't3', 's', 'p'] CURVE_STYLES = [(color, symbol) for symbol in SYMBOLS for color in COLORS] From 5bcda6a5b30964f4d789ea7e434ed7229c824298 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Wed, 5 Feb 2020 07:40:12 +0100 Subject: [PATCH 440/830] Add --taurus-log-level to showscan script --- .../taurus/qt/qtgui/extra_sardana/showscanonline.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index 22b9cefd1a..fb8e5590d7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -89,8 +89,16 @@ class TaurusGuiLite(TaurusGui): @click.option('--group', default='x-axis', type=click.Choice(['single', 'x-axis']), help='group curves') +@click.option('--taurus-log-level', + type=click.Choice(['critical', 'error', 'warning', 'info', + 'debug', 'trace'], case_sensitive=False), + default='error', show_default=True, + help='Show only logs with priority LEVEL or above') @click.argument('door') -def main(group, door): +def main(group, taurus_log_level, door): + import taurus + taurus.setLogLevel(getattr(taurus, taurus_log_level.capitalize())) + from taurus.qt.qtgui.application import TaurusApplication app = TaurusApplication(app_name='Showscan Online', org_domain="Sardana", From bf624c0f9dfc65a9d12ad080c8ca54b553ed55f8 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 10 Oct 2019 12:13:37 +0200 Subject: [PATCH 441/830] Fix Tango inheritance --- src/sardana/tango/core/SardanaDevice.py | 8 ++++---- src/sardana/tango/pool/Pool.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sardana/tango/core/SardanaDevice.py b/src/sardana/tango/core/SardanaDevice.py index 3e496be7a2..7d8d9ca70c 100644 --- a/src/sardana/tango/core/SardanaDevice.py +++ b/src/sardana/tango/core/SardanaDevice.py @@ -35,7 +35,7 @@ import threading import PyTango.constants -from PyTango import Device_4Impl, DeviceClass, Util, DevState, \ +from PyTango import LatestDeviceImpl, DeviceClass, Util, DevState, \ AttrQuality, TimeVal, ArgType, ApiUtil, DevFailed, WAttribute from taurus.core.util.threadpool import ThreadPool @@ -66,7 +66,7 @@ def get_thread_pool(): return __thread_pool -class SardanaDevice(Device_4Impl, Logger): +class SardanaDevice(LatestDeviceImpl, Logger): """SardanaDevice represents the base class for all Sardana :class:`PyTango.DeviceImpl` classes""" @@ -74,7 +74,7 @@ def __init__(self, dclass, name): """Constructor""" self.in_constructor = True try: - Device_4Impl.__init__(self, dclass, name) + LatestDeviceImpl.__init__(self, dclass, name) self.init(name) Logger.__init__(self, name) @@ -512,7 +512,7 @@ def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Generic description", doc_url="http://sardana-controls.org/", __icon=self.get_name().lower() + ".png", - InheritedFrom=["Device_4Impl"]) + InheritedFrom=["Device_5Impl"]) def write_class_property(self): """Write class properties ``ProjectTitle``, ``Description``, diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index e073abf4f4..54c0e29934 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -48,12 +48,12 @@ import collections -class Pool(PyTango.Device_4Impl, Logger): +class Pool(PyTango.LatestDeviceImpl, Logger): ElementsCache = None def __init__(self, cl, name): - PyTango.Device_4Impl.__init__(self, cl, name) + PyTango.LatestDeviceImpl.__init__(self, cl, name) Logger.__init__(self, name) self.init(name) self.init_device() @@ -1576,7 +1576,7 @@ def __init__(self, name): def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Device Pool management class", doc_url="http://sardana-controls.org/", - InheritedFrom="Device_4Impl") + InheritedFrom="Device_5Impl") def write_class_property(self): util = PyTango.Util.instance() From 4e1cffe75024a48f7feb46458ead433e9e86fb5a Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Wed, 26 Feb 2020 17:19:58 +0100 Subject: [PATCH 442/830] Add protection for referable channels Allow to set the parameters only if the channel is referable and set them by default. --- src/sardana/pool/poolmeasurementgroup.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 2dddc74e0c..60f30536b9 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -923,6 +923,13 @@ def _fill_channel_data(self, channel, channel_data): if ctype != ElementType.External and channel.is_referable(): value_ref_enabled = channel_data.get('value_ref_enabled', False) channel_data['value_ref_enabled'] = value_ref_enabled + value_ref_pattern = channel_data.get('value_ref_pattern', '') + channel_data['value_ref_pattern'] = value_ref_pattern + elif 'value_ref_enabled' in channel_data or 'value_ref_pattern' in \ + channel_data: + msg = 'The channel {} is not referable. You can not set the ' \ + 'enable and/or the pattern parameters.'.format(name) + raise ValueError(msg) # Definitively should be initialized by measurement group # index MUST be here already (asserting this in the following line) channel_data['index'] = channel_data['index'] From 17ef7edd27711133e7e93617cc63adda09e17e52 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Wed, 26 Feb 2020 17:26:37 +0100 Subject: [PATCH 443/830] Fix record data event comparison error Make sure we compare the previous event object reference instead of equality. The equality is specially sensible when using numpy arrays --- src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 3cf7b98a96..ee8a0af41f 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -216,7 +216,7 @@ def onRecordDataUpdated(self, arg): """ # Filter events sent by itself - if arg == self.old_arg: + if arg is self.old_arg: return self.old_arg = arg From 2d7d6f0f520270b2a9d8383fdcd4fab5e3516450 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Wed, 26 Feb 2020 17:50:01 +0100 Subject: [PATCH 444/830] Add missing channel name --- src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 6b3c28ac29..7959a72259 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -292,6 +292,7 @@ def prepare(self, data_desc): # event and additionally the x-axis it should be plot against curves = [] for column in data['column_desc']: + ch_name = column['name'] ptype = column.get('plot_type', PlotType.No) if ptype == PlotType.No: continue From c79c84672f9999a8cb39a65b908a2a0fe066fcdb Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Wed, 26 Feb 2020 17:50:18 +0100 Subject: [PATCH 445/830] Fix flake8 errors --- src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 7959a72259..605202391d 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -193,8 +193,8 @@ class DynamicPlotManager(Qt.QObject, TaurusBaseComponent): newShortMessage = Qt.pyqtSignal('QString') - Single = 'single' # each curve has its own plot - XAxis = 'x-axis' # group curves with same X-Axis + Single = 'single' # each curve has its own plot + XAxis = 'x-axis' # group curves with same X-Axis def __init__(self, parent=None): Qt.QObject.__init__(self, parent) @@ -204,7 +204,8 @@ def __init__(self, parent=None): Qt.qApp.SDM.connectWriter("shortMessage", self, 'newShortMessage') self._plot = MultiPlotWidget() - self.createPanel(self._plot, 'Scan plot', registerconfig=False, permanent=False) + self.createPanel( + self._plot, 'Scan plot', registerconfig=False, permanent=False) def setGroupMode(self, group): assert group in (self.Single, self.XAxis) From aeb8ff73e0cb93dac3e462101cc36c51b356315c Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 27 Feb 2020 12:37:22 +0100 Subject: [PATCH 446/830] pyqtgraph 0.10.0 does not support title in GraphicsLayoutWidget constructor --- src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index 605202391d..22338f5a22 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -91,7 +91,7 @@ class MultiPlotWidget(Qt.QWidget): def __init__(self, parent=None): super().__init__(parent) layout = Qt.QVBoxLayout(self) - self.win = pyqtgraph.GraphicsLayoutWidget(title='Widget') + self.win = pyqtgraph.GraphicsLayoutWidget() layout.addWidget(self.win) self._plots = {} self._timer = None From 81df5fc5e9b22c264f801f786a44896d291a1cdb Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 27 Feb 2020 14:58:58 +0100 Subject: [PATCH 447/830] Fix errors on general helpers Return None if the key does not exist. Fix bug when use fullname. --- src/sardana/taurus/core/tango/sardana/pool.py | 54 +++++++++++++------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 9877ef9be9..6a6a7477fe 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1783,6 +1783,10 @@ def _set_channels_key(self, key, value, channels_names=None, self.applyConfiguration() def _get_channels_key(self, key, channels_names=None, use_fullname=False): + """ + Helper method to return the value for one channel configuration key, + if the key does not exist the value will be None. + """ result = collections.OrderedDict({}) if channels_names is None: @@ -1790,19 +1794,22 @@ def _get_channels_key(self, key, channels_names=None, use_fullname=False): for channel_name in channels_names: channel = self._get_channel_data(channel_name) - - value = channel[key] if use_fullname: label = channel else: label = channel['label'] - if key == 'plot_axes': - res = [] - for v in value: - if v not in ['', '']: - v = self.channels[v]['label'] - res.append(v) - value = res + try: + value = channel[key] + except KeyError: + result[label] = None + continue + if key == 'plot_axes': + res = [] + for v in value: + if v not in ['', '']: + v = self.channels[v]['label'] + res.append(v) + value = res result[label] = value return result @@ -1820,6 +1827,10 @@ def _set_ctrls_key(self, key, value, ctrls_names=None, apply_cfg=True): self.applyConfiguration() def _get_ctrls_key(self, key, ctrls_names=None, use_fullname=False): + """ + Helper method to return the value for one controller configuration key, + if the key does not exist the value will be None. + """ result = collections.OrderedDict({}) if ctrls_names is None: ctrls_names = self.controllers.keys() @@ -1828,16 +1839,25 @@ def _get_ctrls_key(self, key, ctrls_names=None, use_fullname=False): if ctrl_name == '__tango__': continue ctrl = self._get_ctrl_data(ctrl_name) - label = ctrl_name - value = ctrl[key] + + if use_fullname: + label = ctrl_name + else: + label = DeviceProxy(ctrl_name).alias() + + try: + value = ctrl[key] + except KeyError: + result[label] = None + continue + if key == 'synchronization': value = AcqSynchType.get(value) - if not use_fullname: - label = DeviceProxy(ctrl_name).alias() - if key in ['timer', 'monitor']: - value = self.channels[value]['label'] - elif key == 'synchronizer' and value != 'software': - value = DeviceProxy(value).alias() + elif key in ['timer', 'monitor']: + value = self.channels[value]['label'] + elif key == 'synchronizer' and value != 'software': + value = DeviceProxy(value).alias() + result[label] = value return result From f48ab73af795e5128f023d13da62487fa06b07ce Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 27 Feb 2020 15:01:38 +0100 Subject: [PATCH 448/830] Adapt and add test for referable parameters --- .../test_measurementgroupconfiguretion.py | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index b9e99bc438..202b72796b 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -221,7 +221,7 @@ def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", self.pool.DeleteElement(mg_name) def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", - "_test_ct_1_1", "_test_ct_1_2"]): + ]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -229,23 +229,36 @@ def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", mg = Device(mg_name) enabled = mg.getValueRefEnabled(*elements) self._assertResult(enabled, elements, False) - mg.setValueRefEnabled(False, *elements) + mg.setValueRefEnabled(True, *elements) enabled = mg.getValueRefEnabled(*elements) - self._assertResult(enabled, elements, False) - enabled = mg.getValueRefEnabled("_test_2d_ctrl_1") - self._assertResult(enabled, elements[:2], False) - enabled = mg.getValueRefEnabled("_test_ct_ctrl_1") - self._assertResult(enabled, elements[-2:], False) + self._assertResult(enabled, elements, True) + mg.setValueRefEnabled(False, elements[0]) + result = mg.getValueRefEnabled(*elements) + expected = [True] * len(elements) + expected[0] = False + self._assertMultipleResults(result, elements, expected) mg.setValueRefEnabled(True, *elements) enabled = mg.getValueRefEnabled(*elements) self._assertResult(enabled, elements, True) + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + def test_ValueRefEnabledCounters(self, elements=["_test_ct_1_3"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + try: + mg = Device(mg_name) + result = mg.getValueRefEnabled(*elements) + self._assertResult(result, elements, None) + with self.assertRaises(Exception): + mg.setValueRefEnabled(True, *elements) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", - "_test_ct_1_3"]): + def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -262,3 +275,18 @@ def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", finally: mg.cleanUp() self.pool.DeleteElement(mg_name) + + def test_ValueRefPatternCounter(self, elements=["_test_ct_1_3"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + try: + mg = Device(mg_name) + pattern = mg.getValueRefEnabled(*elements) + self._assertResult(pattern, elements, None) + with self.assertRaises(Exception): + mg.setValueRefEnabled('/tmp/test_foo.txt', *elements) + + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) From 355641cc47c3d38e8c2b7456f1e7526d558f3abd Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 27 Feb 2020 15:06:24 +0100 Subject: [PATCH 449/830] Fix flake8 errors --- .../tango/sardana/test/test_measurementgroupconfiguretion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index 202b72796b..319cec9cf3 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -253,7 +253,7 @@ def test_ValueRefEnabledCounters(self, elements=["_test_ct_1_3"]): result = mg.getValueRefEnabled(*elements) self._assertResult(result, elements, None) with self.assertRaises(Exception): - mg.setValueRefEnabled(True, *elements) + mg.setValueRefEnabled(True, *elements) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) From 7c8fce2c542b83f1d0befcbd4c80f7228ec53708 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 28 Feb 2020 09:59:01 +0100 Subject: [PATCH 450/830] Protect installation without Taurus with worker_cls argument --- src/sardana/macroserver/scan/gscan.py | 13 +++++++++++-- src/sardana/sardanathreadpool.py | 9 +++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 7c08fc377d..a2f94f9f3a 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1962,8 +1962,17 @@ def scan_loop(self): class CAcquisition(object): def __init__(self): - self._thread_pool = ThreadPool(name="ValueBufferTH", Psize=1, - Qsize=100000, worker_cls=OmniWorker) + # protect older versions of Taurus (without the worker_cls argument) + # remove it whenever we bump Taurus dependency + try: + self._thread_pool = ThreadPool(name="ValueBufferTH", + Psize=1, + Qsize=100000, + worker_cls=OmniWorker) + except TypeError: + self._thread_pool = ThreadPool(name="ValueBufferTH", + Psize=1, + Qsize=100000) self._countdown_latch = CountLatch() self._index_offset = 0 diff --git a/src/sardana/sardanathreadpool.py b/src/sardana/sardanathreadpool.py index ef54e63084..a624c69c77 100644 --- a/src/sardana/sardanathreadpool.py +++ b/src/sardana/sardanathreadpool.py @@ -70,6 +70,11 @@ def get_thread_pool(): global __thread_pool_lock with __thread_pool_lock: if __thread_pool is None: - __thread_pool = ThreadPool(name="SardanaTP", Psize=10, - worker_cls=OmniWorker) + # protect older versions of Taurus (without the worker_cls argument) + # remove it whenever we bump Taurus dependency + try: + __thread_pool = ThreadPool(name="SardanaTP", Psize=10, + worker_cls=OmniWorker) + except TypeError: + __thread_pool = ThreadPool(name="SardanaTP", Psize=10) return __thread_pool From 443dda8900b04213815719f4e9623486894afb8b Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 28 Feb 2020 10:19:41 +0100 Subject: [PATCH 451/830] Fix flake8 --- src/sardana/sardanathreadpool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/sardanathreadpool.py b/src/sardana/sardanathreadpool.py index a624c69c77..4fb337e7da 100644 --- a/src/sardana/sardanathreadpool.py +++ b/src/sardana/sardanathreadpool.py @@ -70,8 +70,8 @@ def get_thread_pool(): global __thread_pool_lock with __thread_pool_lock: if __thread_pool is None: - # protect older versions of Taurus (without the worker_cls argument) - # remove it whenever we bump Taurus dependency + # protect older versions of Taurus (without the worker_cls + # argument) remove it whenever we bump Taurus dependency try: __thread_pool = ThreadPool(name="SardanaTP", Psize=10, worker_cls=OmniWorker) From c5e0c921c7ad0286f0aafed708207f8cfe29c38e Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 28 Feb 2020 15:16:37 +0100 Subject: [PATCH 452/830] Revert "Fix Tango inheritance" This reverts commit bf624c0f9dfc65a9d12ad080c8ca54b553ed55f8. --- src/sardana/tango/core/SardanaDevice.py | 8 ++++---- src/sardana/tango/pool/Pool.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sardana/tango/core/SardanaDevice.py b/src/sardana/tango/core/SardanaDevice.py index 7d8d9ca70c..3e496be7a2 100644 --- a/src/sardana/tango/core/SardanaDevice.py +++ b/src/sardana/tango/core/SardanaDevice.py @@ -35,7 +35,7 @@ import threading import PyTango.constants -from PyTango import LatestDeviceImpl, DeviceClass, Util, DevState, \ +from PyTango import Device_4Impl, DeviceClass, Util, DevState, \ AttrQuality, TimeVal, ArgType, ApiUtil, DevFailed, WAttribute from taurus.core.util.threadpool import ThreadPool @@ -66,7 +66,7 @@ def get_thread_pool(): return __thread_pool -class SardanaDevice(LatestDeviceImpl, Logger): +class SardanaDevice(Device_4Impl, Logger): """SardanaDevice represents the base class for all Sardana :class:`PyTango.DeviceImpl` classes""" @@ -74,7 +74,7 @@ def __init__(self, dclass, name): """Constructor""" self.in_constructor = True try: - LatestDeviceImpl.__init__(self, dclass, name) + Device_4Impl.__init__(self, dclass, name) self.init(name) Logger.__init__(self, name) @@ -512,7 +512,7 @@ def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Generic description", doc_url="http://sardana-controls.org/", __icon=self.get_name().lower() + ".png", - InheritedFrom=["Device_5Impl"]) + InheritedFrom=["Device_4Impl"]) def write_class_property(self): """Write class properties ``ProjectTitle``, ``Description``, diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index 54c0e29934..e073abf4f4 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -48,12 +48,12 @@ import collections -class Pool(PyTango.LatestDeviceImpl, Logger): +class Pool(PyTango.Device_4Impl, Logger): ElementsCache = None def __init__(self, cl, name): - PyTango.LatestDeviceImpl.__init__(self, cl, name) + PyTango.Device_4Impl.__init__(self, cl, name) Logger.__init__(self, name) self.init(name) self.init_device() @@ -1576,7 +1576,7 @@ def __init__(self, name): def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Device Pool management class", doc_url="http://sardana-controls.org/", - InheritedFrom="Device_5Impl") + InheritedFrom="Device_4Impl") def write_class_property(self): util = PyTango.Util.instance() From 271a205fe385b4e38a48a761e22f82d29d5dd7f2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 28 Feb 2020 16:09:40 +0100 Subject: [PATCH 453/830] (minor) rename local variables logReceived is a listener for the log attributes e.g. Output, Info, etc. Rename local variable (which must be a copy&paste) error to avoid confusion. --- src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py index 34b4345b2b..d32ecdcef8 100644 --- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py +++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py @@ -95,8 +95,8 @@ def macroStatusReceived(self, s, t, v): def logReceived(self, log_name, output): res = BaseDoor.logReceived(self, log_name, output) log_name = log_name.lower() - recordDataUpdated = getattr(self, "%sUpdated" % log_name) - recordDataUpdated.emit(output) + logUpdated = getattr(self, "%sUpdated" % log_name) + logUpdated.emit(output) return res def _prepare_connections(self): From 53cbf959b1e745246670bac9486051d44c3a87e1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 28 Feb 2020 16:12:35 +0100 Subject: [PATCH 454/830] Remove filtering of signals emitted by DynamicPlotManager itself The recordDataUpdated signals are no more emitted by the DynamicPlotManager. This was changed in 66c2a65. So there is no need to filter them out. --- .../taurus/qt/qtgui/macrolistener/macrolistener.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py index ee8a0af41f..816b7c9af0 100644 --- a/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py +++ b/src/sardana/taurus/qt/qtgui/macrolistener/macrolistener.py @@ -184,7 +184,6 @@ def setModel(self, doorname): self._checkJsonRecorder() self.door.recordDataUpdated.connect(self.onRecordDataUpdated) - self.old_arg = None self.message_template = 'Ready!' def _checkJsonRecorder(self): @@ -214,13 +213,6 @@ def onRecordDataUpdated(self, arg): :param arg: RecordData Tuple :return: """ - - # Filter events sent by itself - if arg is self.old_arg: - return - - self.old_arg = arg - data = arg[1] if 'type' in data: event_type = data['type'] From b6ed4d1b2b4750b73b157e42e5b1270ae74a4e30 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 28 Feb 2020 16:38:52 +0100 Subject: [PATCH 455/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f72c5c813..8ef665042b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ This file follows the formats and conventions from [keepachangelog.com] pyplot.draw() to make sure that the plot is refreshed (#1280) * Remove TangoAttribute controllers from Sardana (#181, #1279) * Remove deprecation warning revealed when running test suite (#1267) +* Remove event filtering in `DynamicPlotManagerFix` (showscan online) (#1299) ### Deprecated From 31904a50b90bc77dd960fcb41547035542a7ec92 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 28 Feb 2020 18:38:01 +0100 Subject: [PATCH 456/830] Update CHANGELOG.md --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ef665042b..b253af6916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,13 @@ This file follows the formats and conventions from [keepachangelog.com] ### Added -* Support to Python >= 3.5 (#1089, #1173, 1201) +* Support to Python >= 3.5 (#1089, #1173, #1201) +* Showscan online based on pyqtgraph (#1285) + * multiple plots in the same MultiPlot widget (as opposed to different panels before) + * option to group curves by x-axis or individual plot per curve + * new showscan console script + * support fast scans: update curves at a fix rate (5Hz) + * better curve colors and symbols * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) * Instruments creation and configuration in sar_demo (#1198) * Documentation to Taurus Extensions of Sardana Devices: MacroServer part From 256944dfa3a51dd6f0ca32a73b2e22882fe3210f Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 28 Feb 2020 18:40:58 +0100 Subject: [PATCH 457/830] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b253af6916..27eae4d796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,8 @@ This file follows the formats and conventions from [keepachangelog.com] pyplot.draw() to make sure that the plot is refreshed (#1280) * Remove TangoAttribute controllers from Sardana (#181, #1279) * Remove deprecation warning revealed when running test suite (#1267) -* Remove event filtering in `DynamicPlotManagerFix` (showscan online) (#1299) +* Remove event filtering in `DynamicPlotManager` (showscan online) (#1299) +* Avoid unnecessary creations of DeviceProxies in `ascanct` (#1281) ### Deprecated From 0aae6285a5bb64ae9a1ebf07adcdf6b8835026d4 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 1 Mar 2020 17:34:36 +0100 Subject: [PATCH 458/830] Do not use finish hook to reset ctrls Since 6a80fdc ctrls were reset in finish_hook to allow free readout of Value and ValueRef attributes after acquisition has finished. (after SEP17 this won't be needed cause Sardana will always keep in memory the last acquisition results) This was buggy in the case of very fast acquisitions - the elements were already released from the acquisition context and new acquisition could overlap with the finish_hook. Avoid this problem by resetting the controllers still in the action_loop. --- src/sardana/pool/poolacquisition.py | 659 ++++++++++++++-------------- 1 file changed, 338 insertions(+), 321 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 52eb346b89..34caf93fc3 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -37,6 +37,7 @@ import weakref import datetime import traceback +import functools from taurus.core.util.log import DebugIt from taurus.core.util.enumeration import Enumeration @@ -807,114 +808,117 @@ def start_action(self, ctrls, value, master, repetitions, latency, self._set_pool_ctrl_dict_loop(ctrls) # split controllers to read value and value reference self._split_ctrl(ctrls) - self.add_finish_hook(self._reset_ctrl_dicts, False) - - # channels that are acquired (only enabled) - self._channels = [] - - def load(channel, value, repetitions, latency=0): - axis = channel.axis - pool_ctrl = channel.controller - ctrl = pool_ctrl.ctrl - ctrl.PreLoadAll() - try: - res = ctrl.PreLoadOne(axis, value, repetitions, - latency) - except TypeError: + try: + # channels that are acquired (only enabled) + self._channels = [] + + def load(channel, value, repetitions, latency=0): + axis = channel.axis + pool_ctrl = channel.controller + ctrl = pool_ctrl.ctrl + ctrl.PreLoadAll() try: - res = ctrl.PreLoadOne(axis, value, repetitions) - msg = ("PreLoadOne(axis, value, repetitions) is " - "deprecated since version 2.7.0. Use PreLoadOne(" - "axis, value, repetitions, latency_time) instead.") - self.warning(msg) + res = ctrl.PreLoadOne(axis, value, repetitions, + latency) except TypeError: - res = ctrl.PreLoadOne(axis, value) - msg = ("PreLoadOne(axis, value) is deprecated since " - "version 2.3.0. Use PreLoadOne(axis, value, " - "repetitions, latency_time) instead.") - self.warning(msg) - if not res: - msg = ("%s.PreLoadOne(%d) returned False" % - (pool_ctrl.name, axis)) - raise Exception(msg) - try: - ctrl.LoadOne(axis, value, repetitions, latency) - except TypeError: + try: + res = ctrl.PreLoadOne(axis, value, repetitions) + msg = ("PreLoadOne(axis, value, repetitions) is " + "deprecated since version 2.7.0." + "Use PreLoadOne(axis, value, repetitions, " + "latency_time) instead.") + self.warning(msg) + except TypeError: + res = ctrl.PreLoadOne(axis, value) + msg = ("PreLoadOne(axis, value) is deprecated since " + "version 2.3.0. Use PreLoadOne(axis, value, " + "repetitions, latency_time) instead.") + self.warning(msg) + if not res: + msg = ("%s.PreLoadOne(%d) returned False" % + (pool_ctrl.name, axis)) + raise Exception(msg) try: - ctrl.LoadOne(axis, value, repetitions) - msg = ("LoadOne(axis, value, repetitions) is deprecated " - "since version Jan18. Use LoadOne(axis, value, " - "repetitions, latency_time) instead.") - self.warning(msg) + ctrl.LoadOne(axis, value, repetitions, latency) except TypeError: - ctrl.LoadOne(axis, value) - msg = ("LoadOne(axis, value) is deprecated since " - "version 2.3.0. Use LoadOne(axis, value, " - "repetitions) instead.") - self.warning(msg) - ctrl.LoadAll() + try: + ctrl.LoadOne(axis, value, repetitions) + msg = ("LoadOne(axis, value, repetitions) is" + "deprecated since version Jan18." + "Use LoadOne(axis, value, repetitions, " + "latency_time) instead.") + self.warning(msg) + except TypeError: + ctrl.LoadOne(axis, value) + msg = ("LoadOne(axis, value) is deprecated since " + "version 2.3.0. Use LoadOne(axis, value, " + "repetitions) instead.") + self.warning(msg) + ctrl.LoadAll() + + with ActionContext(self): + # PreLoadAll, PreLoadOne, LoadOne and LoadAll + for ctrl in ctrls: + # TODO find solution for master now sardana only use timer + load(ctrl.timer, value, repetitions, latency) + + # TODO: remove when the action allows to use tango attributes + try: + ctrls.pop('__tango__') + except Exception: + pass - with ActionContext(self): - # PreLoadAll, PreLoadOne, LoadOne and LoadAll - for ctrl in ctrls: - # TODO find solution for master now sardana only use timer - load(ctrl.timer, value, repetitions, latency) + # PreStartAll on all enabled controllers + for ctrl in ctrls: + pool_ctrl = ctrl.element + pool_ctrl.ctrl.PreStartAll() - # TODO: remove when the action allows to use tango attributes - try: - ctrls.pop('__tango__') - except Exception: - pass + # PreStartOne & StartOne on all enabled elements + for ctrl in ctrls: + channels = ctrl.get_channels(enabled=True) - # PreStartAll on all enabled controllers - for ctrl in ctrls: - pool_ctrl = ctrl.element - pool_ctrl.ctrl.PreStartAll() - - # PreStartOne & StartOne on all enabled elements - for ctrl in ctrls: - channels = ctrl.get_channels(enabled=True) - - # make sure that the master timer/monitor is started as the - # last one - channels.remove(ctrl.master) - channels.append(ctrl.master) - for channel in channels: - axis = channel.axis - pool_ctrl = ctrl.element - ret = pool_ctrl.ctrl.PreStartOne(axis, value) - if not ret: - msg = ("%s.PreStartOne(%d) returns False" % - (ctrl.name, axis)) - raise Exception(msg) + # make sure that the master timer/monitor is started as + # the last one + channels.remove(ctrl.master) + channels.append(ctrl.master) + for channel in channels: + axis = channel.axis + pool_ctrl = ctrl.element + ret = pool_ctrl.ctrl.PreStartOne(axis, value) + if not ret: + msg = ("%s.PreStartOne(%d) returns False" % + (ctrl.name, axis)) + raise Exception(msg) + try: + pool_ctrl = ctrl.element + pool_ctrl.ctrl.StartOne(axis, value) + except Exception as e: + self.debug(e, exc_info=True) + channel.set_state(State.Fault, propagate=2) + msg = ("%s.StartOne(%d) failed" % + (ctrl.name, axis)) + raise Exception(msg) + + self._channels.append(channel) + + # set the state of all elements to and inform their listeners + for channel in self._channels: + channel.set_state(State.Moving, propagate=2) + + # StartAll on all enabled controllers + for ctrl in ctrls: try: pool_ctrl = ctrl.element - pool_ctrl.ctrl.StartOne(axis, value) + pool_ctrl.ctrl.StartAll() except Exception as e: + channels = ctrl.get_channels(enabled=True) self.debug(e, exc_info=True) - channel.set_state(State.Fault, propagate=2) - msg = ("%s.StartOne(%d) failed" % - (ctrl.name, axis)) + for channel in channels: + channel.set_state(State.Fault, propagate=2) + msg = ("%s.StartAll() failed" % ctrl.name) raise Exception(msg) - - self._channels.append(channel) - - # set the state of all elements to and inform their listeners - for channel in self._channels: - channel.set_state(State.Moving, propagate=2) - - # StartAll on all enabled controllers - for ctrl in ctrls: - try: - pool_ctrl = ctrl.element - pool_ctrl.ctrl.StartAll() - except Exception as e: - channels = ctrl.get_channels(enabled=True) - self.debug(e, exc_info=True) - for channel in channels: - channel.set_state(State.Fault, propagate=2) - msg = ("%s.StartAll() failed" % ctrl.name) - raise Exception(msg) + finally: + self._reset_ctrl_dicts() def _set_pool_ctrl_dict_loop(self, ctrls): ctrl_channels = {} @@ -997,71 +1001,74 @@ def get_read_value_ctrls(self): @DebugIt() def action_loop(self): - i = 0 - - states, values, value_refs = {}, {}, {} - for channel in self._channels: - element = channel.element - states[element] = None - - nap = self._acq_sleep_time - nb_states_per_value = self._nb_states_per_value + try: + i = 0 - while True: - self.read_state_info(ret=states) - if not self.in_acquisition(states): - break + states, values, value_refs = {}, {}, {} + for channel in self._channels: + element = channel.element + states[element] = None + + nap = self._acq_sleep_time + nb_states_per_value = self._nb_states_per_value + + while True: + self.read_state_info(ret=states) + if not self.in_acquisition(states): + break + + # read value every n times + if not i % nb_states_per_value: + self.read_value(ret=values) + for acquirable, value in list(values.items()): + if is_value_error(value): + self.error("Loop read value error for %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value.exc_info)) + self.debug(msg) + acquirable.put_value(value) + else: + acquirable.extend_value_buffer(value) + + time.sleep(nap) + i += 1 + + with ActionContext(self): + self.raw_read_state_info(ret=states) + self.raw_read_value(ret=values) + self.raw_read_value_ref(ret=value_refs) - # read value every n times - if not i % nb_states_per_value: - self.read_value(ret=values) - for acquirable, value in list(values.items()): + for acquirable, state_info in list(states.items()): + # first update the element state so that value calculation + # that is done after takes the updated state into account + acquirable.set_state_info(state_info, propagate=0) + if acquirable in values: + value = values[acquirable] if is_value_error(value): - self.error("Loop read value error for %s" % + self.error("Loop final read value error for: %s" % acquirable.name) msg = "Details: " + "".join( traceback.format_exception(*value.exc_info)) self.debug(msg) acquirable.put_value(value) else: - acquirable.extend_value_buffer(value) - - time.sleep(nap) - i += 1 - - with ActionContext(self): - self.raw_read_state_info(ret=states) - self.raw_read_value(ret=values) - self.raw_read_value_ref(ret=value_refs) - - for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) - if acquirable in values: - value = values[acquirable] - if is_value_error(value): - self.error("Loop final read value error for: %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value.exc_info)) - self.debug(msg) - acquirable.put_value(value) - else: - acquirable.extend_value_buffer(value, propagate=2) - if acquirable in value_refs: - value_ref = value_refs[acquirable] - if is_value_error(value_ref): - self.error("Loop final read value ref error for: %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value_ref.exc_info)) - self.debug(msg) - acquirable.extend_value_ref_buffer(value_ref, propagate=2) - with acquirable: - acquirable.clear_operation() - state_info = acquirable._from_ctrl_state_info(state_info) - acquirable.set_state_info(state_info, propagate=2) + acquirable.extend_value_buffer(value, propagate=2) + if acquirable in value_refs: + value_ref = value_refs[acquirable] + if is_value_error(value_ref): + self.error("Loop final read value ref error for: %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value_ref.exc_info)) + self.debug(msg) + acquirable.extend_value_ref_buffer(value_ref, propagate=2) + with acquirable: + acquirable.clear_operation() + state_info = acquirable._from_ctrl_state_info(state_info) + acquirable.set_state_info(state_info, propagate=2) + finally: + self._reset_ctrl_dicts() class PoolAcquisitionSoftware(PoolAcquisitionBase): @@ -1108,68 +1115,71 @@ def start_action(self, ctrls, value, master, index, acq_sleep_time=None, @DebugIt() def action_loop(self): - states, values, value_refs = {}, {}, {} - for channel in self._channels: - element = channel.element - states[element] = None - - nap = self._acq_sleep_time - nb_states_per_value = self._nb_states_per_value - - i = 0 - while True: - self.read_state_info(ret=states) - if not self.in_acquisition(states): - break - - # read value every n times - if not i % nb_states_per_value: - self.read_value_loop(ret=values) - for acquirable, value in list(values.items()): - acquirable.put_value(value) + try: + states, values, value_refs = {}, {}, {} + for channel in self._channels: + element = channel.element + states[element] = None + + nap = self._acq_sleep_time + nb_states_per_value = self._nb_states_per_value + + i = 0 + while True: + self.read_state_info(ret=states) + if not self.in_acquisition(states): + break + + # read value every n times + if not i % nb_states_per_value: + self.read_value_loop(ret=values) + for acquirable, value in list(values.items()): + acquirable.put_value(value) - time.sleep(nap) - i += 1 + time.sleep(nap) + i += 1 - for slave in self._slaves: - try: - slave.stop_action() - except Exception: - self.warning("Unable to stop slave acquisition %s", - slave.getLogName()) - self.debug("Details", exc_info=1) + for slave in self._slaves: + try: + slave.stop_action() + except Exception: + self.warning("Unable to stop slave acquisition %s", + slave.getLogName()) + self.debug("Details", exc_info=1) - with ActionContext(self): - self.raw_read_state_info(ret=states) - self.raw_read_value(ret=values) - self.raw_read_value_ref(ret=value_refs) + with ActionContext(self): + self.raw_read_state_info(ret=states) + self.raw_read_value(ret=values) + self.raw_read_value_ref(ret=value_refs) - for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) - if acquirable in values: - value = values[acquirable] - if is_value_error(value): - self.error("Loop final read value error for: %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value.exc_info)) - self.debug(msg) - acquirable.append_value_buffer(value, self._index) - if acquirable in value_refs: - value_ref = value_refs[acquirable] - if is_value_error(value_ref): - self.error("Loop final read value ref error for: %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value_ref.exc_info)) - self.debug(msg) - acquirable.append_value_ref_buffer(value_ref, self._index) - with acquirable: - acquirable.clear_operation() - state_info = acquirable._from_ctrl_state_info(state_info) - acquirable.set_state_info(state_info, propagate=2) + for acquirable, state_info in list(states.items()): + # first update the element state so that value calculation + # that is done after takes the updated state into account + acquirable.set_state_info(state_info, propagate=0) + if acquirable in values: + value = values[acquirable] + if is_value_error(value): + self.error("Loop final read value error for: %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value.exc_info)) + self.debug(msg) + acquirable.append_value_buffer(value, self._index) + if acquirable in value_refs: + value_ref = value_refs[acquirable] + if is_value_error(value_ref): + self.error("Loop final read value ref error for: %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value_ref.exc_info)) + self.debug(msg) + acquirable.append_value_ref_buffer(value_ref, self._index) + with acquirable: + acquirable.clear_operation() + state_info = acquirable._from_ctrl_state_info(state_info) + acquirable.set_state_info(state_info, propagate=2) + finally: + self._reset_ctrl_dicts() class PoolAcquisitionSoftwareStart(PoolAcquisitionBase): @@ -1203,83 +1213,86 @@ def start_action(self, ctrls, value, master, repetitions, latency, @DebugIt() def action_loop(self): - i = 0 - - states, values, value_refs = {}, {}, {} - for channel in self._channels: - element = channel.element - states[element] = None - - nap = self._acq_sleep_time - nb_states_per_value = self._nb_states_per_value + try: + i = 0 - while True: - self.read_state_info(ret=states) - if not self.in_acquisition(states): - break + states, values, value_refs = {}, {}, {} + for channel in self._channels: + element = channel.element + states[element] = None + + nap = self._acq_sleep_time + nb_states_per_value = self._nb_states_per_value + + while True: + self.read_state_info(ret=states) + if not self.in_acquisition(states): + break + + # read value every n times + if not i % nb_states_per_value: + self.read_value(ret=values) + for acquirable, value in list(values.items()): + if is_value_error(value): + self.error("Loop read value error for %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value.exc_info)) + self.debug(msg) + acquirable.put_value(value) + else: + acquirable.extend_value_buffer(value) + self.read_value_ref(ret=value_refs) + for acquirable, value_ref in list(value_refs.items()): + if is_value_error(value_ref): + self.error("Loop read value ref error for %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value.exc_info)) + self.debug(msg) + acquirable.put_value_ref(value) + else: + acquirable.extend_value_ref_buffer(value_ref) + time.sleep(nap) + i += 1 + + with ActionContext(self): + self.raw_read_state_info(ret=states) + self.raw_read_value(ret=values) + self.raw_read_value_ref(ret=value_refs) - # read value every n times - if not i % nb_states_per_value: - self.read_value(ret=values) - for acquirable, value in list(values.items()): + for acquirable, state_info in list(states.items()): + # first update the element state so that value calculation + # that is done after takes the updated state into account + acquirable.set_state_info(state_info, propagate=0) + if acquirable in values: + value = values[acquirable] if is_value_error(value): - self.error("Loop read value error for %s" % + self.error("Loop final read value error for: %s" % acquirable.name) msg = "Details: " + "".join( traceback.format_exception(*value.exc_info)) self.debug(msg) acquirable.put_value(value) else: - acquirable.extend_value_buffer(value) - self.read_value_ref(ret=value_refs) - for acquirable, value_ref in list(value_refs.items()): + acquirable.extend_value_buffer(value, propagate=2) + if acquirable in value_refs: + value_ref = value_refs[acquirable] if is_value_error(value_ref): - self.error("Loop read value ref error for %s" % + self.error("Loop final read value ref error for: %s" % acquirable.name) msg = "Details: " + "".join( - traceback.format_exception(*value.exc_info)) + traceback.format_exception(*value_ref.exc_info)) self.debug(msg) - acquirable.put_value_ref(value) + acquirable.put_value_ref(value_ref) else: - acquirable.extend_value_ref_buffer(value_ref) - time.sleep(nap) - i += 1 - - with ActionContext(self): - self.raw_read_state_info(ret=states) - self.raw_read_value(ret=values) - self.raw_read_value_ref(ret=value_refs) - - for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) - if acquirable in values: - value = values[acquirable] - if is_value_error(value): - self.error("Loop final read value error for: %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value.exc_info)) - self.debug(msg) - acquirable.put_value(value) - else: - acquirable.extend_value_buffer(value, propagate=2) - if acquirable in value_refs: - value_ref = value_refs[acquirable] - if is_value_error(value_ref): - self.error("Loop final read value ref error for: %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value_ref.exc_info)) - self.debug(msg) - acquirable.put_value_ref(value_ref) - else: - acquirable.extend_value_ref_buffer(value_ref, propagate=2) - with acquirable: - acquirable.clear_operation() - state_info = acquirable._from_ctrl_state_info(state_info) - acquirable.set_state_info(state_info, propagate=2) + acquirable.extend_value_ref_buffer(value_ref, propagate=2) + with acquirable: + acquirable.clear_operation() + state_info = acquirable._from_ctrl_state_info(state_info) + acquirable.set_state_info(state_info, propagate=2) + finally: + self._reset_ctrl_dicts() class PoolCTAcquisition(PoolAcquisitionBase): @@ -1314,60 +1327,62 @@ def in_acquisition(self, states): @DebugIt() def action_loop(self): - i = 0 - - states, values = {}, {} - for element in self._channels: - states[element] = None - # values[element] = None + try: + i = 0 - nap = self._acq_sleep_time - nb_states_per_value = self._nb_states_per_value + states, values = {}, {} + for element in self._channels: + states[element] = None + # values[element] = None - # read values to send a first event when starting to acquire - with ActionContext(self): - self.raw_read_value_loop(ret=values) - for acquirable, value in list(values.items()): - acquirable.put_value(value, propagate=2) + nap = self._acq_sleep_time + nb_states_per_value = self._nb_states_per_value - while True: - self.read_state_info(ret=states) - if not self.in_acquisition(states): - break - - # read value every n times - if not i % nb_states_per_value: - self.read_value_loop(ret=values) + # read values to send a first event when starting to acquire + with ActionContext(self): + self.raw_read_value_loop(ret=values) for acquirable, value in list(values.items()): - acquirable.put_value(value) + acquirable.put_value(value, propagate=2) - time.sleep(nap) - i += 1 + while True: + self.read_state_info(ret=states) + if not self.in_acquisition(states): + break - for slave in self._slaves: - try: - slave.stop_action() - except Exception: - self.warning("Unable to stop slave acquisition %s", - slave.getLogName()) - self.debug("Details", exc_info=1) + # read value every n times + if not i % nb_states_per_value: + self.read_value_loop(ret=values) + for acquirable, value in list(values.items()): + acquirable.put_value(value) - with ActionContext(self): - self.raw_read_state_info(ret=states) - self.raw_read_value_loop(ret=values) + time.sleep(nap) + i += 1 - for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) - if acquirable in values: - value = values[acquirable] - acquirable.put_value(value, propagate=2) - with acquirable: - acquirable.clear_operation() - state_info = acquirable._from_ctrl_state_info(state_info) - acquirable.set_state_info(state_info, propagate=2) + for slave in self._slaves: + try: + slave.stop_action() + except Exception: + self.warning("Unable to stop slave acquisition %s", + slave.getLogName()) + self.debug("Details", exc_info=1) + with ActionContext(self): + self.raw_read_state_info(ret=states) + self.raw_read_value_loop(ret=values) + + for acquirable, state_info in list(states.items()): + # first update the element state so that value calculation + # that is done after takes the updated state into account + acquirable.set_state_info(state_info, propagate=0) + if acquirable in values: + value = values[acquirable] + acquirable.put_value(value, propagate=2) + with acquirable: + acquirable.clear_operation() + state_info = acquirable._from_ctrl_state_info(state_info) + acquirable.set_state_info(state_info, propagate=2) + finally: + self._reset_ctrl_dicts() class Pool0DAcquisition(PoolAction): @@ -1453,9 +1468,11 @@ def action_loop(self): # that is done after takes the updated state into account state_info = acquirable._from_ctrl_state_info(state_info) acquirable.set_state_info(state_info, propagate=0) - with acquirable: - acquirable.clear_operation() - acquirable.set_state_info(state_info, propagate=2) + set_state_info = functools.partial(acquirable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) def stop_action(self, *args, **kwargs): """Stop procedure for this action.""" From a91c4c2c779236cd33cf9f220f72560d5cc127a6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 1 Mar 2020 17:53:18 +0100 Subject: [PATCH 459/830] Add safe arg to set_state_info Add the possibility to execute thread safe set_state_info --- src/sardana/pool/poolbaseelement.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sardana/pool/poolbaseelement.py b/src/sardana/pool/poolbaseelement.py index 794615a1ea..4eccdd0a81 100644 --- a/src/sardana/pool/poolbaseelement.py +++ b/src/sardana/pool/poolbaseelement.py @@ -262,8 +262,12 @@ def calculate_state_info(self, status_info=None): ctrl_status=status) return status_info[0], new_status - def set_state_info(self, state_info, propagate=1): - self._set_state_info(state_info, propagate=propagate) + def set_state_info(self, state_info, propagate=1, safe=False): + if safe: + with self: + self._set_state_info(state_info, propagate=propagate) + else: + self._set_state_info(state_info, propagate=propagate) def _set_state_info(self, state_info, propagate=1): state_info = self.calculate_state_info(state_info) From 950e7dc9dee140a884d20a293b5e4d74b567a740 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 1 Mar 2020 18:00:55 +0100 Subject: [PATCH 460/830] Do not clear operation in acquisition action_loop OperationConext wrongly cleared twice - within the action_loop and when the OperationContext exits. In the case of fast acquisitions this may cause that when leaving the previous operation context we clear the operation context of the next one. Call set_state_info as the finish_hook so it will be called when the operation context is cleared already. --- src/sardana/pool/poolacquisition.py | 40 +++++++++++++++++------------ 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 34caf93fc3..3dd93d90e1 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -1063,10 +1063,12 @@ def action_loop(self): traceback.format_exception(*value_ref.exc_info)) self.debug(msg) acquirable.extend_value_ref_buffer(value_ref, propagate=2) - with acquirable: - acquirable.clear_operation() - state_info = acquirable._from_ctrl_state_info(state_info) - acquirable.set_state_info(state_info, propagate=2) + state_info = acquirable._from_ctrl_state_info(state_info) + set_state_info = functools.partial(acquirable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) finally: self._reset_ctrl_dicts() @@ -1174,10 +1176,12 @@ def action_loop(self): traceback.format_exception(*value_ref.exc_info)) self.debug(msg) acquirable.append_value_ref_buffer(value_ref, self._index) - with acquirable: - acquirable.clear_operation() - state_info = acquirable._from_ctrl_state_info(state_info) - acquirable.set_state_info(state_info, propagate=2) + state_info = acquirable._from_ctrl_state_info(state_info) + set_state_info = functools.partial(acquirable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) finally: self._reset_ctrl_dicts() @@ -1287,10 +1291,12 @@ def action_loop(self): acquirable.put_value_ref(value_ref) else: acquirable.extend_value_ref_buffer(value_ref, propagate=2) - with acquirable: - acquirable.clear_operation() - state_info = acquirable._from_ctrl_state_info(state_info) - acquirable.set_state_info(state_info, propagate=2) + state_info = acquirable._from_ctrl_state_info(state_info) + set_state_info = functools.partial(acquirable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) finally: self._reset_ctrl_dicts() @@ -1377,10 +1383,12 @@ def action_loop(self): if acquirable in values: value = values[acquirable] acquirable.put_value(value, propagate=2) - with acquirable: - acquirable.clear_operation() - state_info = acquirable._from_ctrl_state_info(state_info) - acquirable.set_state_info(state_info, propagate=2) + state_info = acquirable._from_ctrl_state_info(state_info) + set_state_info = functools.partial(acquirable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) finally: self._reset_ctrl_dicts() From 72bab6e993f29aec692fb179049f2307baf68fc5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 1 Mar 2020 18:03:28 +0100 Subject: [PATCH 461/830] Do not clear operation in motion action_loop OperationConext is wrongly cleared twice - within the action_loop and when the OperationContext exits. In the case of fast motions this may cause that when leaving the previous operation context we clear the operation context of the next one. Call set_state_info as the finish_hook so it will be called when the operation context is cleared already. Also protect the set_state_info with the lock. --- src/sardana/pool/poolmotion.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sardana/pool/poolmotion.py b/src/sardana/pool/poolmotion.py index 65b4e53f78..831b6eb95d 100644 --- a/src/sardana/pool/poolmotion.py +++ b/src/sardana/pool/poolmotion.py @@ -31,6 +31,7 @@ __docformat__ = 'restructuredtext' import time +import functools from taurus.core.util.log import DebugIt from taurus.core.util.enumeration import Enumeration @@ -390,9 +391,11 @@ def action_loop(self): # ... but before protect the motor so that the monitor # doesn't come in between the two instructions below and # send a state event on it's own - with moveable: - moveable.clear_operation() - moveable.set_state_info(real_state_info, propagate=2) + set_state_info = functools.partial(moveable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) # Then update the state if not stopped_now: From 36c36eb7260f33ec88b4313e965ec4c034e7166b Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 1 Mar 2020 18:28:09 +0100 Subject: [PATCH 462/830] Allow to customize OperationContext for PoolAction --- src/sardana/pool/poolaction.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sardana/pool/poolaction.py b/src/sardana/pool/poolaction.py index 25f692d8ec..63de46d371 100644 --- a/src/sardana/pool/poolaction.py +++ b/src/sardana/pool/poolaction.py @@ -195,6 +195,8 @@ class PoolAction(Logger): """A generic class to handle any type of operation (like motion or acquisition)""" + OperationContextClass = OperationContext + def __init__(self, main_element, name="GlobalAction"): Logger.__init__(self, name) self._action_run_lock = threading.Lock() @@ -336,7 +338,7 @@ def run(self, *args, **kwargs): if synch: try: - with OperationContext(self) as context: + with self.OperationContextClass(self) as context: self.start_action(*args, **kwargs) self._started = False self.action_loop() @@ -344,7 +346,7 @@ def run(self, *args, **kwargs): self._started = False self._running = False else: - context = OperationContext(self) + context = self.OperationContextClass(self) context.enter() try: self.start_action(*args, **kwargs) From 5e1a526edca424e1fb2ee101e9cd79dfa27b40c0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 1 Mar 2020 18:31:49 +0100 Subject: [PATCH 463/830] Use acquisition operation context to reset ctrl dicts Improve solution introduced in 0aae628 (try & except & finally) and use custom acquisition operation context which reset the ctrl dicts before clearing operation for the elements. --- src/sardana/pool/poolacquisition.py | 679 ++++++++++++++-------------- 1 file changed, 338 insertions(+), 341 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 3dd93d90e1..b2a66ad7d9 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -45,7 +45,8 @@ from sardana import SardanaValue, State, ElementType, TYPE_TIMERABLE_ELEMENTS from sardana.sardanathreadpool import get_thread_pool from sardana.pool import AcqSynch, AcqMode -from sardana.pool.poolaction import ActionContext, PoolAction +from sardana.pool.poolaction import ActionContext, PoolAction, \ + OperationContext from sardana.pool.poolsynchronization import PoolSynchronization #: enumeration representing possible motion states @@ -266,6 +267,14 @@ def get_channels(self, enabled=None): return list(self._channels_disabled) +class AcquisitionBaseContext(OperationContext): + + def exit(self): + pool_action = self._pool_action + pool_action._reset_ctrl_dicts() + return OperationContext.exit(self) + + class PoolAcquisition(PoolAction): """Acquisition action which is internally composed for sub-actions. @@ -650,6 +659,8 @@ class PoolAcquisitionBase(PoolAction): deemed necessary by the core developers. """ + OperationContextClass = AcquisitionBaseContext + def __init__(self, main_element, name): PoolAction.__init__(self, main_element, name) self._channels = [] @@ -808,117 +819,114 @@ def start_action(self, ctrls, value, master, repetitions, latency, self._set_pool_ctrl_dict_loop(ctrls) # split controllers to read value and value reference self._split_ctrl(ctrls) - try: - # channels that are acquired (only enabled) - self._channels = [] - - def load(channel, value, repetitions, latency=0): - axis = channel.axis - pool_ctrl = channel.controller - ctrl = pool_ctrl.ctrl - ctrl.PreLoadAll() + # channels that are acquired (only enabled) + self._channels = [] + + def load(channel, value, repetitions, latency=0): + axis = channel.axis + pool_ctrl = channel.controller + ctrl = pool_ctrl.ctrl + ctrl.PreLoadAll() + try: + res = ctrl.PreLoadOne(axis, value, repetitions, + latency) + except TypeError: try: - res = ctrl.PreLoadOne(axis, value, repetitions, - latency) + res = ctrl.PreLoadOne(axis, value, repetitions) + msg = ("PreLoadOne(axis, value, repetitions) is " + "deprecated since version 2.7.0." + "Use PreLoadOne(axis, value, repetitions, " + "latency_time) instead.") + self.warning(msg) except TypeError: - try: - res = ctrl.PreLoadOne(axis, value, repetitions) - msg = ("PreLoadOne(axis, value, repetitions) is " - "deprecated since version 2.7.0." - "Use PreLoadOne(axis, value, repetitions, " - "latency_time) instead.") - self.warning(msg) - except TypeError: - res = ctrl.PreLoadOne(axis, value) - msg = ("PreLoadOne(axis, value) is deprecated since " - "version 2.3.0. Use PreLoadOne(axis, value, " - "repetitions, latency_time) instead.") - self.warning(msg) - if not res: - msg = ("%s.PreLoadOne(%d) returned False" % - (pool_ctrl.name, axis)) - raise Exception(msg) + res = ctrl.PreLoadOne(axis, value) + msg = ("PreLoadOne(axis, value) is deprecated since " + "version 2.3.0. Use PreLoadOne(axis, value, " + "repetitions, latency_time) instead.") + self.warning(msg) + if not res: + msg = ("%s.PreLoadOne(%d) returned False" % + (pool_ctrl.name, axis)) + raise Exception(msg) + try: + ctrl.LoadOne(axis, value, repetitions, latency) + except TypeError: try: - ctrl.LoadOne(axis, value, repetitions, latency) + ctrl.LoadOne(axis, value, repetitions) + msg = ("LoadOne(axis, value, repetitions) is" + "deprecated since version Jan18." + "Use LoadOne(axis, value, repetitions, " + "latency_time) instead.") + self.warning(msg) except TypeError: - try: - ctrl.LoadOne(axis, value, repetitions) - msg = ("LoadOne(axis, value, repetitions) is" - "deprecated since version Jan18." - "Use LoadOne(axis, value, repetitions, " - "latency_time) instead.") - self.warning(msg) - except TypeError: - ctrl.LoadOne(axis, value) - msg = ("LoadOne(axis, value) is deprecated since " - "version 2.3.0. Use LoadOne(axis, value, " - "repetitions) instead.") - self.warning(msg) - ctrl.LoadAll() - - with ActionContext(self): - # PreLoadAll, PreLoadOne, LoadOne and LoadAll - for ctrl in ctrls: - # TODO find solution for master now sardana only use timer - load(ctrl.timer, value, repetitions, latency) - - # TODO: remove when the action allows to use tango attributes - try: - ctrls.pop('__tango__') - except Exception: - pass + ctrl.LoadOne(axis, value) + msg = ("LoadOne(axis, value) is deprecated since " + "version 2.3.0. Use LoadOne(axis, value, " + "repetitions) instead.") + self.warning(msg) + ctrl.LoadAll() - # PreStartAll on all enabled controllers - for ctrl in ctrls: - pool_ctrl = ctrl.element - pool_ctrl.ctrl.PreStartAll() + with ActionContext(self): + # PreLoadAll, PreLoadOne, LoadOne and LoadAll + for ctrl in ctrls: + # TODO find solution for master now sardana only use timer + load(ctrl.timer, value, repetitions, latency) - # PreStartOne & StartOne on all enabled elements - for ctrl in ctrls: - channels = ctrl.get_channels(enabled=True) + # TODO: remove when the action allows to use tango attributes + try: + ctrls.pop('__tango__') + except Exception: + pass - # make sure that the master timer/monitor is started as - # the last one - channels.remove(ctrl.master) - channels.append(ctrl.master) - for channel in channels: - axis = channel.axis - pool_ctrl = ctrl.element - ret = pool_ctrl.ctrl.PreStartOne(axis, value) - if not ret: - msg = ("%s.PreStartOne(%d) returns False" % - (ctrl.name, axis)) - raise Exception(msg) - try: - pool_ctrl = ctrl.element - pool_ctrl.ctrl.StartOne(axis, value) - except Exception as e: - self.debug(e, exc_info=True) - channel.set_state(State.Fault, propagate=2) - msg = ("%s.StartOne(%d) failed" % - (ctrl.name, axis)) - raise Exception(msg) - - self._channels.append(channel) - - # set the state of all elements to and inform their listeners - for channel in self._channels: - channel.set_state(State.Moving, propagate=2) - - # StartAll on all enabled controllers - for ctrl in ctrls: + # PreStartAll on all enabled controllers + for ctrl in ctrls: + pool_ctrl = ctrl.element + pool_ctrl.ctrl.PreStartAll() + + # PreStartOne & StartOne on all enabled elements + for ctrl in ctrls: + channels = ctrl.get_channels(enabled=True) + + # make sure that the master timer/monitor is started as + # the last one + channels.remove(ctrl.master) + channels.append(ctrl.master) + for channel in channels: + axis = channel.axis + pool_ctrl = ctrl.element + ret = pool_ctrl.ctrl.PreStartOne(axis, value) + if not ret: + msg = ("%s.PreStartOne(%d) returns False" % + (ctrl.name, axis)) + raise Exception(msg) try: pool_ctrl = ctrl.element - pool_ctrl.ctrl.StartAll() + pool_ctrl.ctrl.StartOne(axis, value) except Exception as e: - channels = ctrl.get_channels(enabled=True) self.debug(e, exc_info=True) - for channel in channels: - channel.set_state(State.Fault, propagate=2) - msg = ("%s.StartAll() failed" % ctrl.name) + channel.set_state(State.Fault, propagate=2) + msg = ("%s.StartOne(%d) failed" % + (ctrl.name, axis)) raise Exception(msg) - finally: - self._reset_ctrl_dicts() + + self._channels.append(channel) + + # set the state of all elements to and inform their listeners + for channel in self._channels: + channel.set_state(State.Moving, propagate=2) + + # StartAll on all enabled controllers + for ctrl in ctrls: + try: + pool_ctrl = ctrl.element + pool_ctrl.ctrl.StartAll() + except Exception as e: + channels = ctrl.get_channels(enabled=True) + self.debug(e, exc_info=True) + for channel in channels: + channel.set_state(State.Fault, propagate=2) + msg = ("%s.StartAll() failed" % ctrl.name) + raise Exception(msg) def _set_pool_ctrl_dict_loop(self, ctrls): ctrl_channels = {} @@ -1001,76 +1009,73 @@ def get_read_value_ctrls(self): @DebugIt() def action_loop(self): - try: - i = 0 + i = 0 - states, values, value_refs = {}, {}, {} - for channel in self._channels: - element = channel.element - states[element] = None - - nap = self._acq_sleep_time - nb_states_per_value = self._nb_states_per_value - - while True: - self.read_state_info(ret=states) - if not self.in_acquisition(states): - break - - # read value every n times - if not i % nb_states_per_value: - self.read_value(ret=values) - for acquirable, value in list(values.items()): - if is_value_error(value): - self.error("Loop read value error for %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value.exc_info)) - self.debug(msg) - acquirable.put_value(value) - else: - acquirable.extend_value_buffer(value) - - time.sleep(nap) - i += 1 - - with ActionContext(self): - self.raw_read_state_info(ret=states) - self.raw_read_value(ret=values) - self.raw_read_value_ref(ret=value_refs) + states, values, value_refs = {}, {}, {} + for channel in self._channels: + element = channel.element + states[element] = None - for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) - if acquirable in values: - value = values[acquirable] + nap = self._acq_sleep_time + nb_states_per_value = self._nb_states_per_value + + while True: + self.read_state_info(ret=states) + if not self.in_acquisition(states): + break + + # read value every n times + if not i % nb_states_per_value: + self.read_value(ret=values) + for acquirable, value in list(values.items()): if is_value_error(value): - self.error("Loop final read value error for: %s" % + self.error("Loop read value error for %s" % acquirable.name) msg = "Details: " + "".join( traceback.format_exception(*value.exc_info)) self.debug(msg) acquirable.put_value(value) else: - acquirable.extend_value_buffer(value, propagate=2) - if acquirable in value_refs: - value_ref = value_refs[acquirable] - if is_value_error(value_ref): - self.error("Loop final read value ref error for: %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value_ref.exc_info)) - self.debug(msg) - acquirable.extend_value_ref_buffer(value_ref, propagate=2) - state_info = acquirable._from_ctrl_state_info(state_info) - set_state_info = functools.partial(acquirable.set_state_info, - state_info, - propagate=2, - safe=True) - self.add_finish_hook(set_state_info, False) - finally: - self._reset_ctrl_dicts() + acquirable.extend_value_buffer(value) + + time.sleep(nap) + i += 1 + + with ActionContext(self): + self.raw_read_state_info(ret=states) + self.raw_read_value(ret=values) + self.raw_read_value_ref(ret=value_refs) + + for acquirable, state_info in list(states.items()): + # first update the element state so that value calculation + # that is done after takes the updated state into account + acquirable.set_state_info(state_info, propagate=0) + if acquirable in values: + value = values[acquirable] + if is_value_error(value): + self.error("Loop final read value error for: %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value.exc_info)) + self.debug(msg) + acquirable.put_value(value) + else: + acquirable.extend_value_buffer(value, propagate=2) + if acquirable in value_refs: + value_ref = value_refs[acquirable] + if is_value_error(value_ref): + self.error("Loop final read value ref error for: %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value_ref.exc_info)) + self.debug(msg) + acquirable.extend_value_ref_buffer(value_ref, propagate=2) + state_info = acquirable._from_ctrl_state_info(state_info) + set_state_info = functools.partial(acquirable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) class PoolAcquisitionSoftware(PoolAcquisitionBase): @@ -1117,73 +1122,70 @@ def start_action(self, ctrls, value, master, index, acq_sleep_time=None, @DebugIt() def action_loop(self): - try: - states, values, value_refs = {}, {}, {} - for channel in self._channels: - element = channel.element - states[element] = None - - nap = self._acq_sleep_time - nb_states_per_value = self._nb_states_per_value - - i = 0 - while True: - self.read_state_info(ret=states) - if not self.in_acquisition(states): - break - - # read value every n times - if not i % nb_states_per_value: - self.read_value_loop(ret=values) - for acquirable, value in list(values.items()): - acquirable.put_value(value) + states, values, value_refs = {}, {}, {} + for channel in self._channels: + element = channel.element + states[element] = None - time.sleep(nap) - i += 1 + nap = self._acq_sleep_time + nb_states_per_value = self._nb_states_per_value - for slave in self._slaves: - try: - slave.stop_action() - except Exception: - self.warning("Unable to stop slave acquisition %s", - slave.getLogName()) - self.debug("Details", exc_info=1) + i = 0 + while True: + self.read_state_info(ret=states) + if not self.in_acquisition(states): + break - with ActionContext(self): - self.raw_read_state_info(ret=states) - self.raw_read_value(ret=values) - self.raw_read_value_ref(ret=value_refs) + # read value every n times + if not i % nb_states_per_value: + self.read_value_loop(ret=values) + for acquirable, value in list(values.items()): + acquirable.put_value(value) - for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) - if acquirable in values: - value = values[acquirable] - if is_value_error(value): - self.error("Loop final read value error for: %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value.exc_info)) - self.debug(msg) - acquirable.append_value_buffer(value, self._index) - if acquirable in value_refs: - value_ref = value_refs[acquirable] - if is_value_error(value_ref): - self.error("Loop final read value ref error for: %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value_ref.exc_info)) - self.debug(msg) - acquirable.append_value_ref_buffer(value_ref, self._index) - state_info = acquirable._from_ctrl_state_info(state_info) - set_state_info = functools.partial(acquirable.set_state_info, - state_info, - propagate=2, - safe=True) - self.add_finish_hook(set_state_info, False) - finally: - self._reset_ctrl_dicts() + time.sleep(nap) + i += 1 + + for slave in self._slaves: + try: + slave.stop_action() + except Exception: + self.warning("Unable to stop slave acquisition %s", + slave.getLogName()) + self.debug("Details", exc_info=1) + + with ActionContext(self): + self.raw_read_state_info(ret=states) + self.raw_read_value(ret=values) + self.raw_read_value_ref(ret=value_refs) + + for acquirable, state_info in list(states.items()): + # first update the element state so that value calculation + # that is done after takes the updated state into account + acquirable.set_state_info(state_info, propagate=0) + if acquirable in values: + value = values[acquirable] + if is_value_error(value): + self.error("Loop final read value error for: %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value.exc_info)) + self.debug(msg) + acquirable.append_value_buffer(value, self._index) + if acquirable in value_refs: + value_ref = value_refs[acquirable] + if is_value_error(value_ref): + self.error("Loop final read value ref error for: %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value_ref.exc_info)) + self.debug(msg) + acquirable.append_value_ref_buffer(value_ref, self._index) + state_info = acquirable._from_ctrl_state_info(state_info) + set_state_info = functools.partial(acquirable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) class PoolAcquisitionSoftwareStart(PoolAcquisitionBase): @@ -1217,88 +1219,85 @@ def start_action(self, ctrls, value, master, repetitions, latency, @DebugIt() def action_loop(self): - try: - i = 0 + i = 0 - states, values, value_refs = {}, {}, {} - for channel in self._channels: - element = channel.element - states[element] = None - - nap = self._acq_sleep_time - nb_states_per_value = self._nb_states_per_value - - while True: - self.read_state_info(ret=states) - if not self.in_acquisition(states): - break - - # read value every n times - if not i % nb_states_per_value: - self.read_value(ret=values) - for acquirable, value in list(values.items()): - if is_value_error(value): - self.error("Loop read value error for %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value.exc_info)) - self.debug(msg) - acquirable.put_value(value) - else: - acquirable.extend_value_buffer(value) - self.read_value_ref(ret=value_refs) - for acquirable, value_ref in list(value_refs.items()): - if is_value_error(value_ref): - self.error("Loop read value ref error for %s" % - acquirable.name) - msg = "Details: " + "".join( - traceback.format_exception(*value.exc_info)) - self.debug(msg) - acquirable.put_value_ref(value) - else: - acquirable.extend_value_ref_buffer(value_ref) - time.sleep(nap) - i += 1 - - with ActionContext(self): - self.raw_read_state_info(ret=states) - self.raw_read_value(ret=values) - self.raw_read_value_ref(ret=value_refs) + states, values, value_refs = {}, {}, {} + for channel in self._channels: + element = channel.element + states[element] = None - for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) - if acquirable in values: - value = values[acquirable] + nap = self._acq_sleep_time + nb_states_per_value = self._nb_states_per_value + + while True: + self.read_state_info(ret=states) + if not self.in_acquisition(states): + break + + # read value every n times + if not i % nb_states_per_value: + self.read_value(ret=values) + for acquirable, value in list(values.items()): if is_value_error(value): - self.error("Loop final read value error for: %s" % + self.error("Loop read value error for %s" % acquirable.name) msg = "Details: " + "".join( traceback.format_exception(*value.exc_info)) self.debug(msg) acquirable.put_value(value) else: - acquirable.extend_value_buffer(value, propagate=2) - if acquirable in value_refs: - value_ref = value_refs[acquirable] + acquirable.extend_value_buffer(value) + self.read_value_ref(ret=value_refs) + for acquirable, value_ref in list(value_refs.items()): if is_value_error(value_ref): - self.error("Loop final read value ref error for: %s" % + self.error("Loop read value ref error for %s" % acquirable.name) msg = "Details: " + "".join( - traceback.format_exception(*value_ref.exc_info)) + traceback.format_exception(*value.exc_info)) self.debug(msg) - acquirable.put_value_ref(value_ref) + acquirable.put_value_ref(value) else: - acquirable.extend_value_ref_buffer(value_ref, propagate=2) - state_info = acquirable._from_ctrl_state_info(state_info) - set_state_info = functools.partial(acquirable.set_state_info, - state_info, - propagate=2, - safe=True) - self.add_finish_hook(set_state_info, False) - finally: - self._reset_ctrl_dicts() + acquirable.extend_value_ref_buffer(value_ref) + time.sleep(nap) + i += 1 + + with ActionContext(self): + self.raw_read_state_info(ret=states) + self.raw_read_value(ret=values) + self.raw_read_value_ref(ret=value_refs) + + for acquirable, state_info in list(states.items()): + # first update the element state so that value calculation + # that is done after takes the updated state into account + acquirable.set_state_info(state_info, propagate=0) + if acquirable in values: + value = values[acquirable] + if is_value_error(value): + self.error("Loop final read value error for: %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value.exc_info)) + self.debug(msg) + acquirable.put_value(value) + else: + acquirable.extend_value_buffer(value, propagate=2) + if acquirable in value_refs: + value_ref = value_refs[acquirable] + if is_value_error(value_ref): + self.error("Loop final read value ref error for: %s" % + acquirable.name) + msg = "Details: " + "".join( + traceback.format_exception(*value_ref.exc_info)) + self.debug(msg) + acquirable.put_value_ref(value_ref) + else: + acquirable.extend_value_ref_buffer(value_ref, propagate=2) + state_info = acquirable._from_ctrl_state_info(state_info) + set_state_info = functools.partial(acquirable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) class PoolCTAcquisition(PoolAcquisitionBase): @@ -1333,64 +1332,62 @@ def in_acquisition(self, states): @DebugIt() def action_loop(self): - try: - i = 0 + i = 0 - states, values = {}, {} - for element in self._channels: - states[element] = None - # values[element] = None + states, values = {}, {} + for element in self._channels: + states[element] = None + # values[element] = None - nap = self._acq_sleep_time - nb_states_per_value = self._nb_states_per_value + nap = self._acq_sleep_time + nb_states_per_value = self._nb_states_per_value - # read values to send a first event when starting to acquire - with ActionContext(self): - self.raw_read_value_loop(ret=values) - for acquirable, value in list(values.items()): - acquirable.put_value(value, propagate=2) + # read values to send a first event when starting to acquire + with ActionContext(self): + self.raw_read_value_loop(ret=values) + for acquirable, value in list(values.items()): + acquirable.put_value(value, propagate=2) + + while True: + self.read_state_info(ret=states) + if not self.in_acquisition(states): + break - while True: - self.read_state_info(ret=states) - if not self.in_acquisition(states): - break + # read value every n times + if not i % nb_states_per_value: + self.read_value_loop(ret=values) + for acquirable, value in list(values.items()): + acquirable.put_value(value) - # read value every n times - if not i % nb_states_per_value: - self.read_value_loop(ret=values) - for acquirable, value in list(values.items()): - acquirable.put_value(value) + time.sleep(nap) + i += 1 - time.sleep(nap) - i += 1 + for slave in self._slaves: + try: + slave.stop_action() + except Exception: + self.warning("Unable to stop slave acquisition %s", + slave.getLogName()) + self.debug("Details", exc_info=1) - for slave in self._slaves: - try: - slave.stop_action() - except Exception: - self.warning("Unable to stop slave acquisition %s", - slave.getLogName()) - self.debug("Details", exc_info=1) + with ActionContext(self): + self.raw_read_state_info(ret=states) + self.raw_read_value_loop(ret=values) - with ActionContext(self): - self.raw_read_state_info(ret=states) - self.raw_read_value_loop(ret=values) + for acquirable, state_info in list(states.items()): + # first update the element state so that value calculation + # that is done after takes the updated state into account + acquirable.set_state_info(state_info, propagate=0) + if acquirable in values: + value = values[acquirable] + acquirable.put_value(value, propagate=2) + state_info = acquirable._from_ctrl_state_info(state_info) + set_state_info = functools.partial(acquirable.set_state_info, + state_info, + propagate=2, + safe=True) + self.add_finish_hook(set_state_info, False) - for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) - if acquirable in values: - value = values[acquirable] - acquirable.put_value(value, propagate=2) - state_info = acquirable._from_ctrl_state_info(state_info) - set_state_info = functools.partial(acquirable.set_state_info, - state_info, - propagate=2, - safe=True) - self.add_finish_hook(set_state_info, False) - finally: - self._reset_ctrl_dicts() class Pool0DAcquisition(PoolAction): From 5f8ce464441991227179966093840bc2ac99f66d Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 2 Mar 2020 09:53:54 +0100 Subject: [PATCH 464/830] Prepare ordered sequences in cache --- src/sardana/taurus/core/tango/sardana/pool.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 6a6a7477fe..cc6d2f4495 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1434,14 +1434,24 @@ def set_data(self, data, force=False): # CaselessDict(getChannelConfigs(data, sort=False)) ##################### - # seq each element is the channel data in form of a dict as - # received by the MG configuration attribute. This seq is just a cache - # ordered by channel index in the MG. + # Create ordered list by channel index in the MG as cache + # channel_list: seq each element is the channel data in form + # of a dict as received by the MG configuration attribute. + # channel_list_name: seq + # controller_list_names: seg self.channel_list = len(channels) * [None] - - for channel in list(channels.values()): - self.channel_list[channel['index']] = channel - + self.channel_list_name = len(channels) * [None] + self.controller_list_name = [] + + for channel, channel_data in channels.items(): + idx = channel_data['index'] + self.channel_list[idx] = channel_data + self.channel_list_name[idx] = channel + + for channel_name in self.channel_list_name.values(): + ctrl = self._get_ctrl_for_element(channel_name) + if ctrl not in self.controller_list_name: + self.channel_list_name.append(ctrl) # dict]> # where key is a device name and value is a list with two elements: # - A device proxy or None if there was an error building it @@ -1790,7 +1800,7 @@ def _get_channels_key(self, key, channels_names=None, use_fullname=False): result = collections.OrderedDict({}) if channels_names is None: - channels_names = self.channels.keys() + channels_names = list(self.channel_list_name) for channel_name in channels_names: channel = self._get_channel_data(channel_name) @@ -1833,7 +1843,7 @@ def _get_ctrls_key(self, key, ctrls_names=None, use_fullname=False): """ result = collections.OrderedDict({}) if ctrls_names is None: - ctrls_names = self.controllers.keys() + ctrls_names = list(self.controller_list_name) for ctrl_name in ctrls_names: if ctrl_name == '__tango__': From 15e530b805d1c1571cdb5c1f55d188cb9d23568d Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 2 Mar 2020 10:37:30 +0100 Subject: [PATCH 465/830] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27eae4d796..1b8c6e1524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This file follows the formats and conventions from [keepachangelog.com] * support fast scans: update curves at a fix rate (5Hz) * better curve colors and symbols * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) +* Use `Device_5Impl` instead of `Device_4Impl` (#1214) * Instruments creation and configuration in sar_demo (#1198) * Documentation to Taurus Extensions of Sardana Devices: MacroServer part and the whole Sardana part of the Qt Taurus Extensions (#1228, #1233) @@ -49,7 +50,7 @@ This file follows the formats and conventions from [keepachangelog.com] created with Taurus 3 (#1266, #1271) * Measurement groups renaming with `renameelem` macro(#951) * Macro plotting in new versions of ipython and matplotlib require extra call to - pyplot.draw() to make sure that the plot is refreshed (#1280) + `pyplot.draw()` to make sure that the plot is refreshed (#1280) * Remove TangoAttribute controllers from Sardana (#181, #1279) * Remove deprecation warning revealed when running test suite (#1267) * Remove event filtering in `DynamicPlotManager` (showscan online) (#1299) From 3d80af193b1f1b94d768798ddf7d7517c12cb31f Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 2 Mar 2020 10:32:34 +0100 Subject: [PATCH 466/830] Fix error --- src/sardana/taurus/core/tango/sardana/pool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index cc6d2f4495..730fa714ca 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1448,10 +1448,10 @@ def set_data(self, data, force=False): self.channel_list[idx] = channel_data self.channel_list_name[idx] = channel - for channel_name in self.channel_list_name.values(): + for channel_name in self.channel_list_name: ctrl = self._get_ctrl_for_element(channel_name) if ctrl not in self.controller_list_name: - self.channel_list_name.append(ctrl) + self.controller_list_name.append(ctrl) # dict]> # where key is a device name and value is a list with two elements: # - A device proxy or None if there was an error building it From f2c6e4c238e5b8963d10253248e3f2171f73b5a7 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 2 Mar 2020 10:38:50 +0100 Subject: [PATCH 467/830] Adapt to python 3 --- src/sardana/taurus/core/tango/sardana/pool.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 730fa714ca..a25a25244e 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1755,7 +1755,8 @@ def _get_channel_data(self, channel_name): except Exception: # The attribute proxy does not have alias. alias = proxy.name() - names = self.channels_names.keys() + self.channels_labels.keys() + names = list(self.channels_names.keys()) + \ + list(self.channels_labels.keys()) if alias not in names: raise KeyError('Channel "{0}" is not on the ' 'MntGrp "{1}"'.format(alias, self.label)) From 57c0ad881c9ffd53c3fe59eb3a4f388ffe07ae50 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 2 Mar 2020 10:55:05 +0100 Subject: [PATCH 468/830] Fix flake8 errors --- src/sardana/taurus/core/tango/sardana/pool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index a25a25244e..f610652b67 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1756,7 +1756,7 @@ def _get_channel_data(self, channel_name): # The attribute proxy does not have alias. alias = proxy.name() names = list(self.channels_names.keys()) + \ - list(self.channels_labels.keys()) + list(self.channels_labels.keys()) if alias not in names: raise KeyError('Channel "{0}" is not on the ' 'MntGrp "{1}"'.format(alias, self.label)) From 6d2583deeb15906542b56cf12cadb684d1a5d5cf Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 2 Mar 2020 12:51:40 +0100 Subject: [PATCH 469/830] Add warning messages. --- src/sardana/macroserver/scan/gscan.py | 4 ++++ src/sardana/sardanathreadpool.py | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index a2f94f9f3a..25bc430f04 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1970,6 +1970,10 @@ def __init__(self): Qsize=100000, worker_cls=OmniWorker) except TypeError: + import taurus + taurus.warning("Your Sardana system is affected by bug" + "tango-controls/pytango#307. Please use " + "Taurus with taurus-org/taurus#1081.") self._thread_pool = ThreadPool(name="ValueBufferTH", Psize=1, Qsize=100000) diff --git a/src/sardana/sardanathreadpool.py b/src/sardana/sardanathreadpool.py index 4fb337e7da..40d29390c4 100644 --- a/src/sardana/sardanathreadpool.py +++ b/src/sardana/sardanathreadpool.py @@ -57,6 +57,10 @@ def run(self): with tango.EnsureOmniThread(): Worker.run(self) else: + import taurus + taurus.warning("Your Sardana system is affected by bug " + "tango-controls/pytango#307. Please use " + "PyTango with tango-controls/pytango#327.") Worker.run(self) @@ -76,5 +80,10 @@ def get_thread_pool(): __thread_pool = ThreadPool(name="SardanaTP", Psize=10, worker_cls=OmniWorker) except TypeError: + import taurus + taurus.warning("Your Sardana system is affected by bug " + "tango-controls/pytango#307. Please use " + "Taurus with taurus-org/taurus#1081.") __thread_pool = ThreadPool(name="SardanaTP", Psize=10) + return __thread_pool From 072bf731ac21749238ac1b07e3152ba0f83a44a9 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 2 Mar 2020 12:54:42 +0100 Subject: [PATCH 470/830] Revert "Fix Tango inheritance" --- src/sardana/tango/core/SardanaDevice.py | 8 ++++---- src/sardana/tango/pool/Pool.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sardana/tango/core/SardanaDevice.py b/src/sardana/tango/core/SardanaDevice.py index 7d8d9ca70c..3e496be7a2 100644 --- a/src/sardana/tango/core/SardanaDevice.py +++ b/src/sardana/tango/core/SardanaDevice.py @@ -35,7 +35,7 @@ import threading import PyTango.constants -from PyTango import LatestDeviceImpl, DeviceClass, Util, DevState, \ +from PyTango import Device_4Impl, DeviceClass, Util, DevState, \ AttrQuality, TimeVal, ArgType, ApiUtil, DevFailed, WAttribute from taurus.core.util.threadpool import ThreadPool @@ -66,7 +66,7 @@ def get_thread_pool(): return __thread_pool -class SardanaDevice(LatestDeviceImpl, Logger): +class SardanaDevice(Device_4Impl, Logger): """SardanaDevice represents the base class for all Sardana :class:`PyTango.DeviceImpl` classes""" @@ -74,7 +74,7 @@ def __init__(self, dclass, name): """Constructor""" self.in_constructor = True try: - LatestDeviceImpl.__init__(self, dclass, name) + Device_4Impl.__init__(self, dclass, name) self.init(name) Logger.__init__(self, name) @@ -512,7 +512,7 @@ def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Generic description", doc_url="http://sardana-controls.org/", __icon=self.get_name().lower() + ".png", - InheritedFrom=["Device_5Impl"]) + InheritedFrom=["Device_4Impl"]) def write_class_property(self): """Write class properties ``ProjectTitle``, ``Description``, diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index 54c0e29934..e073abf4f4 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -48,12 +48,12 @@ import collections -class Pool(PyTango.LatestDeviceImpl, Logger): +class Pool(PyTango.Device_4Impl, Logger): ElementsCache = None def __init__(self, cl, name): - PyTango.LatestDeviceImpl.__init__(self, cl, name) + PyTango.Device_4Impl.__init__(self, cl, name) Logger.__init__(self, name) self.init(name) self.init_device() @@ -1576,7 +1576,7 @@ def __init__(self, name): def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Device Pool management class", doc_url="http://sardana-controls.org/", - InheritedFrom="Device_5Impl") + InheritedFrom="Device_4Impl") def write_class_property(self): util = PyTango.Util.instance() From 2648dedde7ef40b2f05ecac1162b6646f9a85272 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 2 Mar 2020 14:25:48 +0100 Subject: [PATCH 471/830] Fix error --- src/sardana/taurus/core/tango/sardana/pool.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index f610652b67..615c1288da 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1801,7 +1801,7 @@ def _get_channels_key(self, key, channels_names=None, use_fullname=False): result = collections.OrderedDict({}) if channels_names is None: - channels_names = list(self.channel_list_name) + channels_names = self.channel_list_name for channel_name in channels_names: channel = self._get_channel_data(channel_name) @@ -1844,7 +1844,7 @@ def _get_ctrls_key(self, key, ctrls_names=None, use_fullname=False): """ result = collections.OrderedDict({}) if ctrls_names is None: - ctrls_names = list(self.controller_list_name) + ctrls_names = self.controller_list_name for ctrl_name in ctrls_names: if ctrl_name == '__tango__': @@ -1876,11 +1876,14 @@ def _get_ctrl_for_channel(self, channels_names, unique=False): result = collections.OrderedDict({}) if channels_names is None: - channels_names = self.channels.keys() + channels_names = self.channel_list_name for channel_name in channels_names: channel = self._get_channel_data(channel_name) - ctrl = channel['_controller_name'] + try: + ctrl = channel['_controller_name'] + except KeyError: + ctrl = '__tango__' if unique and ctrl in result.values(): raise KeyError('There are more than one channel of the same ' 'controller') From 5616909b4be46d5b981435503d7f37ae6c85b14e Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 2 Mar 2020 15:02:39 +0100 Subject: [PATCH 472/830] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b8c6e1524..1f0cfd7535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,6 @@ This file follows the formats and conventions from [keepachangelog.com] * support fast scans: update curves at a fix rate (5Hz) * better curve colors and symbols * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) -* Use `Device_5Impl` instead of `Device_4Impl` (#1214) * Instruments creation and configuration in sar_demo (#1198) * Documentation to Taurus Extensions of Sardana Devices: MacroServer part and the whole Sardana part of the Qt Taurus Extensions (#1228, #1233) From 7eb5d579b28ca4c56fe92d13f220af6708a1570d Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 2 Mar 2020 15:14:49 +0100 Subject: [PATCH 473/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0cfd7535..784a664253 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ This file follows the formats and conventions from [keepachangelog.com] * OutputBlock view option when macros produce outputs at high rate (#1245) * `showscan online` shows only the online trend and not erroneously online and offline (#1260) +* Fix fast operations (motion & acq) by propertly clearing operation context and + resetting of acq ctrls dicts (#1300) * Use more efficient way to get terminal size for better printing spock output (#1245, #1268) * Reintroduce backwards compatibility for measurement groups' configurations (URIs) created with Taurus 3 (#1266, #1271) From f36bcdd2d402df4dd58c237612646eb3774dff37 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 2 Mar 2020 15:50:18 +0100 Subject: [PATCH 474/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 784a664253..eb026e7342 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ This file follows the formats and conventions from [keepachangelog.com] ### Fixed +* Use `tango.EnsureOmnitThread` to protect Sardana threads + (Tango is not thread safe) (#1298) * Default macro parameter values in macroexecutor (#1153) * Executing RunMacro Door's command with string parameters containing spaces (#1240) * Setting of environment variables in Python 3.7 (#1195) From fd68eb064bacb381da4e26d48f306c9fe5ad894c Mon Sep 17 00:00:00 2001 From: aalonso Date: Mon, 2 Mar 2020 17:19:28 +0100 Subject: [PATCH 475/830] Fix error on use full name --- src/sardana/taurus/core/tango/sardana/pool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 615c1288da..690345fec1 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1806,7 +1806,7 @@ def _get_channels_key(self, key, channels_names=None, use_fullname=False): for channel_name in channels_names: channel = self._get_channel_data(channel_name) if use_fullname: - label = channel + label = channel['full_name'] else: label = channel['label'] try: From cfb6ce85a8a4191e6abf2b2084bcd9446aacc7e7 Mon Sep 17 00:00:00 2001 From: aalonso Date: Tue, 3 Mar 2020 15:37:05 +0100 Subject: [PATCH 476/830] Add ret_full_name tests and controller tests --- .../test_measurementgroupconfiguretion.py | 298 ++++++++++++++---- 1 file changed, 238 insertions(+), 60 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index 319cec9cf3..f37abbe152 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -38,64 +38,123 @@ def _assertMultipleResults(self, result, channels, expected_values): msg = "{} are missing".format(expected_channels) self.assertEqual(len(expected_channels), 0, msg) - def test_enabled(self, elements=["_test_ct_1_1", "_test_ct_1_2"]): + def test_enabled(self, elements=["_test_ct_1_1", "_test_ct_1_2", + "_test_2d_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) + + # Check initial state of all kind of channels, nonexistent + # channels for the feature return None as result. enabled = mg.getEnabled(*elements) - self._assertResult(enabled, elements, True) + expected = [True, True, True] + self._assertMultipleResults(enabled, elements, expected) + + # Check if the nonexistent channels raise error if trying to set + with self.assertRaises(Exception): + mg.setEnabled(True, *elements[-1]) + + # Redefine elements to ony use existing values + elements = ["_test_ct_1_1", "_test_ct_1_2"] + + # Test every possible combination of setting values + # Check that changing one channel doesn't affect the other mg.setEnabled(False, *elements) enabled = mg.getEnabled(*elements) self._assertResult(enabled, elements, False) - enabled = mg.getEnabled("_test_ct_ctrl_1") + mg.setEnabled(True, elements[0]) + result = mg.getEnabled(*elements) + expected = [False] * len(elements) + expected[0] = True + self._assertMultipleResults(result, elements, expected) + mg.setEnabled(False, *elements) + enabled = mg.getEnabled(*elements) self._assertResult(enabled, elements, False) - mg.setEnabled(True, *elements) + + # Set values using the controller instead of channels + mg.setEnabled(True, "_test_ct_ctrl_1") enabled = mg.getEnabled(*elements) self._assertResult(enabled, elements, True) - # enabled = mg.getEnabled(*elements, ret_full_name=True) + + # Get values by controller + mg.setEnabled(False, *elements) + enabled = mg.getEnabled("_test_ct_ctrl_1") + self._assertResult(enabled, elements, False) + + # Check ret_full_name v = TangoDeviceNameValidator() full_names = [v.getNames(element)[0] for element in elements] enabled = mg.getEnabled(*full_names) - self._assertResult(enabled, elements, True) - # TODO Fix ret_full_name error and make a test + self._assertResult(enabled, elements, False) + mg.setEnabled(True, *full_names) + enabled = mg.getEnabled(*elements, ret_full_name=True) + self._assertResult(enabled, full_names, True) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2"]): + def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2", + "_test_2d_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) - is_output = mg.getOutput(*elements) - self._assertResult(is_output, elements, True) + + # Check initial state of all kind of channels, nonexistent + # channels for the feature return None as result. + enabled = mg.getOutput(*elements) + expected = [True, True, True] + self._assertMultipleResults(enabled, elements, expected) + + # Check if the nonexistent channels raise error if trying to set + with self.assertRaises(Exception): + mg.setOutput(True, *elements[-1]) + + # Redefine elements to ony use existing values + elements = ["_test_ct_1_1", "_test_ct_1_2"] + + # Test every possible combination of setting values + # Check that changing one channel doesn't affect the other mg.setOutput(False, *elements) is_output = mg.getOutput(*elements) self._assertResult(is_output, elements, False) - is_output = mg.getOutput("_test_ct_ctrl_1") + mg.setOutput(True, elements[0]) + result = mg.getOutput(*elements) + expected = [False] * len(elements) + expected[0] = True + self._assertMultipleResults(result, elements, expected) + mg.setOutput(False, *elements) + is_output = mg.getOutput(*elements) self._assertResult(is_output, elements, False) - mg.setOutput(True, *elements) + + # Set values using the controller instead of channels + mg.setOutput(True, "_test_ct_ctrl_1") is_output = mg.getOutput(*elements) self._assertResult(is_output, elements, True) - # is_output = mg.getOutput(*elements, ret_full_name=True) + + # Get values by controller + mg.setOutput(False, *elements) + is_output = mg.getOutput("_test_ct_ctrl_1") + self._assertResult(is_output, elements, False) + + # Check ret_full_name v = TangoDeviceNameValidator() - full_names = [] - for element in elements: - full_names.append(v.getNames(element)[0]) - print(full_names) + full_names = [v.getNames(element)[0] for element in elements] is_output = mg.getOutput(*full_names) - self._assertResult(is_output, elements, True) - # TODO Fix ret_full_name error and make a test + self._assertResult(is_output, elements, False) + mg.setOutput(True, *full_names) + is_output = mg.getOutput(*elements, ret_full_name=True) + self._assertResult(is_output, full_names, True) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", - "_test_ct_1_3"]): + "_test_ct_1_3", "_test_2d_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -107,19 +166,42 @@ def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", mg.setPlotType("Image", elements[0]) mg.setPlotType("Spectrum", elements[1]) mg.setPlotType("No", elements[2]) + mg.setPlotType("Image", elements[3]) plottype = mg.getPlotType() - expected_values = [2, 1, 0] + expected_values = [2, 1, 0, 2] self._assertMultipleResults(plottype, elements, expected_values) with self.assertRaises(ValueError): mg.setPlotType("asdf", elements[2]) print(mg.getPlotType()) + # Redefine elements + elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] + + # Set values using the controller instead of channels + mg.setPlotType("Image", "_test_ct_ctrl_1") + plottype = mg.getPlotType(*elements) + self._assertResult(plottype, elements, 2) + + # Get values by controller + mg.setPlotType("Spectrum", *elements) + plottype = mg.getPlotType("_test_ct_ctrl_1") + self._assertResult(plottype, elements, 1) + + # Check ret_full_name + v = TangoDeviceNameValidator() + full_names = [v.getNames(element)[0] for element in elements] + plottype = mg.getPlotType(*full_names) + self._assertResult(plottype, elements, 1) + mg.setPlotType("Image", *full_names) + plottype = mg.getPlotType(*elements, ret_full_name=True) + self._assertResult(plottype, full_names, 2) + finally: mg.cleanUp() self.pool.DeleteElement(mg_name) def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", - "_test_ct_1_3"]): + "_test_ct_1_3", "_test_2d_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -129,21 +211,27 @@ def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", mg.setPlotType("Image", elements[0]) mg.setPlotType("Spectrum", elements[1]) mg.setPlotType("No", elements[2]) + mg.setPlotType("Image", elements[3]) result = mg.getPlotAxes() self._assertResult(result, elements, []) + + mg.setPlotAxes(["", ""], elements[3]) mg.setPlotAxes(["", ""], elements[0]) mg.setPlotAxes([""], elements[1]) result = mg.getPlotAxes() - expected_result = [['', ''], [''], []] + expected_result = [['', ''], [''], [], + ['', '']] self._assertMultipleResults(result, elements, expected_result) mg.setPlotAxes(["", ""], elements[0]) mg.setPlotAxes([""], elements[1]) result = mg.getPlotAxes() - expected_result = [['', ''], [''], []] + expected_result = [['', ''], [''], [], + ['', '']] self._assertMultipleResults(result, elements, expected_result) mg.setPlotAxes(["", ""], elements[0]) result = mg.getPlotAxes() - expected_result = [['', ''], [''], []] + expected_result = [['', ''], [''], [], + ['', '']] self._assertMultipleResults(result, elements, expected_result) with self.assertRaises(RuntimeError): @@ -153,6 +241,28 @@ def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", with self.assertRaises(ValueError): mg.setPlotAxes([""], elements[0]) print(mg.getPlotAxes()) + + elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] + # Set values using the controller instead of channels + with self.assertRaises(RuntimeError): + mg.setPlotAxes([""], "_test_ct_ctrl_1") + # TODO get method by controller doesn't give the order + # Get values by controller + result = mg.getPlotAxes("_test_ct_ctrl_1") + expected_result = [['', ''], [''], []] + self._assertMultipleResults(result, elements, expected_result) + + # Check ret_full_name + v = TangoDeviceNameValidator() + full_names = [v.getNames(element)[0] for element in elements] + result = mg.getPlotAxes(*full_names) + expected_result = [['', ''], [''], []] + self._assertMultipleResults(result, elements, expected_result) + mg.setPlotAxes(["", ""], full_names[0]) + mg.setPlotAxes([""], full_names[1]) + result = mg.getPlotAxes(*elements, ret_full_name=True) + expected_result = [['', ''], [''], []] + self._assertMultipleResults(result, full_names, expected_result) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) @@ -172,6 +282,16 @@ def test_Timer(self, elements=["_test_ct_1_1", "_test_ct_1_2", self._assertResult(mg.getTimer(ret_by_ctrl=True), ['_test_ct_ctrl_1'], '_test_ct_1_3') + # Check ret_full_name + v = TangoDeviceNameValidator() + full_names = [v.getNames(element)[0] for element in elements] + mg.setTimer(full_names[0]) + result = mg.getTimer() + self._assertResult(result, elements, '_test_ct_1_1') + # TODO ret_full_name gives controler name + mg.setTimer("_test_ct_1_2") + result = mg.getTimer(*elements, ret_full_name=True) + self._assertResult(result, full_names, "_test_ct_1_2") finally: mg.cleanUp() self.pool.DeleteElement(mg_name) @@ -191,6 +311,16 @@ def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", self._assertResult(mg.getMonitor(ret_by_ctrl=True), ['_test_ct_ctrl_1'], '_test_ct_1_2') + # Check ret_full_name + v = TangoDeviceNameValidator() + full_names = [v.getNames(element)[0] for element in elements] + mg.setMonitor(full_names[0]) + result = mg.getMonitor() + self._assertResult(result, elements, '_test_ct_1_1') + # TODO ret_full_name gives controler name + mg.setMonitor("_test_ct_1_2") + result = mg.getMonitor(*elements, ret_full_name=True) + self._assertResult(result, full_names, "_test_ct_1_2") finally: mg.cleanUp() self.pool.DeleteElement(mg_name) @@ -214,21 +344,46 @@ def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", self._assertResult(result, ['_test_ct_ctrl_1'], 'software') with self.assertRaises(Exception): mg.setSynchronizer('asdf') - self._assertResult(result, ['_test_ct_ctrl_1'], 'software') + + # Check ret_full_name + v = TangoDeviceNameValidator() + full_names = [v.getNames(element)[0] for element in elements] + full_name_tg = v.getNames('_test_tg_1_2')[0] + mg.setSynchronizer(full_name_tg) + result = mg.getSynchronizer() + self._assertResult(result, elements, '_test_tg_1_2') + # TODO ret_full_name gives controler name + mg.setSynchronizer('software', *full_names) + result = mg.getSynchronizer(*elements, ret_full_name=True) + self._assertResult(result, full_names, 'software') finally: mg.cleanUp() self.pool.DeleteElement(mg_name) def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", - ]): + "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) + + # Check initial state of all kind of channels, nonexistent + # channels for the feature return None as result. enabled = mg.getValueRefEnabled(*elements) - self._assertResult(enabled, elements, False) + expected = [False, False, None] + self._assertMultipleResults(enabled, elements, expected) + + # Check if the nonexistent channels raise error if trying to set + with self.assertRaises(Exception): + mg.setValueRefEnabled(True, *elements[-1]) + + # Redefine elements to ony use existing values + elements = ["_test_2d_1_1", "_test_2d_1_2"] + + # Test every possible combination of setting values + # Check that changing one channel doesn't affect the other mg.setValueRefEnabled(True, *elements) enabled = mg.getValueRefEnabled(*elements) self._assertResult(enabled, elements, True) @@ -240,53 +395,76 @@ def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", mg.setValueRefEnabled(True, *elements) enabled = mg.getValueRefEnabled(*elements) self._assertResult(enabled, elements, True) + + # Set values using the controller instead of channels + mg.setValueRefEnabled(True, "_test_2d_ctrl_1") + enabled = mg.getValueRefEnabled(*elements) + self._assertResult(enabled, elements, True) + + # Get values by controller + mg.setValueRefEnabled(False, *elements) + enabled = mg.getValueRefEnabled("_test_2d_ctrl_1") + self._assertResult(enabled, elements, False) + + # Check ret_full_name + v = TangoDeviceNameValidator() + full_names = [v.getNames(element)[0] for element in elements] + enabled = mg.getValueRefEnabled(*full_names) + self._assertResult(enabled, elements, False) + mg.setValueRefEnabled(True, *full_names) + enabled = mg.getValueRefEnabled(*elements, ret_full_name=True) + self._assertResult(enabled, full_names, True) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - def test_ValueRefEnabledCounters(self, elements=["_test_ct_1_3"]): + def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", + "_test_ct_1_3"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) - result = mg.getValueRefEnabled(*elements) - self._assertResult(result, elements, None) + + # Check initial state of all kind of channels, nonexistent + # channels for the feature return None as result. + pattern = mg.getValueRefPattern(*elements) + expected = ['', '', None] + self._assertMultipleResults(pattern, elements, expected) + + # Check if the nonexistent channels raise error if trying to set with self.assertRaises(Exception): - mg.setValueRefEnabled(True, *elements) - finally: - mg.cleanUp() - self.pool.DeleteElement(mg_name) + mg.setValueRefPattern('/tmp/test_foo.txt', *elements[-1]) - def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2"]): - mg_name = str(uuid.uuid1()) - argin = [mg_name] + elements - self.pool.CreateMeasurementGroup(argin) - try: - mg = Device(mg_name) - pattern = mg.getValueRefEnabled(*elements) - self._assertResult(pattern, elements, False) - mg.setValueRefEnabled('/tmp/test_foo.txt', *elements) - pattern = mg.getValueRefEnabled(*elements) - self._assertResult(pattern, elements, '/tmp/test_foo.txt') - pattern = mg.getValueRefEnabled("_test_2d_ctrl_1") + # Redefine elements to ony use existing values + elements = ["_test_2d_1_1", "_test_2d_1_2"] + + # Test every possible combination of setting values + # Check that changing one channel doesn't affect the other + mg.setValueRefPattern('/tmp/test_foo.txt', *elements) + pattern = mg.getValueRefPattern(*elements) self._assertResult(pattern, elements, '/tmp/test_foo.txt') - finally: - mg.cleanUp() - self.pool.DeleteElement(mg_name) + # Set values using the controller instead of channels + mg.setValueRefPattern('/tmp/test_foo2.txt', "_test_2d_ctrl_1") + pattern = mg.getValueRefPattern(*elements) + self._assertResult(pattern, elements, '/tmp/test_foo2.txt') - def test_ValueRefPatternCounter(self, elements=["_test_ct_1_3"]): - mg_name = str(uuid.uuid1()) - argin = [mg_name] + elements - self.pool.CreateMeasurementGroup(argin) - try: - mg = Device(mg_name) - pattern = mg.getValueRefEnabled(*elements) - self._assertResult(pattern, elements, None) - with self.assertRaises(Exception): - mg.setValueRefEnabled('/tmp/test_foo.txt', *elements) + # Get values by controller + mg.setValueRefPattern('/tmp/test_foo.txt', *elements) + pattern = mg.getValueRefPattern("_test_2d_ctrl_1") + self._assertResult(pattern, elements, '/tmp/test_foo.txt') + + # Check ret_full_name + v = TangoDeviceNameValidator() + full_names = [v.getNames(element)[0] for element in elements] + pattern = mg.getValueRefPattern(*full_names) + self._assertResult(pattern, elements, '/tmp/test_foo.txt') + mg.setValueRefPattern('/tmp/test_foo2.txt', *full_names) + pattern = mg.getValueRefPattern(*elements, ret_full_name=True) + self._assertResult(pattern, full_names, '/tmp/test_foo2.txt') finally: mg.cleanUp() self.pool.DeleteElement(mg_name) + From 460901573fa193106baff6a668afdddbee7969e3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 3 Mar 2020 17:23:28 +0100 Subject: [PATCH 477/830] Add _check_attr_range func Add function to check if new attribute value within allowed range. This function can be used on the server side and avoids using AttributeProxy. --- src/sardana/tango/core/util.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 29040f233e..09d11729a7 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -286,6 +286,33 @@ def __get_last_write_value(attribute): return lrv +def _check_attr_range(dev_name, attr_name, attr_value): + util = PyTango.Util.instance() + dev = util.get_device_by_name(dev_name) + multi_attr = dev.get_device_attr() + attr = multi_attr.get_w_attr_by_name(attr_name) + try: + min_value = attr.get_min_value() + # not specified min value raises DevFailed + except PyTango.DevFailed: + pass + else: + if attr_value < min_value: + msg = "w_value {} of {}/{} is lower than min_value {}".format( + attr_value, dev_name, attr_name, min_value) + raise ValueError(msg) + try: + max_value = attr.get_max_value() + # not specified max value raises DevFailed + except PyTango.DevFailed: + pass + else: + if attr_value > max_value: + msg = "w_value {} of {}/{} is greater than max_value {}".format( + attr_value, dev_name, attr_name, max_value) + raise ValueError(msg) + + def memorize_write_attribute(write_attr_func): """The main purpose is to use this as a decorator for write_ device methods. From 9f0d95b756dde6d858ba3d30b633079a6ae78fbe Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 3 Mar 2020 17:37:26 +0100 Subject: [PATCH 478/830] Avoid using AttributeProxy for limits protection As it was already identified in sardana-org/sardana#663 AttributeProxy creation could be avoided and device WAttribute object could be used directly. Do it for pseudo motors and motor groups to avoid problems due to tango-controls/pytango#315. --- src/sardana/pool/poolmotorgroup.py | 33 +++++++++++---------------- src/sardana/pool/poolpseudomotor.py | 35 +++++++++++------------------ 2 files changed, 26 insertions(+), 42 deletions(-) diff --git a/src/sardana/pool/poolmotorgroup.py b/src/sardana/pool/poolmotorgroup.py index 3e86de5a11..c18fa4ca65 100644 --- a/src/sardana/pool/poolmotorgroup.py +++ b/src/sardana/pool/poolmotorgroup.py @@ -255,28 +255,21 @@ def calculate_motion(self, new_positions, items=None): for new_position, element in zip(new_positions, user_elements): calculated[element] = new_position - # TODO: get the configuration for an specific sardana class and - # get rid of AttributeProxy. - config = AttributeProxy(element.name + '/position').get_config() + # TODO: use Sardana attribute configuration and + # get rid of accessing tango - see sardana-org/sardana#663 + from sardana.tango.core.util import _check_attr_range try: - high = float(config.max_value) - except ValueError: - high = None - try: - low = float(config.min_value) - except ValueError: - low = None - if high is not None: - if float(new_position) > high: - msg = "requested movement of %s is above its upper limit"\ - % element.name - raise RuntimeError(msg) - if low is not None: - if float(new_position) < low: - msg = "requested movement of %s is below its lower limit"\ - % element.name - raise RuntimeError(msg) + _check_attr_range(element.name, "position", new_position) + except ValueError as e: + # TODO: don't concatenate exception message whenever + # tango-controls/pytango#340 is fixed + msg = "requested move of {} is outside of limits ({})".format( + element.name, str(e) + ) + raise ValueError(msg) from e + element.calculate_motion(new_position, items=items, + calculated=calculated) for new_position, element in zip(new_positions, user_elements): element.calculate_motion(new_position, items=items, calculated=calculated) diff --git a/src/sardana/pool/poolpseudomotor.py b/src/sardana/pool/poolpseudomotor.py index 15cd310521..7819b4cdea 100644 --- a/src/sardana/pool/poolpseudomotor.py +++ b/src/sardana/pool/poolpseudomotor.py @@ -45,7 +45,6 @@ from sardana.pool.poolmotion import PoolMotion from sardana.pool.poolexception import PoolException -from PyTango import AttributeProxy class Position(SardanaAttribute): @@ -552,31 +551,23 @@ def calculate_motion(self, new_position, items=None, calculated=None): if items is None: items = {} - for new_position, element in zip(physical_positions.value, user_elements): + for new_position, element in zip(physical_positions.value, + user_elements): if new_position is None: raise PoolException("Cannot calculate motion: %s reports " "position to be None" % element.name) - # TODO: get the configuration for an specific sardana class and - # get rid of AttributeProxy - see sardana-org/sardana#663 - config = AttributeProxy(element.name + '/position').get_config() + # TODO: use Sardana attribute configuration and + # get rid of accessing tango - see sardana-org/sardana#663 + from sardana.tango.core.util import _check_attr_range try: - high = float(config.max_value) - except ValueError: - high = None - try: - low = float(config.min_value) - except ValueError: - low = None - if high is not None: - if float(new_position) > high: - msg = "requested movement of %s is above its upper limit"\ - % element.name - raise RuntimeError(msg) - if low is not None: - if float(new_position) < low: - msg = "requested movement of %s is below its lower limit"\ - % element.name - raise RuntimeError(msg) + _check_attr_range(element.name, "position", new_position) + except ValueError as e: + # TODO: don't concatenate exception message whenever + # tango-controls/pytango#340 is fixed + msg = "requested move of {} is outside of limits ({})".format( + element.name, str(e) + ) + raise ValueError(msg) from e element.calculate_motion(new_position, items=items, calculated=calculated) From 5db1c0432b3f062281bc284157409d653e1ed6d4 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 3 Mar 2020 18:02:51 +0100 Subject: [PATCH 479/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb026e7342..48f8428a7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Use `tango.EnsureOmnitThread` to protect Sardana threads (Tango is not thread safe) (#1298) +* Avoid using Tango `AttributeProxy` in limits protection to not be affected + by bug tango-controls/pytango#315 (#1302) * Default macro parameter values in macroexecutor (#1153) * Executing RunMacro Door's command with string parameters containing spaces (#1240) * Setting of environment variables in Python 3.7 (#1195) From cf82fdddd74132b8a299cecc2adac5886c12815e Mon Sep 17 00:00:00 2001 From: aalonso Date: Wed, 4 Mar 2020 17:25:30 +0100 Subject: [PATCH 480/830] Add tango attribute tests and fix some tests errors. --- .../test_measurementgroupconfiguretion.py | 127 ++++++++++++------ 1 file changed, 85 insertions(+), 42 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index f37abbe152..2f31fe42f7 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -39,7 +39,8 @@ def _assertMultipleResults(self, result, channels, expected_values): self.assertEqual(len(expected_channels), 0, msg) def test_enabled(self, elements=["_test_ct_1_1", "_test_ct_1_2", - "_test_2d_1_3"]): + "_test_2d_1_3", + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -49,21 +50,14 @@ def test_enabled(self, elements=["_test_ct_1_1", "_test_ct_1_2", # Check initial state of all kind of channels, nonexistent # channels for the feature return None as result. enabled = mg.getEnabled(*elements) - expected = [True, True, True] + expected = [True] * len(elements) self._assertMultipleResults(enabled, elements, expected) - # Check if the nonexistent channels raise error if trying to set - with self.assertRaises(Exception): - mg.setEnabled(True, *elements[-1]) - - # Redefine elements to ony use existing values - elements = ["_test_ct_1_1", "_test_ct_1_2"] - # Test every possible combination of setting values # Check that changing one channel doesn't affect the other - mg.setEnabled(False, *elements) - enabled = mg.getEnabled(*elements) - self._assertResult(enabled, elements, False) + enabled = mg.setEnabled(False, *elements) + expected = [False] * len(elements) + self._assertMultipleResults(enabled, elements, expected) mg.setEnabled(True, elements[0]) result = mg.getEnabled(*elements) expected = [False] * len(elements) @@ -73,6 +67,9 @@ def test_enabled(self, elements=["_test_ct_1_1", "_test_ct_1_2", enabled = mg.getEnabled(*elements) self._assertResult(enabled, elements, False) + # Redefine elements to ony use existing values + elements = ["_test_ct_1_1", "_test_ct_1_2"] + # Set values using the controller instead of channels mg.setEnabled(True, "_test_ct_ctrl_1") enabled = mg.getEnabled(*elements) @@ -96,7 +93,8 @@ def test_enabled(self, elements=["_test_ct_1_1", "_test_ct_1_2", self.pool.DeleteElement(mg_name) def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2", - "_test_2d_1_3"]): + "_test_2d_1_3", + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -107,16 +105,9 @@ def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2", # Check initial state of all kind of channels, nonexistent # channels for the feature return None as result. enabled = mg.getOutput(*elements) - expected = [True, True, True] + expected = [True] * len(elements) self._assertMultipleResults(enabled, elements, expected) - # Check if the nonexistent channels raise error if trying to set - with self.assertRaises(Exception): - mg.setOutput(True, *elements[-1]) - - # Redefine elements to ony use existing values - elements = ["_test_ct_1_1", "_test_ct_1_2"] - # Test every possible combination of setting values # Check that changing one channel doesn't affect the other mg.setOutput(False, *elements) @@ -131,6 +122,9 @@ def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2", is_output = mg.getOutput(*elements) self._assertResult(is_output, elements, False) + # Redefine elements to ony use existing values + elements = ["_test_ct_1_1", "_test_ct_1_2"] + # Set values using the controller instead of channels mg.setOutput(True, "_test_ct_ctrl_1") is_output = mg.getOutput(*elements) @@ -154,7 +148,8 @@ def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2", self.pool.DeleteElement(mg_name) def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", - "_test_ct_1_3", "_test_2d_1_3"]): + "_test_ct_1_3", "_test_2d_1_3", + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -167,8 +162,9 @@ def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", mg.setPlotType("Spectrum", elements[1]) mg.setPlotType("No", elements[2]) mg.setPlotType("Image", elements[3]) + mg.setPlotType("Image", elements[4]) plottype = mg.getPlotType() - expected_values = [2, 1, 0, 2] + expected_values = [2, 1, 0, 2, 2] self._assertMultipleResults(plottype, elements, expected_values) with self.assertRaises(ValueError): mg.setPlotType("asdf", elements[2]) @@ -201,7 +197,8 @@ def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", self.pool.DeleteElement(mg_name) def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", - "_test_ct_1_3", "_test_2d_1_3"]): + "_test_ct_1_3", "_test_2d_1_3", + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -212,15 +209,17 @@ def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", mg.setPlotType("Spectrum", elements[1]) mg.setPlotType("No", elements[2]) mg.setPlotType("Image", elements[3]) + mg.setPlotType("Image", elements[4]) result = mg.getPlotAxes() self._assertResult(result, elements, []) + mg.setPlotAxes(["", ""], elements[4]) mg.setPlotAxes(["", ""], elements[3]) mg.setPlotAxes(["", ""], elements[0]) mg.setPlotAxes([""], elements[1]) result = mg.getPlotAxes() expected_result = [['', ''], [''], [], - ['', '']] + ['', ''], ['', '']] self._assertMultipleResults(result, elements, expected_result) mg.setPlotAxes(["", ""], elements[0]) mg.setPlotAxes([""], elements[1]) @@ -268,18 +267,32 @@ def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", self.pool.DeleteElement(mg_name) def test_Timer(self, elements=["_test_ct_1_1", "_test_ct_1_2", - "_test_ct_1_3"]): + "_test_ct_1_3", + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) - previous = mg.getTimer() + + previous = mg.getTimer(elements[-1]) + # TODO see if 2d channels should rise exception on setting + with self.assertRaises(Exception): + mg.setTimer(elements[-1], "_test_2d_1_2") + self.assertEqual(mg.getTimer(*elements), previous) + previous = mg.getTimer(elements[-2]) + with self.assertRaises(Exception): + mg.setTimer(elements[-2], "_test_mt_1_3") + self.assertEqual(mg.getTimer(*elements), previous) + + elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] + + previous = mg.getTimer(*elements) print(previous) mg.setTimer('_test_ct_1_3') - self.assertNotEqual(mg.getTimer(), previous) - self._assertResult(mg.getTimer(), elements, '_test_ct_1_3') - self._assertResult(mg.getTimer(ret_by_ctrl=True), + self.assertNotEqual(mg.getTimer(*elements), previous) + self._assertResult(mg.getTimer(*elements), elements, '_test_ct_1_3') + self._assertResult(mg.getTimer(*elements, ret_by_ctrl=True), ['_test_ct_ctrl_1'], '_test_ct_1_3') # Check ret_full_name @@ -297,25 +310,40 @@ def test_Timer(self, elements=["_test_ct_1_1", "_test_ct_1_2", self.pool.DeleteElement(mg_name) def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", - "_test_ct_1_3"]): + "_test_ct_1_3", "_test_2d_1_1", + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) - previous = mg.getMonitor() + + previous = mg.getMonitor(elements[-1]) + # TODO see if 2d channels should rise exception on setting + with self.assertRaises(Exception): + mg.setMonitor(elements[-1], "_test_2d_1_2") + self.assertEqual(mg.getMonitor(*elements), previous) + previous = mg.getMonitor(elements[-2]) + with self.assertRaises(Exception): + mg.setMonitor(elements[-2], "_test_mt_1_3") + self.assertEqual(mg.getMonitor(*elements), previous) + + elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] + + previous = mg.getMonitor(*elements) print(previous) mg.setMonitor("_test_ct_1_2") - self.assertNotEqual(mg.getMonitor(), previous) - self._assertResult(mg.getMonitor(), elements, '_test_ct_1_2') - self._assertResult(mg.getMonitor(ret_by_ctrl=True), + self.assertNotEqual(mg.getMonitor(*elements), previous) + self._assertResult(mg.getMonitor(*elements), elements, + '_test_ct_1_2') + self._assertResult(mg.getMonitor(*elements, ret_by_ctrl=True), ['_test_ct_ctrl_1'], '_test_ct_1_2') # Check ret_full_name v = TangoDeviceNameValidator() full_names = [v.getNames(element)[0] for element in elements] mg.setMonitor(full_names[0]) - result = mg.getMonitor() + result = mg.getMonitor(*elements) self._assertResult(result, elements, '_test_ct_1_1') # TODO ret_full_name gives controler name mg.setMonitor("_test_ct_1_2") @@ -326,14 +354,23 @@ def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", self.pool.DeleteElement(mg_name) def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", - "_test_ct_1_3"]): + "_test_ct_1_3", "_test_2d_1_1", + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) result = mg.getSynchronizer() - self._assertResult(result, elements, 'software') + expected = ['', '', '', None, None] + self._assertResult(result, elements, expected) + with self.assertRaises(Exception): + mg.setSynchronizer('_test_tg_1_2', elements[-1]) + with self.assertRaises(Exception): + mg.setSynchronizer('_test_tg_1_2', elements[-2]) + + elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] + mg.setSynchronizer('_test_tg_1_2') result = mg.getSynchronizer() self._assertResult(result, elements, '_test_tg_1_2') @@ -362,7 +399,8 @@ def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", self.pool.DeleteElement(mg_name) def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", - "_test_ct_1_3"]): + "_test_ct_1_3", + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -372,10 +410,12 @@ def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", # Check initial state of all kind of channels, nonexistent # channels for the feature return None as result. enabled = mg.getValueRefEnabled(*elements) - expected = [False, False, None] + expected = [False, False, None, None] self._assertMultipleResults(enabled, elements, expected) # Check if the nonexistent channels raise error if trying to set + with self.assertRaises(Exception): + mg.setValueRefEnabled(True, *elements[-2]) with self.assertRaises(Exception): mg.setValueRefEnabled(True, *elements[-1]) @@ -419,7 +459,8 @@ def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", self.pool.DeleteElement(mg_name) def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", - "_test_ct_1_3"]): + "_test_ct_1_3", + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) @@ -429,10 +470,12 @@ def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", # Check initial state of all kind of channels, nonexistent # channels for the feature return None as result. pattern = mg.getValueRefPattern(*elements) - expected = ['', '', None] + expected = ['', '', None, None] self._assertMultipleResults(pattern, elements, expected) # Check if the nonexistent channels raise error if trying to set + with self.assertRaises(Exception): + mg.setValueRefPattern('/tmp/test_foo.txt', *elements[-2]) with self.assertRaises(Exception): mg.setValueRefPattern('/tmp/test_foo.txt', *elements[-1]) From f27b34cac0b214007b9ba6c453793c2e6daaf765 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Wed, 4 Mar 2020 14:30:11 +0100 Subject: [PATCH 481/830] Do not set default values for value referable parameters --- src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index 1038d424cb..6089412f32 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -124,8 +124,8 @@ def createChannelDict(channel, index=None, **kwargs): # 'timer': '', #should contain a channel name # 'monitor': '', #should contain a channel name # 'trigger': '', #should contain a channel name - 'value_ref_enabled': False, # bool - 'value_ref_pattern': '', # str + # 'value_ref_enabled': False, # bool + # 'value_ref_pattern': '', # str '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 # string indicating the location of the data of this channel within From aaa98fff092588f8bc717c6c12513b6a4b60b7d9 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Wed, 4 Mar 2020 14:31:02 +0100 Subject: [PATCH 482/830] Add more explicit docstring --- src/sardana/pool/poolmeasurementgroup.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 60f30536b9..a8f18c304d 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -598,7 +598,17 @@ def get_configuration_for_user(self): return self._user_config def set_configuration_from_user(self, cfg, to_fqdn=True): - """Load measurement configuration from serializable data structure.""" + """ + Load measurement configuration from serializable data structure. + + The method will validate the configuration and raises exceptions in + case of fails. The configuration will be different for each type of + channel and controller, For that reason the configuration can have + different keys and values according to the element. + + The client should not validate the keys/values, any validation must be + included in this method. + """ pool = self._parent.pool From 3f9ca87973dbdef341bdf7cf012b7950592383e8 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 5 Mar 2020 14:56:07 +0100 Subject: [PATCH 483/830] Move protection to the server Add protection for external controller in the Pool instead of in the client side. --- src/sardana/pool/poolmeasurementgroup.py | 103 ++++++++++-------- src/sardana/taurus/core/tango/sardana/pool.py | 4 +- 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index a8f18c304d..0e444748c4 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -667,56 +667,69 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): user_config['controllers'][ctrl_name] = user_config_ctrl = {} ctrl_conf = {} - synchronizer = ctrl_data.get('synchronizer', 'software') - conf_synch = None - if synchronizer is None or synchronizer == 'software': - ctrl_conf['synchronizer'] = 'software' - user_config_ctrl['synchronizer'] = 'software' - else: - if to_fqdn: - synchronizer = _to_fqdn(synchronizer, - logger=self._parent) + # The external controllers should not have synchronizer - user_config_ctrl['synchronizer'] = synchronizer - pool_synch = pool.get_element_by_full_name(synchronizer) - pool_synch_ctrl = pool_synch.controller - conf_synch_ctrl = None + if external: + if 'synchronizer' in ctrl_data: + raise ValueError('External controller does not allow ' + 'to have synchronizer') + if 'monitor' in ctrl_data: + raise ValueError('External controller does not allow ' + 'to have monitor') + if 'timer' in ctrl_data: + raise ValueError('External controller does not allow ' + 'to have timer') + else: + synchronizer = ctrl_data.get('synchronizer', 'software') conf_synch = None - for conf_ctrl_created in synch_ctrls: - if pool_synch_ctrl == conf_ctrl_created.element: - conf_synch_ctrl = conf_ctrl_created - for conf_synch_created in \ - conf_ctrl_created.get_channels(): - if pool_synch == conf_synch_created.element: - conf_synch = conf_synch_created - break - break - - if conf_synch_ctrl is None: - conf_synch_ctrl = ControllerConfiguration(pool_synch_ctrl) - synch_ctrls.append(conf_synch_ctrl) - - if conf_synch is None: - conf_synch = SynchronizerConfiguration(pool_synch) - conf_synch_ctrl.add_channel(conf_synch) - - ctrl_conf['synchronizer'] = conf_synch + if synchronizer is None or synchronizer == 'software': + ctrl_conf['synchronizer'] = 'software' + user_config_ctrl['synchronizer'] = 'software' + else: + if to_fqdn: + synchronizer = _to_fqdn(synchronizer, + logger=self._parent) + + user_config_ctrl['synchronizer'] = synchronizer + pool_synch = pool.get_element_by_full_name(synchronizer) + pool_synch_ctrl = pool_synch.controller + conf_synch_ctrl = None + conf_synch = None + for conf_ctrl_created in synch_ctrls: + if pool_synch_ctrl == conf_ctrl_created.element: + conf_synch_ctrl = conf_ctrl_created + for conf_synch_created in \ + conf_ctrl_created.get_channels(): + if pool_synch == conf_synch_created.element: + conf_synch = conf_synch_created + break + break + + if conf_synch_ctrl is None: + conf_synch_ctrl = ControllerConfiguration(pool_synch_ctrl) + synch_ctrls.append(conf_synch_ctrl) + + if conf_synch is None: + conf_synch = SynchronizerConfiguration(pool_synch) + conf_synch_ctrl.add_channel(conf_synch) + + ctrl_conf['synchronizer'] = conf_synch - try: - synchronization = ctrl_data['synchronization'] - except KeyError: - # backwards compatibility for configurations before SEP6 try: - synchronization = ctrl_data['trigger_type'] - msg = ("trigger_type configuration parameter is deprecated" - " in favor of synchronization. Re-apply " - "configuration in order to upgrade.") - self._parent.warning(msg) + synchronization = ctrl_data['synchronization'] except KeyError: - synchronization = AcqSynchType.Trigger - - ctrl_conf['synchronization'] = synchronization - user_config_ctrl['synchronization'] = synchronization + # backwards compatibility for configurations before SEP6 + try: + synchronization = ctrl_data['trigger_type'] + msg = ("trigger_type configuration parameter is deprecated" + " in favor of synchronization. Re-apply " + "configuration in order to upgrade.") + self._parent.warning(msg) + except KeyError: + synchronization = AcqSynchType.Trigger + + ctrl_conf['synchronization'] = synchronization + user_config_ctrl['synchronization'] = synchronization acq_synch = None if external: diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 690345fec1..9439ecefd9 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1830,8 +1830,8 @@ def _set_ctrls_key(self, key, value, ctrls_names=None, apply_cfg=True): ctrls_names = self.controllers.keys() for ctrl_name in ctrls_names: - if ctrl_name == '__tango__': - continue + # if ctrl_name == '__tango__': + # continue ctrl = self._get_ctrl_data(ctrl_name) ctrl[key] = value if apply_cfg: From e0b1e7916878a928eeb1b6b60e9f105924efaf5f Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 5 Mar 2020 15:09:02 +0100 Subject: [PATCH 484/830] Generate cache data for external controller. --- src/sardana/taurus/core/tango/sardana/pool.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 9439ecefd9..db9ad0512d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1411,8 +1411,9 @@ def set_data(self, data, force=False): if ctrl_name != '__tango__': proxy = DeviceProxy(ctrl_name) ctrl_name = proxy.alias() - controllers_names[ctrl_name] = ctrl_data - controllers_channels[ctrl_name] = [] + + controllers_names[ctrl_name] = ctrl_data + controllers_channels[ctrl_name] = [] except Exception: pass for channel_name, channel_data in \ @@ -1422,11 +1423,10 @@ def set_data(self, data, force=False): channels_names[name] = channel_data label = channel_data['label'] channels_labels[name] = channel_data - if ctrl_name != '__tango__': - ch_data = {'fullname': channel_name, - 'label': label, - 'name': name} - controllers_channels[ctrl_name].append(ch_data) + ch_data = {'fullname': channel_name, + 'label': label, + 'name': name} + controllers_channels[ctrl_name].append(ch_data) ##################### # @todo: the for-loops above could be replaced by something like: From 5729236792c19a74ba8dd8efc6a15c751e328a95 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 5 Mar 2020 15:22:24 +0100 Subject: [PATCH 485/830] Fix flake8 errors --- src/sardana/pool/poolmeasurementgroup.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 0e444748c4..073398b59f 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -706,7 +706,8 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): break if conf_synch_ctrl is None: - conf_synch_ctrl = ControllerConfiguration(pool_synch_ctrl) + conf_synch_ctrl = \ + ControllerConfiguration(pool_synch_ctrl) synch_ctrls.append(conf_synch_ctrl) if conf_synch is None: @@ -721,7 +722,8 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): # backwards compatibility for configurations before SEP6 try: synchronization = ctrl_data['trigger_type'] - msg = ("trigger_type configuration parameter is deprecated" + msg = ("trigger_type configuration parameter " + "is deprecated" " in favor of synchronization. Re-apply " "configuration in order to upgrade.") self._parent.warning(msg) @@ -842,7 +844,7 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): # Skip controllers disabled pass elif acq_synch in (AcqSynch.SoftwareTrigger, - AcqSynch.SoftwareGate): + AcqSynch.SoftwareGate): if ctrl_item.timer.index < master_timer_idx_sw: master_timer_sw = ctrl_item.timer master_timer_idx_sw = ctrl_item.timer.index From 89c1b7ccbb96f7da682e9611a15b423b534040e9 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 5 Mar 2020 15:23:20 +0100 Subject: [PATCH 486/830] Allow to read the configuration of external controllers --- src/sardana/taurus/core/tango/sardana/pool.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index db9ad0512d..828d237a1a 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1848,6 +1848,7 @@ def _get_ctrls_key(self, key, ctrls_names=None, use_fullname=False): for ctrl_name in ctrls_names: if ctrl_name == '__tango__': + result[ctrl_name] = None continue ctrl = self._get_ctrl_data(ctrl_name) From 89b1c7c84290fc39e193da71ce0e639027f8e69c Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 5 Mar 2020 16:58:54 +0100 Subject: [PATCH 487/830] Fix errors in test_enabled --- .../test_measurementgroupconfiguretion.py | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index 2f31fe42f7..ff14f4c324 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -49,45 +49,46 @@ def test_enabled(self, elements=["_test_ct_1_1", "_test_ct_1_2", # Check initial state of all kind of channels, nonexistent # channels for the feature return None as result. - enabled = mg.getEnabled(*elements) + result = mg.getEnabled(*elements) expected = [True] * len(elements) - self._assertMultipleResults(enabled, elements, expected) + self._assertMultipleResults(result, elements, expected) # Test every possible combination of setting values # Check that changing one channel doesn't affect the other - enabled = mg.setEnabled(False, *elements) + mg.setEnabled(False, *elements) + result = mg.getEnabled(*elements) expected = [False] * len(elements) - self._assertMultipleResults(enabled, elements, expected) + self._assertMultipleResults(result, elements, expected) mg.setEnabled(True, elements[0]) result = mg.getEnabled(*elements) expected = [False] * len(elements) expected[0] = True self._assertMultipleResults(result, elements, expected) mg.setEnabled(False, *elements) - enabled = mg.getEnabled(*elements) - self._assertResult(enabled, elements, False) + resutl = mg.getEnabled(*elements) + self._assertResult(resutl, elements, False) # Redefine elements to ony use existing values elements = ["_test_ct_1_1", "_test_ct_1_2"] # Set values using the controller instead of channels mg.setEnabled(True, "_test_ct_ctrl_1") - enabled = mg.getEnabled(*elements) - self._assertResult(enabled, elements, True) + resutl = mg.getEnabled(*elements) + self._assertResult(resutl, elements, True) # Get values by controller mg.setEnabled(False, *elements) - enabled = mg.getEnabled("_test_ct_ctrl_1") - self._assertResult(enabled, elements, False) + resutl = mg.getEnabled("_test_ct_ctrl_1") + self._assertResult(resutl, elements, False) # Check ret_full_name v = TangoDeviceNameValidator() full_names = [v.getNames(element)[0] for element in elements] - enabled = mg.getEnabled(*full_names) - self._assertResult(enabled, elements, False) + resutl = mg.getEnabled(*full_names) + self._assertResult(resutl, elements, False) mg.setEnabled(True, *full_names) - enabled = mg.getEnabled(*elements, ret_full_name=True) - self._assertResult(enabled, full_names, True) + resutl = mg.getEnabled(*elements, ret_full_name=True) + self._assertResult(resutl, full_names, True) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) From ac5a540e64f92d48c840a598016ff39a94e6b34d Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Thu, 5 Mar 2020 16:59:33 +0100 Subject: [PATCH 488/830] Remove deprecation warning --- .../tango/sardana/test/test_measurementgroupconfiguretion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index ff14f4c324..38d2cff9a4 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -1,6 +1,6 @@ import uuid +from unittest import TestCase from taurus import Device -from taurus.external.unittest import TestCase from taurus.core.tango.tangovalidator import TangoDeviceNameValidator from sardana.taurus.core.tango.sardana.pool import registerExtensions from sardana.tango.pool.test.base_sartest import SarTestTestCase From 2bc945a842ef8ada012e1a70242798b5433fec50 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 7 Mar 2020 12:44:07 +0100 Subject: [PATCH 489/830] Don't use case_sensive (not available on click < 7.0) --- src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index fb8e5590d7..102eab1aae 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -91,7 +91,7 @@ class TaurusGuiLite(TaurusGui): help='group curves') @click.option('--taurus-log-level', type=click.Choice(['critical', 'error', 'warning', 'info', - 'debug', 'trace'], case_sensitive=False), + 'debug', 'trace']), default='error', show_default=True, help='Show only logs with priority LEVEL or above') @click.argument('door') From 391c1102377dec639241e604c7b602a2813cf4f0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 7 Mar 2020 12:46:56 +0100 Subject: [PATCH 490/830] Fix dummy C/T reaction on "hardware" trigger Very demanding (fast) synchronizations may not emit active events with 0 index. It is better to react on start event. --- src/sardana/pool/poolcontrollers/DummyCounterTimerController.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py b/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py index 81e7560f8d..ae1276651a 100644 --- a/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py +++ b/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py @@ -339,7 +339,7 @@ def event_received(self, src, type_, value): e.g. start, active passive """ # for the moment only react on first trigger - if type_.name.lower() == "active" and value == 0: + if type_.name.lower() == "start": self._armed = False for axis, channel in self.counting_channels.items(): channel.is_counting = True From 35152a3c603c49f1326b0c9ff1aa5a4c45b213d3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 10 Mar 2020 18:00:53 +0100 Subject: [PATCH 491/830] Adapt to new taurus behavior of cmd_line_parser taurus-org/taurus#1089 changes default behavior of cmd_line_parser kwarg of TaurusApplication. If not passed Taurus won't provide any parser. Use get_taurus_pareser (OptParse) and pass it to the TaurusApplication. --- .../qt/qtgui/extra_macroexecutor/macroexecutor.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index be2f222a07..622cd8d800 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -1075,10 +1075,18 @@ def createMacroExecutor(args): def main(): + from taurus.core.util import argparse from taurus.qt.qtgui.application import TaurusApplication - import taurus - app = TaurusApplication(sys.argv, app_version=sardana.Release.version) + parser = argparse.get_taurus_parser() + parser.set_usage("%prog [options]") + parser.set_description("Sardana macro executor.\n" + "It allows execution of macros, keeping history " + "of previous executions and favourites.") + app = TaurusApplication(sys.argv, + cmd_line_parser=parser, + app_name="macroexecutor", + app_version=sardana.Release.version) args = app.get_command_line_args() app.setOrganizationName("Taurus") From 68d110ceb0b84021f01a144b8bcc973c3bc7166e Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 11 Mar 2020 10:33:32 +0100 Subject: [PATCH 492/830] Adapt to new taurus behavior of cmd_line_parser taurus-org/taurus#1089 changes default behavior of cmd_line_parser kwarg of TaurusApplication. If not passed Taurus won't provide any parser. Use get_taurus_pareser (OptParse) and pass it to the TaurusApplication. Do it for not-sardana application and for the main sections of modules (used for testing). --- src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py | 4 +++- src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py | 4 +++- .../extra_macroexecutor/favouriteseditor/favouriteseditor.py | 4 +++- .../extra_macroexecutor/favouriteseditor/historyviewer.py | 4 +++- .../taurus/qt/qtgui/extra_macroexecutor/macrobutton.py | 3 ++- .../macroparameterseditor/customeditors/senv.py | 4 +++- 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py index 2aee0b78ea..a22149c7e9 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/common.py @@ -357,8 +357,10 @@ def test_macrocombobox(ms_name): if __name__ == "__main__": import sys + from taurus.core.util.argparse import get_taurus_parser from taurus.qt.qtgui.application import TaurusApplication - app = TaurusApplication() + parser = get_taurus_parser() + app = TaurusApplication(cmd_line_parser=parser) args = app.get_command_line_args() ms_name = args[0] test_macrocombobox(ms_name) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py index b0c19991de..9ca657d932 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py @@ -246,9 +246,11 @@ def eventReceived(self, src, type, value): if __name__ == "__main__": import sys import taurus + from taurus.core.util.argparse import get_taurus_parser from taurus.qt.qtgui.application import TaurusApplication - app = TaurusApplication(sys.argv) + parser = get_taurus_parser() + app = TaurusApplication(sys.argv, cmd_line_parser=parser) args = app.get_command_line_args() doorOutput = DoorOutput() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py index 68fe626764..c63ebcfd0d 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/favouriteseditor.py @@ -220,9 +220,11 @@ def test(): import sys import taurus import time + from taurus.core.util.argparse import get_taurus_parser from taurus.qt.qtgui.application import TaurusApplication - app = TaurusApplication(sys.argv) + parser = get_taurus_parser() + app = TaurusApplication(sys.argv, cmd_line_parser=parser) favouritesEditor = FavouritesMacrosEditor() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py index 6300fdaf38..a8f14bad7b 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py @@ -169,9 +169,11 @@ def test(): import sys import taurus import time + from taurus.core.util.argparse import get_taurus_parser from taurus.qt.qtgui.application import TaurusApplication - app = TaurusApplication(sys.argv) + parser = get_taurus_parser() + app = TaurusApplication(sys.argv, cmd_line_parser=parser) historyViewer = HistoryMacrosViewer() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 9542fbdbc9..3ee561f19e 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -381,7 +381,8 @@ def abort(self): parser.set_description("Macro button for macro execution") app = TaurusApplication(app_name="macrobutton", - app_version=taurus.Release.version) + app_version=taurus.Release.version, + cmd_line_parser=parser) args = app.get_command_line_args() diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py index 3af96a985f..1e2df55853 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/customeditors/senv.py @@ -383,9 +383,11 @@ def removeRow(self, row, parentIndex=None): if __name__ == "__main__": import sys import taurus + from taurus.core.util.argparse import get_taurus_parser from taurus.qt.qtgui.application import TaurusApplication - app = TaurusApplication(sys.argv) + parser = get_taurus_parser() + app = TaurusApplication(sys.argv, cmd_line_parser=parser) args = app.get_command_line_args() editor = SenvEditor() macroServer = taurus.Device(args[0]) From 734fa16147683c8f48c0b46559794cf3718b6c39 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 11 Mar 2020 10:35:31 +0100 Subject: [PATCH 493/830] Don't pass cmd_line_parser=None for showscanline application showscanonline application already uses click. Don't pass cmd_line_parser=None since it is not necessary since taurus-org/taurus#1089. --- src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index 102eab1aae..7a6f5c446f 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -102,7 +102,7 @@ def main(group, taurus_log_level, door): from taurus.qt.qtgui.application import TaurusApplication app = TaurusApplication(app_name='Showscan Online', org_domain="Sardana", - org_name="Tango communinity", cmd_line_parser=None) + org_name="Tango communinity") assertPlotAvailability() From a21f5d71ab1bb6b28fc635aa3b06d64d86e3608c Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Wed, 11 Mar 2020 11:08:03 +0100 Subject: [PATCH 494/830] Use device name on the PoolTangoObject name --- src/sardana/pool/poolexternal.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/pool/poolexternal.py b/src/sardana/pool/poolexternal.py index a52723a79c..38113f0003 100644 --- a/src/sardana/pool/poolexternal.py +++ b/src/sardana/pool/poolexternal.py @@ -80,7 +80,8 @@ def __init__(self, **kwargs): self._attribute_name = attribute_name self._config = None self._device = None - kwargs['name'] = attribute_name + # TODO evaluate to use alias instead of device_name + kwargs['name'] = '{0}/{1}'.format(device_name, attribute_name) kwargs['full_name'] = full_name PoolBaseExternalObject.__init__(self, **kwargs) From 02e8144f262bda8fc30ac9dfd480b56ba2e44b31 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Wed, 11 Mar 2020 11:08:48 +0100 Subject: [PATCH 495/830] Consider as external only the __tango__ controller --- src/sardana/pool/poolmeasurementgroup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 073398b59f..f794be2760 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -655,7 +655,8 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): if ch_count == 0: continue - external = ctrl_name.startswith('__') + external = ctrl_name in ['__tango__'] + if external: ctrl = ctrl_name else: From fed5e6ea528e6408a1cf0f47ee1544990e7c0c59 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 11 Mar 2020 13:15:32 +0100 Subject: [PATCH 496/830] Revert "Don't pass cmd_line_parser=None for showscanline application" This reverts commit 734fa16147683c8f48c0b46559794cf3718b6c39. --- src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py index 7a6f5c446f..102eab1aae 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/showscanonline.py @@ -102,7 +102,7 @@ def main(group, taurus_log_level, door): from taurus.qt.qtgui.application import TaurusApplication app = TaurusApplication(app_name='Showscan Online', org_domain="Sardana", - org_name="Tango communinity") + org_name="Tango communinity", cmd_line_parser=None) assertPlotAvailability() From ac682b06590648db6cd0b47c27a788fcdb180fe0 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 11 Mar 2020 17:31:21 +0100 Subject: [PATCH 497/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48f8428a7b..8d2fcc2501 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Remove deprecation warning revealed when running test suite (#1267) * Remove event filtering in `DynamicPlotManager` (showscan online) (#1299) * Avoid unnecessary creations of DeviceProxies in `ascanct` (#1281) +* Adapt to new taurus behavior of `cmd_line_parser` kwarg of `TaurusApplication` (#1306) ### Deprecated From 7b91117b2fecf673e13885310379a740f70586bd Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 12 Mar 2020 09:26:25 +0100 Subject: [PATCH 498/830] Limit macroexecutor history Macroexecutor history may grow infinitely. This slows down the GUI operation (startup and changing of the perspectives). Limit the macroexecution hitory to 100. Allow to change it using the sardanacustomsettings. --- src/sardana/sardanacustomsettings.py | 3 +++ .../favouriteseditor/historyviewer.py | 5 +++++ .../extra_macroexecutor/favouriteseditor/model.py | 14 +++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py index 5b5989ed6a..90ca023bc8 100644 --- a/src/sardana/sardanacustomsettings.py +++ b/src/sardana/sardanacustomsettings.py @@ -91,3 +91,6 @@ #: this documentation it is not available for conda. #: - "dumb" - worst performance but directly available with Python 3. MS_ENV_SHELVE_BACKEND = None + +#: macroexecutor maximum number of macros stored in the history +MACROEXECUTOR_MAX_HISTORY = 100 diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py index 6300fdaf38..e2d5a60f9b 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/historyviewer.py @@ -28,6 +28,7 @@ """ import copy +from sardana import sardanacustomsettings from taurus.external.qt import Qt, compat from taurus.qt.qtgui.container import TaurusWidget from taurus.qt.qtcore.configuration import BaseConfigurableClass @@ -49,6 +50,10 @@ def initComponents(self): self.list = HistoryMacrosList(self) self._model = MacrosListModel() + max_history = getattr(sardanacustomsettings, + "MACROEXECUTOR_MAX_HISTORY", + None) + self._model.setMaxLen(max_history) self.list.setModel(self._model) # self.registerConfigDelegate(self.list) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py index 2852137ac2..ca0a9922c4 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py @@ -38,6 +38,11 @@ class MacrosListModel(Qt.QAbstractListModel): def __init__(self, parent=None): Qt.QAbstractListModel.__init__(self, parent) self.list = [] + self._max_len = None + self._len = 0 + + def setMaxLen(self, max_len): + self._max_len = max_len def rowCount(self, parent=Qt.QModelIndex()): return len(self.list) @@ -58,12 +63,16 @@ def index(self, row, column=0, parent=Qt.QModelIndex()): def insertRow(self, macroNode, row=0): self.beginInsertRows(Qt.QModelIndex(), row, row) self.list.insert(row, macroNode) + self._len += 1 + if self._max_len is not None and self._len > self._max_len: + self.list.pop(self._len - 1) + self._len -= 1 self.endInsertRows() return self.index(row) def removeRow(self, row): self.beginRemoveRows(Qt.QModelIndex(), row, row) - self.list.pop(row) + self.endRemoveRows() if row == self.rowCount(): row = row - 1 @@ -100,7 +109,10 @@ def fromXmlString(self, xmlString): self.beginResetModel() listElement = etree.fromstring(xmlString) for childElement in listElement.iterchildren("macro"): + if self._max_len is not None and self._len >= self._max_len: + break macroNode = macro.MacroNode() macroNode.fromXml(childElement) self.list.append(macroNode) + self._len += 1 self.endResetModel() From 25f3e8f6dfd78b4b985abd847f790d998586e83c Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Fri, 13 Mar 2020 10:21:48 +0100 Subject: [PATCH 499/830] Update internal variables in case of failures --- src/sardana/taurus/core/tango/sardana/pool.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 828d237a1a..5b721b78ed 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1927,7 +1927,14 @@ def applyConfiguration(self, timeout=3): self.set_data(self._pending_event_data, force=True) raise RuntimeError('The configuration changed on the server ' 'during your changes.') - self._mg.setConfiguration(self._raw_data) + try: + self._mg.setConfiguration(self._raw_data) + except Exception as e: + self._local_changes = False + self._pending_event_data = None + data = self._mg.getConfigurationAttrEG().readValue(force=True) + self.set_data(data, force=True) + raise e self._local_changes = False self._pending_event_data = None t1 = time.time() From e70105e84731d346b1dfe19685e764943bb19e51 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 13 Mar 2020 15:10:18 +0100 Subject: [PATCH 500/830] Re-add accidentally removed code --- .../qt/qtgui/extra_macroexecutor/favouriteseditor/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py index ca0a9922c4..d01afc1bd1 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py @@ -72,7 +72,7 @@ def insertRow(self, macroNode, row=0): def removeRow(self, row): self.beginRemoveRows(Qt.QModelIndex(), row, row) - + self.list.pop(row) self.endRemoveRows() if row == self.rowCount(): row = row - 1 From a2f69eae347e519240652a16fe181c6efd6b7db0 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Fri, 13 Mar 2020 15:46:37 +0100 Subject: [PATCH 501/830] Create cache for controller full names and aliases --- src/sardana/taurus/core/tango/sardana/pool.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 5b721b78ed..6411a969c5 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1404,13 +1404,16 @@ def set_data(self, data, force=False): self.channels_labels = channels_labels = CaselessDict() self.controllers_names = controllers_names = CaselessDict() self.controllers_channels = controllers_channels = CaselessDict() + self.controllers_alias = CaselessDict() # TODO private controllers attr for ctrl_name, ctrl_data in list(self.controllers.items()): try: if ctrl_name != '__tango__': proxy = DeviceProxy(ctrl_name) + ctrl_full_name = ctrl_name ctrl_name = proxy.alias() + self.controllers_alias[ctrl_full_name] = ctrl_name controllers_names[ctrl_name] = ctrl_data controllers_channels[ctrl_name] = [] @@ -1894,6 +1897,8 @@ def _get_ctrl_for_channel(self, channels_names, unique=False): def _get_ctrl_channels(self, ctrl, use_fullname=False): channels = [] + if ctrl not in self.controllers_channels: + ctrl = self.controllers_alias[ctrl] channels_datas = self.controllers_channels[ctrl] for channel_data in channels_datas: if use_fullname: From 65015ca2ce3cd18e03ec6c937c96aa6b46930fe4 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Fri, 13 Mar 2020 15:48:08 +0100 Subject: [PATCH 502/830] Fix error on return channels full names --- src/sardana/taurus/core/tango/sardana/pool.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 6411a969c5..d780d204eb 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2827,10 +2827,10 @@ def getValueRefPattern(self, *elements, ret_full_name=False): return config._getValueRefPatternChannels(channels, use_fullname=ret_full_name) - def _get_value_per_channel(self, config, ctrls_values): + def _get_value_per_channel(self, config, ctrls_values, use_fullname=False): channels_values = collections.OrderedDict({}) for ctrl, value in ctrls_values.items(): - for channel in config._get_ctrl_channels(ctrl): + for channel in config._get_ctrl_channels(ctrl, use_fullname): channels_values[channel] = value return channels_values @@ -2892,7 +2892,8 @@ def getTimer(self, *elements, ret_full_name=False, ret_by_ctrl=False): if ret_by_ctrl: return ctrls_timers else: - return self._get_value_per_channel(config, ctrls_timers) + return self._get_value_per_channel(config, ctrls_timers, + use_fullname=ret_full_name) def setMonitor(self, monitor, *elements, apply=True): """Set the monitor configuration for the given channels of the same @@ -2953,7 +2954,8 @@ def getMonitor(self, *elements, ret_full_name=False, ret_by_ctrl=False): if ret_by_ctrl: return ctrls_monitor else: - return self._get_value_per_channel(config, ctrls_monitor) + return self._get_value_per_channel(config, ctrls_monitor, + use_fullname=ret_full_name) def setSynchronizer(self, synchronizer, *elements, apply=True): """Set the synchronizer configuration for the given channels or @@ -3016,7 +3018,8 @@ def getSynchronizer(self, *elements, ret_full_name=False, if ret_by_ctrl: return ctrls_sync else: - return self._get_value_per_channel(config, ctrls_sync) + return self._get_value_per_channel(config, ctrls_sync, + use_fullname=ret_full_name) # NbStarts Methods def getNbStartsObj(self): From 44a8fb2b2f6ca4f573ecf0613db4083101ab2647 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Fri, 13 Mar 2020 15:49:53 +0100 Subject: [PATCH 503/830] Remove deprecation warning --- .../tango/sardana/test/test_measurementgroupconfiguretion.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index 38d2cff9a4..d5a6f406c9 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -1,12 +1,12 @@ import uuid -from unittest import TestCase +import unittest from taurus import Device from taurus.core.tango.tangovalidator import TangoDeviceNameValidator from sardana.taurus.core.tango.sardana.pool import registerExtensions from sardana.tango.pool.test.base_sartest import SarTestTestCase -class TestMeasurementGroupConfiguration(SarTestTestCase, TestCase): +class TestMeasurementGroupConfiguration(SarTestTestCase, unittest.TestCase): def setUp(self): SarTestTestCase.setUp(self) From 07e35bf5bb13301d4fb0ff38e61d904e2477b09f Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Fri, 13 Mar 2020 15:50:43 +0100 Subject: [PATCH 504/830] Fix errors in plot axes test --- .../test/test_measurementgroupconfiguretion.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index d5a6f406c9..7c385b18ff 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -214,24 +214,29 @@ def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", result = mg.getPlotAxes() self._assertResult(result, elements, []) - mg.setPlotAxes(["", ""], elements[4]) - mg.setPlotAxes(["", ""], elements[3]) mg.setPlotAxes(["", ""], elements[0]) mg.setPlotAxes([""], elements[1]) + with self.assertRaises(Exception): + mg.setPlotAxes([''], elements[2]) + mg.setPlotAxes(["", ""], elements[3]) + mg.setPlotAxes(["", ""], elements[4]) + result = mg.getPlotAxes() expected_result = [['', ''], [''], [], ['', ''], ['', '']] self._assertMultipleResults(result, elements, expected_result) + mg.setPlotAxes(["", ""], elements[0]) mg.setPlotAxes([""], elements[1]) result = mg.getPlotAxes() expected_result = [['', ''], [''], [], - ['', '']] + ['', ''], ['', '']] self._assertMultipleResults(result, elements, expected_result) + mg.setPlotAxes(["", ""], elements[0]) result = mg.getPlotAxes() expected_result = [['', ''], [''], [], - ['', '']] + ['', ''], ['', '']] self._assertMultipleResults(result, elements, expected_result) with self.assertRaises(RuntimeError): @@ -244,7 +249,7 @@ def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] # Set values using the controller instead of channels - with self.assertRaises(RuntimeError): + with self.assertRaises(Exception): mg.setPlotAxes([""], "_test_ct_ctrl_1") # TODO get method by controller doesn't give the order # Get values by controller From 21503565bbc567e125ab97d5f82e689ac32e2257 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Fri, 13 Mar 2020 15:51:45 +0100 Subject: [PATCH 505/830] Fix errors in timer test --- .../test_measurementgroupconfiguretion.py | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index 7c385b18ff..df7d1803c5 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -281,35 +281,36 @@ def test_Timer(self, elements=["_test_ct_1_1", "_test_ct_1_2", try: mg = Device(mg_name) - previous = mg.getTimer(elements[-1]) - # TODO see if 2d channels should rise exception on setting - with self.assertRaises(Exception): - mg.setTimer(elements[-1], "_test_2d_1_2") - self.assertEqual(mg.getTimer(*elements), previous) - previous = mg.getTimer(elements[-2]) + result = mg.getTimer("_test_mt_1_3/position") with self.assertRaises(Exception): - mg.setTimer(elements[-2], "_test_mt_1_3") - self.assertEqual(mg.getTimer(*elements), previous) + mg.setTimer("_test_mt_1_3/position") + self._assertResult(result, ["_test_mt_1_3/position"], None) + mg.setTimer('_test_ct_1_3') + result = mg.getTimer(*elements) + expected = ['_test_ct_1_3', '_test_ct_1_3', '_test_ct_1_3', None] + self._assertMultipleResults(result, elements, expected) - elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] + mg.setTimer('_test_ct_1_2') + result = mg.getTimer(*elements) + expected = ['_test_ct_1_2', '_test_ct_1_2', '_test_ct_1_2', None] + self._assertMultipleResults(result, elements, expected) - previous = mg.getTimer(*elements) - print(previous) - mg.setTimer('_test_ct_1_3') - self.assertNotEqual(mg.getTimer(*elements), previous) - self._assertResult(mg.getTimer(*elements), elements, '_test_ct_1_3') - self._assertResult(mg.getTimer(*elements, ret_by_ctrl=True), - ['_test_ct_ctrl_1'], '_test_ct_1_3') + result = mg.getTimer(*elements, ret_by_ctrl=True) + self._assertMultipleResults(result, + ['_test_ct_ctrl_1', '__tango__'], + ['_test_ct_1_2', None]) # Check ret_full_name v = TangoDeviceNameValidator() - full_names = [v.getNames(element)[0] for element in elements] - mg.setTimer(full_names[0]) + counters = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] + full_names = [v.getNames(counter)[0] for counter in counters] + mg.setTimer(v.getNames('_test_ct_1_1')[0]) result = mg.getTimer() - self._assertResult(result, elements, '_test_ct_1_1') + expected = ['_test_ct_1_1', '_test_ct_1_1', '_test_ct_1_1', None] + self._assertMultipleResults(result, elements, expected) # TODO ret_full_name gives controler name mg.setTimer("_test_ct_1_2") - result = mg.getTimer(*elements, ret_full_name=True) + result = mg.getTimer(*counters, ret_full_name=True) self._assertResult(result, full_names, "_test_ct_1_2") finally: mg.cleanUp() From 622fe2ebe04bea1e5b77b616d40e10fc2fcc4734 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Fri, 13 Mar 2020 16:26:37 +0100 Subject: [PATCH 506/830] Fix errors in monitor test --- .../test_measurementgroupconfiguretion.py | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index df7d1803c5..86a02557b5 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -318,44 +318,43 @@ def test_Timer(self, elements=["_test_ct_1_1", "_test_ct_1_2", def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3", "_test_2d_1_1", + '_test_2d_1_2', "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) + print(mg_name) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: mg = Device(mg_name) - previous = mg.getMonitor(elements[-1]) - # TODO see if 2d channels should rise exception on setting with self.assertRaises(Exception): - mg.setMonitor(elements[-1], "_test_2d_1_2") - self.assertEqual(mg.getMonitor(*elements), previous) - previous = mg.getMonitor(elements[-2]) - with self.assertRaises(Exception): - mg.setMonitor(elements[-2], "_test_mt_1_3") - self.assertEqual(mg.getMonitor(*elements), previous) + mg.setMonitor("_test_mt_1_3/position") - elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] + mg.setMonitor('_test_2d_1_2') + mg.setMonitor("_test_ct_1_3") + expected = ["_test_ct_1_3", "_test_ct_1_3", "_test_ct_1_3", + "_test_2d_1_2", '_test_2d_1_2', None] + result = mg.getMonitor() + self._assertMultipleResults(result, elements, expected) - previous = mg.getMonitor(*elements) - print(previous) - mg.setMonitor("_test_ct_1_2") - self.assertNotEqual(mg.getMonitor(*elements), previous) - self._assertResult(mg.getMonitor(*elements), elements, - '_test_ct_1_2') - self._assertResult(mg.getMonitor(*elements, ret_by_ctrl=True), - ['_test_ct_ctrl_1'], '_test_ct_1_2') + expected = ["_test_ct_1_3", '_test_2d_1_2', None] + result = mg.getMonitor(ret_by_ctrl=True) + ctrls = ['_test_ct_ctrl_1', '_test_2d_ctrl_1', '__tango__'] + self._assertMultipleResults(result, ctrls, expected) + # Check ret_full_name # Check ret_full_name v = TangoDeviceNameValidator() - full_names = [v.getNames(element)[0] for element in elements] - mg.setMonitor(full_names[0]) - result = mg.getMonitor(*elements) - self._assertResult(result, elements, '_test_ct_1_1') - # TODO ret_full_name gives controler name - mg.setMonitor("_test_ct_1_2") - result = mg.getMonitor(*elements, ret_full_name=True) - self._assertResult(result, full_names, "_test_ct_1_2") + counters = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3", + '_test_2d_1_1', '_test_2d_1_2'] + full_names = [v.getNames(counter)[0] for counter in counters] + mg.setMonitor(v.getNames('_test_ct_1_1')[0]) + mg.setMonitor(v.getNames('_test_2d_1_2')[0]) + + result = mg.getMonitor(*counters, ret_full_name=True) + expected = ["_test_ct_1_1", "_test_ct_1_1", "_test_ct_1_1", + "_test_2d_1_2", '_test_2d_1_2'] + self._assertMultipleResults(result, full_names, expected) finally: mg.cleanUp() self.pool.DeleteElement(mg_name) From da0b80006a52d7b64af9fb358b751c28455a8b3f Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Fri, 13 Mar 2020 17:15:34 +0100 Subject: [PATCH 507/830] Fix errors in synchronizer test --- .../test_measurementgroupconfiguretion.py | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index 86a02557b5..d654ad0a12 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -342,7 +342,6 @@ def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", ctrls = ['_test_ct_ctrl_1', '_test_2d_ctrl_1', '__tango__'] self._assertMultipleResults(result, ctrls, expected) - # Check ret_full_name # Check ret_full_name v = TangoDeviceNameValidator() counters = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3", @@ -368,37 +367,40 @@ def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", try: mg = Device(mg_name) result = mg.getSynchronizer() - expected = ['', '', '', None, None] - self._assertResult(result, elements, expected) - with self.assertRaises(Exception): - mg.setSynchronizer('_test_tg_1_2', elements[-1]) + expected = ['software', 'software', 'software', 'software', None] + self._assertMultipleResults(result, elements, expected) with self.assertRaises(Exception): - mg.setSynchronizer('_test_tg_1_2', elements[-2]) + mg.setSynchronizer('_test_tg_1_2', "_test_mt_1_3/position") - elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] + mg.setSynchronizer('_test_tg_1_2', "_test_ct_ctrl_1", + "_test_2d_ctrl_1") - mg.setSynchronizer('_test_tg_1_2') + expected = ['_test_tg_1_2', '_test_tg_1_2', '_test_tg_1_2', + '_test_tg_1_2', None] result = mg.getSynchronizer() - self._assertResult(result, elements, '_test_tg_1_2') - mg.setSynchronizer('software') + self._assertMultipleResults(result, elements, expected) + + mg.setSynchronizer('software', "_test_ct_ctrl_1", + "_test_2d_ctrl_1") result = mg.getSynchronizer() - self._assertResult(result, elements, 'software') - result = mg.getSynchronizer(ret_by_ctrl=True) - self._assertResult(result, ['_test_ct_ctrl_1'], 'software') + expected = ['software', 'software', 'software', 'software', None] + self._assertMultipleResults(result, elements, expected) + with self.assertRaises(Exception): - mg.setSynchronizer('asdf') + mg.setSynchronizer('asdf', "_test_ct_ctrl_1", + "_test_2d_ctrl_1") # Check ret_full_name v = TangoDeviceNameValidator() - full_names = [v.getNames(element)[0] for element in elements] - full_name_tg = v.getNames('_test_tg_1_2')[0] - mg.setSynchronizer(full_name_tg) - result = mg.getSynchronizer() - self._assertResult(result, elements, '_test_tg_1_2') - # TODO ret_full_name gives controler name - mg.setSynchronizer('software', *full_names) - result = mg.getSynchronizer(*elements, ret_full_name=True) - self._assertResult(result, full_names, 'software') + counters = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3", + '_test_2d_1_1'] + full_names = [v.getNames(counter)[0] for counter in counters] + mg.setSynchronizer('_test_tg_1_2', "_test_ct_ctrl_1", + "_test_2d_ctrl_1") + + result = mg.getSynchronizer(*counters, ret_full_name=True) + + self._assertResult(result, full_names, '_test_tg_1_2') finally: mg.cleanUp() From fb43a6a22921a66404fe0ed48e0766005e554e3f Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 13 Mar 2020 19:06:40 +0100 Subject: [PATCH 508/830] Use len() to get list length Instead of keeping a length of the list just use the len(). --- .../extra_macroexecutor/favouriteseditor/model.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py index d01afc1bd1..b1b51f5ec4 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py @@ -39,7 +39,6 @@ def __init__(self, parent=None): Qt.QAbstractListModel.__init__(self, parent) self.list = [] self._max_len = None - self._len = 0 def setMaxLen(self, max_len): self._max_len = max_len @@ -63,10 +62,8 @@ def index(self, row, column=0, parent=Qt.QModelIndex()): def insertRow(self, macroNode, row=0): self.beginInsertRows(Qt.QModelIndex(), row, row) self.list.insert(row, macroNode) - self._len += 1 - if self._max_len is not None and self._len > self._max_len: - self.list.pop(self._len - 1) - self._len -= 1 + if self._max_len is not None and len(self.list) > self._max_len: + self.list.pop() self.endInsertRows() return self.index(row) @@ -109,10 +106,9 @@ def fromXmlString(self, xmlString): self.beginResetModel() listElement = etree.fromstring(xmlString) for childElement in listElement.iterchildren("macro"): - if self._max_len is not None and self._len >= self._max_len: + if self._max_len is not None and len(self.list) >= self._max_len: break macroNode = macro.MacroNode() macroNode.fromXml(childElement) self.list.append(macroNode) - self._len += 1 self.endResetModel() From a8f5e1253322197d6636cdd9910ca9b794d9437b Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 13 Mar 2020 20:21:36 +0100 Subject: [PATCH 509/830] Invert order of popping and inserting last macro First inserting and then popping means abusing the maximum length. It also may cause sporadic list out of range errors when index method is called. Revert the order and first pop the last macro and then insert the new one. --- .../qt/qtgui/extra_macroexecutor/favouriteseditor/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py index b1b51f5ec4..cdf69dd8ff 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/favouriteseditor/model.py @@ -61,9 +61,9 @@ def index(self, row, column=0, parent=Qt.QModelIndex()): def insertRow(self, macroNode, row=0): self.beginInsertRows(Qt.QModelIndex(), row, row) - self.list.insert(row, macroNode) - if self._max_len is not None and len(self.list) > self._max_len: + if self._max_len is not None and len(self.list) == self._max_len: self.list.pop() + self.list.insert(row, macroNode) self.endInsertRows() return self.index(row) From 1397110c16ae9131576b5f11479fb6a54a41a666 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 13 Mar 2020 23:08:54 +0100 Subject: [PATCH 510/830] Parametrize dump_information with list of elements Dumping of information is implicitly assuming dumping information about moveables. Allow to parametrize it so it could be reused for example for channels. --- src/sardana/macroserver/scan/gscan.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index b26901bd52..6c02ee3458 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1118,7 +1118,7 @@ def stepUp(self, n, step, lstep): except InterruptException: raise except Exception: - self.dump_information(n, step) + self.dump_information(n, step, self.motion.moveable_list) raise self.debug("[ END ] motion") @@ -1213,11 +1213,10 @@ def stepUp(self, n, step, lstep): except Exception: pass - def dump_information(self, n, step): - moveables = self.motion.moveable_list + def dump_information(self, n, step, elements): msg = ["Report: Stopped at step #" + str(n) + " with:"] - for moveable in moveables: - msg.append(moveable.information()) + for element in elements: + msg.append(element.information()) self.macro.info("\n".join(msg)) From 83f30846a7b4f222566170aebb9f7011a0c25aab Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 13 Mar 2020 23:11:41 +0100 Subject: [PATCH 511/830] Dump channels information if acquisition fails in step scan Similarly to dumping information about moveables also dump it for channels in the case the acquisition fails. --- src/sardana/macroserver/scan/gscan.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 6c02ee3458..29ec6fea89 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1157,10 +1157,17 @@ def stepUp(self, n, step, lstep): integ_time = step['integ_time'] # Acquire data self.debug("[START] acquisition") - if self._deterministic_scan: - state, data_line = mg.count_raw() - else: - state, data_line = mg.count(integ_time) + try: + if self._deterministic_scan: + state, data_line = mg.count_raw() + else: + state, data_line = mg.count(integ_time) + except InterruptException: + raise + except Exception: + channels = [self.macro.getExpChannel(ch) for ch in mg.ElementList] + self.dump_information(n, step, channels) + raise for ec in self._extra_columns: data_line[ec.getName()] = ec.read() self.debug("[ END ] acquisition") From 40a6f792edacf0a64b047c573064cf3a48a0afd2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 13 Mar 2020 23:58:41 +0100 Subject: [PATCH 512/830] Fix dump_information usages --- src/sardana/macroserver/scan/gscan.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 29ec6fea89..2704c0ca23 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1139,7 +1139,7 @@ def stepUp(self, n, step, lstep): self.macro.checkPoint() if state != Ready: - self.dump_information(n, step) + self.dump_information(n, step, self.motion.moveable_list) m = "Scan aborted after problematic motion: " \ "Motion ended with %s\n" % str(state) raise ScanException({'msg': m}) @@ -2651,7 +2651,7 @@ def stepUp(self, n, step, lstep): except InterruptException: raise except Exception: - self.dump_information(n, step) + self.dump_information(n, step, motion.moveable_list) raise try: @@ -2660,7 +2660,7 @@ def stepUp(self, n, step, lstep): except InterruptException: raise except Exception: - self.dump_information(n, step) + self.dump_information(n, step, motion.moveable_list) raise self._sum_acq_time += integ_time @@ -2670,7 +2670,7 @@ def stepUp(self, n, step, lstep): m_state, m_positions = motion.readState(), motion.readPosition() if m_state != Ready: - self.dump_information(n, step) + self.dump_information(n, step, motion.moveable_list) m = "Scan aborted after problematic motion: " \ "Motion ended with %s\n" % str(m_state) raise ScanException({'msg': m}) @@ -2699,11 +2699,10 @@ def stepUp(self, n, step, lstep): except Exception: pass - def dump_information(self, n, step): - moveables = self.motion.moveable_list + def dump_information(self, n, step, elements): msg = ["Report: Stopped at step #" + str(n) + " with:"] - for moveable in moveables: - msg.append(moveable.information()) + for element in elements: + msg.append(element.information()) self.macro.info("\n".join(msg)) From 2ed3246baf0eec3177148bfed1eb88516ce492c5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 14 Mar 2020 00:52:28 +0100 Subject: [PATCH 513/830] Dump info also from TriggerGate elements MeasurementGroup may contain TriggerGate elements. Dump info also from them. --- src/sardana/macroserver/scan/gscan.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 2704c0ca23..8996808c27 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -1165,8 +1165,9 @@ def stepUp(self, n, step, lstep): except InterruptException: raise except Exception: - channels = [self.macro.getExpChannel(ch) for ch in mg.ElementList] - self.dump_information(n, step, channels) + names = mg.ElementList + elements = [self.macro.getObj(name) for name in names] + self.dump_information(n, step, elements) raise for ec in self._extra_columns: data_line[ec.getName()] = ec.read() From fa7054bb2e0a5d0cda73357259748cc19f0f6b41 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 14 Mar 2020 00:53:15 +0100 Subject: [PATCH 514/830] Dump information if acquisition fails in count macros --- src/sardana/macroserver/macros/standard.py | 46 +++++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index e4db5736c4..436db6d4b0 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -659,7 +659,16 @@ def _value_to_repr(data): return data -class ct(Macro, Hookable): +class _ct: + + def dump_information(self, elements): + msg = ["Elements ended acquisition with:"] + for element in elements: + msg.append(element.information()) + self.info("\n".join(msg)) + + +class ct(Macro, Hookable, _ct): """Count for the specified time on the measurement group or experimental channel given as second argument (if not given the active measurement group is used)""" @@ -701,7 +710,20 @@ def run(self, integ_time, countable_elem): for preAcqHook in self.getHooks('pre-acq'): preAcqHook() - state, data = self.countable_elem.count(integ_time) + try: + state, data = self.countable_elem.count(integ_time) + except Exception: + if self.countable_elem.type == Type.MeasurementGroup: + names = self.countable_elem.ElementList + elements = [self.getObj(name) for name in names] + self.dump_information(elements) + raise + if state != DevState.ON: + if self.countable_elem.type == Type.MeasurementGroup: + names = self.countable_elem.ElementList + elements = [self.getObj(name) for name in names] + self.dump_information(elements) + raise ValueError("Acquisition ended with {}".format(state)) for postAcqHook in self.getHooks('post-acq'): postAcqHook() @@ -726,7 +748,7 @@ def run(self, integ_time, countable_elem): self.output(line) -class uct(Macro): +class uct(Macro, _ct): """Count on the active measurement group and update""" param_def = [ @@ -785,14 +807,26 @@ def run(self, integ_time, countable_elem): self.print_value = True try: - _, data = self.countable_elem.count(integ_time) - self.setData(Record(data)) + state, data = self.countable_elem.count(integ_time) + except Exception: + if self.countable_elem.type == Type.MeasurementGroup: + names = self.countable_elem.ElementList + elements = [self.getObj(name) for name in names] + self.dump_information(elements) + raise finally: self.finish() + if state != DevState.ON: + if self.countable_elem.type == Type.MeasurementGroup: + names = self.countable_elem.ElementList + elements = [self.getObj(name) for name in names] + self.dump_information(elements) + raise ValueError("Acquisition ended with {}".format(state)) + self.setData(Record(data)) + self.printAllValues() def finish(self): self._clean() - self.printAllValues() def _clean(self): for channel in self.channels: From dad9dd449b6636ec0d58579d02a0830455e33f29 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 13 Mar 2020 23:27:39 +0100 Subject: [PATCH 515/830] Fix dummy C/T and 2D ctrls for unfinished start When StartAll method is not called StateOne, ReadOne and AbortOne raises exception - start_time is not set. Avoid this problem by detecting and dealing especially with this situation. Fixes #1188. --- .../poolcontrollers/DummyCounterTimerController.py | 10 +++++++--- .../pool/poolcontrollers/DummyTwoDController.py | 8 ++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py b/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py index ae1276651a..eab5cf1416 100644 --- a/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py +++ b/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py @@ -139,7 +139,7 @@ def StateOne(self, axis): if self._armed: sta = State.Moving status = "Armed" - elif axis in self.counting_channels: + elif axis in self.counting_channels and self.start_time is not None: channel = self.channels[idx] now = time.time() elapsed_time = now - self.start_time @@ -182,7 +182,7 @@ def ReadAll(self): if self._armed: return # still armed - no trigger/gate arrived yet # if in acquisition then calculate the values to return - if self.counting_channels: + if self.counting_channels and self.start_time is not None: now = time.time() elapsed_time = now - self.start_time for axis, channel in list(self.read_channels.items()): @@ -263,12 +263,16 @@ def _finish(self, elapsed_time, axis=None): AcqSynch.HardwareGate): self._disconnect_hardware_synchronization() self._armed = False + self.start_time = None def AbortOne(self, axis): if axis not in self.counting_channels: return now = time.time() - elapsed_time = now - self.start_time + if self.start_time is not None: + elapsed_time = now - self.start_time + else: + elapsed_time = 0 self._finish(elapsed_time, axis) def GetCtrlPar(self, par): diff --git a/src/sardana/pool/poolcontrollers/DummyTwoDController.py b/src/sardana/pool/poolcontrollers/DummyTwoDController.py index 043d757aed..fdd543b933 100644 --- a/src/sardana/pool/poolcontrollers/DummyTwoDController.py +++ b/src/sardana/pool/poolcontrollers/DummyTwoDController.py @@ -264,7 +264,7 @@ def StateOne(self, axis): if self._armed: sta = State.Moving status = "Armed" - elif axis in self.counting_channels: + elif axis in self.counting_channels and self.start_time is not None: channel = self.channels[idx] now = time.time() elapsed_time = now - self.start_time @@ -342,12 +342,16 @@ def _finish(self, elapsed_time, axis=None): AcqSynch.HardwareGate): self._disconnect_hardware_synchronization() self._armed = False + self.start_time = None def AbortOne(self, axis): if axis not in self.counting_channels: return now = time.time() - elapsed_time = now - self.start_time + if self.start_time is not None: + elapsed_time = now - self.start_time + else: + elapsed_time = 0 self._finish(elapsed_time, axis) def getAmplitude(self, axis): From c341824a3795572f69b3b31a2721cc27f36321ac Mon Sep 17 00:00:00 2001 From: reszelaz Date: Sun, 15 Mar 2020 18:09:44 +0100 Subject: [PATCH 516/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d2fcc2501..ab934ad19c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Remove event filtering in `DynamicPlotManager` (showscan online) (#1299) * Avoid unnecessary creations of DeviceProxies in `ascanct` (#1281) * Adapt to new taurus behavior of `cmd_line_parser` kwarg of `TaurusApplication` (#1306) +* Fix dummy C/T and 2D controller classes in the case the start sequence was interrupted + (#1188, #1309) ### Deprecated From 45cbe19c50a313c7ebcb1ccf12e11576bdc4a1d2 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 16 Mar 2020 08:56:40 +0100 Subject: [PATCH 517/830] Fix flake8 errors --- .../tango/sardana/test/test_measurementgroupconfiguretion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index d654ad0a12..9e99b2ea5b 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -150,7 +150,7 @@ def test_output(self, elements=["_test_ct_1_1", "_test_ct_1_2", def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3", "_test_2d_1_3", - "_test_mt_1_3/position"]): + "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) From 8d5fe6168c24aacb72124b3374fb5cf8939d44a5 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Mon, 16 Mar 2020 09:14:09 +0100 Subject: [PATCH 518/830] Fix flake8 errors --- .../tango/sardana/test/test_measurementgroupconfiguretion.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py index 9e99b2ea5b..6f44b8a15b 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py @@ -518,4 +518,3 @@ def test_ValueRefPattern(self, elements=["_test_2d_1_1", "_test_2d_1_2", finally: mg.cleanUp() self.pool.DeleteElement(mg_name) - From 5742b91241f9b111183702bb514969473e0999db Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 17 Mar 2020 16:42:45 +0100 Subject: [PATCH 519/830] Fix error on declaration --- src/sardana/pool/poolmeasurementgroup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index f794be2760..fa1cd77244 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -667,6 +667,7 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): user_config['controllers'][ctrl_name] = user_config_ctrl = {} ctrl_conf = {} + conf_synch = None # The external controllers should not have synchronizer @@ -682,7 +683,6 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): 'to have timer') else: synchronizer = ctrl_data.get('synchronizer', 'software') - conf_synch = None if synchronizer is None or synchronizer == 'software': ctrl_conf['synchronizer'] = 'software' user_config_ctrl['synchronizer'] = 'software' From 4e4b33298ed18f010b68b9ab6d73fd5c12905f60 Mon Sep 17 00:00:00 2001 From: Roberto Javier Homs Puron Date: Tue, 17 Mar 2020 17:30:29 +0100 Subject: [PATCH 520/830] Return ordered channels name per controller --- src/sardana/taurus/core/tango/sardana/pool.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index d780d204eb..c8aa07c8d9 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1426,9 +1426,11 @@ def set_data(self, data, force=False): channels_names[name] = channel_data label = channel_data['label'] channels_labels[name] = channel_data + index = channel_data['index'] ch_data = {'fullname': channel_name, 'label': label, - 'name': name} + 'name': name, + 'index': index} controllers_channels[ctrl_name].append(ch_data) ##################### @@ -1896,7 +1898,7 @@ def _get_ctrl_for_channel(self, channels_names, unique=False): return result def _get_ctrl_channels(self, ctrl, use_fullname=False): - channels = [] + idx_channel = {} if ctrl not in self.controllers_channels: ctrl = self.controllers_alias[ctrl] channels_datas = self.controllers_channels[ctrl] @@ -1905,7 +1907,12 @@ def _get_ctrl_channels(self, ctrl, use_fullname=False): name = channel_data['fullname'] else: name = channel_data['label'] - channels.append(name) + idx = channel_data['index'] + idx_channel[idx] = name + channels = [] + for idx in sorted(idx_channel): + channels.append(idx_channel[idx]) + return channels def _get_channels_for_element(self, element, use_fullname=False): From 840d05e56fffb49694026d71d95089548450d1e8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 17 Mar 2020 21:36:45 +0100 Subject: [PATCH 521/830] Fix dummy motor velocity In Sardana Motor velocity assumes user unit / s. Dummy controller interprets it in steps / s. Fix it and consider step_per_unit. --- .../pool/poolcontrollers/DummyMotorController.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sardana/pool/poolcontrollers/DummyMotorController.py b/src/sardana/pool/poolcontrollers/DummyMotorController.py index d95667a234..13789d9009 100644 --- a/src/sardana/pool/poolcontrollers/DummyMotorController.py +++ b/src/sardana/pool/poolcontrollers/DummyMotorController.py @@ -148,6 +148,12 @@ def setMaxVelocity(self, vf): def getMaxVelocity(self): return self.max_vel + def setMaxUserVelocity(self, vel): + self.setMaxVelocity(vel * self.step_per_unit) + + def getMaxUserVelocity(self): + return self.getMaxVelocity() / self.step_per_unit + def setAccelerationTime(self, at): """Sets the time to go from minimum velocity to maximum velocity in seconds""" at = float(at) @@ -528,7 +534,7 @@ def AddDevice(self, axis): if self.m[idx] is None: m = Motion() m.setMinVelocity(0) - m.setMaxVelocity(100) + m.setMaxUserVelocity(100) m.setAccelerationTime(2) m.setDecelerationTime(2) m.setCurrentPosition(0) @@ -690,7 +696,7 @@ def AddDevice(self, axis): idx = axis - 1 m = self.m[idx] m.setMinVelocity(0) - m.setMaxVelocity(1) + m.setMaxUserVelocity(1) m.setAccelerationTime(0.01) m.setDecelerationTime(0.01) m.setCurrentPosition(0) @@ -764,7 +770,7 @@ def SetAxisPar(self, axis, name, value): elif name == "base_rate": m.setMinVelocity(value) elif name == "velocity": - m.setMaxVelocity(value) + m.setMaxUserVelocity(value) elif name == "step_per_unit": m.setStepPerUnit(value) @@ -779,7 +785,7 @@ def GetAxisPar(self, axis, name): elif name == "base_rate": v = m.getMinVelocity() elif name == "velocity": - v = m.getMaxVelocity() + v = m.getMaxUserVelocity() elif name == "step_per_unit": v = m.getStepPerUnit() return v From f5ad43f8557f16abc73f4d22f228d2c66fc9942d Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 18 Mar 2020 11:32:49 +0100 Subject: [PATCH 522/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab934ad19c..2233cc94a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Adapt to new taurus behavior of `cmd_line_parser` kwarg of `TaurusApplication` (#1306) * Fix dummy C/T and 2D controller classes in the case the start sequence was interrupted (#1188, #1309) +* Fix dummy motor velocity so it respects steps_per_unit (#1310) ### Deprecated From 9ff5f8be02c2b00ae62843dae4cdb837c36dfcf7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 20 Mar 2020 02:00:29 +0100 Subject: [PATCH 523/830] Fix exception raising (Py3) in dummy mot --- .../pool/poolcontrollers/DummyMotorController.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sardana/pool/poolcontrollers/DummyMotorController.py b/src/sardana/pool/poolcontrollers/DummyMotorController.py index d95667a234..42fea5dd5a 100644 --- a/src/sardana/pool/poolcontrollers/DummyMotorController.py +++ b/src/sardana/pool/poolcontrollers/DummyMotorController.py @@ -112,7 +112,7 @@ def setMinVelocity(self, vi): """ Sets the minimum velocity in ms^-1. A.k.a. base rate""" vi = float(vi) if vi < 0: - raise "Minimum velocity must be >= 0" + raise ValueError("Minimum velocity must be >= 0") self.min_vel = vi @@ -132,7 +132,7 @@ def setMaxVelocity(self, vf): """ Sets the maximum velocity in ms^-1.""" vf = float(vf) if vf <= 0: - raise "Maximum velocity must be > 0" + raise ValueError("Maximum velocity must be > 0") self.max_vel = vf @@ -152,7 +152,7 @@ def setAccelerationTime(self, at): """Sets the time to go from minimum velocity to maximum velocity in seconds""" at = float(at) if at <= 0: - raise "Acceleration time must be > 0" + raise ValueError("Acceleration time must be > 0") self.accel_time = at self.accel = (self.max_vel - self.min_vel) / at @@ -166,7 +166,7 @@ def setDecelerationTime(self, dt): """Sets the time to go from maximum velocity to minimum velocity in seconds""" dt = float(dt) if dt <= 0: - raise "Deceleration time must be > 0" + raise ValueError("Deceleration time must be > 0") self.decel_time = dt self.decel = (self.min_vel - self.max_vel) / dt @@ -180,7 +180,7 @@ def setAcceleration(self, a): """Sets the acceleration in ms^-2""" a = float(a) if a < 0: - raise "Acceleration must be >= 0" + raise ValueError("Acceleration must be >= 0") self.accel = float(a) @@ -195,7 +195,7 @@ def setDeceleration(self, d): """Sets the deceleration in ms^-2""" d = float(d) if d > 0: - raise "Deceleration must be <= 0" + raise ValueError("Deceleration must be <= 0") self.decel = d From bbaf06e609a5a9fb7168f575b575f275b96b2fba Mon Sep 17 00:00:00 2001 From: reszelaz Date: Sat, 21 Mar 2020 11:58:36 +0100 Subject: [PATCH 524/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2233cc94a3..21bf11eafe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This file follows the formats and conventions from [keepachangelog.com] ### Added -* Support to Python >= 3.5 (#1089, #1173, #1201) +* Support to Python >= 3.5 (#1089, #1173, #1201, #1313) * Showscan online based on pyqtgraph (#1285) * multiple plots in the same MultiPlot widget (as opposed to different panels before) * option to group curves by x-axis or individual plot per curve From 2b7525813c8a4059756d266df7620ed9256c97cf Mon Sep 17 00:00:00 2001 From: reszelaz Date: Sat, 21 Mar 2020 12:00:52 +0100 Subject: [PATCH 525/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21bf11eafe..e46a026251 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Measurement groups renaming with `renameelem` macro(#951) * Macro plotting in new versions of ipython and matplotlib require extra call to `pyplot.draw()` to make sure that the plot is refreshed (#1280) +* Allow MacroButton widget to be smaller - minimum size to show the macro name (#1265) * Remove TangoAttribute controllers from Sardana (#181, #1279) * Remove deprecation warning revealed when running test suite (#1267) * Remove event filtering in `DynamicPlotManager` (showscan online) (#1299) From 432c2f425bf2728ca7d299b40081237c9fa479aa Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 21 Mar 2020 12:09:34 +0100 Subject: [PATCH 526/830] Add documentation to the MACROEXECUTOR_MAX_HISTORY --- src/sardana/sardanacustomsettings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py index 90ca023bc8..1182bdd747 100644 --- a/src/sardana/sardanacustomsettings.py +++ b/src/sardana/sardanacustomsettings.py @@ -93,4 +93,8 @@ MS_ENV_SHELVE_BACKEND = None #: macroexecutor maximum number of macros stored in the history +#: - None (or no setting) - unlimited history (may slow down the GUI operation +#: if grows too big) +#: - 0 - history will not be filled +#: - - max number of macros stored in the history MACROEXECUTOR_MAX_HISTORY = 100 From 541a1533030b215ac01e77c2b5066dbc4218a803 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 21 Mar 2020 16:05:31 +0100 Subject: [PATCH 527/830] Fix sphinx warning --- src/sardana/sardanacustomsettings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py index 1182bdd747..fb32f5c73a 100644 --- a/src/sardana/sardanacustomsettings.py +++ b/src/sardana/sardanacustomsettings.py @@ -93,6 +93,8 @@ MS_ENV_SHELVE_BACKEND = None #: macroexecutor maximum number of macros stored in the history +#: Available options: +#: #: - None (or no setting) - unlimited history (may slow down the GUI operation #: if grows too big) #: - 0 - history will not be filled From aa3b0ee2bdccebab94045c1b543dd304ed56d85c Mon Sep 17 00:00:00 2001 From: reszelaz Date: Sat, 21 Mar 2020 18:08:51 +0100 Subject: [PATCH 528/830] Cosmetic change in docs --- src/sardana/sardanacustomsettings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py index fb32f5c73a..97c4859628 100644 --- a/src/sardana/sardanacustomsettings.py +++ b/src/sardana/sardanacustomsettings.py @@ -30,7 +30,7 @@ """ #: UnitTest door name: the door to be used by unit tests. -#: UNITTEST_DOOR_NAME Must be defined for running sardana unittests. +#: UNITTEST_DOOR_NAME must be defined for running sardana unittests. UNITTEST_DOOR_NAME = "door/demo1/1" #: UnitTests Pool DS name: Pool DS to use in unit tests. UNITTEST_POOL_DS_NAME = "unittest1" @@ -92,7 +92,7 @@ #: - "dumb" - worst performance but directly available with Python 3. MS_ENV_SHELVE_BACKEND = None -#: macroexecutor maximum number of macros stored in the history +#: macroexecutor maximum number of macros stored in the history. #: Available options: #: #: - None (or no setting) - unlimited history (may slow down the GUI operation From 1286377934d9d81b51a361e979ad236f246d3579 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Sat, 21 Mar 2020 19:41:57 +0100 Subject: [PATCH 529/830] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e46a026251..360ae08abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,9 @@ This file follows the formats and conventions from [keepachangelog.com] * Use of env and hints in `macro` function decorator (#1239) * PMTV widget not updating the following attributes: limit switches, state and status (#1244) +* Avoid Taurus GUI slowness on startup and changing of perspectives due to too + large macroexecutor history by limitting it to 100 - + configurable with `MACROEXECUTOR_MAX_HISTORY` (#1307) * OutputBlock view option when macros produce outputs at high rate (#1245) * `showscan online` shows only the online trend and not erroneously online and offline (#1260) From 4880ce45e96fb7245a079e7c012a0f693d2de649 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 24 Mar 2020 17:18:57 +0100 Subject: [PATCH 530/830] Fix MacroButton with repeat parameters (#1172) Use ParamParser in MacroButton. --- .../qt/qtgui/extra_macroexecutor/macrobutton.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 3ee561f19e..332622b2c7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -45,6 +45,8 @@ from taurus.core.util.colors import DEVICE_STATE_PALETTE from taurus.qt.qtgui.util.ui import UILoadable +from sardana.util.parser import ParamParser + class DoorStateListener(Qt.QObject): '''A listener of Change and periodic events from a Door State attribute. @@ -289,13 +291,12 @@ def runMacro(self): '''execute the macro with the current arguments''' if self.door is None: return - - # TODO: make macrobutton compatible with macros with advanced usage of - # repeat parameters e.g. multiple repeat parameters, nested repeat - # parameters, etc. - macro_args = shlex.split(' '.join(self.macro_args)) + param_defs = self.door.macro_server.getMacroInfoObj( + self.macro_name).parameters + parser = ParamParser(param_defs) + parameters = parser.parse(" ".join(self.macro_args)) try: - self.door.runMacro(self.macro_name, macro_args) + self.door.runMacro(self.macro_name, parameters) sec_xml = self.door.getRunningXML() # get the id of the current running macro self.macro_id = sec_xml[0].get("id") From 2aa823f597f631c3c2d329c080c6cd9ff4477627 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 24 Mar 2020 20:13:33 +0100 Subject: [PATCH 531/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 360ae08abf..d6b8eaeacc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Reintroduce backwards compatibility for measurement groups' configurations (URIs) created with Taurus 3 (#1266, #1271) * Measurement groups renaming with `renameelem` macro(#951) +* MacroButton with repeat parameters (#1172, #1314) * Macro plotting in new versions of ipython and matplotlib require extra call to `pyplot.draw()` to make sure that the plot is refreshed (#1280) * Allow MacroButton widget to be smaller - minimum size to show the macro name (#1265) From 696382506b437f831480f08c99517f46667a01ad Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 26 Mar 2020 23:01:28 +0100 Subject: [PATCH 532/830] Consider never stopped macros (Fix bug introduced in #1256) Fix typo: _stopped_macro_objs instead of _stopped_macro_obj. When macro was never stopped and its reserved objects were never stopped it won't be in the dict. Then return defaul value to not fall into KeyError. --- src/sardana/macroserver/msmacromanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 9e0ba5df7e..b752027603 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1822,8 +1822,8 @@ def returnObjs(self, macro_obj): """Free the macro reserved objects""" if macro_obj is None: return - # remove stopped objects to not keep reference to them - self._stopped_macro_obj.pop(macro_obj) + # remove eventually stopped objects to not keep reference to them + self._stopped_macro_objs.pop(macro_obj, None) objs = self._reserved_macro_objs.get(macro_obj) if objs is None: return From f7e8d509f0c31edd14c34d1fb90b964f6f1b625d Mon Sep 17 00:00:00 2001 From: Jan Kotanski Date: Fri, 3 Apr 2020 13:20:05 +0200 Subject: [PATCH 533/830] add a check for type keyword in spockms --- src/sardana/spock/spockms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index 0945d51fb9..eca8d0b2c9 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -469,7 +469,7 @@ def processRecordData(self, data): if data is None: return data = data[1] - if data['type'] == 'function': + if 'type' in data.keys() and data['type'] == 'function': func_name = data['func_name'] if func_name.startswith("pyplot."): func_name = self.MathFrontend + "." + func_name From 9a147dfccd254f28e0fdf599dda879b01950634e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Piekarski?= Date: Mon, 27 Apr 2020 14:58:09 +0200 Subject: [PATCH 534/830] fix regscan points repeat Fix regscan points repeat when start region point is the same as start position --- src/sardana/macroserver/macros/examples/scans.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index 95f9317654..7a94381b3a 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -347,7 +347,7 @@ def _generator(self): r][0], self.regions[r][1] positions = numpy.linspace( region_start, region_stop, region_nr_intervals + 1) - if region_start != self.start_pos: + if point_id != 0: # positions must be calculated from the start to the end of the region # but after the first region, the 'start' point must not be # repeated @@ -408,7 +408,7 @@ def _generator(self): r][0], self.regions[r][1] positions = numpy.linspace( region_start, region_stop, region_nr_intervals + 1) - if region_start != self.start_pos: + if point_id != 0: # positions must be calculated from the start to the end of the region # but after the first region, the 'start' point must not be # repeated @@ -473,7 +473,7 @@ def _generator(self): r][0], self.regions[r][1] positions = numpy.linspace( region_start, region_stop, region_nr_intervals + 1) - if region_start != self.start_pos: + if point_id != 0: # positions must be calculated from the start to the end of the region # but after the first region, the 'start' point must not be # repeated From bed455f31d4e7dec7050aa5502c8c0cf1221d4ef Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 30 Apr 2020 17:55:20 +0200 Subject: [PATCH 535/830] Update index.md --- doc/source/sep/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/sep/index.md b/doc/source/sep/index.md index 5749785cce..7c40d204a5 100644 --- a/doc/source/sep/index.md +++ b/doc/source/sep/index.md @@ -29,6 +29,7 @@ Proposals list [SEP16][] | ACCEPTED | Plugins (controllers, macros, etc.) catalogue [SEP17][] | DRAFT | Ongoing acquisition formalization and implementation [SEP18][] | ACCEPTED | Extend acquisition and synchronization concepts for SEP2 needs + [SEP19][] | DRAFT | Refactor plugin system @@ -52,6 +53,7 @@ Proposals list [SEP16]: http://www.sardana-controls.org/sep/?SEP16.md [SEP17]: https://github.com/reszelaz/sardana/blob/sep17/doc/source/sep/SEP17.md [SEP18]: http://www.sardana-controls.org/sep/?SEP18.md +[SEP19]: https://github.com/reszelaz/sardana/blob/sep19/doc/source/sep/SEP19.md [TEP3]: http://www.taurus-scada.org/tep/?TEP3.md [TEP13]: http://www.taurus-scada.org/tep/?TEP13.md From 5a70179f24079f47bfad0a4ac4034bdea13cf7db Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 5 May 2020 17:18:59 +0200 Subject: [PATCH 536/830] Make record data check more strict Custom record data format is not determined. Discard non-function type events in SpockBaseDoor.processRecordData. --- src/sardana/spock/spockms.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py index eca8d0b2c9..123f57d17b 100644 --- a/src/sardana/spock/spockms.py +++ b/src/sardana/spock/spockms.py @@ -469,7 +469,9 @@ def processRecordData(self, data): if data is None: return data = data[1] - if 'type' in data.keys() and data['type'] == 'function': + if (isinstance(data, dict) + and 'type' in data + and data['type'] == 'function'): func_name = data['func_name'] if func_name.startswith("pyplot."): func_name = self.MathFrontend + "." + func_name From 2e2d87a977e71885b16732c348fc37ae2d571b7d Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 5 May 2020 18:10:44 +0200 Subject: [PATCH 537/830] Use utf8_json as default codec of RecordData Tango attribute Macro.sendRecordData makes codec argument optional. This does not work in a Tango server where data must be encoded. Use utf8_json whenever None reaches Tango server layer. --- src/sardana/tango/macroserver/Door.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py index fb1ca618cb..88eb8ccecd 100644 --- a/src/sardana/tango/macroserver/Door.py +++ b/src/sardana/tango/macroserver/Door.py @@ -271,6 +271,8 @@ def on_door_changed(self, event_source, event_type, event_value): event_value = self.calculate_tango_status(event_value) elif name == "recorddata": format, value = event_value + if format is None: + format = "utf8_json" codec = CodecFactory().getCodec(format) event_value = codec.encode(('', value)) else: From b8c9bebc30977eccf159610810dbc78d381ebe25 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 5 May 2020 18:11:13 +0200 Subject: [PATCH 538/830] Update Macro.sendRecordData docstring --- src/sardana/macroserver/macro.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index f062901a95..7a8f58fed4 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -772,7 +772,12 @@ def sendRecordData(self, data, codec=None): """**Macro API**. Sends the given data to the RecordData attribute of the Door - :param data: (sequence) the data to be sent""" + :param data: data to be sent (must be compatible with the codec) + :type data: object + :param codec: codec to encode data (in Tango server None defaults + to "utf8_json") + :type codec: str or None + """ self._sendRecordData(data, codec) def _sendRecordData(self, data, codec=None): From 00f93fd5efcf2f23d4ed8d69bd481272303300c2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 5 May 2020 19:31:33 +0200 Subject: [PATCH 539/830] Update CHANGELOG.md with 2.8.5 hotfix release --- CHANGELOG.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6b8eaeacc..17f19f7b48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,8 +54,6 @@ This file follows the formats and conventions from [keepachangelog.com] * Fix fast operations (motion & acq) by propertly clearing operation context and resetting of acq ctrls dicts (#1300) * Use more efficient way to get terminal size for better printing spock output (#1245, #1268) -* Reintroduce backwards compatibility for measurement groups' configurations (URIs) - created with Taurus 3 (#1266, #1271) * Measurement groups renaming with `renameelem` macro(#951) * MacroButton with repeat parameters (#1172, #1314) * Macro plotting in new versions of ipython and matplotlib require extra call to @@ -87,6 +85,13 @@ This file follows the formats and conventions from [keepachangelog.com] * `online` kwarg in `SpockBaseDoor` constructor (#1260) * `sardana.requirements` (#1185) +## [2.8.5] 2020-04-27 + +### Fixed + +* Reintroduce backwards compatibility for measurement groups' configurations + (URIs) created with Taurus 3 (#1266, #1271) + ## [2.8.4] 2019-11-13 ### Fixed @@ -817,7 +822,8 @@ Main improvements since sardana 1.5.0 (aka Jan15): [keepachangelog.com]: http://keepachangelog.com -[Unreleased]: https://github.com/sardana-org/sardana/compare/2.8.4...HEAD +[Unreleased]: https://github.com/sardana-org/sardana/compare/2.8.5...HEAD +[2.8.5]: https://github.com/sardana-org/sardana/compare/2.8.5...2.8.4 [2.8.4]: https://github.com/sardana-org/sardana/compare/2.8.4...2.8.3 [2.8.3]: https://github.com/sardana-org/sardana/compare/2.8.3...2.8.2 [2.8.2]: https://github.com/sardana-org/sardana/compare/2.8.2...2.8.1 From b868a66131a6f861f7b0b06bf41d41002834137d Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 5 May 2020 19:43:35 +0200 Subject: [PATCH 540/830] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17f19f7b48..f39eba1d7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,10 @@ This file follows the formats and conventions from [keepachangelog.com] * Fix dummy C/T and 2D controller classes in the case the start sequence was interrupted (#1188, #1309) * Fix dummy motor velocity so it respects steps_per_unit (#1310) +* Make handling of `Macro.sendRecordData()` with arbitrary data more robust in Spock + (#1320, #1319) +* Use `utf8_json` as default codec (in Tango) if `Macro.sendRecordData()` does not specify one + (#1320, #1319 ### Deprecated From d866e4c7fe169aa8271970f87fa5719368b63793 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 5 May 2020 23:55:35 +0200 Subject: [PATCH 541/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f39eba1d7e..803bf90de8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,8 @@ This file follows the formats and conventions from [keepachangelog.com] (#1320, #1319) * Use `utf8_json` as default codec (in Tango) if `Macro.sendRecordData()` does not specify one (#1320, #1319 +* Avoid repeating of positions when `regscan`, `reg2scan` and `reg3scan` pass through start + position(s) (#1326) ### Deprecated From 41ff50529087cb7e7e5d9fc4258df8fe0714da60 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 6 May 2020 18:59:48 +0200 Subject: [PATCH 542/830] Avoid Sphinx v3 warnings --- doc/source/conf.py | 10 ++++++++++ doc/source/devel/api/api_test.rst | 4 +--- doc/source/users/spock.rst | 10 +++++----- src/sardana/macroserver/macros/test/base.py | 4 ++-- src/sardana/pool/controller.py | 5 +++-- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 1e92dd0904..ee7f1e6dd8 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -299,3 +299,13 @@ def type_getattr(self, name): 'https://matplotlib.org/': None, 'https://pythonhosted.org/guiqwt/': None, } + + +def _skip_read_attr_hardware(app, what, name, obj, skip, options): + if what == "class" and name == "read_attr_hardware": + return True + + +def setup(app): + # due to tango-controls/pytango#352 we need to exclude read_attr_hardware + app.connect('autodoc-skip-member', _skip_read_attr_hardware) diff --git a/doc/source/devel/api/api_test.rst b/doc/source/devel/api/api_test.rst index 9b6f677f8f..af9b63c182 100644 --- a/doc/source/devel/api/api_test.rst +++ b/doc/source/devel/api/api_test.rst @@ -24,9 +24,7 @@ Macro test API Decorator --------- -.. decorator:: macroTest - - .. autofunction:: macroTest +.. autodecorator:: macroTest diff --git a/doc/source/users/spock.rst b/doc/source/users/spock.rst index d99a283fbb..10420b55d3 100644 --- a/doc/source/users/spock.rst +++ b/doc/source/users/spock.rst @@ -645,17 +645,17 @@ spock console: Using spock as a Tango_ console ------------------------------- -As metioned in the beggining of this chapter, the sardana spock automatically -activates the PyTango_ 's ipython console extension. Therefore all Tango_ +As mentioned in the beginning of this chapter, the sardana spock automatically +activates the PyTango_ 's ipython console extension [#]_. Therefore all Tango_ features are automatically available on the sardana spock console. For example, -creating a :class:`~PyTango.DeviceProxy` will work inside the sardana spock +creating a :class:`tango.DeviceProxy` will work inside the sardana spock console: .. sourcecode:: spock - LAB-01-D01 [1]: tgtest = PyTango.DeviceProxy("sys/tg_test/1") + LAB-01-D01 [1]: tgtest = Device("sys/tg_test/1") - LAB-01-D01 [2]: print( tgtest.state() ) + LAB-01-D01 [2]: print(tgtest.state()) RUNNING .. rubric:: Footnotes diff --git a/src/sardana/macroserver/macros/test/base.py b/src/sardana/macroserver/macros/test/base.py index d463ce6738..1135e432b7 100644 --- a/src/sardana/macroserver/macros/test/base.py +++ b/src/sardana/macroserver/macros/test/base.py @@ -44,7 +44,7 @@ class __NotPassedType(int): def macroTest(klass=None, helper_name=None, test_method_name=None, test_method_doc=None, **helper_kwargs): - """This decorator is an specialization of :function::`taurus.test.insertTest` + """This decorator is an specialization of `taurus.test.insertTest` for macro testing. It inserts test methods from a helper method that may accept arguments. @@ -110,7 +110,7 @@ class FooTest(RunMacroTestCase, unittest.TestCase): class FooTest(RunMacroTestCase, unittest.TestCase): macro_name = 'twice' - .. seealso:: :function::`taurus.test.insertTest` + .. seealso:: `taurus.test.insertTest` """ # recipe to allow decorating with and without arguments diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index e13694c3df..4fde8318a8 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -109,13 +109,14 @@ class Controller(object): :keyword kwargs: """ + #: #: .. deprecated:: 1.0 #: use :attr:`~Controller.ctrl_properties` instead class_prop = {} #: A sequence of :obj:`str` representing the controller features ctrl_features = [] - + #: #: .. deprecated:: 1.0 #: use :attr:`~Controller.axis_attributes` instead ctrl_extra_attributes = {} @@ -1522,7 +1523,7 @@ class IORegisterController(Controller, Readable): """Base class for a IORegister controller. Inherit from this class to implement your own IORegister controller for the device pool. """ - + #: #: .. deprecated:: 1.0 #: use :attr:`~Controller.axis_attributes` instead predefined_values = () From 54d9200bd99412a3b03563a1780781720bcaa82f Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 6 May 2020 19:00:41 +0200 Subject: [PATCH 543/830] update intersphinx_mapping --- doc/source/conf.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index ee7f1e6dd8..1754c99461 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -289,15 +289,15 @@ def type_getattr(self, name): # -- Options for reference to other documentation ------------------------ intersphinx_mapping = { - 'https://docs.python.org/dev': None, - 'https://docs.scipy.org/doc/scipy/reference': None, - 'https://docs.scipy.org/doc/numpy': None, - 'http://ipython.org/ipython-doc/stable/': None, - 'http://pytango.readthedocs.io/en/stable/': None, - 'http://taurus-scada.org': None, - 'http://pyqt.sourceforge.net/Docs/PyQt4/': None, - 'https://matplotlib.org/': None, - 'https://pythonhosted.org/guiqwt/': None, + 'python': ('https://docs.python.org/3.5', None), + 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), + 'numpy': ('https://docs.scipy.org/doc/numpy', None), + 'ipython': ('http://ipython.org/ipython-doc/stable/', None), + 'pytango': ('https://pytango.readthedocs.io/en/stable/', None), + 'taurus': ('http://taurus-scada.org', None), + 'pyqt': ('https://www.riverbankcomputing.com/static/Docs/PyQt5/', None), + 'matplotlib': ('https://matplotlib.org/', None), + 'guiqwt': ('https://pythonhosted.org/guiqwt/', None), } From cdea1d2b3bb5544f2ef7543e381f7e060ed32c07 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 6 May 2020 19:01:21 +0200 Subject: [PATCH 544/830] use default rest role: obj in docs --- doc/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 1754c99461..e367afb403 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -135,7 +135,7 @@ def type_getattr(self, name): exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all documents. -# default_role = None +default_role = "obj" # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True From b1e2434fb14d2f83ae63b635e1855f7a0a3223e2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 6 May 2020 19:08:25 +0200 Subject: [PATCH 545/830] relax intersphinx_mapping to Python 3 --- doc/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index e367afb403..a26c2a43f2 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -289,7 +289,7 @@ def type_getattr(self, name): # -- Options for reference to other documentation ------------------------ intersphinx_mapping = { - 'python': ('https://docs.python.org/3.5', None), + 'python': ('https://docs.python.org/3', None), 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), 'numpy': ('https://docs.scipy.org/doc/numpy', None), 'ipython': ('http://ipython.org/ipython-doc/stable/', None), From ca8f8066d64e820372a8ba6f0d48c85c3d809b29 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 6 May 2020 21:59:01 +0200 Subject: [PATCH 546/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 803bf90de8..5df4364992 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ This file follows the formats and conventions from [keepachangelog.com] (#1320, #1319 * Avoid repeating of positions when `regscan`, `reg2scan` and `reg3scan` pass through start position(s) (#1326) +* Build docs with Sphinx 3 (#1330) ### Deprecated From 7fdb8b47f20034eb6c18b21c5f8cf2bdb450720d Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 8 May 2020 16:31:26 +0200 Subject: [PATCH 547/830] Dump state&status timestamp Dump timestamps so it will be easier to interpret the dumped information. Also refactor a little bit how the eventual state/status information is extracted from the exc_info. --- src/sardana/taurus/core/tango/sardana/pool.py | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index bc59c81148..a5b9d9649a 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -47,6 +47,7 @@ import time import traceback import weakref +from datetime import datetime import numpy import PyTango @@ -554,37 +555,43 @@ def _information(self, tab=' '): indent = "\n" + tab + 10 * ' ' msg = [self.getName() + ":"] try: - state_value = self.stateObj.read().rvalue + t = time.time() + state_time = datetime.fromtimestamp(t).strftime("%H:%M:%S.%f") + # TODO: use expiration_period=float("inf") to always use event + # value (taurus-org/taurus#1105) + state = self.stateObj.read() + state_time = state.time.strftime("%H:%M:%S.%f") # state_value is DevState enumeration (IntEnum) - state = state_value.name.capitalize() + state = state.rvalue.name.capitalize() except DevFailed as df: if len(df.args): state = df.args[0].desc else: e_info = sys.exc_info()[:2] - state = traceback.format_exception_only(*e_info) + state = traceback.format_exception_only(*e_info)[0].rstrip() except: e_info = sys.exc_info()[:2] - state = traceback.format_exception_only(*e_info) - try: - msg.append(tab + " State: " + state) - except TypeError: - msg.append(tab + " State: " + state[0]) + state = traceback.format_exception_only(*e_info)[0].rstrip() + msg.append(tab + " State: " + state + " ({})".format(state_time)) try: - e_info = sys.exc_info()[:2] - status = self.status() - status = status.replace('\n', indent) + t = time.time() + status_time = datetime.fromtimestamp(t).strftime("%H:%M:%S.%f") + # TODO: ideally status should come from the event and no extra + # readout should be made + status = self.read_attribute("status") + status_time = status.time.strftime("%H:%M:%S.%f") + status = status.value.replace('\n', indent) except DevFailed as df: if len(df.args): status = df.args[0].desc else: e_info = sys.exc_info()[:2] - status = traceback.format_exception_only(*e_info) + status = traceback.format_exception_only(*e_info)[0].rstrip() except: e_info = sys.exc_info()[:2] - status = traceback.format_exception_only(*e_info) - msg.append(tab + " Status: " + status) + status = traceback.format_exception_only(*e_info)[0].rstrip() + msg.append(tab + " Status: " + status + " ({})".format(status_time)) return msg From 888f13bf131d162def3c3a90a07d98ad5da70043 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 8 May 2020 17:18:02 +0200 Subject: [PATCH 548/830] Fix flake8 --- src/sardana/taurus/core/tango/sardana/pool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index a5b9d9649a..b2c893239f 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -569,7 +569,7 @@ def _information(self, tab=' '): else: e_info = sys.exc_info()[:2] state = traceback.format_exception_only(*e_info)[0].rstrip() - except: + except Exception: e_info = sys.exc_info()[:2] state = traceback.format_exception_only(*e_info)[0].rstrip() msg.append(tab + " State: " + state + " ({})".format(state_time)) @@ -588,7 +588,7 @@ def _information(self, tab=' '): else: e_info = sys.exc_info()[:2] status = traceback.format_exception_only(*e_info)[0].rstrip() - except: + except Exception: e_info = sys.exc_info()[:2] status = traceback.format_exception_only(*e_info)[0].rstrip() msg.append(tab + " Status: " + status + " ({})".format(status_time)) From 0fae3ebb43c5baef030ce714d634e7de1a8ca64f Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 8 May 2020 18:01:55 +0200 Subject: [PATCH 549/830] Check if model changed only if cmd line args are used One may execute macroexecutor without cmd line args. Then the comparison of model raises IndexError. Compare models only if one specified cmd line args. --- .../qt/qtgui/extra_macroexecutor/macroexecutor.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 73fb650d33..3b75b5e281 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -1083,14 +1083,17 @@ def createMacroExecutor(args): macroExecutor = TaurusMacroExecutor() macroExecutor.setModelInConfig(True) macroExecutor.doorChanged.connect(macroExecutor.onDoorChanged) - settings = macroExecutor.getQSettings() - taurus_config_raw = settings.value("TaurusConfig") - taurus_config = pickle.loads(taurus_config_raw.data()) - oldmodel = taurus_config['__itemConfigurations__']['model'] + load_settings = True if len(args) == 2: macroExecutor.setModel(args[0]) macroExecutor.doorChanged.emit(args[1]) - if args[0] == oldmodel: + settings = macroExecutor.getQSettings() + taurus_config_raw = settings.value("TaurusConfig") + taurus_config = pickle.loads(taurus_config_raw.data()) + oldmodel = taurus_config['__itemConfigurations__']['model'] + if args[0] == oldmodel: + load_settings = False + if load_settings: macroExecutor.loadSettings() return macroExecutor From 0c0ea909e56b607c8ca3d055e06d1c0d131a8d29 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 8 May 2020 18:13:53 +0200 Subject: [PATCH 550/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df4364992..14fec3d5b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ This file follows the formats and conventions from [keepachangelog.com] * support fast scans: update curves at a fix rate (5Hz) * better curve colors and symbols * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) +* Dump info on channels if MG acq fails in step scan, ct and uct (#1308) +* Add timestamp to element's dumped information (#1308) * Instruments creation and configuration in sar_demo (#1198) * Documentation to Taurus Extensions of Sardana Devices: MacroServer part and the whole Sardana part of the Qt Taurus Extensions (#1228, #1233) From c11bc2256f9fe972f4c6585a7b5953705911fd75 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 8 May 2020 18:19:17 +0200 Subject: [PATCH 551/830] Consider case when there is no settings file --- .../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 3b75b5e281..8b36204a69 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -1089,10 +1089,11 @@ def createMacroExecutor(args): macroExecutor.doorChanged.emit(args[1]) settings = macroExecutor.getQSettings() taurus_config_raw = settings.value("TaurusConfig") - taurus_config = pickle.loads(taurus_config_raw.data()) - oldmodel = taurus_config['__itemConfigurations__']['model'] - if args[0] == oldmodel: - load_settings = False + if taurus_config_raw is not None: + taurus_config = pickle.loads(taurus_config_raw.data()) + oldmodel = taurus_config['__itemConfigurations__']['model'] + if args[0] == oldmodel: + load_settings = False if load_settings: macroExecutor.loadSettings() return macroExecutor From 2e7d25de5aa035029dc735aa7d4da42f6e3e04a0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 9 May 2020 17:56:04 +0200 Subject: [PATCH 552/830] Minor correction in docs --- doc/source/users/taurus/macroexecutor.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/source/users/taurus/macroexecutor.rst b/doc/source/users/taurus/macroexecutor.rst index de246737e1..d6bfa18b5c 100644 --- a/doc/source/users/taurus/macroexecutor.rst +++ b/doc/source/users/taurus/macroexecutor.rst @@ -159,8 +159,10 @@ First select favourite macro, button with '-' sign appears enabled. After pressi Using history viewer -------------------- -Once a macreo is used, it gets registered in the history viewer with the same values it has been executed. +Once a macro is used, it gets registered in the history viewer with the same +values it has been executed. -To load a macro from the history viewer, doubleclick on the macro of the history viewer. +To load a macro from the history viewer, click on the macro in the history +viewer. -To remove all history, click on the bin button. \ No newline at end of file +To remove all history, click on the *bin* button. \ No newline at end of file From 6a558c540c33e289ec6aafaad6de437d8f52c1b8 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Sat, 9 May 2020 19:08:25 +0200 Subject: [PATCH 553/830] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14fec3d5b6..02efa7f8a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,9 @@ This file follows the formats and conventions from [keepachangelog.com] resetting of acq ctrls dicts (#1300) * Use more efficient way to get terminal size for better printing spock output (#1245, #1268) * Measurement groups renaming with `renameelem` macro(#951) +* `macroexecutor` correctly loads macro combo box if it was started with server down and + server started afterwards (#599, #1278) +* `TaurusMacroExecutorWidget` does not use _parent model_ feature (#599, #1278) * MacroButton with repeat parameters (#1172, #1314) * Macro plotting in new versions of ipython and matplotlib require extra call to `pyplot.draw()` to make sure that the plot is refreshed (#1280) From cc777b5a61d1137a3212b75e555fb09604e3f724 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 11 May 2020 07:33:39 +0200 Subject: [PATCH 554/830] Minor corrections after review - unify check if encoder widgets were created to always check just the existence of lbl_enc_read - change create_encoder to protected method - don't check the existence of taurusValueBuddy in setModel() - it should be there, at least the rest of this method code assumes it --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 26bba3f34a..e612a3f223 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1060,7 +1060,7 @@ def setExpertView(self, expertView): hw_limits = self.taurusValueBuddy().hasHwLimits() self.btn_lim_neg.setEnabled(hw_limits) self.btn_lim_pos.setEnabled(hw_limits) - if self.lbl_enc_read and self.lbl_enc is not None: + if self.lbl_enc_read is not None: self.lbl_enc.setVisible(False) self.lbl_enc_read.setVisible(False) if expertView and self.taurusValueBuddy().motor_dev is not None: @@ -1077,7 +1077,7 @@ def prepare_button(self, btn): btn.setMaximumSize(25, 25) btn.setText('') - def create_encoder(self): + def _create_encoder(self): if self.taurusValueBuddy().hasEncoder() and self.lbl_enc_read is None: self.lbl_enc = Qt.QLabel('Encoder') self.layout().addWidget(self.lbl_enc, 1, 0) @@ -1097,13 +1097,10 @@ def create_encoder(self): self.lbl_enc_read.setVisible(False) def setModel(self, model): - if hasattr(self, 'taurusValueBuddy'): - self.create_encoder() - try: - self.taurusValueBuddy().expertViewChanged.disconnect( - self.setExpertView) - except TypeError: - pass + if self.taurusValueBuddy().hasEncoder() and self.lbl_enc_read is None: + self._create_encoder() + self.taurusValueBuddy().expertViewChanged.disconnect( + self.setExpertView) if model in (None, ''): TaurusWidget.setModel(self, model) self.lbl_read.setModel(model) @@ -1112,7 +1109,8 @@ def setModel(self, model): return TaurusWidget.setModel(self, model + '/Position') self.lbl_read.setModel(model + '/Position') - if self.lbl_enc_read and self.taurusValueBuddy().motor_dev is not None: + if (self.lbl_enc_read is not None + and self.taurusValueBuddy().motor_dev is not None): self.lbl_enc_read.setModel(model + '/Encoder') # Handle User/Expert view self.setExpertView(self.taurusValueBuddy()._expertView) From 5f0ebefb198328192b9e8d786764845c372ed831 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 11 May 2020 08:01:29 +0200 Subject: [PATCH 555/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02efa7f8a7..fc02b56ecf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ This file follows the formats and conventions from [keepachangelog.com] using context menu option (#1242) * Store PMTV (motor widget) configurations: *expert view* and *write mode* (relative or absolute) permanently as TaurusGUI settings. +* Do not create encoder widget in PMTV if the motod does not have encoder + in order to avoid errors comming from the polling (#209, #1288) * Improve documentation (#1241) * Better macro exception message and hint to use `www` (#1191) * Add basic information to "how to write custom recorder" to From 3ed8fde15b8854d06b4fa45a1484e44144b0539a Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 11 May 2020 09:13:50 +0200 Subject: [PATCH 556/830] Don't use modelInConfig for sub-widgets of macroexecutor See discussion in sardana-org/sardana#1278. --- .../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 8b36204a69..4efadbc5f7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -654,7 +654,6 @@ def __init__(self, parent=None, designMode=False): actionsLayout.addWidget(addToFavouritsButton) self.macroComboBox = MacroComboBox(self) - self.macroComboBox.setModelInConfig(True) self.macroComboBox.setModelColumn(0) actionsLayout.addWidget(self.macroComboBox) stopMacroButton = Qt.QToolButton() @@ -686,13 +685,11 @@ def __init__(self, parent=None, designMode=False): self._favouritesBuffer = None self.favouritesMacrosEditor = FavouritesMacrosEditor(self) self.registerConfigDelegate(self.favouritesMacrosEditor) - self.favouritesMacrosEditor.setModelInConfig(True) self.favouritesMacrosEditor.setFocusPolicy(Qt.Qt.NoFocus) self._historyBuffer = None self.historyMacrosViewer = HistoryMacrosViewer(self) self.registerConfigDelegate(self.historyMacrosViewer) - self.historyMacrosViewer.setModelInConfig(True) self.historyMacrosViewer.setFocusPolicy(Qt.Qt.NoFocus) self.tabMacroListsWidget = Qt.QTabWidget(self) @@ -1071,7 +1068,6 @@ def getQtDesignerPluginInfo(cls): def createMacroExecutorWidget(args): macroExecutor = TaurusMacroExecutorWidget() - macroExecutor.setModelInConfig(True) macroExecutor.doorChanged.connect(macroExecutor.onDoorChanged) if len(args) == 2: macroExecutor.setModel(args[0]) @@ -1081,7 +1077,6 @@ def createMacroExecutorWidget(args): def createMacroExecutor(args): macroExecutor = TaurusMacroExecutor() - macroExecutor.setModelInConfig(True) macroExecutor.doorChanged.connect(macroExecutor.onDoorChanged) load_settings = True if len(args) == 2: From 28b64395ff74eed72ee38e640bd83992f9037d0d Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Tue, 12 May 2020 03:09:13 +0200 Subject: [PATCH 557/830] Register a TaurusValue factory for pool widgets Taurus now provides a new entrypoint for registering "TaurusValue item factories" (the entry point group name is "taurus.qt.taurusform.item_factories") Register one such factory to achieve the same result as with the old T_FORM_CUSTOM_WIDGET_MAP (i.e., using PoolMotorTV, etc. for sardana elements in a TaurusForm) --- setup.py | 12 +++- .../qt/qtgui/extra_pool/formitemfactory.py | 60 +++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py diff --git a/setup.py b/setup.py index 217c544c37..d2cf07598d 100644 --- a/setup.py +++ b/setup.py @@ -83,9 +83,15 @@ def get_release_info(): "showscan = sardana.taurus.qt.qtgui.extra_sardana.showscanonline:main" ] -entry_points = {'console_scripts': console_scripts, - 'gui_scripts': gui_scripts, - } +form_factories = [ + "sdn_pool = sardana.taurus.qt.qtgui.extra_pool.formitemfactory:pool_item_factory" +] + +entry_points = { + 'console_scripts': console_scripts, + 'gui_scripts': gui_scripts, + 'taurus.qt.taurusform.item_factories': form_factories, +} classifiers = [ 'Development Status :: 4 - Beta', diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py new file mode 100644 index 0000000000..b556ed7e32 --- /dev/null +++ b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +# Sardana 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. +## +# Sardana 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 Sardana. If not, see . +## +############################################################################## + +""" +This module provides TaurusValue item factories to be registered as providers +of custom TaurusForm item widgets. +""" + +from . import PoolMotorTV, PoolChannelTV, PoolIORegisterTV + +T_FORM_POOL_WIDGET_MAP = { + "SimuMotor": PoolMotorTV, + "Motor": PoolMotorTV, + "PseudoMotor": PoolMotorTV, + "PseudoCounter": PoolChannelTV, + "CTExpChannel": PoolChannelTV, + "ZeroDExpChannel": PoolChannelTV, + "OneDExpChannel": PoolChannelTV, + "TwoDExpChannel": PoolChannelTV, + "IORegister": PoolIORegisterTV, +} + + +def pool_item_factory(model): + """ + Taurus Value Factory to be registered as a TaurusForm item factory plugin + + :param model: taurus model object + + :return: custom TaurusValue class + """ + # TODO: use sardana element types instead of tango classes + try: + key = model.getDeviceProxy().info().dev_class + klass = T_FORM_POOL_WIDGET_MAP.get[key] + except: + return None + return klass() From 16761d1860a44ba709ff7c710594d97e2b155c48 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 12 May 2020 08:38:36 +0200 Subject: [PATCH 558/830] Fix mistake when getting class from the map --- src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py index b556ed7e32..099c61cde8 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py @@ -54,7 +54,7 @@ def pool_item_factory(model): # TODO: use sardana element types instead of tango classes try: key = model.getDeviceProxy().info().dev_class - klass = T_FORM_POOL_WIDGET_MAP.get[key] + klass = T_FORM_POOL_WIDGET_MAP.get(key) except: return None return klass() From e4fe27a4e2f46c13663af2d8849d9484e7bada1d Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 12 May 2020 10:21:44 +0200 Subject: [PATCH 559/830] Fix flake8 --- setup.py | 2 +- src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index d2cf07598d..f20547dddf 100644 --- a/setup.py +++ b/setup.py @@ -84,7 +84,7 @@ def get_release_info(): ] form_factories = [ - "sdn_pool = sardana.taurus.qt.qtgui.extra_pool.formitemfactory:pool_item_factory" + "sdn_pool = sardana.taurus.qt.qtgui.extra_pool.formitemfactory:pool_item_factory" # noqa ] entry_points = { diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py index 099c61cde8..dc1ca4a5a7 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py @@ -55,6 +55,6 @@ def pool_item_factory(model): try: key = model.getDeviceProxy().info().dev_class klass = T_FORM_POOL_WIDGET_MAP.get(key) - except: + except Exception: return None return klass() From b3d55cd97123a796948175332e4c5bfe74ce232a Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Tue, 12 May 2020 15:18:58 +0200 Subject: [PATCH 560/830] Update formitemfactory.py Ensure that exception is raised if key not found using key indexing instead of .get() because we want to raise an exception if key is not found --- src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py index dc1ca4a5a7..2406bfbd22 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py @@ -54,7 +54,7 @@ def pool_item_factory(model): # TODO: use sardana element types instead of tango classes try: key = model.getDeviceProxy().info().dev_class - klass = T_FORM_POOL_WIDGET_MAP.get(key) + klass = T_FORM_POOL_WIDGET_MAP[key] except Exception: return None return klass() From 9ade1d718a3e4d769bc71c5bebb2181942e09975 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 13 May 2020 15:31:14 +0200 Subject: [PATCH 561/830] Add back. comp. for value_ref_* parameters Measurement groups created with expconf with Sardana prior to Jul20 release has wrongly configured value_ref_enabled/value_ref_pattern parameters for non-referable channels. This is now protected when setting new configuration, and it raises unwanted exception when memorized attribute is recovered. For backwards compatibility recognize when server is starting and in this case just warn the user and raise the exception when setting this parameter by user. --- src/sardana/pool/poolmeasurementgroup.py | 18 +++++++++++++++--- src/sardana/tango/pool/MeasurementGroup.py | 7 ++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index fa1cd77244..483d3b0561 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -454,6 +454,9 @@ def __init__(self, parent=None): self._channel_acq_synch = {} self._ctrl_acq_synch = {} self.changed = False + # provide back. compatibility for value_ref_{enabled,pattern} + # config parameters created with Sardana < 3. + self._value_ref_compat = False def get_acq_synch_by_channel(self, channel): """Return acquisition synchronization configured for this element. @@ -953,9 +956,18 @@ def _fill_channel_data(self, channel, channel_data): channel_data['value_ref_pattern'] = value_ref_pattern elif 'value_ref_enabled' in channel_data or 'value_ref_pattern' in \ channel_data: - msg = 'The channel {} is not referable. You can not set the ' \ - 'enable and/or the pattern parameters.'.format(name) - raise ValueError(msg) + if self._value_ref_compat: + msg = 'value_ref_pattern/value_ref_enabled is deprecated ' \ + 'for non-referable channels since Jul20. Re-apply ' \ + 'configuration in order to upgrade.' + self._parent.warning(msg) + channel_data.pop('value_ref_enabled') + channel_data.pop('value_ref_pattern') + else: + msg = 'The channel {} is not referable. You can not set ' \ + 'the enabled and/or the pattern parameters.'.format( + name) + raise ValueError(msg) # Definitively should be initialized by measurement group # index MUST be here already (asserting this in the following line) channel_data['index'] = channel_data['index'] diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 3a9e4d219e..3ffff6e37f 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -33,7 +33,7 @@ import time from PyTango import Except, DevVoid, DevLong, DevDouble, DevString, \ - DispLevel, DevState, AttrQuality, READ, READ_WRITE, SCALAR + DispLevel, DevState, AttrQuality, READ, READ_WRITE, SCALAR, Util from taurus.core.util.codecs import CodecFactory from taurus.core.util.log import DebugIt @@ -237,6 +237,11 @@ def read_Configuration(self, attr): def write_Configuration(self, attr): data = attr.get_write_value() cfg = CodecFactory().decode(('json', data)) + util = Util.instance() + if util.is_svr_starting(): + self.measurement_group._config._value_ref_compat = True + else: + self.measurement_group._config._value_ref_compat = False self.measurement_group.set_configuration_from_user(cfg) def read_NbStarts(self, attr): From 5f83214f5ac997947909e58fb2b194d2e240bd6d Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 13 May 2020 17:36:57 +0200 Subject: [PATCH 562/830] Use weakref for measurement group --- src/sardana/taurus/core/tango/sardana/pool.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 87f1cc86da..93051a65c4 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1390,7 +1390,7 @@ def getChannelConfigs(mgconfig, ctrls=None, sort=True): class MGConfiguration(object): def __init__(self, mg, data): - self._mg = weakref.ref(mg)() + self._mg = weakref.ref(mg) self._raw_data = None self._pending_event_data = None self._local_changes = False @@ -1958,17 +1958,17 @@ def applyConfiguration(self, timeout=3): raise RuntimeError('The configuration changed on the server ' 'during your changes.') try: - self._mg.setConfiguration(self._raw_data) + self._mg().setConfiguration(self._raw_data) except Exception as e: self._local_changes = False self._pending_event_data = None - data = self._mg.getConfigurationAttrEG().readValue(force=True) + data = self._mg().getConfigurationAttrEG().readValue(force=True) self.set_data(data, force=True) raise e self._local_changes = False self._pending_event_data = None t1 = time.time() - while self._mg._flg_event: + while self._mg()._flg_event: time.sleep(0.01) if (time.time() - t1) >= timeout: raise RuntimeError('Timeout on applying configuration') From 90de645e8bef9178d887d80f49e203a1017308ad Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 13 May 2020 17:55:45 +0200 Subject: [PATCH 563/830] Use threading.Event instead of a loop & sleep --- src/sardana/taurus/core/tango/sardana/pool.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 93051a65c4..771c66c589 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -49,6 +49,7 @@ import json from datetime import datetime import numpy +import threading import PyTango import collections @@ -1957,21 +1958,19 @@ def applyConfiguration(self, timeout=3): self.set_data(self._pending_event_data, force=True) raise RuntimeError('The configuration changed on the server ' 'during your changes.') + mg = self._mg() try: - self._mg().setConfiguration(self._raw_data) + mg.setConfiguration(self._raw_data) except Exception as e: self._local_changes = False self._pending_event_data = None - data = self._mg().getConfigurationAttrEG().readValue(force=True) + data = mg.getConfigurationAttrEG().readValue(force=True) self.set_data(data, force=True) raise e self._local_changes = False self._pending_event_data = None - t1 = time.time() - while self._mg()._flg_event: - time.sleep(0.01) - if (time.time() - t1) >= timeout: - raise RuntimeError('Timeout on applying configuration') + if not mg._flg_event.wait(timeout): + raise RuntimeError('timeout on applying configuration') def _getValueRefEnabledChannels(self, channels=None, use_fullname=False): """get acquisition Enabled channels. @@ -2395,9 +2394,9 @@ def __init__(self, name, **kw): self._last_integ_time = None self.call__init__(PoolElement, name, **kw) + self._flg_event = threading.Event() self.__cfg_attr = self.getAttribute('configuration') self.__cfg_attr.addListener(self.on_configuration_changed) - self._flg_event = False self._value_buffer_cb = None self._value_buffer_channels = None @@ -2432,7 +2431,7 @@ def getConfigurationAttrEG(self): return self._getAttrEG('Configuration') def setConfiguration(self, configuration): - self._flg_event = True + self._flg_event.clear() codec = CodecFactory().getCodec('json') f, data = codec.encode(('', configuration)) self.write_attribute('configuration', data) @@ -2454,7 +2453,7 @@ def on_configuration_changed(self, evt_src, evt_type, evt_value): return self.info("Configuration changed") self._setConfiguration(evt_value.rvalue) - self._flg_event = False + self._flg_event.set() # TODO, should be removed def getChannelsInfo(self): From eae64cff753caa86c8e5f2056593e114e3334acc Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 14 May 2020 07:46:11 +0200 Subject: [PATCH 564/830] Fix setting of channel names as plot axes --- src/sardana/taurus/core/tango/sardana/pool.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 771c66c589..909154ced5 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2133,6 +2133,13 @@ def _setPlotAxesChannels(self, axes, channels_names=None, apply_cfg=True): :param channels_names: (seq) a list of strings indicating the channels to apply the PlotAxes """ + # Validate axes values + for i, value in enumerate(axes): + if value in ['', '']: + continue + else: + axes[i] = self._get_channel_data(value)["full_name"] + if channels_names is None: channels_names = self.channels.keys() @@ -2150,15 +2157,7 @@ def _setPlotAxesChannels(self, axes, channels_names=None, apply_cfg=True): if len(axes) != 2: raise ValueError('The Image Type only allows two axis') - # Validate axes values - for value in axes: - if value in ['', '']: - continue - else: - self._get_channel_data(value) - channel_name = channel_data['name'] - self._set_channels_key('plot_axes', axes, [channel_name], - apply_cfg) + self._set_channels_key('plot_axes', axes, [channel_name], apply_cfg) def _getCtrlsTimer(self, ctrls=None, use_fullname=False): """get the acquisition Timer. From b8db9a38c3381ef682b0aac7501033e01d1420eb Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 14 May 2020 10:25:45 +0200 Subject: [PATCH 565/830] Avoid creating proxies and use taurus validators --- src/sardana/taurus/core/tango/sardana/pool.py | 59 ++++++++----------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 909154ced5..6e9321e3d5 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -58,7 +58,8 @@ from taurus import Factory, Device from taurus.core.taurusbasetypes import TaurusEventType -from taurus.core.tango.tangovalidator import TangoAttributeNameValidator +from taurus.core.tango.tangovalidator import TangoAttributeNameValidator, \ + TangoDeviceNameValidator from taurus.core.util.log import Logger from taurus.core.util.codecs import CodecFactory from taurus.core.util.containers import CaselessDict @@ -1754,16 +1755,6 @@ def _read(self): ret[channel_data['full_name']] = None return ret - def _get_proxy(self, element): - try: - proxy = DeviceProxy(element) - except Exception: - try: - proxy = AttributeProxy(element) - except Exception: - raise KeyError(element) - return proxy - def _get_channel_data(self, channel_name): if channel_name in self.channels_names: return self.channels_names[channel_name] @@ -1771,34 +1762,36 @@ def _get_channel_data(self, channel_name): return self.channels_labels[channel_name] elif channel_name in self.channels: return self.channels[channel_name] - else: - # TODO: Improve this way - proxy = self._get_proxy(channel_name) - try: - alias = proxy.alias() - except Exception: - # The attribute proxy does not have alias. - alias = proxy.name() - names = list(self.channels_names.keys()) + \ - list(self.channels_labels.keys()) - if alias not in names: - raise KeyError('Channel "{0}" is not on the ' - 'MntGrp "{1}"'.format(alias, self.label)) - return self._get_channel_data(alias) + v = TangoDeviceNameValidator() + names = v.getNames(channel_name) + msg = 'element "{}" is not in {}'.format(channel_name, self.label) + if names is None: + v = TangoAttributeNameValidator() + names = v.getNames(channel_name) + if names is None: + raise KeyError(msg) + full_name = names[0] + data = self.channels.get(full_name) + if data is None: + raise KeyError(msg) + return data + def _get_ctrl_data(self, ctrl_name): if ctrl_name in self.controllers_names: return self.controllers_names[ctrl_name] elif ctrl_name in self.controllers: return self.controllers[ctrl_name] - else: - # TODO: Improve this way - proxy = self._get_proxy(ctrl_name) - alias = proxy.alias() - if alias not in self.controllers_names: - raise KeyError('Controller "{0}" is not on the ' - 'MntGrp "{1}"'.format(alias, self.label)) - return self._get_ctrl_data(alias) + v = TangoDeviceNameValidator() + names = v.getNames(ctrl_name) + msg = 'element "{}" is not in {}'.format(ctrl_name, self.label) + if names is None: + raise KeyError(msg) + full_name = names[0] + data = self.controllers.get(full_name) + if data is None: + raise KeyError(msg) + return data def _set_channels_key(self, key, value, channels_names=None, apply_cfg=True): From 28940b868ca9566057e49aaa6ccd6b0e0ef18ea7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 14 May 2020 11:46:56 +0200 Subject: [PATCH 566/830] Reintroduce MeasurementGroup methods and mark some as deprecated In order to eliminate __getattr__ from the MeasurementGroup class reintroduce some methods moved to the MGConfiguration in order to maintain backwards compatibility. Mark some of them as deprecated. --- src/sardana/taurus/core/tango/sardana/pool.py | 184 ++++++++++++------ 1 file changed, 125 insertions(+), 59 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 6e9321e3d5..e77847dc09 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2290,54 +2290,40 @@ def _setCtrlsSynchronizer(self, synchronizer, ctrls=None, apply_cfg=True): synchronizer = sync.fullname self._set_ctrls_key('synchronizer', synchronizer, ctrls, apply_cfg) - def getTimerName(self): - return self.getTimer()['name'] + def _getTimerName(self): + return self._getTimer()['name'] - def getTimer(self): + def _getTimer(self): return self.channels[self.timer] - def getTimerValue(self): - return self.getTimerName() + def _getTimerValue(self): + return self._getTimerName() - def getMonitorName(self): - return self.getMonitor()['name'] + def _getMonitorName(self): + return self._getMonitor()['name'] - def getMonitor(self): + def _getMonitor(self): return self.channels[self.monitor] def getValues(self, parallel=True): return self.read(parallel=parallel) - def setTimer(self, timer, apply_cfg=True): - """ - Set the Global Timer to the measurement group, also it changes the - timer in the controllers with the previous timer. - - :param timer: timer name - """ - result = self._get_ctrl_for_channel([timer], unique=True) - - for timer, ctrl in result.items(): - self._local_changes = True - self._raw_data['timer'] = timer - self._set_ctrls_key('timer', timer, [ctrl], apply_cfg) - - def getCounters(self): + def _getCounters(self): return [c for c in self.getChannels() if c['full_name'] != self.timer] - def getChannelNames(self): + def _getChannelNames(self): return [ch['name'] for ch in self.getChannels()] - def getCounterNames(self): + def _getCounterNames(self): return [ch['name'] for ch in self.getCounters()] - def getChannelLabels(self): + def _getChannelLabels(self): return [ch['label'] for ch in self.getChannels()] - def getCounterLabels(self): + def _getCounterLabels(self): return [ch['label'] for ch in self.getCounters()] - def getChannel(self, name): + def _getChannel(self, name): return self.channels[name] def getChannelsEnabledInfo(self): @@ -2354,23 +2340,33 @@ def getChannelsEnabledInfo(self): def getCountersInfo(self): return self.getCountersInfoList() - def enableChannels(self, channels, apply_cfg=True): - """ - Enable acquisition of the indicated channels. + def setTimer(self, timer, apply_cfg=True): + """DEPRECATED: Set the Global Timer to the measurement group. - :param channels: (seq) a sequence of strings indicating - channel names - """ - self._setEnabledChannels(True, channels, apply_cfg) + Also it changes the timer in the controllers with the previous timer. - def disableChannels(self, channels, apply_cfg=True): + :param timer: timer name """ - Disable acquisition of the indicated channels. + self._mg().warning("setTimer() is deprecated since Jul20. " + "Global measurement group timer does not exist") + result = self._get_ctrl_for_channel([timer], unique=True) - :param channels: (seq) a sequence of strings indicating - channel names - """ - self._setEnabledChannels(False, channels, apply_cfg) + for timer, ctrl in result.items(): + self._local_changes = True + self._raw_data['timer'] = timer + self._set_ctrls_key('timer', timer, [ctrl], apply_cfg) + + def getTimer(self): + """DEPRECATED""" + self._mg().warning("getTimer() is deprecated since Jul20. " + "Global measurement group timer does not exist") + return self._getTimer() + + def getMonitor(self): + """DEPRECATED""" + self._mg().warning("getMonitor() is deprecated since Jul20. " + "Global measurement group monitor does not exist") + return self._getMonitor() def __repr__(self): return json.dumps(self._raw_data, indent=4, sort_keys=True) @@ -2405,16 +2401,6 @@ def cleanUp(self): f = self.factory() f.removeExistingAttribute(self.__cfg_attr) - def __getattr__(self, item): - try: - return PoolElement.__getattr__(self, item) - except Exception: - try: - return self._configuration.__getattribute__(item) - except Exception: - raise AttributeError("'{0}' object has not attribute " - "'{1}'".format('MeasurementGroup', item)) - def _create_str_tuple(self): channel_names = ", ".join(self.getChannelNames()) return self.getName(), self.getTimerName(), channel_names @@ -2447,12 +2433,6 @@ def on_configuration_changed(self, evt_src, evt_type, evt_value): self._setConfiguration(evt_value.rvalue) self._flg_event.set() - # TODO, should be removed - def getChannelsInfo(self): - self.warning('Deprecation warning: you should use ' - '"getChannelsInfoList" instead of "getChannelsInfo"') - return self.getConfiguration().getChannelsInfoList() - def getValueBuffers(self): value_buffers = [] for channel_info in self.getChannels(): @@ -3025,8 +3005,8 @@ def getSynchronizer(self, *elements, ret_full_name=False, configurations :rtype: dict(str, str) """ - # TODO: Implement solution to set the timer per channel when it is - # allowed. + # TODO: Implement solution to set the synchronizer per channel when it + # is allowed. ctrls = self._get_ctrl_for_elements(elements) config = self.getConfiguration() ctrls_sync = config._getCtrlsSynchronizer(ctrls, @@ -3037,6 +3017,92 @@ def getSynchronizer(self, *elements, ret_full_name=False, return self._get_value_per_channel(config, ctrls_sync, use_fullname=ret_full_name) + ######################################################################### + # TODO: review the following API + + def getChannelsEnabledInfo(self): + """Returns information about **only enabled** channels present in the + measurement group in a form of ordered, based on the channel index, + list. + :return: list with channels info + :rtype: list + """ + return self.getConfiguration().getChannelsInfoList(only_enabled=True) + + def getCountersInfo(self): + return self.getConfiguration().getCountersInfoList() + + def getValues(self, parallel=True): + return self.getConfiguration().getValues(parallel) + + def getChannels(self): + return self.getConfiguration().getChannels() + + def getCounters(self): + return self.getConfiguration()._getCounters() + + def getChannelNames(self): + return self.getConfiguration()._getChannelNames() + + def getCounterNames(self): + return self.getConfiguration()._getCounterNames() + + def getChannelLabels(self): + return self.getConfiguration()._getChannelLabels() + + def getCounterLabels(self): + return self.getConfiguration()._getCounterLabels() + + def getChannel(self, name): + return self.getConfiguration()._getChannel(name) + + def getChannelInfo(self, name): + return self.getConfiguration().getChannelInfo(name) + + ######################################################################### + + def getChannelsInfo(self): + """DEPRECATED""" + self.warning('Deprecation warning: you should use ' + '"getChannelsInfoList" instead of "getChannelsInfo"') + return self.getConfiguration().getChannelsInfoList() + + def getMonitorName(self): + """DEPRECATED""" + self.warning("getMonitorName() is deprecated since Jul20. " + "Global measurement group monitor does not exist.") + return self.getConfiguration()._getMonitorName() + + def getTimerName(self): + """DEPRECATED""" + self.warning("getTimerName() is deprecated since Jul20. " + "Global measurement group timer does not exist.") + return self.getConfiguration()._getTimerName() + + def getTimerValue(self): + """DEPRECATED""" + self.warning("getTimerValue() is deprecated since Jul20. " + "Global measurement group timer does not exist.") + return self.getConfiguration()._getTimerValue() + + def enableChannels(self, channels): + '''DEPRECATED: Enable acquisition of the indicated channels. + :param channels: (seq) a sequence of strings indicating + channel names + ''' + self.warning("enableChannels() in deprecated since Jul20. " + "Use setEnabled() instead.") + self.setEnabled(True, *channels) + + def disableChannels(self, channels): + '''Disable acquisition of the indicated channels. + :param channels: (seq) a sequence of strings indicating + channel names + ''' + self.warning("enableChannels() in deprecated since Jul20. " + "Use setEnabled() instead.") + self.setEnabled(False, *channels) + # NbStarts Methods def getNbStartsObj(self): return self._getAttrEG('NbStarts') From ec7347e40e99f38babd176e466e494dc6a613b59 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 14 May 2020 15:57:53 +0200 Subject: [PATCH 567/830] Minor corrections in docstrings --- src/sardana/pool/poolmeasurementgroup.py | 15 +++++----- src/sardana/taurus/core/tango/sardana/pool.py | 28 +++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 483d3b0561..6a8e7dd9e2 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -601,16 +601,15 @@ def get_configuration_for_user(self): return self._user_config def set_configuration_from_user(self, cfg, to_fqdn=True): - """ - Load measurement configuration from serializable data structure. + """Set measurement configuration from serializable data structure. - The method will validate the configuration and raises exceptions in - case of fails. The configuration will be different for each type of - channel and controller, For that reason the configuration can have - different keys and values according to the element. + Setting of the configuration includes the validation process. Setting + of invalid configuration raises an exception hence it is not necessary + that the client application does the validation. - The client should not validate the keys/values, any validation must be - included in this method. + The configuration parameters for given channels/controllers may differ + depending on their types e.g. 0D channel does not support timer + parameter while C/T does. """ pool = self._parent.pool diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index e77847dc09..da445929eb 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2832,9 +2832,11 @@ def _get_value_per_channel(self, config, ctrls_values, use_fullname=False): def setTimer(self, timer, *elements, apply=True): """Set the timer configuration for the given channels of the same - controller. NOTE: The current configuration does not allow to set - the different value per channel of the same controller, it allows to - set per controller. + controller. + + .. note: Currently the controller's timer must be unique. Hence this + method will set it for the whole controller regardless of the + ``elements`` argument. Configuration by default is directly applied on the server. Since setting the configuration means passing to the server all the @@ -2893,13 +2895,15 @@ def getTimer(self, *elements, ret_full_name=False, ret_by_ctrl=False): def setMonitor(self, monitor, *elements, apply=True): """Set the monitor configuration for the given channels of the same - controller. NOTE: The current configuration does not allow to set - the different value per channel of the same controller, it allows to - set per controller. + controller. + + .. note: Currently the controller's monitor must be unique. + Hence this method will set it for the whole controller regardless of + the ``elements`` argument. Configuration by default is directly applied on the server. Since setting the configuration means passing to the server all the - configuration paramters of the measurement group at once this + configuration parameters of the measurement group at once this behavior can be changed with the *apply* argument and we can keep the configuration changes only locally. This is useful when we want to change more then one parameter, in this case only the setting of @@ -2915,7 +2919,7 @@ def setMonitor(self, monitor, *elements, apply=True): :type apply: bool """ config = self.getConfiguration() - # TODO: Implement solution to set the timer per channel when it is + # TODO: Implement solution to set the moniotor per channel when it is # allowed. config._setCtrlsMonitor([monitor], apply_cfg=apply) @@ -2955,9 +2959,11 @@ def getMonitor(self, *elements, ret_full_name=False, ret_by_ctrl=False): def setSynchronizer(self, synchronizer, *elements, apply=True): """Set the synchronizer configuration for the given channels or - controller. NOTE: The current configuration does not allow to set - the different value per channel of the same controller, it allows to - set per controller. + controller. + + .. note: Currently the controller's synchronizer must be unique. + Hence this method will set it for the whole controller regardless of + the ``elements`` argument. Configuration by default is directly applied on the server. Since setting the configuration means passing to the server all the From e98edddacda48ff954f7529650b22dbbe8e9649c Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 14 May 2020 16:01:20 +0200 Subject: [PATCH 568/830] Rename test_measgrpconf module --- ...{test_measurementgroupconfiguretion.py => test_measgrpconf.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/sardana/taurus/core/tango/sardana/test/{test_measurementgroupconfiguretion.py => test_measgrpconf.py} (100%) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py b/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py similarity index 100% rename from src/sardana/taurus/core/tango/sardana/test/test_measurementgroupconfiguretion.py rename to src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py From 09fd7b40c8f87d98dbb44f5120b499c52b8ecfdf Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 14 May 2020 16:05:35 +0200 Subject: [PATCH 569/830] fix flake8 --- src/sardana/pool/poolmeasurementgroup.py | 6 +++--- src/sardana/taurus/core/tango/sardana/pool.py | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 6a8e7dd9e2..97f0907a16 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -963,9 +963,9 @@ def _fill_channel_data(self, channel, channel_data): channel_data.pop('value_ref_enabled') channel_data.pop('value_ref_pattern') else: - msg = 'The channel {} is not referable. You can not set ' \ - 'the enabled and/or the pattern parameters.'.format( - name) + msg = ('The channel {} is not referable. You can not set ' + 'the enabled and/or the pattern parameters.').format( + name) raise ValueError(msg) # Definitively should be initialized by measurement group # index MUST be here already (asserting this in the following line) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index da445929eb..a8653e0f30 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1776,7 +1776,6 @@ def _get_channel_data(self, channel_name): raise KeyError(msg) return data - def _get_ctrl_data(self, ctrl_name): if ctrl_name in self.controllers_names: return self.controllers_names[ctrl_name] @@ -2359,13 +2358,13 @@ def setTimer(self, timer, apply_cfg=True): def getTimer(self): """DEPRECATED""" self._mg().warning("getTimer() is deprecated since Jul20. " - "Global measurement group timer does not exist") + "Global measurement group timer does not exist") return self._getTimer() def getMonitor(self): """DEPRECATED""" self._mg().warning("getMonitor() is deprecated since Jul20. " - "Global measurement group monitor does not exist") + "Global measurement group monitor does not exist") return self._getMonitor() def __repr__(self): From aa0a7f333197481875c8776d822e354dd16e94c5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 14 May 2020 16:05:47 +0200 Subject: [PATCH 570/830] Update CHANGELOG --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc02b56ecf..81770d7cd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ This file follows the formats and conventions from [keepachangelog.com] * new showscan console script * support fast scans: update curves at a fix rate (5Hz) * better curve colors and symbols +* Measurement group (Taurus extension) configuration API with methods to + set/get: enabled, output, plot type, plot axes, timer, monitor, synchronizer, + value ref enabled, value ref pattern parameters(#867) * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) * Dump info on channels if MG acq fails in step scan, ct and uct (#1308) * Add timestamp to element's dumped information (#1308) @@ -85,10 +88,15 @@ This file follows the formats and conventions from [keepachangelog.com] ### Deprecated * `DoorDebug` widget - use `DoorOutput` with enabled debugging (#1242) +* Global measurement group timer/monitor on all levels (#867) ### Changed -* requirements are no longer checked when importing sardana (#1185) +* Requirements are no longer checked when importing sardana (#1185) +* Measurement group (Taurus extension) configuration API methods, known in + the old sense for setting a global measurement group timer/monitor: + `getTimer()`, `setTimer()`, `getMonitor()` were moved to `MGConfiguration` + class and are deprecated (#867) ### Removed @@ -99,6 +107,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `online` kwarg in `SpockBaseDoor` constructor (#1260) * `sardana.requirements` (#1185) + ## [2.8.5] 2020-04-27 ### Fixed From 76c556ac16810fcca880416f44c182c9224a0d99 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 14 May 2020 16:11:35 +0200 Subject: [PATCH 571/830] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81770d7cd2..74edba31fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,6 +89,8 @@ This file follows the formats and conventions from [keepachangelog.com] * `DoorDebug` widget - use `DoorOutput` with enabled debugging (#1242) * Global measurement group timer/monitor on all levels (#867) +* `value_ref_enabled` and `value_ref_pattern` measurement group parameters + for non-referable channels (#867) ### Changed From fb04a7b6d54d55535ec8c9594d12124f2a0159fe Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 14 May 2020 16:56:19 +0200 Subject: [PATCH 572/830] Fix sphinx warnings --- src/sardana/taurus/core/tango/sardana/pool.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index a8653e0f30..0cee12e00e 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2834,8 +2834,8 @@ def setTimer(self, timer, *elements, apply=True): controller. .. note: Currently the controller's timer must be unique. Hence this - method will set it for the whole controller regardless of the - ``elements`` argument. + method will set it for the whole controller regardless of the + ``elements`` argument. Configuration by default is directly applied on the server. Since setting the configuration means passing to the server all the @@ -2897,8 +2897,8 @@ def setMonitor(self, monitor, *elements, apply=True): controller. .. note: Currently the controller's monitor must be unique. - Hence this method will set it for the whole controller regardless of - the ``elements`` argument. + Hence this method will set it for the whole controller regardless of + the ``elements`` argument. Configuration by default is directly applied on the server. Since setting the configuration means passing to the server all the @@ -2961,8 +2961,8 @@ def setSynchronizer(self, synchronizer, *elements, apply=True): controller. .. note: Currently the controller's synchronizer must be unique. - Hence this method will set it for the whole controller regardless of - the ``elements`` argument. + Hence this method will set it for the whole controller regardless of + the ``elements`` argument. Configuration by default is directly applied on the server. Since setting the configuration means passing to the server all the @@ -3092,17 +3092,19 @@ def getTimerValue(self): def enableChannels(self, channels): '''DEPRECATED: Enable acquisition of the indicated channels. + :param channels: (seq) a sequence of strings indicating - channel names + channel names ''' self.warning("enableChannels() in deprecated since Jul20. " "Use setEnabled() instead.") self.setEnabled(True, *channels) def disableChannels(self, channels): - '''Disable acquisition of the indicated channels. + '''DEPRECATED: Disable acquisition of the indicated channels. + :param channels: (seq) a sequence of strings indicating - channel names + channel names ''' self.warning("enableChannels() in deprecated since Jul20. " "Use setEnabled() instead.") From ed4aa26b97840b601b00b847f41a77293133081b Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 09:02:09 +0200 Subject: [PATCH 573/830] Remove old macro proposal --- src/sardana/macroserver/macros/expconf.py | 187 ---------------------- 1 file changed, 187 deletions(-) diff --git a/src/sardana/macroserver/macros/expconf.py b/src/sardana/macroserver/macros/expconf.py index 4dc62347d8..e69de29bb2 100644 --- a/src/sardana/macroserver/macros/expconf.py +++ b/src/sardana/macroserver/macros/expconf.py @@ -1,187 +0,0 @@ -import time - -from sardana.macroserver.macro import Macro, Type, ParamRepeat - - -class MGManager(object): - """ - Class to manages the measurement group - """ - - def __init__(self, macro_obj, mnt_grp, channels=None): - self.macro = macro_obj - self.mnt_grp = mnt_grp - self.__filterMntChannels(channels) - - def __filterMntChannels(self, channels): - # Check if the channels exit in the mntGrp - self.all_channels_names = self.mnt_grp.getChannelNames() - if channels is None: - return - channels_names = [] - for channel in channels: - if isinstance(channel, str): - channel_name = channel - else: - channel_name = channel.name - - if channel_name in self.all_channels_names: - channels_names.append(channel_name) - else: - msg = 'The channel {0} is not in {1}'.format(channel_name, - self.mnt_grp) - self.macro.warning(msg) - self.channels_names = channels_names - - def enable_channels(self): - self.mnt_grp.enableChannels(self.channels_names) - self.macro.output('Channels enabled') - - def enable_only_channels(self): - dis_ch = list(set(self.all_channels_names) - set(self.channels_names)) - self.disable_only_channels(dis_ch) - self.macro.output('Enabled only the selected channels') - - def disable_channels(self): - self.mnt_grp.disableChannels(self.channels_names) - self.macro.output('Channels disabled') - - def disable_only_channels(self, dis_ch=None): - self.mnt_grp.enableChannels(self.all_channels_names) - time.sleep(0.2) - if dis_ch is None: - dis_ch = self.channels_names - self.mnt_grp.disableChannels(dis_ch) - self.macro.output('Disable only the selected channels') - - def enable_all(self): - self.mnt_grp.enableChannels(self.all_channels_names) - self.macro.output('Enable all the channels') - - def status(self): - out_line = '{0:<15} {1:^10} {2:^10} {3:^10} {4:^10}' - self.macro.output(out_line.format('Channel', 'Enabled', 'Plot_type', - 'Plot axes', 'Output')) - for channel in self.mnt_grp.getChannels(): - name = channel['name'] - enabled = ['False', 'True'][channel['enabled']] - plot_type = ['No', 'Spectrum', 'Image'][channel['plot_type']] - plot_axis = channel['plot_axes'] - output = ['False', 'True'][channel['output']] - self.macro.output(out_line.format(name, enabled, plot_type, - plot_axis, output)) - - -class meas_enable_ch(Macro): - """ - Enable the Counter Timers selected - - """ - - param_def = [ - ['MeasurementGroup', Type.MeasurementGroup, None, - "Measurement Group to work"], - ['ChannelState', - ParamRepeat(['channel', Type.ExpChannel, None, 'Channel to change ' - 'state'], min=1), - None, 'List of channels to Enable'], - ] - - def run(self, mntGrp, channels): - mg_manager = MGManager(self, mntGrp, channels) - mg_manager.enable_channels() - - -class meas_enable_ch_only(Macro): - """ - Enable the Counter Timers selected - - """ - - param_def = [ - ['MeasurementGroup', Type.MeasurementGroup, None, - "Measurement Group to work"], - ['ChannelState', - ParamRepeat(['channel', Type.ExpChannel, None, 'Channel to change ' - 'state'], min=1), - None, 'List of channels to Enable'], - ] - - def run(self, mntGrp, channels): - mg_manager = MGManager(self, mntGrp, channels) - mg_manager.enable_only_channels() - - -class meas_disable_ch(Macro): - """ - Enable the Counter Timers selected - - """ - - param_def = [ - ['MeasurementGroup', Type.MeasurementGroup, None, - "Measurement Group to work"], - ['ChannelState', - ParamRepeat(['channel', Type.ExpChannel, None, 'Channel to change ' - 'state'], min=1), - None, 'List of channels to Enable'], - ] - - def run(self, mntGrp, channels): - mg_manager = MGManager(self, mntGrp, channels) - mg_manager.disable_channels() - - -class meas_disable_ch_only(Macro): - """ - Enable the Counter Timers selected - - """ - - param_def = [ - ['MeasurementGroup', Type.MeasurementGroup, None, - "Measurement Group to work"], - ['ChannelState', - ParamRepeat(['channel', Type.ExpChannel, None, 'Channel to change ' - 'state'], min=1), - None, 'List of channels to Enable'], - ] - - def run(self, mntGrp, channels): - mg_manager = MGManager(self, mntGrp, channels) - mg_manager.disable_only_channels() - - -class meas_enable_all(Macro): - """ - Enable all counter channels of the measurement group - """ - - param_def = [ - ['MeasurementGroup', Type.MeasurementGroup, None, "Measurement"], ] - - def run(self, mntGrp): - mg_manager = MGManager(self, mntGrp) - mg_manager.enable_all() - - -class meas_status(Macro): - """ - Shows the current configuration of the measurementGroup, - if the parameter is empty it shows the state of the ActiveMeasurementGroup - """ - param_def = [ - ['MeasurementGroup', Type.MeasurementGroup, None, "Measurement"], ] - - def run(self, mntGrp): - mg_manager = MGManager(self, mntGrp) - mg_manager.status() - - -class select_mntGrp(Macro): - param_def = [ - ['mntGrp', Type.MeasurementGroup, None, 'mntGroup name']] - - def run(self, mntGrp): - self.setEnv('ActiveMntGrp', str(mntGrp)) - self.info("Active Measurement Group : %s" % str(mntGrp)) From ee07eedfbc3601933eb9ed01afdb87d2eefe1d36 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 09:02:49 +0200 Subject: [PATCH 574/830] Add expconf meas configuration macros --- src/sardana/macroserver/macros/expconf.py | 228 ++++++++++++++++++++++ 1 file changed, 228 insertions(+) diff --git a/src/sardana/macroserver/macros/expconf.py b/src/sardana/macroserver/macros/expconf.py index e69de29bb2..efd7f14ed3 100644 --- a/src/sardana/macroserver/macros/expconf.py +++ b/src/sardana/macroserver/macros/expconf.py @@ -0,0 +1,228 @@ +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +# Sardana 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. +## +# Sardana 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 Sardana. If not, see . +## +############################################################################## + +"""Experiment configuration related macros""" + +__all__ = ["get_meas", "get_meas_conf", "set_meas", "set_meas_conf"] + +__docformat__ = 'restructuredtext' + +from collections import OrderedDict + +from taurus.console import Alignment +from taurus.console.list import List + +from sardana.macroserver.msexception import UnknownEnv +from sardana.taurus.core.tango.sardana import PlotType +from sardana.macroserver.macro import macro, Type, Optional + + +def sanitizer(values): + return ["n/a" if v is None else v for v in values] + + +def plot_type_sanitizer(values): + return [PlotType.whatis(v) for v in values] + + +def plot_axes_sanitizer(values): + return ["n/a" if len(v) == 0 else v[0] for v in values] + + +def plot_axes_validator(value): + value = value.lower() + if value in ("idx", ""): + value = [""] + elif value in ("mov", ""): + value = [""] + return value + + +def bool_validator(value): + value = value.lower() + if value in ['true', '1']: + value = True + elif value in ['false', '0']: + value = False + else: + raise ValueError('{0} is not a boolean'.format(value)) + return value + + +# if sanitizers and validators evolve to sth too complicated refactor this +# to use classes +parameter_map = OrderedDict([ + ("enabled", ("Enabled", None, bool_validator)), + ("output", ("Output", None, bool_validator)), + ("plottype", ("PlotType", plot_type_sanitizer, None)), + ("plotaxes", ("PlotAxes", plot_axes_sanitizer, plot_axes_validator)), + ("timer", ("Timer", sanitizer, None)), + ("monitor", ("Monitor", sanitizer, None)), + ("synchronizer", ("Synchronizer", sanitizer, None)), + ("valuerefenabled", ("ValueRefEnabled", sanitizer, bool_validator)), + ("valuerefpattern", ("ValueRefPattern", sanitizer, None)) +]) + + +simple_parameters = ("enabled", "plottype", "plotaxes") + + +@macro([ + ["detail", Type.String, Optional, + "Detail level of parameters. If omitted or \"simple\" then " + "get simple parameters. If \"all\" then get all parameters."], + ["meas_grp", Type.MeasurementGroup, Optional, "Measurement group"] +]) +def get_meas_conf(self, detail, meas_grp): + """Print measurement group configuration in form of a table. + + Examples of usage: + + >>> get_meas_conf # get simple configuration + >>> get_meas_conf all # get complete configuration + >>> get_meas_conf simple mntgrp01 # get mntgrp01 simple configuration + >>> get_meas_conf all mntgrp01 # get mntgrp01 complete configuration + """ + if detail is None or detail == "simple": + parameters = simple_parameters + elif detail == "all": + parameters = parameter_map.keys() + else: + raise ValueError("wrong detail level: {}".format(detail)) + if meas_grp is None: + meas_grp = self.getEnv("ActiveMntGrp") + self.print("ActiveMntGrp = {}".format(meas_grp)) + meas_grp = self.getMeasurementGroup(meas_grp) + col_headers = ["Channel"] + width = [-1] + align = [Alignment.Right] + cols = [] + for parameter in parameters: + parameter, sanitizer, _ = parameter_map[parameter] + getter = getattr(meas_grp, "get" + parameter) + ret = getter() + if len(cols) == 0: + # add channel names as first column + cols.append(ret.keys()) + values = ret.values() + if sanitizer is not None: + values = sanitizer(values) + cols.append(values) + col_headers.append(parameter) + width.append(-1) + align.append(Alignment.Right) + out = List(col_headers, text_alignment=align, max_col_width=width) + for row in zip(*cols): + out.appendRow(row) + for line in out.genOutput(): + self.output(line) + + +@macro([ + ["parameter", Type.String, None, "Parameter (case insensitive) to set."], + ["value", Type.String, None, "Parameter value to set"], + ["items", [ + ["item", Type.String, None, "Experimental channel/controller"], + {"min": 0}], None, + "Experimental channels (also external e.g. Tango attribute: " + "sys/tg_test/1/ampli) or their controllers" + ], + ["meas_grp", Type.MeasurementGroup, Optional, "Measurement group"], +]) +def set_meas_conf(self, parameter, value, items, meas_grp): + """Set measurement group configuration parameter. + + Available configuration parameters and values: + + - **Enabled**: True/1 or False/0 + - **Output**: True/1 or False/0 + - **PlotType**: No, Spectrum or Image + - **PlotAxes**: idx, mov or a e.g. ct01 + (for image use "|" as separator of axes e.g. idx|ct01) + - **Timer**: e.g. ct01 + - **Monitor**: e.g. ct01 + - **Synchronizer**: software or e.g. tg01 + - **ValueRefEnabled** - True/1 or False/0 + - **ValueRefPattern** - URI e.g. file:///tmp/img_{index}.tiff + + Examples of usage: + + >>> set_meas_conf enabled True # enable all channels in + >>> set_meas_conf plottype spectrum ct01 # enable spectrum plotting for ct01 on + >>> set_meas_conf plotaxes mov # set plot x-axis to for all channels of #noqa + >>> set_meas_conf plottype spectrum [] mntgrp01 # enable spectrum plotting for all mntgrp01 # noqa + """ + try: + parameter, _, validator = parameter_map[parameter.lower()] + except KeyError: + raise ValueError("wrong parameter: {}".format(parameter)) + if meas_grp is None: + meas_grp = self.getEnv("ActiveMntGrp") + self.print("ActiveMntGrp = {}".format(meas_grp)) + meas_grp = self.getMeasurementGroup(meas_grp) + setter = getattr(meas_grp, "set" + parameter) + if validator is not None: + value = validator(value) + setter(value, *items) + + +@macro([ + ["meas_grp", Type.MeasurementGroup, None, + "Measurement group to activate"], + ["macro", Type.Macro, Optional, + "Activate measurement group on this macro." + "If omitted activate on all."], +]) +def set_meas(self, meas_grp, macro): + """Activate measurement group. + + It sets the ActiveMntGrp environment variable. + """ + var = "ActiveMntGrp" + if macro is not None: + var = "{}.{}".format(macro.name, var) + self.setEnv(var, meas_grp.getName()) + + +@macro([ + ["macro", Type.Macro, Optional, + "Get active measurement group set for this macro." + "If omitted get the one set for all."], +]) +def get_meas(self, macro): + """Activate measurement group. + + It gets the ActiveMntGrp environment variable. + """ + if macro is not None: + macro_name = macro.name + else: + macro_name = None + var = "ActiveMntGrp" + try: + value = self.getEnv(var, macro_name=macro_name) + except UnknownEnv: + self.warning("Active measurement group is not set. " + "Hint: use `set_meas` macro to set it.") + else: + self.print("{} = {}".format(var, value)) From d19fedc9647766dc79ac1aa03898f216f352258a Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 09:05:26 +0200 Subject: [PATCH 575/830] Add expconf macros to catalgue --- doc/source/devel/api/sardana/macroserver/macros.rst | 1 + doc/source/users/standard_macro_catalog.rst | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/source/devel/api/sardana/macroserver/macros.rst b/doc/source/devel/api/sardana/macroserver/macros.rst index 8b30c787cf..5701f1f48f 100644 --- a/doc/source/devel/api/sardana/macroserver/macros.rst +++ b/doc/source/devel/api/sardana/macroserver/macros.rst @@ -14,6 +14,7 @@ communication demo env + expconf expert hkl ioregister diff --git a/doc/source/users/standard_macro_catalog.rst b/doc/source/users/standard_macro_catalog.rst index faf4d26543..c5e0aa2f35 100644 --- a/doc/source/users/standard_macro_catalog.rst +++ b/doc/source/users/standard_macro_catalog.rst @@ -119,7 +119,7 @@ list related macros * :class:`~sardana.macroserver.macros.env.lsgh` * :class:`~sardana.macroserver.macros.env.lssnap` -measurement configuration macros +experiment configuration macros -------------------------------- .. hlist:: @@ -127,6 +127,10 @@ measurement configuration macros * :class:`~sardana.macroserver.macros.expert.defmeas` * :class:`~sardana.macroserver.macros.expert.udefmeas` + * :class:`~sardana.macroserver.macros.expconf.set_meas` + * :class:`~sardana.macroserver.macros.expconf.get_meas` + * :class:`~sardana.macroserver.macros.expconf.set_meas_conf` + * :class:`~sardana.macroserver.macros.expconf.get_meas_conf` * :class:`~sardana.macroserver.macros.expert.defsnap` * :class:`~sardana.macroserver.macros.expert.udefsnap` * :class:`~sardana.macroserver.macros.expert.lssnap` From ac7c3315df6b6c765d932b5deaa455015da3a91a Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 09:15:22 +0200 Subject: [PATCH 576/830] Move snap macros to expconf module --- doc/source/users/standard_macro_catalog.rst | 6 +- src/sardana/macroserver/macros/env.py | 126 -------------------- src/sardana/macroserver/macros/expconf.py | 119 +++++++++++++++++- 3 files changed, 118 insertions(+), 133 deletions(-) diff --git a/doc/source/users/standard_macro_catalog.rst b/doc/source/users/standard_macro_catalog.rst index c5e0aa2f35..0c7254a950 100644 --- a/doc/source/users/standard_macro_catalog.rst +++ b/doc/source/users/standard_macro_catalog.rst @@ -131,9 +131,9 @@ experiment configuration macros * :class:`~sardana.macroserver.macros.expconf.get_meas` * :class:`~sardana.macroserver.macros.expconf.set_meas_conf` * :class:`~sardana.macroserver.macros.expconf.get_meas_conf` - * :class:`~sardana.macroserver.macros.expert.defsnap` - * :class:`~sardana.macroserver.macros.expert.udefsnap` - * :class:`~sardana.macroserver.macros.expert.lssnap` + * :class:`~sardana.macroserver.macros.expconf.defsnap` + * :class:`~sardana.macroserver.macros.expconf.udefsnap` + * :class:`~sardana.macroserver.macros.expconf.lssnap` general hooks macros -------------------- diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 8f60281fea..cbcd0a835e 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -436,129 +436,3 @@ def run(self, macro_name, hook_pos): self.info("Hook %s is undefineed" % macro_name) self.setEnv("_GeneralHooks", macros_list) - - -class lssnap(Macro): - """List pre-scan snapshot group. - - .. todo:: print in form of a table - - .. note:: - The `lssnap` macro has been included in Sardana - on a provisional basis. Backwards incompatible changes - (up to and including its removal) may occur if - deemed necessary by the core developers. - """ - - def run(self): - try: - snapshot_items = self.getEnv("PreScanSnapshot") - except UnknownEnv: - self.output("No pre-scan snapshot") - return - out = List(['Snap item', 'Snap item full name']) - for full_name, label in snapshot_items: - out.appendRow([label, full_name]) - for line in out.genOutput(): - self.output(line) - - -class defsnap(Macro): - """Define snapshot group item(s). Accepts: - - Pool moveables: motor, pseudo motor - - Pool experimental channels: counter/timer, 0D, 1D, 2D, pseudo counter - - Taurus attributes - - .. note:: - The `addsnap` macro has been included in Sardana - on a provisional basis. Backwards incompatible changes - (up to and including its removal) may occur if - deemed necessary by the core developers. - """ - - param_def = [ - ["snap_names", [[ - "name", Type.String, None, "Name of an item to be added to the " - "pre-scan snapshot group"]], - None, - "Items to be added to the pre-scan snapshot group"], - ] - - def run(self, snap_names): - - def get_item_info(item): - if isinstance(item, taurus.core.TaurusAttribute): - return item.fullname, item.label - else: - return item.full_name, item.name - try: - snap_items = self.getEnv("PreScanSnapshot") - except UnknownEnv: - snap_items = [] - snap_full_names = [item[0] for item in snap_items] - new_snap_items = [] - for name in snap_names: - obj = self.getObj(name) - if obj is None: - try: - obj = taurus.Attribute(name) - except taurus.TaurusException: - raise ValueError("item is neither Pool element not " - "Taurus attribute") - elif obj.type == "MotorGroup": - raise ValueError("MotorGroup item type is not accepted") - new_full_name, new_label = get_item_info(obj) - if new_full_name in snap_full_names: - msg = "{} already in pre-scan snapshot".format(name) - raise ValueError(msg) - new_snap_items.append((new_full_name, new_label)) - self.setEnv("PreScanSnapshot", snap_items + new_snap_items) - - -class udefsnap(Macro): - """Undefine snapshot group item(s). Without arguments undefine all. - - .. note:: - The `udefsnap` macro has been included in Sardana - on a provisional basis. Backwards incompatible changes - (up to and including its removal) may occur if - deemed necessary by the core developers. - """ - - param_def = [ - ["snap_names", [[ - "name", Type.String, None, "Name of an item to be removed " - "from the pre-scan snapshot group", - ], {"min": 0}], - None, - "Items to be remove from the pre-scan snapshot group"], - ] - - def run(self, snap_names): - if len(snap_names) == 0: - self.unsetEnv("PreScanSnapshot") - return - try: - snap_items = self.getEnv("PreScanSnapshot") - except UnknownEnv: - raise RuntimeError("no pre-scan snapshot defined") - snap_full_names = {} - for i, item in enumerate(snap_items): - snap_full_names[item[0]] = i - for name in snap_names: - obj = self.getObj(name) - if obj is None: - try: - obj = taurus.Attribute(name) - except taurus.TaurusException: - raise ValueError("item is neither Pool element not " - "Taurus attribute") - elif obj.type == "MotorGroup": - raise ValueError("MotorGroup item type is not accepted") - rm_full_name = obj.fullname - if rm_full_name not in snap_full_names.keys(): - msg = "{} not in pre-scan snapshot".format(name) - raise ValueError(msg) - i = snap_full_names[rm_full_name] - snap_items.pop(i) - self.setEnv("PreScanSnapshot", snap_items) diff --git a/src/sardana/macroserver/macros/expconf.py b/src/sardana/macroserver/macros/expconf.py index efd7f14ed3..371f0059a0 100644 --- a/src/sardana/macroserver/macros/expconf.py +++ b/src/sardana/macroserver/macros/expconf.py @@ -23,18 +23,20 @@ """Experiment configuration related macros""" -__all__ = ["get_meas", "get_meas_conf", "set_meas", "set_meas_conf"] +__all__ = ["get_meas", "get_meas_conf", "set_meas", "set_meas_conf", + "lssnap", "defsnap", "udefsnap"] __docformat__ = 'restructuredtext' from collections import OrderedDict +import taurus from taurus.console import Alignment from taurus.console.list import List from sardana.macroserver.msexception import UnknownEnv from sardana.taurus.core.tango.sardana import PlotType -from sardana.macroserver.macro import macro, Type, Optional +from sardana.macroserver.macro import macro, Macro, Type, Optional def sanitizer(values): @@ -169,8 +171,8 @@ def set_meas_conf(self, parameter, value, items, meas_grp): >>> set_meas_conf enabled True # enable all channels in >>> set_meas_conf plottype spectrum ct01 # enable spectrum plotting for ct01 on - >>> set_meas_conf plotaxes mov # set plot x-axis to for all channels of #noqa - >>> set_meas_conf plottype spectrum [] mntgrp01 # enable spectrum plotting for all mntgrp01 # noqa + >>> set_meas_conf plotaxes mov # set plot x-axis to for all channels of + >>> set_meas_conf plottype spectrum [] mntgrp01 # enable spectrum plotting for all mntgrp01 """ try: parameter, _, validator = parameter_map[parameter.lower()] @@ -226,3 +228,112 @@ def get_meas(self, macro): "Hint: use `set_meas` macro to set it.") else: self.print("{} = {}".format(var, value)) + + +class lssnap(Macro): + """List pre-scan snapshot group. + """ + + def run(self): + try: + snapshot_items = self.getEnv("PreScanSnapshot") + except UnknownEnv: + self.output("No pre-scan snapshot") + return + out = List(['Snap item', 'Snap item full name']) + for full_name, label in snapshot_items: + out.appendRow([label, full_name]) + for line in out.genOutput(): + self.output(line) + + +class defsnap(Macro): + """Define snapshot group item(s). + + Accepts: + + - Pool moveables: motor, pseudo motor + - Pool experimental channels: counter/timer, 0D, 1D, 2D, pseudo counter + - Taurus attributes + """ + + param_def = [ + ["snap_names", [[ + "name", Type.String, None, "Name of an item to be added to the " + "pre-scan snapshot group"]], + None, + "Items to be added to the pre-scan snapshot group"], + ] + + def run(self, snap_names): + + def get_item_info(item): + if isinstance(item, taurus.core.TaurusAttribute): + return item.fullname, item.label + else: + return item.full_name, item.name + try: + snap_items = self.getEnv("PreScanSnapshot") + except UnknownEnv: + snap_items = [] + snap_full_names = [item[0] for item in snap_items] + new_snap_items = [] + for name in snap_names: + obj = self.getObj(name) + if obj is None: + try: + obj = taurus.Attribute(name) + except taurus.TaurusException: + raise ValueError("item is neither Pool element not " + "Taurus attribute") + elif obj.type == "MotorGroup": + raise ValueError("MotorGroup item type is not accepted") + new_full_name, new_label = get_item_info(obj) + if new_full_name in snap_full_names: + msg = "{} already in pre-scan snapshot".format(name) + raise ValueError(msg) + new_snap_items.append((new_full_name, new_label)) + self.setEnv("PreScanSnapshot", snap_items + new_snap_items) + + +class udefsnap(Macro): + """Undefine snapshot group item(s). Without arguments undefine all. + """ + + param_def = [ + ["snap_names", [[ + "name", Type.String, None, "Name of an item to be removed " + "from the pre-scan snapshot group", + ], {"min": 0}], + None, + "Items to be remove from the pre-scan snapshot group"], + ] + + def run(self, snap_names): + if len(snap_names) == 0: + self.unsetEnv("PreScanSnapshot") + return + try: + snap_items = self.getEnv("PreScanSnapshot") + except UnknownEnv: + raise RuntimeError("no pre-scan snapshot defined") + snap_full_names = {} + for i, item in enumerate(snap_items): + snap_full_names[item[0]] = i + for name in snap_names: + obj = self.getObj(name) + if obj is None: + try: + obj = taurus.Attribute(name) + except taurus.TaurusException: + raise ValueError("item is neither Pool element not " + "Taurus attribute") + elif obj.type == "MotorGroup": + raise ValueError("MotorGroup item type is not accepted") + rm_full_name = obj.fullname + if rm_full_name not in snap_full_names.keys(): + msg = "{} not in pre-scan snapshot".format(name) + raise ValueError(msg) + i = snap_full_names[rm_full_name] + snap_items.pop(i) + self.setEnv("PreScanSnapshot", snap_items) From f429d797dfdfc247a4ea932b8bc4fd2cd682073f Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 09:55:53 +0200 Subject: [PATCH 577/830] Add forgotten expconf.rst --- doc/source/devel/api/sardana/macroserver/macros/expconf.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 doc/source/devel/api/sardana/macroserver/macros/expconf.rst diff --git a/doc/source/devel/api/sardana/macroserver/macros/expconf.rst b/doc/source/devel/api/sardana/macroserver/macros/expconf.rst new file mode 100644 index 0000000000..368632d947 --- /dev/null +++ b/doc/source/devel/api/sardana/macroserver/macros/expconf.rst @@ -0,0 +1,6 @@ +:mod:`~sardana.macroserver.macros.expconf` +========================================== + +.. automodule:: sardana.macroserver.macros.expconf + :imported-members: + :members: From c2651928fc1cda6eab45525ee55fe2d913d4752e Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 12:24:42 +0200 Subject: [PATCH 578/830] Fix flake8 --- src/sardana/macroserver/macros/expconf.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/macros/expconf.py b/src/sardana/macroserver/macros/expconf.py index 371f0059a0..8f40e60d3e 100644 --- a/src/sardana/macroserver/macros/expconf.py +++ b/src/sardana/macroserver/macros/expconf.py @@ -169,10 +169,14 @@ def set_meas_conf(self, parameter, value, items, meas_grp): Examples of usage: - >>> set_meas_conf enabled True # enable all channels in - >>> set_meas_conf plottype spectrum ct01 # enable spectrum plotting for ct01 on - >>> set_meas_conf plotaxes mov # set plot x-axis to for all channels of - >>> set_meas_conf plottype spectrum [] mntgrp01 # enable spectrum plotting for all mntgrp01 + >>> # enable all channels in + >>> set_meas_conf enabled True + >>> # enable spectrum plotting for ct01 on + >>> set_meas_conf plottype spectrum ct01 + >>> # set plot x-axis to for all channels of + >>> set_meas_conf plotaxes mov + >>> # enable spectrum plotting for all mntgrp01 + >>> set_meas_conf plottype spectrum [] mntgrp01 """ try: parameter, _, validator = parameter_map[parameter.lower()] From 1ac1bc0d73d084cf58c049ebd72ade221da169fa Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 12:38:13 +0200 Subject: [PATCH 579/830] doc: fix notes --- src/sardana/taurus/core/tango/sardana/pool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 0cee12e00e..a3e672a9b2 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2833,7 +2833,7 @@ def setTimer(self, timer, *elements, apply=True): """Set the timer configuration for the given channels of the same controller. - .. note: Currently the controller's timer must be unique. Hence this + .. note:: Currently the controller's timer must be unique. Hence this method will set it for the whole controller regardless of the ``elements`` argument. @@ -2896,7 +2896,7 @@ def setMonitor(self, monitor, *elements, apply=True): """Set the monitor configuration for the given channels of the same controller. - .. note: Currently the controller's monitor must be unique. + .. note:: Currently the controller's monitor must be unique. Hence this method will set it for the whole controller regardless of the ``elements`` argument. @@ -2960,7 +2960,7 @@ def setSynchronizer(self, synchronizer, *elements, apply=True): """Set the synchronizer configuration for the given channels or controller. - .. note: Currently the controller's synchronizer must be unique. + .. note:: Currently the controller's synchronizer must be unique. Hence this method will set it for the whole controller regardless of the ``elements`` argument. From ec775c82f47d79fdc513ba6bb3322ec7e55400d6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 13:34:23 +0200 Subject: [PATCH 580/830] Move repeated documentation to class docstring --- src/sardana/taurus/core/tango/sardana/pool.py | 92 ++++--------------- 1 file changed, 19 insertions(+), 73 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index a3e672a9b2..1394e473c0 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2372,7 +2372,25 @@ def __repr__(self): class MeasurementGroup(PoolElement): - """ Class encapsulating MeasurementGroup functionality.""" + """MeasurementGroup Sardana-Taurus extension. + + Setting configuration parameters using e.g., + `~sardana.taurus.core.tango.sardana.pool.MeasurementGroup.setEnabled` or + `~sardana.taurus.core.tango.sardana.pool.MeasurementGroup.setTimer`, etc. + by default applies changes on the server. Since setting the configuration + means passing to the server all the configuration parameters of + the measurement group at once this behavior can be changed with the + ``apply=False``. Then the configuration changes are kept locally. + This is useful when changing more then one parameter. In this case only + setting of the last parameter should use ``apply=True`` or use + `~sardana.taurus.core.tango.sardana.pool.MeasurementGroup.applyConfiguration` + afterwards:: + + # or in a macro use: meas_grp = self.getMeasurementGroup("mntgrp01") + meas_grp = taurus.Device("mntgrp01") + meas_grp.setEnabled(False, apply=False) + meas_grp.setEnabled(True, "ct01", "ct02") + """ def __init__(self, name, **kw): """PoolElement initialization.""" @@ -2503,14 +2521,6 @@ def setOutput(self, output, *elements, apply=True): on the controller means setting it to all channels of this controller present in this measurement group. - Configuration by default is directly applied on the server. - Since setting the configuration means passing to the server all the - configuration paramters of the measurement group at once this - behavior can be changed with the *apply* argument and we can keep - the configuration changes only locally. This is useful when we want - to change more then one parameter, in this case only the setting of - the last parameter should use `apply=True`. - :param output: `True` - output enabled, `False` - output disabled :type output: bool :param elements: sequence of element names or full names, no elements @@ -2556,14 +2566,6 @@ def setEnabled(self, enabled, *elements, apply=True): on the controller means setting it to all channels of this controller present in this measurement group. - Configuration by default is directly applied on the server. - Since setting the configuration means passing to the server all the - configuration paramters of the measurement group at once this - behavior can be changed with the *apply* argument and we can keep - the configuration changes only locally. This is useful when we want - to change more then one parameter, in this case only the setting of - the last parameter should use `apply=True`. - :param enabled: `True` - element enabled, `False` - element disabled :type enabled: bool :param elements: sequence of element names or full names, no elements @@ -2609,14 +2611,6 @@ def setPlotType(self, plot_type, *elements, apply=True): type on the controller means setting it to all channels of this controller present in this measurement group. - Configuration by default is directly applied on the server. - Since setting the configuration means passing to the server all the - configuration paramters of the measurement group at once this - behavior can be changed with the *apply* argument and we can keep - the configuration changes only locally. This is useful when we want - to change more then one parameter, in this case only the setting of - the last parameter should use `apply=True`. - :param plot_type: 'No'/0 , 'Spectrum'/1, 'Image'/2 :type plot_type: str or int :param elements: sequence of element names or full names, no elements @@ -2665,14 +2659,6 @@ def setPlotAxes(self, plot_axes, *elements, apply=True): axes on the controller means setting it to all channels of this controller present in this measurement group. - Configuration by default is directly applied on the server. - Since setting the configuration means passing to the server all the - configuration paramters of the measurement group at once this - behavior can be changed with the *apply* argument and we can keep - the configuration changes only locally. This is useful when we want - to change more then one parameter, in this case only the setting of - the last parameter should use `apply=True`. - :param plot_axes: [''] / ['', ''] :type plot_axes: list(str) :param elements: sequence of element names or full names, no elements @@ -2719,14 +2705,6 @@ def setValueRefEnabled(self, value_ref_enabled, *elements, apply=True): reference enabled on the controller means setting it to all channels of this controller present in this measurement group. - Configuration by default is directly applied on the server. - Since setting the configuration means passing to the server all the - configuration paramters of the measurement group at once this - behavior can be changed with the *apply* argument and we can keep - the configuration changes only locally. This is useful when we want - to change more then one parameter, in this case only the setting of - the last parameter should use `apply=True`. - :param value_ref_enabled: `True` - enabled, `False` - disabled :type value_ref_enabled: bool :param elements: sequence of element names or full names, no elements @@ -2774,14 +2752,6 @@ def setValueRefPattern(self, value_ref_pattern, *elements, apply=True): reference pattern on the controller means setting it to all channels of this controller present in this measurement group. - Configuration by default is directly applied on the server. - Since setting the configuration means passing to the server all the - configuration paramters of the measurement group at once this - behavior can be changed with the *apply* argument and we can keep - the configuration changes only locally. This is useful when we want - to change more then one parameter, in this case only the setting of - the last parameter should use `apply=True`. - :param value_ref_pattern: `/path/file{index:03d}.txt` :type value_ref_pattern: :py:obj:`str` :param elements: sequence of element names or full names, no elements @@ -2837,14 +2807,6 @@ def setTimer(self, timer, *elements, apply=True): method will set it for the whole controller regardless of the ``elements`` argument. - Configuration by default is directly applied on the server. - Since setting the configuration means passing to the server all the - configuration paramters of the measurement group at once this - behavior can be changed with the *apply* argument and we can keep - the configuration changes only locally. This is useful when we want - to change more then one parameter, in this case only the setting of - the last parameter should use `apply=True`. - :param timer: channel use as timer :type timer: :py:obj:`str` :param elements: sequence of channels names or full names, no elements @@ -2900,14 +2862,6 @@ def setMonitor(self, monitor, *elements, apply=True): Hence this method will set it for the whole controller regardless of the ``elements`` argument. - Configuration by default is directly applied on the server. - Since setting the configuration means passing to the server all the - configuration parameters of the measurement group at once this - behavior can be changed with the *apply* argument and we can keep - the configuration changes only locally. This is useful when we want - to change more then one parameter, in this case only the setting of - the last parameter should use `apply=True`. - :param monitor: channel use as monitor :type monitor: :py:obj:`str` :param elements: sequence of channels names or full names, no elements @@ -2964,14 +2918,6 @@ def setSynchronizer(self, synchronizer, *elements, apply=True): Hence this method will set it for the whole controller regardless of the ``elements`` argument. - Configuration by default is directly applied on the server. - Since setting the configuration means passing to the server all the - configuration paramters of the measurement group at once this - behavior can be changed with the *apply* argument and we can keep - the configuration changes only locally. This is useful when we want - to change more then one parameter, in this case only the setting of - the last parameter should use `apply=True`. - :param synchronizer: triger/gate element name or software :type synchronizer: :py:obj:`str` :param elements: sequence of channels names or full names, no elements From efd2ea4b6bf19b37702c5f6e8c89313f980d803f Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 13:35:23 +0200 Subject: [PATCH 581/830] Add accidentally removed applyConfiguration method 28940b86 accidentally removed this method. --- src/sardana/taurus/core/tango/sardana/pool.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 1394e473c0..3cce0bcb55 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2968,6 +2968,19 @@ def getSynchronizer(self, *elements, ret_full_name=False, return self._get_value_per_channel(config, ctrls_sync, use_fullname=ret_full_name) + def applyConfiguration(self): + """Apply configuration changes kept locally on the server. + + Setting configuration parameters using e.g., + `~sardana.taurus.core.tango.sardana.pool.MeasurementGroup.setEnabled` + or + `~sardana.taurus.core.tango.sardana.pool.MeasurementGroup.setTimer`, + etc. + with ``apply=False`` keeps the changes locally. Use this method to + apply them on the server afterwards. + """ + self.getConfiguration().applyConfiguration() + ######################################################################### # TODO: review the following API From 132c8f8cd3026a033225f9773b5c515924197fb8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 15 May 2020 16:21:13 +0200 Subject: [PATCH 582/830] Add direct links to Sardana-Taurus model API Fixes #1324. --- doc/source/devel/api/api_sardana.rst | 2 ++ .../devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst | 2 ++ doc/source/users/taurus/index.rst | 7 ++++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/source/devel/api/api_sardana.rst b/doc/source/devel/api/api_sardana.rst index 77c8256348..14a418c31c 100644 --- a/doc/source/devel/api/api_sardana.rst +++ b/doc/source/devel/api/api_sardana.rst @@ -9,6 +9,8 @@ Sardana API Macro API Controller API + Sardana-Taurus model API + Sardana-Taurus Qt model API Motor API I/O register Counter/timer API diff --git a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst index 7564368efc..1621c45a3b 100644 --- a/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst +++ b/doc/source/devel/api/sardana/taurus/qt/qtcore/tango/sardana.rst @@ -1,5 +1,7 @@ .. currentmodule:: sardana.taurus.qt.qtcore.tango.sardana +.. _sardana-taurus-qt-api: + :mod:`~sardana.taurus.qt.qtcore.tango.sardana` ============================================== diff --git a/doc/source/users/taurus/index.rst b/doc/source/users/taurus/index.rst index e642cee2fd..ee87836b43 100644 --- a/doc/source/users/taurus/index.rst +++ b/doc/source/users/taurus/index.rst @@ -1,7 +1,7 @@ .. _sardana-taurus: ================================ -Sardana Taurus Extension widgets +Sardana-Taurus Extension widgets ================================ @@ -17,3 +17,8 @@ Sardana provides several :mod:`taurus`-based widgets for being used in GUIs Sardana Editor Showscan PoolMotorTV + +If you do not find a widget which meets your requirements and plan to develop +a new one consider using +:ref:`Sardana-Taurus Qt model classes `. + From b7f830a4fb2c6d5d84682fa96d7a09727edf8f91 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 19 May 2020 18:28:26 +0200 Subject: [PATCH 583/830] Do not config limits when switching to and from expert view --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 1639e1ba55..545749257c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1050,21 +1050,14 @@ def abort(self): motor_dev.abort() def setExpertView(self, expertView): - if self.taurusValueBuddy().motor_dev is not None: - if self.taurusValueBuddy().hasHwLimits(): - self.config_limit(self.lim_neg, "negative", "Enabled") - self.config_limit(self.lim_pos, "positive", "Enabled") - else: - self.config_limit(self.lim_neg, "negative", "Disabled") - self.config_limit(self.lim_pos, "positive", "Disabled") - - if self.lbl_enc_read is not None: - self.lbl_enc.setVisible(False) - self.lbl_enc_read.setVisible(False) - if expertView and self.taurusValueBuddy().motor_dev is not None: - encoder = self.taurusValueBuddy().hasEncoder() - self.lbl_enc.setVisible(encoder) - self.lbl_enc_read.setVisible(encoder) + if self.lbl_enc_read is None: + return + self.lbl_enc.setVisible(False) + self.lbl_enc_read.setVisible(False) + if expertView and self.taurusValueBuddy().motor_dev is not None: + encoder = self.taurusValueBuddy().hasEncoder() + self.lbl_enc.setVisible(encoder) + self.lbl_enc_read.setVisible(encoder) def prepare_button(self, btn): btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) From e5acb0fdc83d956af99973ec7306dac653b7f7be Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 20 May 2020 11:14:04 +0200 Subject: [PATCH 584/830] Rename arg in prepare_limit --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 545749257c..dace4d1e0b 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1068,12 +1068,12 @@ def prepare_button(self, btn): btn.setMaximumSize(25, 25) btn.setText('') - def prepare_limit(self, btn): - btn_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) - btn_policy.setHorizontalStretch(0) - btn_policy.setVerticalStretch(0) - btn.setSizePolicy(btn_policy) - btn.setText('') + def prepare_limit(self, lbl): + lbl_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) + lbl_policy.setHorizontalStretch(0) + lbl_policy.setVerticalStretch(0) + lbl.setSizePolicy(lbl_policy) + lbl.setText('') def config_limit(self, limit, polarity, state): """ From 526b08fdbfa9b3abbee453eb7f414c0d1e9e237f Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 20 May 2020 11:15:22 +0200 Subject: [PATCH 585/830] Call prepare_limit only once at the initialization --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index dace4d1e0b..26f2c2dc16 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -984,10 +984,12 @@ def __init__(self, parent=None, designMode=False): limits_layout.setSpacing(0) self.lim_neg = Qt.QLabel() + self.prepare_limit(self.lim_neg) limits_layout.addWidget(self.lim_neg) self.config_limit(self.lim_neg, "negative", "Disabled") self.lim_pos = Qt.QLabel() + self.prepare_limit(self.lim_pos) limits_layout.addWidget(self.lim_pos) self.config_limit(self.lim_pos, "positive", "Disabled") @@ -1081,8 +1083,6 @@ def config_limit(self, limit, polarity, state): :param polarity: {negative, positive} :param state: {Disabled, Enabled, SwActive, HwActive} """ - self.prepare_limit(limit) - if polarity == "negative": tooltip = 'Negative Limit' icon = Qt.QIcon("designer:minus.png") From c8ef7d59ab04c75d6176a284a47ce0477f638214 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 20 May 2020 17:47:13 +0200 Subject: [PATCH 586/830] Reintroduce taurusValueBuddy check in setModel This check was wrongly removed in cc777b5 --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 2e833f09c5..670ac573af 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1097,10 +1097,12 @@ def _create_encoder(self): self.lbl_enc_read.setVisible(False) def setModel(self, model): - if self.taurusValueBuddy().hasEncoder() and self.lbl_enc_read is None: - self._create_encoder() - self.taurusValueBuddy().expertViewChanged.disconnect( - self.setExpertView) + if hasattr(self, 'taurusValueBuddy'): + try: + self.taurusValueBuddy().expertViewChanged.disconnect( + self.setExpertView) + except TypeError: + pass if model in (None, ''): TaurusWidget.setModel(self, model) self.lbl_read.setModel(model) @@ -1109,8 +1111,9 @@ def setModel(self, model): return TaurusWidget.setModel(self, model + '/Position') self.lbl_read.setModel(model + '/Position') - if (self.lbl_enc_read is not None - and self.taurusValueBuddy().motor_dev is not None): + if (self.taurusValueBuddy().hasEncoder() + and self.lbl_enc_read is None): + self._create_encoder() self.lbl_enc_read.setModel(model + '/Encoder') # Handle User/Expert view self.setExpertView(self.taurusValueBuddy()._expertView) From af2f145a75564d1ddc9984857b2e2a24b46a9b53 Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Wed, 20 May 2020 20:28:48 +0200 Subject: [PATCH 587/830] Adapt to renaming of item factories entry point The entry point group for Taurus form item factories has been renamed from "taurus.qt.taurusform.item_factories" to "taurus.form.item_factories". Adapt our registration. Also register using a name that contains "sardana" in order to avoid potencial collissions. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index f20547dddf..1573fb3dd1 100644 --- a/setup.py +++ b/setup.py @@ -84,13 +84,13 @@ def get_release_info(): ] form_factories = [ - "sdn_pool = sardana.taurus.qt.qtgui.extra_pool.formitemfactory:pool_item_factory" # noqa + "sardana.pool = sardana.taurus.qt.qtgui.extra_pool.formitemfactory:pool_item_factory" # noqa ] entry_points = { 'console_scripts': console_scripts, 'gui_scripts': gui_scripts, - 'taurus.qt.taurusform.item_factories': form_factories, + 'taurus.form.item_factories': form_factories, } classifiers = [ From db07ae7f2e1a8312a26401810e920e853ccd7b5f Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 21 May 2020 13:06:13 +0200 Subject: [PATCH 588/830] Add utilities to refactor limits updates --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 4c660f4ffb..c5897d8acf 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -23,6 +23,9 @@ ## ############################################################################## +import operator +from enum import IntEnum + import PyTango from taurus.external.qt import Qt, compat @@ -64,6 +67,91 @@ def eventReceived(self, evt_src, evt_type, evt_value): self.updateLimits.emit(limits.tolist()) +class Limit(IntEnum): + Home = 0 + Positive = 1 + Negative = 2 + + +class LimitInfo: + + def __init__(self, exist=False, active=False, status=""): + # for software limit True when defined + # for hardware limit True when physical motor, False when pseudo + self.exist = exist + self.active = active + self.status = status + + +def eval_hw_limit(limit, value): + """Evaluate hardware limit information + + :param limit: which limit (negative or positive) + :type limit: `Limit` + :param value: limit value, `None` means no hardware limit (pseudo motor) + :type value: `bool` or `None` + :return: limit information + :rtype: `LimitInfo` + """ + if limit is Limit.Positive: + status = "Positive " + elif limit is Limit.Negative: + status = "Negative " + else: + raise ValueError("{} wrong limit".format(limit)) + exist = True + active = False + status += "hardware limit " + if value is None: + exist = False + status += "does not exist." + elif value: + active = True + status += "is active." + else: + status += "is not active." + return LimitInfo(exist, active, status) + + +def eval_sw_limit(limit, position_attr): + """Evaluate software limit information + + :param limit: which limit (negative or positive) + :type limit: `Limit` + :param position_attr: position attribute + :type position_attr: `taurus.core.tango.TangoAttribute` + :return: limit information + :rtype: `LimitInfo` + """ + if limit == Limit.Positive: + getter_name = "getMaxRange" + comparator = operator.ge + not_defined = float("inf") + status = "Positive " + elif limit == Limit.Negative: + getter_name = "getMinRange" + comparator = operator.le + not_defined = float("-inf") + status = "Negative " + else: + raise ValueError("{} wrong limit".format(limit)) + active = False + exist = False + lim_value = getattr(position_attr, getter_name)().magnitude + status += "software limit is " + if lim_value == not_defined: + status += "not defined." + else: + exist = True + position = position_attr.read().rvalue.magnitude + if comparator(position, lim_value): + status += "active." + active = True + else: + status += "defined." + return LimitInfo(exist, active, status) + + class PoolMotorClient(): maxint_in_32_bits = 2147483647 From 7841e893a8e4b3610877f250593a73eada4f9c8d Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 21 May 2020 15:36:58 +0200 Subject: [PATCH 589/830] Refactor limits updates * evaluate only once all the limits information * pass the limits information to read and write widget with _config_limit() method Change pseudo motor limits labels behavior: when software limit is enabled this activates the icon. --- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 191 +++++++----------- 1 file changed, 74 insertions(+), 117 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index c5897d8acf..313cebb2a9 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1073,13 +1073,17 @@ def __init__(self, parent=None, designMode=False): self.lim_neg = Qt.QLabel() self.prepare_limit(self.lim_neg) + self._config_limit(Limit.Negative, + hw_lim=LimitInfo(), + sw_lim=LimitInfo()) limits_layout.addWidget(self.lim_neg) - self.config_limit(self.lim_neg, "negative", "Disabled") self.lim_pos = Qt.QLabel() self.prepare_limit(self.lim_pos) + self._config_limit(Limit.Positive, + hw_lim=LimitInfo(), + sw_lim=LimitInfo()) limits_layout.addWidget(self.lim_pos) - self.config_limit(self.lim_pos, "positive", "Disabled") self.layout().addLayout(limits_layout, 0, 0) @@ -1165,44 +1169,38 @@ def prepare_limit(self, lbl): lbl.setSizePolicy(lbl_policy) lbl.setText('') - def config_limit(self, limit, polarity, state): - """ - :param limit: Qt.Label - :param polarity: {negative, positive} - :param state: {Disabled, Enabled, SwActive, HwActive} + def _config_limit(self, limit, hw_lim, sw_lim): + """Configure sub-widgets according to limit information + + :param limit: which limit (negative or positive) + :type limit: `Limit` + :param hw_lim: hardware limit information + :type hw_lim: `LimitInfo` + :param hw_lim: software limit information + :type hw_lim: `LimitInfo` """ - if polarity == "negative": - tooltip = 'Negative Limit' + if limit == Limit.Negative: + widget = self.lim_neg icon = Qt.QIcon("designer:minus.png") - elif polarity == "positive": - tooltip = 'Positive Limit' + elif limit == Limit.Positive: + widget = self.lim_pos icon = Qt.QIcon("designer:plus.png") else: - raise ValueError("Wrong polarity {}".format(polarity)) - limit.setStyleSheet('') - if state == "Disabled": - pixmap = Qt.QPixmap(icon.pixmap(Qt.QSize(32, 32), - Qt.QIcon.Disabled)) - else: + raise ValueError("{} wrong limit".format(limit)) + widget.setStyleSheet('') + if hw_lim.exist or sw_lim.exist: pixmap = Qt.QPixmap(icon.pixmap(Qt.QSize(32, 32), Qt.QIcon.Active)) - if state == "SwActive": + if hw_lim.active or sw_lim.active: colour = DEVICE_STATE_PALETTE.qtStyleSheet( PyTango.DevState.ALARM) - limit.setStyleSheet(colour) - tooltip = tooltip + " - Software limit reached" - elif state == "HwActive": - colour = DEVICE_STATE_PALETTE.qtStyleSheet( - PyTango.DevState.ALARM) - limit.setStyleSheet(colour) - tooltip = tooltip + " - Hardware limit reached" - elif state == "Enabled": - pass - else: - raise ValueError("Wrong state {}".format(state)) - - limit.setToolTip(tooltip) - limit.setPixmap(pixmap) + widget.setStyleSheet(colour) + else: + pixmap = Qt.QPixmap(icon.pixmap(Qt.QSize(32, 32), + Qt.QIcon.Disabled)) + tool_tip = hw_lim.status + " " + sw_lim.status + widget.setToolTip(tool_tip) + widget.setPixmap(pixmap) def _create_encoder(self): if self.taurusValueBuddy().hasEncoder() and self.lbl_enc_read is None: @@ -1523,6 +1521,37 @@ def keyPressEvent(self, key_event): def emitEditingFinished(self): self.applied.emit() + def _config_limit(self, limit, hw_lim, sw_lim): + """Configure sub-widgets according to limit information + + :param limit: which limit (negative or positive) + :type limit: `Limit` + :param hw_lim: hardware limit information + :type hw_lim: `LimitInfo` + :param hw_lim: software limit information + :type hw_lim: `LimitInfo` + """ + if limit == Limit.Negative: + step_widget = self.btn_step_down + to_widget = self.btn_to_neg + to_press_widget = self.btn_to_neg_press + elif limit == Limit.Positive: + step_widget = self.btn_step_up + to_widget = self.btn_to_pos + to_press_widget = self.btn_to_pos_press + else: + raise ValueError("{} wrong limit".format(limit)) + active = hw_lim.active or sw_lim.active + style_sheet = "" + if active: + style_sheet = 'QPushButton{{{}}}'.format( + DEVICE_STATE_PALETTE.qtStyleSheet(PyTango.DevState.ALARM)) + step_widget.setEnabled(not active) + allow_to = not active and sw_lim.exist + to_widget.setEnabled(allow_to) + to_press_widget.setEnabled(allow_to) + step_widget.setStyleSheet(style_sheet) + ################################################## # UNITS WIDGET # @@ -1688,92 +1717,20 @@ def hasHwLimits(self): def updateLimits(self, limits, position=None): if isinstance(limits, dict): limits = limits["limits"] - limits = list(limits) - HOME = 0 - POS = 1 - NEG = 2 - # Check also if the software limit is 'active' - if self.motor_dev is not None: - position_attribute = self.motor_dev.getAttribute('Position') - if position is None: - position = position_attribute.read().rvalue.magnitude - max_value_str = position_attribute.max_value - min_value_str = position_attribute.min_value - try: - max_value = float(max_value_str) - if position >= max_value: - limits[POS] = True - pos_lim_status = "HwActive" - else: - pos_lim_status = "SwActive" - except: - pos_lim_status = "SwActive" - try: - min_value = float(min_value_str) - if position <= min_value: - limits[NEG] = True - neg_lim_status = "HwActive" - else: - neg_lim_status = "SwActive" - except: - neg_lim_status = "SwActive" + pos_hw_lim = eval_hw_limit(Limit.Positive, limits[Limit.Positive]) + neg_hw_lim = eval_hw_limit(Limit.Negative, limits[Limit.Negative]) + position_attr = self.motor_dev.getAttribute('Position') + pos_sw_lim = eval_sw_limit(Limit.Positive, position_attr) + neg_sw_lim = eval_sw_limit(Limit.Negative, position_attr) - pos_lim = limits[POS] - pos_btnstylesheet = '' - enabled = True - if pos_lim: - pos_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( - PyTango.DevState.ALARM) - enabled = False - self.readWidget(followCompact=True).config_limit( - self.readWidget(followCompact=True).lim_pos, - "positive", pos_lim_status) - elif self.hasHwLimits(): - self.readWidget(followCompact=True).config_limit( - self.readWidget(followCompact=True).lim_pos, - "positive", "Enabled") - else: - self.readWidget(followCompact=True).config_limit( - self.readWidget(followCompact=True).lim_pos, - "positive", "Disabled") - - self.writeWidget(followCompact=True).btn_step_up.setEnabled(enabled) - self.writeWidget(followCompact=True).btn_step_up.setStyleSheet( - pos_btnstylesheet) - enabled = enabled and self.motor_dev.getAttribute( - 'Position').max_value.lower() != 'not specified' - self.writeWidget(followCompact=True).btn_to_pos.setEnabled(enabled) - self.writeWidget( - followCompact=True).btn_to_pos_press.setEnabled(enabled) - - neg_lim = limits[NEG] - neg_btnstylesheet = '' - enabled = True - if neg_lim: - neg_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( - PyTango.DevState.ALARM) - enabled = False - self.readWidget(followCompact=True).config_limit( - self.readWidget(followCompact=True).lim_neg, - "negative", neg_lim_status) - elif self.hasHwLimits(): - self.readWidget(followCompact=True).config_limit( - self.readWidget(followCompact=True).lim_neg, - "negative", "Enabled") - else: - self.readWidget(followCompact=True).config_limit( - self.readWidget(followCompact=True).lim_neg, - "negative", "Disabled") - - self.writeWidget(followCompact=True).btn_step_down.setEnabled(enabled) - self.writeWidget(followCompact=True).btn_step_down.setStyleSheet( - neg_btnstylesheet) - enabled = enabled and self.motor_dev.getAttribute( - 'Position').min_value.lower() != 'not specified' - self.writeWidget(followCompact=True).btn_to_neg.setEnabled(enabled) - self.writeWidget( - followCompact=True).btn_to_neg_press.setEnabled(enabled) + read_widget = self.readWidget(followCompact=True) + read_widget._config_limit(Limit.Positive, pos_hw_lim, pos_sw_lim) + read_widget._config_limit(Limit.Negative, neg_hw_lim, neg_sw_lim) + + write_widget = self.writeWidget(followCompact=True) + write_widget._config_limit(Limit.Positive, pos_hw_lim, pos_sw_lim) + write_widget._config_limit(Limit.Negative, neg_hw_lim, neg_sw_lim) def updatePowerOn(self, poweron='__no_argument__'): if poweron == '__no_argument__': @@ -1801,7 +1758,7 @@ def updatePosition(self, position='__no_argument__'): # we just want to check if any software limit is 'active' # and updateLimits takes care of it if self.motor_dev is not None: - limit_switches = [False, False, False] + limit_switches = [None, None, None] if self.hasHwLimits(): limit_switches = self.motor_dev.getAttribute( 'Limit_switches').read().rvalue From a8a9f56522527f355b7ea221e39401f94fd6bf01 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 21 May 2020 16:52:09 +0200 Subject: [PATCH 590/830] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74edba31fe..a291f97ee6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,10 +27,12 @@ This file follows the formats and conventions from [keepachangelog.com] * Improve scans to detect if a ScanFile od ScanDir are set but empty (#1262) * Possibility to view debug messages in `DoorOutput` widget - enable/disable using context menu option (#1242) -* Store PMTV (motor widget) configurations: *expert view* and *write mode* - (relative or absolute) permanently as TaurusGUI settings. -* Do not create encoder widget in PMTV if the motod does not have encoder - in order to avoid errors comming from the polling (#209, #1288) +* Improve user experience with PMTV: + * Store PMTV (motor widget) configurations: *expert view* and *write mode* + (relative or absolute) permanently as TaurusGUI settings. + * Do not create encoder widget in PMTV if the motod does not have encoder + in order to avoid errors comming from the polling (#209, #1288) + * Change limit switches indicators from buttons to labels (#210, #1290) * Improve documentation (#1241) * Better macro exception message and hint to use `www` (#1191) * Add basic information to "how to write custom recorder" to From d72c00c739090f28b3b24d2317ceb1dde5e2a4e0 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 21 May 2020 16:53:53 +0200 Subject: [PATCH 591/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a291f97ee6..5ee8af547e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ This file follows the formats and conventions from [keepachangelog.com] using context menu option (#1242) * Improve user experience with PMTV: * Store PMTV (motor widget) configurations: *expert view* and *write mode* - (relative or absolute) permanently as TaurusGUI settings. + (relative or absolute) permanently as TaurusGUI settings (#1286) * Do not create encoder widget in PMTV if the motod does not have encoder in order to avoid errors comming from the polling (#209, #1288) * Change limit switches indicators from buttons to labels (#210, #1290) From b38a6fe729280dbf65ce7529e51c7768e0b54c94 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 21 May 2020 19:04:11 +0200 Subject: [PATCH 592/830] Fix dummy 1D: cast to int number of new frames Commit 223cdf7 wrongly removed the cast assuming that in Python 3 // operation always returns integer number. This however depends on the operands types. When one of the operands is float, the result is float. Here we are dividing times which are floats. Re-introduce the cast. The rest of the said commit is ok cause it was actually removing float casts for / operations which in Python 3 always return float. --- src/sardana/pool/poolcontrollers/DummyOneDController.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/poolcontrollers/DummyOneDController.py b/src/sardana/pool/poolcontrollers/DummyOneDController.py index 669a814d86..d49c342ec9 100644 --- a/src/sardana/pool/poolcontrollers/DummyOneDController.py +++ b/src/sardana/pool/poolcontrollers/DummyOneDController.py @@ -174,7 +174,7 @@ def _updateChannelValue(self, axis, elapsed_time): elif self._synchronization in (AcqSynch.HardwareTrigger, AcqSynch.HardwareGate): if self.integ_time is not None: - n = t // self.integ_time + n = int(t // self.integ_time) cp = 0 if n > self._repetitions: cp = n - self._repetitions From eb963ef526c6709009490a6ba75cc33bf50f1c4d Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 22 May 2020 17:11:48 +0200 Subject: [PATCH 593/830] Remove redundant reset of pending operation Pending operation is reset on switching between the editors. Do not reset it again when emitting editing finished. --- src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index b2d8420bf9..415bb5f293 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -1382,8 +1382,6 @@ def keyPressEvent(self, key_event): @Qt.pyqtSlot() def emitEditingFinished(self): self.applied.emit() - if self.cbAbsoluteRelative.currentIndex() == 1: - self.resetPendingOperations() ################################################## From 9d8555342b763e7a7962cd04ca7899c8e41074a9 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 22 May 2020 17:17:24 +0200 Subject: [PATCH 594/830] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ee8af547e..d8760c40be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,8 +52,10 @@ This file follows the formats and conventions from [keepachangelog.com] * Remove Taurus deprecated code what reduces deprecation warnings (#1206, #1252) * Macro functions which define results now really report the result (#1238) * Use of env and hints in `macro` function decorator (#1239) -* PMTV widget not updating the following attributes: limit switches, state - and status (#1244) +* Fix several issues with PMTV: + * Reset pending operations of absolute movement on switching to relative movement (#1293) + * PMTV widget not updating the following attributes: limit switches, state + and status (#1244) * Avoid Taurus GUI slowness on startup and changing of perspectives due to too large macroexecutor history by limitting it to 100 - configurable with `MACROEXECUTOR_MAX_HISTORY` (#1307) From 39857b61ead6cbcf74f5366998e03eaa0ef63a27 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 25 May 2020 19:59:04 +0200 Subject: [PATCH 595/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8760c40be..c8f5a27e5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Better macro exception message and hint to use `www` (#1191) * Add basic information to "how to write custom recorder" to the documentation (#1275) +* Register a TaurusValue factory for pool widgets (#1333) ### Fixed From 428ebcc8aef741e03be189b480df74327f541a76 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 26 May 2020 16:48:43 +0200 Subject: [PATCH 596/830] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8f5a27e5c..27b377eae8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,10 @@ This file follows the formats and conventions from [keepachangelog.com] * Measurement group (Taurus extension) configuration API with methods to set/get: enabled, output, plot type, plot axes, timer, monitor, synchronizer, value ref enabled, value ref pattern parameters(#867) -* Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) +* Experiment configuration (expconf) macros + * Measurement group configuration macros: `set_meas_conf` and `get_meas_conf` (#690) + * Active measurement group selection macros: `set_meas` and `get_meas` (#690) + * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) * Dump info on channels if MG acq fails in step scan, ct and uct (#1308) * Add timestamp to element's dumped information (#1308) * Instruments creation and configuration in sar_demo (#1198) From 0a546118073684a5bc83af607ce2bd96d07c0731 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 27 May 2020 11:03:32 +0200 Subject: [PATCH 597/830] Consider case when there is no settings file --- .../sequenceeditor/sequenceeditor.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index b1768d47c3..8c17738597 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -987,15 +987,19 @@ def createSequencer(args, options): sequencer = TaurusSequencer() sequencer.setModelInConfig(True) sequencer.doorChanged.connect(sequencer.onDoorChanged) - settings = sequencer.getQSettings() - taurus_config_raw = settings.value("TaurusConfig") - taurus_config = pickle.loads(taurus_config_raw.data()) - oldmodel = taurus_config['__itemConfigurations__']['model'] + load_settings = True if len(args) == 2: sequencer.setModel(args[0]) sequencer.doorChanged.emit(args[1]) - if args[0] == oldmodel: - sequencer.loadSettings() + settings = sequencer.getQSettings() + taurus_config_raw = settings.value("TaurusConfig") + if taurus_config_raw is not None: + taurus_config = pickle.loads(taurus_config_raw.data()) + oldmodel = taurus_config['__itemConfigurations__']['model'] + if args[0] == oldmodel: + load_settings = False + if load_settings: + sequencer.loadSettings() if options.file is not None: sequencer.taurusSequencerWidget.loadFile(options.file) return sequencer From 6d7aef747f1588facd36d4ac6ec958755f8f5870 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 27 May 2020 11:26:28 +0200 Subject: [PATCH 598/830] Use base class setModel in sequencer and macroexecutor --- .../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 4 +--- .../extra_macroexecutor/sequenceeditor/sequenceeditor.py | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py index 4efadbc5f7..e1a4193a57 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py @@ -1056,9 +1056,7 @@ def onDoorChanged(self, doorName): self.taurusMacroExecutorWidget.onDoorChanged(doorName) def setModel(self, model): - """Reimplemented from :meth:`TaurusWidget.setModel`""" - TaurusWidget.setModel(self, model) - self.setWindowTitle(Qt.QApplication.applicationName() + ": " + model) + MacroExecutionWindow.setModel(self, model) self.taurusMacroExecutorWidget.setModel(model) @classmethod diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 8c17738597..d949c0d043 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -962,9 +962,7 @@ def onDoorChanged(self, doorName): self.taurusSequencerWidget.onDoorChanged(doorName) def setModel(self, model): - """Reimplemented from :meth:`TaurusWidget.setModel`""" - TaurusWidget.setModel(self, model) - self.setWindowTitle(Qt.QApplication.applicationName() + ": " + model) + MacroExecutionWindow.setModel(self, model) self.taurusSequencerWidget.setModel(model) @classmethod From aea10cd6589f151b45d2e7e0ee3140e7058ed7df Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 27 May 2020 11:47:13 +0200 Subject: [PATCH 599/830] Use model-in-config only for TaurusSequencerWidget According to the discussion from sardana-org/sardana#1278 the model-in-config does not have sence for the sub-widget nor beeing set multiple times. Set it just for the TaurusSequencerWidget when used within the sequencer app. --- .../extra_macroexecutor/sequenceeditor/sequenceeditor.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index d949c0d043..1a62d5df52 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -353,7 +353,6 @@ def __init__(self, parent=None, designMode=False): self.sequenceEditor = TaurusWidget() splitter.addWidget(self.sequenceEditor) - self.sequenceEditor.setModelInConfig(True) self.sequenceEditor.setLayout(Qt.QVBoxLayout()) self.sequenceEditor.layout().setContentsMargins(0, 0, 0, 0) @@ -437,7 +436,6 @@ def __init__(self, parent=None, designMode=False): macroLabel = Qt.QLabel("Macro:") macroLayout.addWidget(macroLabel) self.macroComboBox = MacroComboBox(self) - self.macroComboBox.setModelInConfig(True) self.macroComboBox.setModelColumn(0) self.macroComboBox.setSizePolicy( Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum) @@ -925,8 +923,6 @@ def __init__(self, parent=None, designMode=False): MacroExecutionWindow.__init__(self) def initComponents(self): - #@todo: take care about storing model - self.setModelInConfig(True) self.taurusSequencerWidget = TaurusSequencerWidget(self) self.taurusSequencerWidget.setModelInConfig(True) self.taurusSequencerWidget.doorChanged.connect( @@ -972,7 +968,6 @@ def getQtDesignerPluginInfo(cls): def createSequencerWidget(args): sequencer = TaurusSequencerWidget() - sequencer.setModelInConfig(True) sequencer.doorChanged.connect(sequencer.onDoorChanged) if len(args) == 2: @@ -983,7 +978,6 @@ def createSequencerWidget(args): def createSequencer(args, options): sequencer = TaurusSequencer() - sequencer.setModelInConfig(True) sequencer.doorChanged.connect(sequencer.onDoorChanged) load_settings = True if len(args) == 2: From e6db37483640297b3833eebd95d733ba769bf052 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 27 May 2020 12:07:37 +0200 Subject: [PATCH 600/830] Fix flake8 --- .../qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py index 1a62d5df52..45c6feab7c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py @@ -991,7 +991,7 @@ def createSequencer(args, options): if args[0] == oldmodel: load_settings = False if load_settings: - sequencer.loadSettings() + sequencer.loadSettings() if options.file is not None: sequencer.taurusSequencerWidget.loadFile(options.file) return sequencer From 7988b64ba8280d15b31905435f635a134b5a1642 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 27 May 2020 13:13:21 +0200 Subject: [PATCH 601/830] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b377eae8..71282643ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `macroexecutor` correctly loads macro combo box if it was started with server down and server started afterwards (#599, #1278) * `TaurusMacroExecutorWidget` does not use _parent model_ feature (#599, #1278) +* `TaurusSequencerWidget` does not use _parent model_ feature (#1284) * MacroButton with repeat parameters (#1172, #1314) * Macro plotting in new versions of ipython and matplotlib require extra call to `pyplot.draw()` to make sure that the plot is refreshed (#1280) @@ -107,6 +108,9 @@ This file follows the formats and conventions from [keepachangelog.com] the old sense for setting a global measurement group timer/monitor: `getTimer()`, `setTimer()`, `getMonitor()` were moved to `MGConfiguration` class and are deprecated (#867) +* `macroexecutor` and `sequencer` discard settings if the models passed + as command line arguments had changed with respect to the previous execution + (#1278, #1284) ### Removed From 5ae01fce8f7f8714267edcd383fcb7e7e2be324d Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 27 May 2020 13:14:34 +0200 Subject: [PATCH 602/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71282643ec..e9bdd39150 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This file follows the formats and conventions from [keepachangelog.com] ### Added -* Support to Python >= 3.5 (#1089, #1173, #1201, #1313) +* Support to Python >= 3.5 (#1089, #1173, #1201, #1313, #1336) * Showscan online based on pyqtgraph (#1285) * multiple plots in the same MultiPlot widget (as opposed to different panels before) * option to group curves by x-axis or individual plot per curve From a9de6a36ea8ad4911a95393459e995b2da87b085 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 27 May 2020 13:22:48 +0200 Subject: [PATCH 603/830] Add backwards compatibility for PseudoCounter and ZeroDExpChannel --- src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py index 2406bfbd22..ee9ee1646d 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/formitemfactory.py @@ -28,15 +28,15 @@ of custom TaurusForm item widgets. """ -from . import PoolMotorTV, PoolChannelTV, PoolIORegisterTV +from . import PoolMotorTV, PoolChannelTV, PoolIORegisterTV, _PoolChannelTV T_FORM_POOL_WIDGET_MAP = { "SimuMotor": PoolMotorTV, "Motor": PoolMotorTV, "PseudoMotor": PoolMotorTV, - "PseudoCounter": PoolChannelTV, + "PseudoCounter": _PoolChannelTV, "CTExpChannel": PoolChannelTV, - "ZeroDExpChannel": PoolChannelTV, + "ZeroDExpChannel": _PoolChannelTV, "OneDExpChannel": PoolChannelTV, "TwoDExpChannel": PoolChannelTV, "IORegister": PoolIORegisterTV, From 38c69613ff1005a4c4c9d74d35b8433939a66924 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 27 May 2020 17:26:59 +0200 Subject: [PATCH 604/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9bdd39150..fed76fd210 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Add basic information to "how to write custom recorder" to the documentation (#1275) * Register a TaurusValue factory for pool widgets (#1333) +* Direct links to Sardana-Taurus model API (#1335) ### Fixed From da2a350e529262466921bb8d98142ab610932f88 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 27 May 2020 17:46:58 +0200 Subject: [PATCH 605/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fed76fd210..93d4569b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Dump info on channels if MG acq fails in step scan, ct and uct (#1308) * Add timestamp to element's dumped information (#1308) * Instruments creation and configuration in sar_demo (#1198) +* Allow _experimental channel acquisition_ with PoolChannelTaurusValue (PCTV) widget (#1203) * Documentation to Taurus Extensions of Sardana Devices: MacroServer part and the whole Sardana part of the Qt Taurus Extensions (#1228, #1233) * Advertise newfile macro in case no ScanDir or ScanFile is set (#1254, #1258) From f2ea076c30b7426750cc7a5f59a66a7b8d4b7bd7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 30 May 2020 23:05:24 +0200 Subject: [PATCH 606/830] Rename Synchronization attribute to SynchDescritpion Rename in Tango MeasurementGroup, core MeasurementGroup and Taurus extension level. This change was agreed in sardana#867. --- doc/source/devel/api/api_measurementgroup.rst | 2 +- src/sardana/macroserver/scan/gscan.py | 69 +++++++------- src/sardana/pool/poolacquisition.py | 8 +- .../test/test_DummyTriggerGateController.py | 24 ++--- src/sardana/pool/pooldefs.py | 2 +- src/sardana/pool/poolmeasurementgroup.py | 39 ++++---- src/sardana/pool/poolsynchronization.py | 23 ++--- src/sardana/pool/test/test_acquisition.py | 16 ++-- .../pool/test/test_measurementgroup.py | 92 +++++++++---------- src/sardana/tango/pool/MeasurementGroup.py | 39 ++++---- .../tango/pool/test/test_measurementgroup.py | 12 +-- src/sardana/taurus/core/tango/sardana/pool.py | 29 +++--- 12 files changed, 184 insertions(+), 171 deletions(-) diff --git a/doc/source/devel/api/api_measurementgroup.rst b/doc/source/devel/api/api_measurementgroup.rst index f1355afbb1..aa6cc743c5 100644 --- a/doc/source/devel/api/api_measurementgroup.rst +++ b/doc/source/devel/api/api_measurementgroup.rst @@ -42,7 +42,7 @@ latency time Latency time between two consecutive acquisitions in the same acquisition operation. -synchronization +synch description Describes the acquisition operation synchronization. It is composed from the group(s) of equidistant acquisitions described by the following parameters: diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 4d9dda33d7..e7830885c6 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2071,19 +2071,20 @@ def is_measurement_group_compatible(measurement_group): return is_compatible, non_compatible_channels -def generate_timestamps(synchronization, initial_timestamp=0): +def generate_timestamps(synch_description, initial_timestamp=0): """Generate theoretical timestamps at which the acquisition should take place according to the synchronization description - :param synchronization: synchronization description data structure - :type synchronization: list + :param synch_description: synchronization description data + structure + :type synch_description: list :param initial_timestamp: initial timestamp to start from :type initial_timestamp: float """ ret = dict() timestamp = initial_timestamp index = 0 - for group in synchronization: + for group in synch_description: delay = group[SynchParam.Delay][SynchDomain.Time] total = group[SynchParam.Total][SynchDomain.Time] repeats = group[SynchParam.Repeats] @@ -2389,8 +2390,8 @@ def _go_through_waypoints(self): SynchParam.Total: {SynchDomain.Position: total_position, SynchDomain.Time: total_time}, SynchParam.Repeats: repeats}] - self.debug('Synchronization: %s' % synch) - measurement_group.setSynchronization(synch) + self.debug('SynchDescription: %s' % synch) + measurement_group.setSynchDescription(synch) self.macro.checkPoint() # extra post configuration @@ -2711,9 +2712,9 @@ class TScan(GScan, CAcquisition): """Time scan. Macro that employs the time scan must define the synchronization - information in either of the following two ways: - - synchronization attribute that follows the synchronization format of - the measurement group + description in either of the following two ways: + - synch_description attribute that follows the synchronization + description format of the measurement group - integ_time, nb_points and latency_time (optional) attributes """ def __init__(self, macro, generator=None, @@ -2722,9 +2723,10 @@ def __init__(self, macro, generator=None, moveables=moveables, env=env, constraints=constraints, extrainfodesc=extrainfodesc) CAcquisition.__init__(self) - self._synchronization = None + self._synch_description = None - def _create_synchronization(self, active_time, repeats, latency_time=0): + def _create_synch_description(self, active_time, repeats, + latency_time=0): delay_time = 0 mg_latency_time = self.measurement_group.getLatencyTime() if mg_latency_time > latency_time: @@ -2732,38 +2734,39 @@ def _create_synchronization(self, active_time, repeats, latency_time=0): mg_latency_time) latency_time = mg_latency_time total_time = active_time + latency_time - synchronization = [ + synch_description = [ {SynchParam.Delay: {SynchDomain.Time: delay_time}, SynchParam.Active: {SynchDomain.Time: active_time}, SynchParam.Total: {SynchDomain.Time: total_time}, SynchParam.Repeats: repeats}] - return synchronization + return synch_description - def get_synchronization(self): - if self._synchronization is not None: - return self._synchronization - if hasattr(self.macro, "synchronization"): - synchronization = self.macro.synchronization + def get_synch_description(self): + if self._synch_description is not None: + return self._synch_description + if hasattr(self.macro, "synch_description"): + synch_description = self.macro.synch_description else: try: active_time = getattr(self.macro, "integ_time") repeats = getattr(self.macro, "nb_points") except AttributeError: - msg = "Macro object is missing synchronization attributes" + msg = "Macro object is missing synchronization description " \ + "attributes" raise ScanSetupError(msg) latency_time = getattr(self.macro, "latency_time", 0) - synchronization = self._create_synchronization(active_time, - repeats, - latency_time) - self._synchronization = synchronization - return synchronization + synch_description = \ + self._create_synch_description(active_time, repeats, + latency_time) + self._synch_description = synch_description + return synch_description - synchronization = property(get_synchronization) + synch_description = property(get_synch_description) def scan_loop(self): macro = self.macro measurement_group = self.measurement_group - synchronization = self.synchronization + synch_description = self.synch_description compatible, channels = \ self.is_measurement_group_compatible(measurement_group) @@ -2774,7 +2777,8 @@ def scan_loop(self): (measurement_group.getName(), macro.getName()) raise ScanException(msg) - theoretical_timestamps = generate_timestamps(synchronization) + theoretical_timestamps = \ + generate_timestamps(synch_description) self.data.initial_data = theoretical_timestamps msg = "Relative timestamp (dt) column contains theoretical values" self.macro.warning(msg) @@ -2785,7 +2789,7 @@ def scan_loop(self): yield 0 measurement_group.setNbStarts(1) - measurement_group.count_continuous(synchronization, + measurement_group.count_continuous(synch_description, self.value_buffer_changed) self.debug("Waiting for value buffer events to be processed") self.wait_value_buffer() @@ -2813,12 +2817,13 @@ def _estimate(self): i = self.macro.getIntervalEstimation() return t, i - if not hasattr(self.macro, "synchronization"): - raise AttributeError("synchronization is mandatory to estimate") - synchronization = self.macro.synchronization + if not hasattr(self.macro, "synch_description"): + raise AttributeError("synch_description is mandatory " + "to estimate") + synch_description = self.macro.synch_description time = 0 intervals = 0 - for group in synchronization: + for group in synch_description: delay = group[SynchParam.Delay][SynchDomain.Time] time += delay total = group[SynchParam.Total][SynchDomain.Time] diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index b2a66ad7d9..dbe2abaaef 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -365,7 +365,7 @@ def event_received(self, *args, **kwargs): self.debug('Stopping ZeroD acquisition.') self._0d_acq.stop_action() - def prepare(self, config, acq_mode, value, synchronization=None, + def prepare(self, config, acq_mode, value, synch_description=None, moveable=None, sw_synch_initial_domain=None, nb_starts=1, **kwargs): """Prepare measurement process. @@ -382,8 +382,8 @@ def prepare(self, config, acq_mode, value, synchronization=None, ctrls_sw = [] ctrls_sw_start = [] - repetitions = synchronization.repetitions - latency = synchronization.passive_time + repetitions = synch_description.repetitions + latency = synch_description.passive_time # Prepare controllers synchronized by hardware acq_sync_hw = [AcqSynch.HardwareTrigger, AcqSynch.HardwareStart, AcqSynch.HardwareGate] @@ -442,7 +442,7 @@ def prepare(self, config, acq_mode, value, synchronization=None, # Prepare synchronizer controllers ctrls = config.get_synch_ctrls(enabled=True) ctrls_synch = get_acq_ctrls(ctrls) - synch_args = (ctrls_synch, synchronization) + synch_args = (ctrls_synch, synch_description) synch_kwargs = {'moveable': moveable, 'sw_synch_initial_domain': sw_synch_initial_domain} synch_kwargs.update(kwargs) diff --git a/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py b/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py index d9e0a680f2..3b7c0f908e 100644 --- a/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py +++ b/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py @@ -8,19 +8,19 @@ createPoolTriggerGate, dummyPoolTGCtrlConf01, dummyTriggerGateConf01, \ createControllerConfiguration -synchronization1 = [{SynchParam.Delay: {SynchDomain.Time: 0}, - SynchParam.Active: {SynchDomain.Time: .03}, - SynchParam.Total: {SynchDomain.Time: .1}, - SynchParam.Repeats: 0}] +synch_description1 = [{SynchParam.Delay: {SynchDomain.Time: 0}, + SynchParam.Active: {SynchDomain.Time: .03}, + SynchParam.Total: {SynchDomain.Time: .1}, + SynchParam.Repeats: 0}] -synchronization2 = [{SynchParam.Delay: {SynchDomain.Time: 0}, - SynchParam.Active: {SynchDomain.Time: .01}, - SynchParam.Total: {SynchDomain.Time: .02}, - SynchParam.Repeats: 10}] +synch_description2 = [{SynchParam.Delay: {SynchDomain.Time: 0}, + SynchParam.Active: {SynchDomain.Time: .01}, + SynchParam.Total: {SynchDomain.Time: .02}, + SynchParam.Repeats: 10}] -@insertTest(helper_name='generation', synchronization=synchronization1) -@insertTest(helper_name='generation', synchronization=synchronization2) +@insertTest(helper_name='generation', synch_description=synch_description1) +@insertTest(helper_name='generation', synch_description=synch_description2) class PoolDummyTriggerGateTestCase(unittest.TestCase): """Parameterizable integration test of the PoolSynchronization action and the DummTriggerGateController. @@ -49,10 +49,10 @@ def setUp(self): self.tg_action = PoolSynchronization(self.dummy_tg) self.tg_action.add_element(self.dummy_tg) - def generation(self, synchronization): + def generation(self, synch_description): """Verify that the created PoolTGAction start_action starts correctly the involved controller.""" - args = ([self.ctrl_conf], synchronization) + args = ([self.ctrl_conf], synch_description) self.tg_action.start_action(*args) self.tg_action.action_loop() # TODO: add asserts applicable to a dummy controller e.g. listen to diff --git a/src/sardana/pool/pooldefs.py b/src/sardana/pool/pooldefs.py index 37e1a24935..e454d41063 100644 --- a/src/sardana/pool/pooldefs.py +++ b/src/sardana/pool/pooldefs.py @@ -89,7 +89,7 @@ class SynchDomain(SynchEnum): class SynchParam(SynchEnum): - """Enumeration of synchronization's group parameters. + """Enumeration of synchronization description group parameters. - Delay - initial delay (relative to the synchronization start) - Total - total interval diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 97f0907a16..db10a073d5 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -45,7 +45,7 @@ from sardana.pool.poolgroupelement import PoolGroupElement from sardana.pool.poolacquisition import PoolAcquisition -from sardana.pool.poolsynchronization import SynchronizationDescription +from sardana.pool.poolsynchronization import SynchDescription from sardana.pool.poolexternal import PoolExternalObject from sardana.taurus.core.tango.sardana import PlotType, Normalization @@ -1010,7 +1010,7 @@ def __init__(self, **kwargs): # by default software synchronizer initial domain is set to Position self._sw_synch_initial_domain = SynchDomain.Position - self._synchronization = SynchronizationDescription() + self._synch_description = SynchDescription() kwargs['elem_type'] = ElementType.MeasurementGroup PoolGroupElement.__init__(self, **kwargs) @@ -1124,14 +1124,15 @@ def get_timer(self): # ------------------------------------------------------------------------- def get_integration_time(self): - integration_time = self._synchronization.active_time + integration_time = self._synch_description.active_time if isinstance(integration_time, float): return integration_time elif len(integration_time) == 0: - raise Exception("The synchronization group has not been" - " initialized") + raise Exception("The synchronization description group has not" + " been initialized") elif len(integration_time) > 1: - raise Exception("There are more than one synchronization groups") + raise Exception("There are more than one synchronization" + "description groups") def set_integration_time(self, integration_time, propagate=1): total_time = integration_time + self.latency_time @@ -1139,7 +1140,7 @@ def set_integration_time(self, integration_time, propagate=1): SynchParam.Active: {SynchDomain.Time: integration_time}, SynchParam.Total: {SynchDomain.Time: total_time}, SynchParam.Repeats: 1}] - self.set_synchronization(synch) + self.set_synch_description(synch) if not propagate: return self.fire_event(EventType("integration_time", priority=propagate), @@ -1184,21 +1185,24 @@ def set_acquisition_mode(self, acquisition_mode, propagate=1): doc="the current acquisition mode") # ------------------------------------------------------------------------- - # synchronization + # synch_description # ------------------------------------------------------------------------- - def get_synchronization(self): - return self._synchronization + def get_synch_description(self): + return self._synch_description - def set_synchronization(self, synchronization, propagate=1): - self._synchronization = SynchronizationDescription(synchronization) + def set_synch_description(self, description, propagate=1): + self._synch_description = \ + SynchDescription(description) self._config_dirty = True # acquisition mode goes to configuration if not propagate: return - self.fire_event(EventType("synchronization", priority=propagate), - synchronization) + self.fire_event(EventType("synch_description", + priority=propagate), + description) - synchronization = property(get_synchronization, set_synchronization, + synch_description = property(get_synch_description, + set_synch_description, doc="the current acquisition mode") # ------------------------------------------------------------------------- @@ -1218,7 +1222,8 @@ def set_moveable(self, moveable, propagate=1, to_fqdn=True): moveable) moveable = property(get_moveable, set_moveable, - doc="moveable source used in synchronization") + doc="moveable source used in synchronization " + "description") # ------------------------------------------------------------------------- # latency time @@ -1317,7 +1322,7 @@ def prepare(self, multiple=1): self.acquisition.prepare(self.configuration, self.acquisition_mode, value, - self._synchronization, + self._synch_description, self._moveable_obj, self.sw_synch_initial_domain, self.nb_starts, diff --git a/src/sardana/pool/poolsynchronization.py b/src/sardana/pool/poolsynchronization.py index feb1934a67..d1b88f80f4 100644 --- a/src/sardana/pool/poolsynchronization.py +++ b/src/sardana/pool/poolsynchronization.py @@ -27,7 +27,7 @@ """This module is part of the Python Pool library. It defines the classes for the synchronization""" -__all__ = ["PoolSynchronization", "SynchronizationDescription", "TGChannel"] +__all__ = ["PoolSynchronization", "SynchDescription", "TGChannel"] import time from functools import partial @@ -61,7 +61,7 @@ def __getattr__(self, name): return getattr(self.element, name) -class SynchronizationDescription(list): +class SynchDescription(list): """Synchronization description. It is composed from groups - repetitions of equidistant synchronization events. Each group is described by :class:`~sardana.pool.pooldefs.SynchParam` parameters which may have @@ -90,8 +90,9 @@ def passive_time(self): def _get_param(self, param, domain=SynchDomain.Time): """ Extract parameter from synchronization description and its groups. If - there is only one group in the synchronization then returns float - with the value. Otherwise a list of floats with different values. + there is only one group in the synchronization description then + returns float with the value. Otherwise a list of floats with + different values. :param param: parameter type :type param: :class:`~sardana.pool.pooldefs.SynchParam` @@ -133,15 +134,15 @@ def __init__(self, main_element, name="Synchronization"): def add_listener(self, listener): self._listener = listener - def start_action(self, ctrls, synchronization, moveable=None, + def start_action(self, ctrls, synch_description, moveable=None, sw_synch_initial_domain=None, *args, **kwargs): """Start synchronization action. :param ctrls: list of enabled trigger/gate controllers :type ctrls: list - :param synchronization: synchronization description - :type synchronization: - :class:`~sardana.pool.poolsynchronization.SynchronizationDescription` + :param synch_description: synchronization description + :type synch_description: + :class:`~sardana.pool.poolsynchronization.SynchDescription` :param moveable: (optional) moveable object used as the synchronization source in the Position domain :type moveable: :class:`~sardna.pool.poolmotor.PoolMotor` or @@ -159,12 +160,12 @@ def start_action(self, ctrls, synchronization, moveable=None, pool_ctrl.ctrl.PreSynchAll() for channel in ctrl.get_channels(enabled=True): axis = channel.axis - ret = pool_ctrl.ctrl.PreSynchOne(axis, synchronization) + ret = pool_ctrl.ctrl.PreSynchOne(axis, synch_description) if not ret: msg = ("%s.PreSynchOne(%d) returns False" % (ctrl.name, axis)) raise Exception(msg) - pool_ctrl.ctrl.SynchOne(axis, synchronization) + pool_ctrl.ctrl.SynchOne(axis, synch_description) pool_ctrl.ctrl.SynchAll() # attaching listener (usually acquisition action) @@ -172,7 +173,7 @@ def start_action(self, ctrls, synchronization, moveable=None, if self._listener is not None: if sw_synch_initial_domain is not None: self._synch_soft.initial_domain = sw_synch_initial_domain - self._synch_soft.set_configuration(synchronization) + self._synch_soft.set_configuration(synch_description) self._synch_soft.add_listener(self._listener) remove_acq_listener = partial(self._synch_soft.remove_listener, self._listener) diff --git a/src/sardana/pool/test/test_acquisition.py b/src/sardana/pool/test/test_acquisition.py index 6d5d3dbda6..b6598aa5d5 100644 --- a/src/sardana/pool/test/test_acquisition.py +++ b/src/sardana/pool/test/test_acquisition.py @@ -229,11 +229,11 @@ def continuous_acquisition(self, offset, active_interval, passive_interval, SynchParam.Total: {SynchDomain.Time: total_interval}, SynchParam.Repeats: repetitions } - synchronization = [group] + synch_description = [group] # get the current number of jobs jobs_before = get_thread_pool().qsize self.hw_acq.run(hw_ctrls, integ_time, repetitions, 0) - self.synchronization.run(synch_ctrls, synchronization) + self.synchronization.run(synch_ctrls, synch_description) # waiting for acquisition and synchronization to finish while (self.hw_acq.is_running() or self.sw_acq.is_running() @@ -311,10 +311,10 @@ def acquire(self, integ_time, repetitions, latency_time): SynchParam.Total: {SynchDomain.Time: total_interval}, SynchParam.Repeats: repetitions } - synchronization = [group] + synch_description = [group] # get the current number of jobs jobs_before = get_thread_pool().qsize - self.synchronization.run([], synchronization) + self.synchronization.run([], synch_description) self.wait_finish() self.do_asserts(repetitions, jobs_before, strict=False) @@ -370,10 +370,10 @@ def acquire(self, integ_time, repetitions, latency_time): SynchParam.Total: {SynchDomain.Time: total_interval}, SynchParam.Repeats: repetitions } - synchronization = [group] + synch_description = [group] # get the current number of jobs jobs_before = get_thread_pool().qsize - self.synchronization.run([], synchronization) + self.synchronization.run([], synch_description) self.wait_finish() self.do_asserts(repetitions, jobs_before, strict=False) @@ -415,11 +415,11 @@ def acquire(self, integ_time, repetitions, latency_time): SynchParam.Total: {SynchDomain.Time: total_interval}, SynchParam.Repeats: repetitions } - synchronization = [group] + synch_description = [group] # get the current number of jobs jobs_before = get_thread_pool().qsize self.acquisition.run(ctrls, integ_time, repetitions, 0) - self.synchronization.run(synch_ctrls, synchronization) + self.synchronization.run(synch_ctrls, synch_description) self.wait_finish() self.do_asserts(repetitions, jobs_before) diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index fe9170e727..d52c784038 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -102,38 +102,38 @@ def acq_asserts(self, channel_names, repetitions): (ch_name, ch_data_len, repetitions) self.assertEqual(ch_data_len, repetitions, msg) - def meas_double_acquisition(self, config, synchronization, moveable=None): + def meas_double_acquisition(self, config, synch_description, moveable=None): """ Run two acquisition with the same measurement group, first with multiple repetitions and then one repetition. """ channel_names = self.prepare_meas(config) # setting measurement parameters - self.pmg.set_synchronization(synchronization) + self.pmg.set_synch_description(synch_description) self.pmg.set_moveable(moveable, to_fqdn=False) repetitions = 0 - for group in synchronization: + for group in synch_description: repetitions += group[SynchParam.Repeats] self.prepare_attribute_listener() self.acquire() self.acq_asserts(channel_names, repetitions) self.remove_attribute_listener() - synchronization = [{SynchParam.Delay: {SynchDomain.Time: 0}, - SynchParam.Active: {SynchDomain.Time: 0.1}, - SynchParam.Total: {SynchDomain.Time: 0.2}, - SynchParam.Repeats: 1}] - self.pmg.synchronization = synchronization + synch_description = [{SynchParam.Delay: {SynchDomain.Time: 0}, + SynchParam.Active: {SynchDomain.Time: 0.1}, + SynchParam.Total: {SynchDomain.Time: 0.2}, + SynchParam.Repeats: 1}] + self.pmg.synch_description = synch_description self.acquire() # TODO: implement asserts of Timer acquisition - def meas_double_acquisition_samemode(self, config, synchronization, + def meas_double_acquisition_samemode(self, config, synch_description, moveable=None): """ Run two acquisition with the same measurement group. """ channel_names = self.prepare_meas(config) - self.pmg.set_synchronization(synchronization) + self.pmg.set_synch_description(synch_description) self.pmg.set_moveable(moveable, to_fqdn=False) repetitions = 0 - for group in synchronization: + for group in synch_description: repetitions += group[SynchParam.Repeats] self.prepare_attribute_listener() self.acquire() @@ -145,7 +145,7 @@ def meas_double_acquisition_samemode(self, config, synchronization, self.remove_attribute_listener() # TODO: implement asserts of Timer acquisition - def consecutive_acquisitions(self, pool, config, synchronization): + def consecutive_acquisitions(self, pool, config, synch_description): # creating mg user configuration and obtaining channel ids mg_conf, channel_ids, channel_names = createMGUserConfiguration( pool, config) @@ -153,23 +153,23 @@ def consecutive_acquisitions(self, pool, config, synchronization): # setting mg configuration - this cleans the action cache! self.pmg.set_configuration_from_user(mg_conf, to_fqdn=False) repetitions = 0 - for group in synchronization: + for group in synch_description: repetitions += group[SynchParam.Repeats] self.prepare_attribute_listener() self.acquire() self.acq_asserts(channel_names, repetitions) - def meas_cont_acquisition(self, config, synchronization, moveable=None, + def meas_cont_acquisition(self, config, synch_description, moveable=None, second_config=None): """Executes measurement using the measurement group. Checks the lengths of the acquired data. """ jobs_before = get_thread_pool().qsize channel_names = self.prepare_meas(config) - self.pmg.set_synchronization(synchronization) + self.pmg.set_synch_description(synch_description) self.pmg.set_moveable(moveable, to_fqdn=False) repetitions = 0 - for group in synchronization: + for group in synch_description: repetitions += group[SynchParam.Repeats] self.prepare_attribute_listener() self.acquire() @@ -177,7 +177,7 @@ def meas_cont_acquisition(self, config, synchronization, moveable=None, if second_config is not None: self.consecutive_acquisitions( - self.pool, second_config, synchronization) + self.pool, second_config, synch_description) # checking if there are no pending jobs jobs_after = get_thread_pool().qsize @@ -189,13 +189,13 @@ def stop_acquisition(self): """Method used to abort a running acquisition""" self.pmg.stop() - def meas_cont_stop_acquisition(self, config, synchronization, + def meas_cont_stop_acquisition(self, config, synch_description, moveable=None): """Executes measurement using the measurement group and tests that the acquisition can be stopped. """ self.prepare_meas(config) - self.pmg.synchronization = synchronization + self.pmg.synch_description = synch_description self.pmg.set_moveable(moveable, to_fqdn=False) self.prepare_attribute_listener() @@ -221,22 +221,22 @@ def meas_cont_stop_acquisition(self, config, synchronization, enumerate(zip(*list(self.attr_listener.data.values()))): print(i, record) - def meas_contpos_acquisition(self, config, synchronization, moveable, + def meas_contpos_acquisition(self, config, synch_description, moveable, second_config=None): # TODO: this code is ready only for one group configuration - initial = synchronization[0][SynchParam.Initial][SynchDomain.Position] - total = synchronization[0][SynchParam.Total][SynchDomain.Position] - repeats = synchronization[0][SynchParam.Repeats] + initial = synch_description[0][SynchParam.Initial][SynchDomain.Position] + total = synch_description[0][SynchParam.Total][SynchDomain.Position] + repeats = synch_description[0][SynchParam.Repeats] position = initial + total * repeats mot = self.mots[moveable] mot.set_base_rate(0) mot.set_acceleration(0.1) mot.set_velocity(0.5) channel_names = self.prepare_meas(config) - self.pmg.synchronization = synchronization + self.pmg.synch_description = synch_description self.pmg.set_moveable(moveable, to_fqdn=False) repetitions = 0 - for group in synchronization: + for group in synch_description: repetitions += group[SynchParam.Repeats] self.prepare_attribute_listener() self.pmg.prepare() @@ -254,17 +254,17 @@ def tearDown(self): self.pmg = None -synchronization1 = [{SynchParam.Delay: {SynchDomain.Time: 0}, +synch_description1 = [{SynchParam.Delay: {SynchDomain.Time: 0}, SynchParam.Active: {SynchDomain.Time: .01}, SynchParam.Total: {SynchDomain.Time: .02}, SynchParam.Repeats: 10}] -synchronization2 = [{SynchParam.Delay: {SynchDomain.Time: 0}, +synch_description2 = [{SynchParam.Delay: {SynchDomain.Time: 0}, SynchParam.Active: {SynchDomain.Time: .01}, SynchParam.Total: {SynchDomain.Time: .02}, SynchParam.Repeats: 100}] -synchronization3 = [{SynchParam.Delay: {SynchDomain.Time: .1}, +synch_description3 = [{SynchParam.Delay: {SynchDomain.Time: .1}, SynchParam.Initial: {SynchDomain.Position: 0}, SynchParam.Active: {SynchDomain.Position: .1, SynchDomain.Time: .01, }, @@ -272,7 +272,7 @@ def tearDown(self): SynchDomain.Time: .1}, SynchParam.Repeats: 10}] -synchronization4 = [{SynchParam.Delay: {SynchDomain.Time: 0.1}, +synch_description4 = [{SynchParam.Delay: {SynchDomain.Time: 0.1}, SynchParam.Initial: {SynchDomain.Position: 0}, SynchParam.Active: {SynchDomain.Position: -.1, SynchDomain.Time: .01, }, @@ -355,25 +355,25 @@ def tearDown(self): # TODO: listener is not ready to handle 2D # @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_15, -# config=config_15, synchronization=synchronization1) +# config=config_15, synch_description=synch_description1) @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_14, - config=config_14, synchronization=synchronization1) + config=config_14, synch_description=synch_description1) @insertTest(helper_name='meas_contpos_acquisition', test_method_doc=doc_12, - config=config_12, synchronization=synchronization4, + config=config_12, synch_description=synch_description4, moveable="_test_mot_1_1") @insertTest(helper_name='meas_contpos_acquisition', test_method_doc=doc_12, - config=config_12, synchronization=synchronization3, + config=config_12, synch_description=synch_description3, moveable="_test_mot_1_1") @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_1, - config=config_1, synchronization=synchronization1) + config=config_1, synch_description=synch_description1) @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_2, - config=config_2, synchronization=synchronization1) + config=config_2, synch_description=synch_description1) @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_3, - config=config_3, synchronization=synchronization1) + config=config_3, synch_description=synch_description1) @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_4, - config=config_4, synchronization=synchronization1) + config=config_4, synch_description=synch_description1) @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_5, - config=config_5, synchronization=synchronization1) + config=config_5, synch_description=synch_description1) # TODO: implement dedicated asserts/test for only software synchronized # acquisition. # Until this TODO gets implemented we comment the test since it may @@ -383,24 +383,24 @@ def tearDown(self): # @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_6, # params=params_1, config=config_6) @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_7, - config=config_7, synchronization=synchronization1) + config=config_7, synch_description=synch_description1) @insertTest(helper_name='meas_cont_stop_acquisition', test_method_doc=doc_8, - config=config_7, synchronization=synchronization2) + config=config_7, synch_description=synch_description2) @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_9, config=config_1, second_config=config_7, - synchronization=synchronization1) + synch_description=synch_description1) @insertTest(helper_name='meas_double_acquisition', test_method_doc=doc_10, - config=config_7, synchronization=synchronization2) + config=config_7, synch_description=synch_description2) @insertTest(helper_name='meas_double_acquisition', test_method_doc=doc_10, - config=config_4, synchronization=synchronization1) + config=config_4, synch_description=synch_description1) @insertTest(helper_name='meas_double_acquisition_samemode', test_method_doc=doc_11, - config=config_11, synchronization=synchronization2) + config=config_11, synch_description=synch_description2) @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_9, config=config_1, second_config=config_7, - synchronization=synchronization1) + synch_description=synch_description1) @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_13, config=config_13, - synchronization=synchronization1) + synch_description=synch_description1) class AcquisitionTestCase(BasePoolTestCase, BaseAcquisition, unittest.TestCase): """Integration test of TGGeneration and Acquisition actions.""" diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 3ffff6e37f..89ada423ff 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -73,7 +73,7 @@ def delete_device(self): def init_device(self): PoolGroupDevice.init_device(self) # state and status are already set by the super class - detect_evts = "moveable", "synchronization", \ + detect_evts = "moveable", "synchdescription", \ "softwaresynchronizerinitialdomain" # TODO: nbstarts could be moved to detect events with # abs_change criteria of 1, but be careful with @@ -142,7 +142,7 @@ def _on_measurement_group_changed(self, event_source, event_type, cfg = self.measurement_group.get_user_configuration() codec = CodecFactory().getCodec('json') _, event_value = codec.encode(('', cfg)) - elif name == "synchronization": + elif name == "synchdescription": codec = CodecFactory().getCodec('json') _, event_value = codec.encode(('', event_value)) elif name == "moveable" and event_value is None: @@ -158,8 +158,8 @@ def _on_measurement_group_changed(self, event_source, event_type, quality=quality, priority=priority, error=error, synch=False) - def _synchronization_str2enum(self, synchronization_str): - """Translates synchronization data structure so it uses + def _synch_description_str2enum(self, _synch_description_str): + """Translates synchronization description data structure so it uses SynchParam and SynchDomain enums as keys instead of strings. .. todo:: At some point remove the backwards compatibility @@ -167,8 +167,8 @@ def _synchronization_str2enum(self, synchronization_str): serialized to "." e.g. "SynchDomain.Time" and we were using a class method `fromStr` to interpret the enumeration objects. """ - synchronization = [] - for group_str in synchronization_str: + synch_description = [] + for group_str in _synch_description_str: group = {} for param_str, conf_str in group_str.items(): try: @@ -186,8 +186,8 @@ def _synchronization_str2enum(self, synchronization_str): else: conf = conf_str group[param] = conf - synchronization.append(group) - return synchronization + synch_description.append(group) + return synch_description def always_executed_hook(self): pass @@ -265,18 +265,19 @@ def write_Moveable(self, attr): moveable = None self.measurement_group.moveable = moveable - def read_Synchronization(self, attr): - synchronization = self.measurement_group.synchronization + def read_SynchDescription(self, attr): + synch_description = self.measurement_group.synch_description codec = CodecFactory().getCodec('json') - data = codec.encode(('', synchronization)) + data = codec.encode(('', synch_description)) attr.set_value(data[1]) - def write_Synchronization(self, attr): + def write_SynchDescription(self, attr): data = attr.get_write_value() - synchronization = CodecFactory().decode(('json', data)) + synch_description = CodecFactory().decode(('json', data)) # translate dictionary keys - synchronization = self._synchronization_str2enum(synchronization) - self.measurement_group.synchronization = synchronization + synch_description = \ + self._synch_description_str2enum(synch_description) + self.measurement_group.synch_description = synch_description def read_LatencyTime(self, attr): latency_time = self.measurement_group.latency_time @@ -355,10 +356,10 @@ class MeasurementGroupClass(PoolGroupDeviceClass): 'Moveable': [[DevString, SCALAR, READ_WRITE], {'Memorized': "true", 'Display level': DispLevel.EXPERT}], - # TODO: Does it have sense to memorize Synchronization? - 'Synchronization': [[DevString, SCALAR, READ_WRITE], - {'Memorized': "true", - 'Display level': DispLevel.EXPERT}], + # TODO: Does it have sense to memorize SynchDescription? + 'SynchDescription': [[DevString, SCALAR, READ_WRITE], + {'Memorized': "true", + 'Display level': DispLevel.EXPERT}], 'LatencyTime': [[DevDouble, SCALAR, READ], {'Display level': DispLevel.EXPERT}], 'SoftwareSynchronizerInitialDomain': [[DevString, SCALAR, READ_WRITE], diff --git a/src/sardana/tango/pool/test/test_measurementgroup.py b/src/sardana/tango/pool/test/test_measurementgroup.py index 9ca57e7232..9c0e807b80 100644 --- a/src/sardana/tango/pool/test/test_measurementgroup.py +++ b/src/sardana/tango/pool/test/test_measurementgroup.py @@ -171,10 +171,10 @@ def create_meas(self, config): def prepare_meas(self, params): """ Set the measurement group parameters """ - synchronization = params["synchronization"] + synch_description = params["synch_description"] codec = CodecFactory().getCodec('json') - data = codec.encode(('', synchronization)) - self.meas.write_attribute('synchronization', data[1]) + data = codec.encode(('', synch_description)) + self.meas.write_attribute('SynchDescription', data[1]) def _add_attribute_listener(self, config): self.attr_listener = TangoAttributeListener() @@ -225,7 +225,7 @@ def meas_cont_acquisition(self, params, config): print("Acquiring...") time.sleep(0.1) time.sleep(1) - repetitions = params['synchronization'][0][SynchParam.Repeats] + repetitions = params['synch_description'][0][SynchParam.Repeats] self._acq_asserts(chn_names, repetitions) def stop_meas_cont_acquisition(self, params, config): @@ -269,13 +269,13 @@ def tearDown(self): SarTestTestCase.tearDown(self) -synchronization1 = [{SynchParam.Delay: {SynchDomain.Time: 0}, +synch_description1 = [{SynchParam.Delay: {SynchDomain.Time: 0}, SynchParam.Active: {SynchDomain.Time: .01}, SynchParam.Total: {SynchDomain.Time: .02}, SynchParam.Repeats: 10}] params_1 = { - "synchronization": synchronization1, + "synch_description": synch_description1, "integ_time": 0.01, "name": '_exp_01' } diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 3cce0bcb55..74b86508a2 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2481,16 +2481,16 @@ def getAcquisitionMode(self): def setAcquisitionMode(self, acqMode): self.getAcquisitionModeObj().write(acqMode) - def getSynchronizationObj(self): - return self._getAttrEG('Synchronization') + def getSynchDescriptionObj(self): + return self._getAttrEG('SynchDescription') - def getSynchronization(self): - return self._getAttrValue('Synchronization') + def getSynchDescription(self): + return self._getAttrValue('SynchDescription') - def setSynchronization(self, synchronization): + def setSynchDescription(self, synch_description): codec = CodecFactory().getCodec('json') - _, data = codec.encode(('', synchronization)) - self.getSynchronizationObj().write(data) + _, data = codec.encode(('', synch_description)) + self.getSynchDescriptionObj().write(data) self._last_integ_time = None def _get_channels_for_elements(self, elements): @@ -2839,7 +2839,7 @@ def getTimer(self, *elements, ret_full_name=False, ret_by_ctrl=False): controllers or channels (default: `False` means return channels) :type ret_by_ctrl: bool :return: ordered dictionary where keys are **channel** names (or full - names if `ret_full_name=True`) and values are their synchronization + names if `ret_full_name=True`) and values are their timer configurations :rtype: dict(str, str) """ @@ -2894,7 +2894,7 @@ def getMonitor(self, *elements, ret_full_name=False, ret_by_ctrl=False): controllers or channels (default: `False` means return channels) :type ret_by_ctrl: bool :return: ordered dictionary where keys are **channel** names (or full - names if `ret_full_name=True`) and values are their synchronization + names if `ret_full_name=True`) and values are their monitor configurations :rtype: dict(str, str) """ @@ -2952,7 +2952,7 @@ def getSynchronizer(self, *elements, ret_full_name=False, controllers or channels (default: `False` means return channels) :type ret_by_ctrl: bool :return: ordered dictionary where keys are **channel** names (or full - names if `ret_full_name=True`) and values are their synchronization + names if `ret_full_name=True`) and values are their synchronizer configurations :rtype: dict(str, str) """ @@ -3315,12 +3315,13 @@ def go(self, *args, **kwargs): self.prepare() return self.count_raw(start_time) - def count_continuous(self, synchronization, value_buffer_cb=None): + def count_continuous(self, synch_description, value_buffer_cb=None): """Execute measurement process according to the given synchronization description. - :param synchronization: synchronization description - :type synchronization: list of groups with equidistant synchronizations + :param synch_description: synchronization description + :type synch_description: list of groups with equidistant + synchronizations :param value_buffer_cb: callback on value buffer updates :type value_buffer_cb: callable :return: state and eventually value buffers if no callback was passed @@ -3336,7 +3337,7 @@ class on a provisional basis. Backwards incompatible changes start_time = time.time() cfg = self.getConfiguration() cfg.prepare() - self.setSynchronization(synchronization) + self.setSynchDescription(synch_description) self.prepare() self.subscribeValueBuffer(value_buffer_cb) try: From 504e6645cdcc1e82c33d3fe046e669c8649a411d Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 31 May 2020 22:34:07 +0200 Subject: [PATCH 607/830] Add synchronization configuration methods to MeasurementGroup --- src/sardana/taurus/core/tango/sardana/pool.py | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 74b86508a2..b090358b2d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1879,9 +1879,7 @@ def _get_ctrls_key(self, key, ctrls_names=None, use_fullname=False): result[label] = None continue - if key == 'synchronization': - value = AcqSynchType.get(value) - elif key in ['timer', 'monitor']: + if key in ['timer', 'monitor']: value = self.channels[value]['label'] elif key == 'synchronizer' and value != 'software': value = DeviceProxy(value).alias() @@ -2968,6 +2966,66 @@ def getSynchronizer(self, *elements, ret_full_name=False, return self._get_value_per_channel(config, ctrls_sync, use_fullname=ret_full_name) + def setSynchronization(self, synchronization, *elements, apply=True): + """Set the synchronization configuration for the given channels or + controller. + + .. note:: Currently the controller's synchronization must be unique. + Hence this method will set it for the whole controller regardless of + the ``elements`` argument. + + :param synchronization: synchronization type e.g. Trigger, Gate or + Start + :type synchronization: `sardana.pool.AcqSynchType` + :param elements: sequence of channels names or full names, no elements + means set to all + :type elements: list(str) + :param apply: `True` - apply on the server, `False` - do not apply yet + on the server and keep locally (default: `True`) + :type apply: bool + """ + config = self.getConfiguration() + # TODO: Implement solution to set the synchronization per channel when + # it is allowed. + ctrls = self._get_ctrl_for_elements(elements) + config._setCtrlsSynchronization(synchronization, ctrls, + apply_cfg=apply) + + def getSynchronization(self, *elements, ret_full_name=False, + ret_by_ctrl=False): + """Get the synchronization configuration of the given elements. + + Channels and controllers are accepted as elements. Getting the output + from the controller means getting it from all channels of this + controller present in this measurement group, unless + `ret_by_ctrl=True`. + + :param elements: sequence of element names or full names, no elements + means get from all + :type elements: list(str) + :param ret_full_name: whether keys in the returned dictionary are + full names or names (default: `False` means return names) + :type ret_full_name: bool + :param ret_by_ctrl: whether keys in the returned dictionary are + controllers or channels (default: `False` means return channels) + :type ret_by_ctrl: bool + :return: ordered dictionary where keys are **channel** names (or full + names if `ret_full_name=True`) and values are their + synchronization configurations + :rtype: dict<`str`, `sardana.pool.AcqSynchType`> + """ + # TODO: Implement solution to set the synchronization per channel when it + # is allowed. + ctrls = self._get_ctrl_for_elements(elements) + config = self.getConfiguration() + ctrls_sync = config._getCtrlsSynchronization(ctrls, + use_fullname=ret_full_name) + if ret_by_ctrl: + return ctrls_sync + else: + return self._get_value_per_channel(config, ctrls_sync, + use_fullname=ret_full_name) + def applyConfiguration(self): """Apply configuration changes kept locally on the server. From bca4a5bea40a286a2ae66d3b5c4a7ae439aef4e7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 31 May 2020 22:36:11 +0200 Subject: [PATCH 608/830] Add tests for MeasurementGroup synchronization methods --- .../tango/sardana/test/test_measgrpconf.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py b/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py index 6f44b8a15b..539dc429d3 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py @@ -2,6 +2,7 @@ import unittest from taurus import Device from taurus.core.tango.tangovalidator import TangoDeviceNameValidator +from sardana.pool import AcqSynchType from sardana.taurus.core.tango.sardana.pool import registerExtensions from sardana.tango.pool.test.base_sartest import SarTestTestCase @@ -406,6 +407,58 @@ def test_Synchronizer(self, elements=["_test_ct_1_1", "_test_ct_1_2", mg.cleanUp() self.pool.DeleteElement(mg_name) + def test_Synchronization(self, elements=["_test_ct_1_1", "_test_ct_1_2", + "_test_ct_1_3", "_test_2d_1_1", + "_test_mt_1_3/position"]): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + try: + mg = Device(mg_name) + result = mg.getSynchronization() + expected = [AcqSynchType.Trigger, AcqSynchType.Trigger, + AcqSynchType.Trigger, AcqSynchType.Trigger, None] + self._assertMultipleResults(result, elements, expected) + # TODO: maybe we should raise an exception here? + # with self.assertRaises(Exception): + # mg.setSynchronization(AcqSynchType.Trigger, + # "_test_mt_1_3/position") + + mg.setSynchronization(AcqSynchType.Gate, "_test_ct_ctrl_1", + "_test_2d_ctrl_1") + + expected = [AcqSynchType.Gate, AcqSynchType.Gate, + AcqSynchType.Gate, AcqSynchType.Gate, None] + result = mg.getSynchronization() + self._assertMultipleResults(result, elements, expected) + + mg.setSynchronization(AcqSynchType.Start, "_test_ct_ctrl_1", + "_test_2d_ctrl_1") + result = mg.getSynchronization() + expected = [AcqSynchType.Start, AcqSynchType.Start, + AcqSynchType.Start, AcqSynchType.Start, None] + self._assertMultipleResults(result, elements, expected) + + with self.assertRaises(Exception): + mg.setSynchronization('asdf', "_test_ct_ctrl_1", + "_test_2d_ctrl_1") + + # Check ret_full_name + v = TangoDeviceNameValidator() + counters = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3", + '_test_2d_1_1'] + full_names = [v.getNames(counter)[0] for counter in counters] + mg.setSynchronization(AcqSynchType.Trigger, "_test_ct_ctrl_1", + "_test_2d_ctrl_1") + + result = mg.getSynchronization(*counters, ret_full_name=True) + + self._assertResult(result, full_names, AcqSynchType.Trigger) + + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + def test_ValueRefEnabled(self, elements=["_test_2d_1_1", "_test_2d_1_2", "_test_ct_1_3", "_test_mt_1_3/position"]): From 0fe856102b2b5bd6476005ea38bc9fe63d4b31c6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 31 May 2020 22:38:48 +0200 Subject: [PATCH 609/830] Remove prints from MeasurementGroup configuration tests --- .../taurus/core/tango/sardana/test/test_measgrpconf.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py b/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py index 539dc429d3..18be8cf5a7 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py @@ -18,7 +18,6 @@ def tearDown(self): def _assertResult(self, result, channels, expected_value): expected_channels = list(channels) - print(result) for channel, value in result.items(): msg = "unexpected key: {}".format(channel) self.assertIn(channel, expected_channels, msg) @@ -29,7 +28,6 @@ def _assertResult(self, result, channels, expected_value): def _assertMultipleResults(self, result, channels, expected_values): expected_channels = list(channels) - print(result) for (channel, value), expected_value in zip(result.items(), expected_values): msg = "unexpected key: {}".format(channel) @@ -170,7 +168,6 @@ def test_PlotType(self, elements=["_test_ct_1_1", "_test_ct_1_2", self._assertMultipleResults(plottype, elements, expected_values) with self.assertRaises(ValueError): mg.setPlotType("asdf", elements[2]) - print(mg.getPlotType()) # Redefine elements elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] @@ -246,7 +243,6 @@ def test_PlotAxes(self, elements=["_test_ct_1_1", "_test_ct_1_2", mg.setPlotAxes(["", ""], elements[1]) with self.assertRaises(ValueError): mg.setPlotAxes([""], elements[0]) - print(mg.getPlotAxes()) elements = ["_test_ct_1_1", "_test_ct_1_2", "_test_ct_1_3"] # Set values using the controller instead of channels @@ -322,7 +318,6 @@ def test_Monitor(self, elements=["_test_ct_1_1", "_test_ct_1_2", '_test_2d_1_2', "_test_mt_1_3/position"]): mg_name = str(uuid.uuid1()) - print(mg_name) argin = [mg_name] + elements self.pool.CreateMeasurementGroup(argin) try: From f398b9fd719a32c6fbd55e11250393a35723ae39 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 31 May 2020 23:23:08 +0200 Subject: [PATCH 610/830] Add synchrtonization to set/get_meas_conf macro --- src/sardana/macroserver/macros/expconf.py | 26 ++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/expconf.py b/src/sardana/macroserver/macros/expconf.py index 8f40e60d3e..5c0c5a3203 100644 --- a/src/sardana/macroserver/macros/expconf.py +++ b/src/sardana/macroserver/macros/expconf.py @@ -34,6 +34,7 @@ from taurus.console import Alignment from taurus.console.list import List +from sardana.pool import AcqSynchType from sardana.macroserver.msexception import UnknownEnv from sardana.taurus.core.tango.sardana import PlotType from sardana.macroserver.macro import macro, Macro, Type, Optional @@ -51,6 +52,10 @@ def plot_axes_sanitizer(values): return ["n/a" if len(v) == 0 else v[0] for v in values] +def synchrtonization_sanitizer(values): + return [AcqSynchType.whatis(v) for v in values] + + def plot_axes_validator(value): value = value.lower() if value in ("idx", ""): @@ -61,16 +66,31 @@ def plot_axes_validator(value): def bool_validator(value): + in_value = value value = value.lower() if value in ['true', '1']: value = True elif value in ['false', '0']: value = False else: - raise ValueError('{0} is not a boolean'.format(value)) + raise ValueError('{0} is not a boolean'.format(in_value)) return value +def synchronization_validator(value): + in_value = value + value = value.lower() + try: + try: + value = int(value) + except: + value = AcqSynchType.get(value.capitalize()) + else: + value = AcqSynchType.get(value) + except KeyError: + raise ValueError("{0} is not a synchronization type".format(in_value)) + return value + # if sanitizers and validators evolve to sth too complicated refactor this # to use classes parameter_map = OrderedDict([ @@ -81,6 +101,9 @@ def bool_validator(value): ("timer", ("Timer", sanitizer, None)), ("monitor", ("Monitor", sanitizer, None)), ("synchronizer", ("Synchronizer", sanitizer, None)), + ("synchronization", + ("Synchronization", synchrtonization_sanitizer, + synchronization_validator)), ("valuerefenabled", ("ValueRefEnabled", sanitizer, bool_validator)), ("valuerefpattern", ("ValueRefPattern", sanitizer, None)) ]) @@ -164,6 +187,7 @@ def set_meas_conf(self, parameter, value, items, meas_grp): - **Timer**: e.g. ct01 - **Monitor**: e.g. ct01 - **Synchronizer**: software or e.g. tg01 + - **Synchronization**: 0/Trigger, 1/Gate or 2/Start - **ValueRefEnabled** - True/1 or False/0 - **ValueRefPattern** - URI e.g. file:///tmp/img_{index}.tiff From fcae2644c5f257a4810dbfec24d54a5c408771cd Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 31 May 2020 23:33:09 +0200 Subject: [PATCH 611/830] Fix flake8 --- src/sardana/macroserver/macros/expconf.py | 2 +- src/sardana/macroserver/scan/gscan.py | 5 +-- src/sardana/pool/poolmeasurementgroup.py | 5 +-- .../pool/test/test_measurementgroup.py | 38 ++++++++++--------- src/sardana/tango/pool/MeasurementGroup.py | 4 +- src/sardana/taurus/core/tango/sardana/pool.py | 8 ++-- .../tango/sardana/test/test_measgrpconf.py | 4 +- 7 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/sardana/macroserver/macros/expconf.py b/src/sardana/macroserver/macros/expconf.py index 5c0c5a3203..b561aee69d 100644 --- a/src/sardana/macroserver/macros/expconf.py +++ b/src/sardana/macroserver/macros/expconf.py @@ -83,7 +83,7 @@ def synchronization_validator(value): try: try: value = int(value) - except: + except ValueError: value = AcqSynchType.get(value.capitalize()) else: value = AcqSynchType.get(value) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index e7830885c6..6330825a2a 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2725,8 +2725,7 @@ def __init__(self, macro, generator=None, CAcquisition.__init__(self) self._synch_description = None - def _create_synch_description(self, active_time, repeats, - latency_time=0): + def _create_synch_description(self, active_time, repeats, latency_time=0): delay_time = 0 mg_latency_time = self.measurement_group.getLatencyTime() if mg_latency_time > latency_time: @@ -2757,7 +2756,7 @@ def get_synch_description(self): latency_time = getattr(self.macro, "latency_time", 0) synch_description = \ self._create_synch_description(active_time, repeats, - latency_time) + latency_time) self._synch_description = synch_description return synch_description diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index db10a073d5..434c00a719 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -1201,9 +1201,8 @@ def set_synch_description(self, description, propagate=1): priority=propagate), description) - synch_description = property(get_synch_description, - set_synch_description, - doc="the current acquisition mode") + synch_description = property(get_synch_description, set_synch_description, + doc="the current acquisition mode") # ------------------------------------------------------------------------- # moveable diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index d52c784038..904a51bb66 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -102,7 +102,8 @@ def acq_asserts(self, channel_names, repetitions): (ch_name, ch_data_len, repetitions) self.assertEqual(ch_data_len, repetitions, msg) - def meas_double_acquisition(self, config, synch_description, moveable=None): + def meas_double_acquisition(self, config, synch_description, + moveable=None): """ Run two acquisition with the same measurement group, first with multiple repetitions and then one repetition. """ @@ -224,7 +225,8 @@ def meas_cont_stop_acquisition(self, config, synch_description, def meas_contpos_acquisition(self, config, synch_description, moveable, second_config=None): # TODO: this code is ready only for one group configuration - initial = synch_description[0][SynchParam.Initial][SynchDomain.Position] + initial = \ + synch_description[0][SynchParam.Initial][SynchDomain.Position] total = synch_description[0][SynchParam.Total][SynchDomain.Position] repeats = synch_description[0][SynchParam.Repeats] position = initial + total * repeats @@ -255,30 +257,30 @@ def tearDown(self): synch_description1 = [{SynchParam.Delay: {SynchDomain.Time: 0}, - SynchParam.Active: {SynchDomain.Time: .01}, - SynchParam.Total: {SynchDomain.Time: .02}, - SynchParam.Repeats: 10}] + SynchParam.Active: {SynchDomain.Time: .01}, + SynchParam.Total: {SynchDomain.Time: .02}, + SynchParam.Repeats: 10}] synch_description2 = [{SynchParam.Delay: {SynchDomain.Time: 0}, - SynchParam.Active: {SynchDomain.Time: .01}, - SynchParam.Total: {SynchDomain.Time: .02}, - SynchParam.Repeats: 100}] + SynchParam.Active: {SynchDomain.Time: .01}, + SynchParam.Total: {SynchDomain.Time: .02}, + SynchParam.Repeats: 100}] synch_description3 = [{SynchParam.Delay: {SynchDomain.Time: .1}, - SynchParam.Initial: {SynchDomain.Position: 0}, - SynchParam.Active: {SynchDomain.Position: .1, + SynchParam.Initial: {SynchDomain.Position: 0}, + SynchParam.Active: {SynchDomain.Position: .1, SynchDomain.Time: .01, }, - SynchParam.Total: {SynchDomain.Position: .2, + SynchParam.Total: {SynchDomain.Position: .2, SynchDomain.Time: .1}, - SynchParam.Repeats: 10}] + SynchParam.Repeats: 10}] synch_description4 = [{SynchParam.Delay: {SynchDomain.Time: 0.1}, - SynchParam.Initial: {SynchDomain.Position: 0}, - SynchParam.Active: {SynchDomain.Position: -.1, - SynchDomain.Time: .01, }, - SynchParam.Total: {SynchDomain.Position: -.2, - SynchDomain.Time: .1}, - SynchParam.Repeats: 10}] + SynchParam.Initial: {SynchDomain.Position: 0}, + SynchParam.Active: {SynchDomain.Position: -.1, + SynchDomain.Time: .01, }, + SynchParam.Total: {SynchDomain.Position: -.2, + SynchDomain.Time: .1}, + SynchParam.Repeats: 10}] doc_1 = 'Synchronized acquisition with two channels from the same controller'\ ' using the same trigger' diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index 89ada423ff..a194d56373 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -358,8 +358,8 @@ class MeasurementGroupClass(PoolGroupDeviceClass): 'Display level': DispLevel.EXPERT}], # TODO: Does it have sense to memorize SynchDescription? 'SynchDescription': [[DevString, SCALAR, READ_WRITE], - {'Memorized': "true", - 'Display level': DispLevel.EXPERT}], + {'Memorized': "true", + 'Display level': DispLevel.EXPERT}], 'LatencyTime': [[DevDouble, SCALAR, READ], {'Display level': DispLevel.EXPERT}], 'SoftwareSynchronizerInitialDomain': [[DevString, SCALAR, READ_WRITE], diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index b090358b2d..20d1b29b85 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -3014,12 +3014,12 @@ def getSynchronization(self, *elements, ret_full_name=False, synchronization configurations :rtype: dict<`str`, `sardana.pool.AcqSynchType`> """ - # TODO: Implement solution to set the synchronization per channel when it - # is allowed. + # TODO: Implement solution to set the synchronization per channel + # when it is allowed. ctrls = self._get_ctrl_for_elements(elements) config = self.getConfiguration() - ctrls_sync = config._getCtrlsSynchronization(ctrls, - use_fullname=ret_full_name) + ctrls_sync = \ + config._getCtrlsSynchronization(ctrls, use_fullname=ret_full_name) if ret_by_ctrl: return ctrls_sync else: diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py b/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py index 18be8cf5a7..d7addfc2de 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measgrpconf.py @@ -428,7 +428,7 @@ def test_Synchronization(self, elements=["_test_ct_1_1", "_test_ct_1_2", self._assertMultipleResults(result, elements, expected) mg.setSynchronization(AcqSynchType.Start, "_test_ct_ctrl_1", - "_test_2d_ctrl_1") + "_test_2d_ctrl_1") result = mg.getSynchronization() expected = [AcqSynchType.Start, AcqSynchType.Start, AcqSynchType.Start, AcqSynchType.Start, None] @@ -436,7 +436,7 @@ def test_Synchronization(self, elements=["_test_ct_1_1", "_test_ct_1_2", with self.assertRaises(Exception): mg.setSynchronization('asdf', "_test_ct_ctrl_1", - "_test_2d_ctrl_1") + "_test_2d_ctrl_1") # Check ret_full_name v = TangoDeviceNameValidator() From eba52aeaedb476459116af0d5f9cf7be60391e07 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 31 May 2020 23:33:22 +0200 Subject: [PATCH 612/830] Update CHANGELOG --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93d4569b5b..d4a9a3570e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,13 @@ This file follows the formats and conventions from [keepachangelog.com] ### Changed +* Renamed _synchronization_ to _synch description_ + * Tango MeasurementGroup `Synchronization` attribute to `SynchDescription` + * Core MeasurementGroup `synchronization` property to `synch_description` + * Sardana-Taurus Device MeasurementGroup `getSynchronizationObj()`, + `getSynchronization()` and `setSynchronization()` methods to + `getSynchDescriptionObj()`,`getSynchDescription()` + and `setSynchDescription()` (#1337) * Requirements are no longer checked when importing sardana (#1185) * Measurement group (Taurus extension) configuration API methods, known in the old sense for setting a global measurement group timer/monitor: From 31aefee5f80f4d1048770b4e1f6202983ace6d8c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Jun 2020 16:48:12 +0200 Subject: [PATCH 613/830] Rename SynchronizationDescription in docs --- .../devel/api/sardana/pool/poolsynchronization.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/devel/api/sardana/pool/poolsynchronization.rst b/doc/source/devel/api/sardana/pool/poolsynchronization.rst index 35e600de2f..b62c52aff7 100644 --- a/doc/source/devel/api/sardana/pool/poolsynchronization.rst +++ b/doc/source/devel/api/sardana/pool/poolsynchronization.rst @@ -10,7 +10,7 @@ .. hlist:: :columns: 3 - * :class:`SynchronizationDescription` + * :class:`SynchDescription` * :class:`PoolSynchronization` PoolSynchronization @@ -24,13 +24,13 @@ PoolSynchronization :members: :undoc-members: -SynchronizationDescription --------------------------- +SynchDescription +---------------- -.. inheritance-diagram:: SynchronizationDescription +.. inheritance-diagram:: SynchDescription :parts: 1 -.. autoclass:: SynchronizationDescription +.. autoclass:: SynchDescription :show-inheritance: :members: :undoc-members: From f11fa047fab6cff1218f4b1557b6ecb79c5e70dc Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Jun 2020 16:48:21 +0200 Subject: [PATCH 614/830] Fix flake8 --- src/sardana/pool/test/test_measurementgroup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index 904a51bb66..c5a1aa1c03 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -277,7 +277,7 @@ def tearDown(self): synch_description4 = [{SynchParam.Delay: {SynchDomain.Time: 0.1}, SynchParam.Initial: {SynchDomain.Position: 0}, SynchParam.Active: {SynchDomain.Position: -.1, - SynchDomain.Time: .01, }, + SynchDomain.Time: .01, }, SynchParam.Total: {SynchDomain.Position: -.2, SynchDomain.Time: .1}, SynchParam.Repeats: 10}] From 0e02532f5331bbf33cdc5a46b558e397f39b75fd Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Jun 2020 16:50:51 +0200 Subject: [PATCH 615/830] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4a9a3570e..bcf2f01aa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,7 @@ This file follows the formats and conventions from [keepachangelog.com] `getSynchronization()` and `setSynchronization()` methods to `getSynchDescriptionObj()`,`getSynchDescription()` and `setSynchDescription()` (#1337) + * `SynchronizationDescription` helper class to `SynchDescription` * Requirements are no longer checked when importing sardana (#1185) * Measurement group (Taurus extension) configuration API methods, known in the old sense for setting a global measurement group timer/monitor: From b275612c255e840441aa3e35292b331040cee88a Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Jun 2020 16:12:08 +0200 Subject: [PATCH 616/830] Add link to "Adding parameters to your macros" in docs --- doc/source/devel/howto_macros/macros_general.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst index d0ac20c412..3f4b6e166b 100644 --- a/doc/source/devel/howto_macros/macros_general.rst +++ b/doc/source/devel/howto_macros/macros_general.rst @@ -250,6 +250,8 @@ includes :keyword:`for` and :keyword:`while` loops, :keyword:`if` ... wave_fft = numpy.fft.fft(wave) +.. _sardana-macro-parameters: + Adding parameters to your macro ------------------------------- From 65b6b17902c91991cb49359a72327b0032f0b39d Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Jun 2020 16:12:37 +0200 Subject: [PATCH 617/830] Rename extensions to models in a link to Sardana-Taurus models --- doc/source/devel/howto_macros/macros_general.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst index 3f4b6e166b..fa87eeada5 100644 --- a/doc/source/devel/howto_macros/macros_general.rst +++ b/doc/source/devel/howto_macros/macros_general.rst @@ -468,7 +468,7 @@ this parameter any name but usually, by convention it is called ``self``). your macro to do all kinds of things, among others, to obtain the sardana elements. The :ref:`Macro API reference ` describes all these functions and the -:ref:`Sardana-Taurus extensions API reference ` +:ref:`Sardana-Taurus model API reference ` describes the obtained sardana elements. Let's say you want to write a macro that explicitly moves a known *theta* motor From 81b46eba38c6203b8a1c922d6c2d81d6ef41f57c Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Jun 2020 16:12:59 +0200 Subject: [PATCH 618/830] Add documentation on how to access Tango in macros --- .../devel/howto_macros/macros_general.rst | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst index fa87eeada5..3246f02e37 100644 --- a/doc/source/devel/howto_macros/macros_general.rst +++ b/doc/source/devel/howto_macros/macros_general.rst @@ -922,6 +922,49 @@ using the :meth:`~sardana.macroserver.macro.Hookable.appendHook` method:: the macro. Using the method appends just one hook but does not affect the general hooks eventually attached to the macro. +.. _sardana-macro-accessing-tango: + +Accessing Tango from your macros +-------------------------------- + +Your macros almost certainly will need to access Tango_ devices, either +external, for example, coming from the vacuum system or any other +arbitrary system integrated with Tango Device Server or internal to Sardana +(Sardana elements are currently exported as Tango devices) e.g.: motors, +measurement group, etc. + +There exists different :term:`API` to access to Tango_ devices. + +First, to access to Sardana elements it is recommended to use the Sardana +:term:`API`: e.g.: `~sardana.macroserver.macro.Macro.getMoveable` to obtain +any moveable (motor or pseudo motor), +`~sardana.macroserver.macro.Macro.getMeasurementGroup` to obtain a measurement +group. + +.. note:: + By :ref:`adding parameters to your macro ` + you get the same objects as if you were using the above methods. + Their classes are documented in: + :ref:`Sardana-Taurus model API reference ` + +Any external Tango device could be accessed with Taurus_ (using +`taurus.Device`) or simply with PyTango_ (using `tango.DeviceProxy`). +Taurus gives you some benefits over PyTango: + +- seamless attribute configuration management e.g.: unit aware attribute + read and write, simplified way to read/write attribute configuration, etc. +- unique way to access different data sources e.g. Tango_, EPICS_, + `hdf5 `_, etc. +- simplified way to use Tango_ events + +However the above benefits are not for free and more precisely are for some +extra time overhead (in milliseconds range). + +As a rule of thumb, if you you don't mind the extra overhead and value the +simplified usage you should use Taurus. If you strive for very optimal access +to Tango and don't need these benefits then most probably PyTango will work +better for you. + .. _sardana-macro-using-external-libraries: Using external python libraries From de22ba38cc1de90547883ad54d0af4431fb30c3f Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Jun 2020 16:13:31 +0200 Subject: [PATCH 619/830] Add documentation on how access Tango in controllers --- .../howto_controllers/howto_controller.rst | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/source/devel/howto_controllers/howto_controller.rst b/doc/source/devel/howto_controllers/howto_controller.rst index b753b7abb6..f1c743b9a3 100644 --- a/doc/source/devel/howto_controllers/howto_controller.rst +++ b/doc/source/devel/howto_controllers/howto_controller.rst @@ -538,6 +538,31 @@ Example:: except: pass +.. _sardana-controller-accessing-tango: + +Accessing Tango from your controllers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is a very common pattern that when integrating a new hardware (or +eventually a software component) in Sardana you start from a Tango Device +Server (either already existing one or you develop one as an intermediate +layer). In this case your controller will need to access Tango and there are +two ways od doing that, either with Taurus_ (using `taurus.Device`) or +with PyTango_ (using `tango.DeviceProxy`). Please consult a similar discussion +:ref:`sardana-macro-accessing-tango` on which one to use. + +For accessing Sardana elements e.g.: motors, experimental channels, etc. +currently there is no Sardana :term:`API` and you will need to use one of the +above methods. + +.. note:: + For a very simplified integration of Tango devices in Sardana you may + consider using + `sardana-tango controllers `_. + + + + .. rubric:: Footnotes .. [#f1] Pseudo controllers don't need to manage their individual axis. Therefore, From f8bff1e88961b928a3591d144ad4d1d38f3d9a62 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Jun 2020 16:13:49 +0200 Subject: [PATCH 620/830] Add FAQ on how to access Tango from plugins --- doc/source/users/faq.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/source/users/faq.rst b/doc/source/users/faq.rst index a07f603777..221d85564d 100644 --- a/doc/source/users/faq.rst +++ b/doc/source/users/faq.rst @@ -92,6 +92,16 @@ write Sardana controllers and pseudo controllers can be found This documentation also includes the :term:`API` which can be used to interface to the specific hardware item. +How to access Tango from within macros or controllers +-------------------------------------------------------------------------------- +In your macros and controllers almost certainly you will need to access Tango +devices (including Sardana elements) to read or write their attributes, +execute commands, etc. There exist different ways of accessing them: Sardana, +Taurus or PyTango :term:`API`. See more on which to choose in this chapters: + +* :ref:`sardana-macro-accessing-tango` +* :ref:`sardana-controller-accessing-tango` + How to add your own file format? -------------------------------- Documentation how to add your own file format can be found here ****. From 862b1edc13b19b8fd2a074d3481961da8726df43 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Jun 2020 17:16:18 +0200 Subject: [PATCH 621/830] Document PoolUtil singleton object --- .../devel/api/sardana/pool/poolutil.rst | 16 ++++++++++-- src/sardana/pool/poolutil.py | 26 ++++++++++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/doc/source/devel/api/sardana/pool/poolutil.rst b/doc/source/devel/api/sardana/pool/poolutil.rst index 8c9ddbd258..55c6049536 100644 --- a/doc/source/devel/api/sardana/pool/poolutil.rst +++ b/doc/source/devel/api/sardana/pool/poolutil.rst @@ -1,8 +1,20 @@ .. currentmodule:: sardana.pool.poolutil :mod:`~sardana.pool.poolutil` -========================================= +============================= .. automodule:: sardana.pool.poolutil -.. rubric:: Classes \ No newline at end of file +.. rubric:: Singletons + +.. autodata:: PoolUtil + +.. rubric:: Classes + +_PoolUtil +--------- + +.. autoclass:: _PoolUtil + :show-inheritance: + :members: + :undoc-members: diff --git a/src/sardana/pool/poolutil.py b/src/sardana/pool/poolutil.py index 93c5c9bf62..f5c6fa2be2 100644 --- a/src/sardana/pool/poolutil.py +++ b/src/sardana/pool/poolutil.py @@ -43,6 +43,16 @@ def __call__(self, *args, **kwargs): return self def get_device(self, *args, **kwargs): + """Factory method to create a single instance the `tango.DeviceProxy` + per controller instance. + + :param ctrl_name: Controller name to which assign the proxy object + :type ctrl_name: `str` + :param device_name: Tango device name + :type device_name: `str` + :return: single device proxy object + :rtype: `tango.DeviceProxy` + """ ctrl_name = args[0] device_name = args[1] with self._lock: @@ -52,12 +62,20 @@ def get_device(self, *args, **kwargs): dev = ctrl_devs.get(device_name) if dev is None: import PyTango - ctrl_devs[device_name] = dev = PyTango.DeviceProxy(device_name) + ctrl_devs[device_name] = dev = \ + PyTango.DeviceProxy(device_name) return dev get_motor = get_phy_motor = get_pseudo_motor = get_motor_group = \ - get_exp_channel = get_ct_channel = get_zerod_channel = get_oned_channel = \ - get_twod_channel = get_pseudo_counter_channel = get_measurement_group = \ - get_com_channel = get_ioregister = get_device + get_exp_channel = get_ct_channel = get_zerod_channel = \ + get_oned_channel = get_twod_channel = get_pseudo_counter_channel = \ + get_measurement_group = get_com_channel = get_ioregister = get_device + +#: Singleton instance of the `~sardana.pool.poolutil._PoolUtil` class. +#: +#: It is a factory of `tango.DeviceProxy` objects and ensures only one +#: instance of such objects is created for the whole process. +#: Please refer to the `~sardana.pool.poolutil._PoolUtil` API on the available +#: methods. PoolUtil = _PoolUtil() From b50621c0d051e35e5c5731a950261f0d644464d1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Jun 2020 06:58:36 +0200 Subject: [PATCH 622/830] Add TODO on raising exceptions when setting Synchronization param --- src/sardana/pool/poolmeasurementgroup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 434c00a719..f5633338b0 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -610,6 +610,10 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): The configuration parameters for given channels/controllers may differ depending on their types e.g. 0D channel does not support timer parameter while C/T does. + + .. todo:: + Raise exceptions when setting _Synchronization_ parameter for + external channels, 0D and PSeudoCounters. """ pool = self._parent.pool From e77ba48ea9db1d3efd2d5179b7011e74cfc8a618 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jun 2020 12:47:51 +0200 Subject: [PATCH 623/830] Reintroduce MeasurementGroup test_acquisition Now, there is default software synchronizer in the measurement group and the test can be re-enabled. Correctly retrieve the Value attribute value. --- src/sardana/pool/test/test_ctacquisition.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sardana/pool/test/test_ctacquisition.py b/src/sardana/pool/test/test_ctacquisition.py index 6e58e7f135..df454887ff 100644 --- a/src/sardana/pool/test/test_ctacquisition.py +++ b/src/sardana/pool/test/test_ctacquisition.py @@ -64,9 +64,6 @@ def test_init(self): 'PoolMeasurementGroup instance' self.assertIsInstance(self.pmg, PoolMeasurementGroup, msg) - # TODO: until the measurement group does not have a default software - # synchronizer mark this test as expected failure. - @unittest.expectedFailure def test_acquisition(self): """Test acquisition using the created measurement group without using a Sardana pool.""" @@ -80,7 +77,7 @@ def test_acquisition(self): # 'acquiring..' while acq.is_running(): time.sleep(0.05) - self.assertEqual(self._pct.value, integ_time, msg) + self.assertEqual(self._pct.value.value, integ_time, msg) def tearDown(self): unittest.TestCase.tearDown(self) From 95884bece67faa337fe5cd803b2c5f32a7b7177a Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jun 2020 23:03:57 +0200 Subject: [PATCH 624/830] Run sardana tests with pytest Current sardanatestsuite duplicates some tests. It seems like a bug in Python unittest. Instead of investigating it change the test runner to pytest which could open doors for other interesting features comming with pytest. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5d7ce8a1c8..7c002c8276 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,7 +49,7 @@ script: # run flake8 check on all python files in the project - if [ $TEST == "flake8" ]; then ci/flake8_diff.sh; fi # run the full testsuite - - if [ $TEST == "testsuite" ]; then docker exec sardana-test sardanatestsuite; fi + - if [ $TEST == "testsuite" ]; then docker exec sardana-test pytest /usr/local/lib/python3.5/dist-packages/sardana; fi # build docs - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; fi - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; fi From 34d9442f98bd84d87a5331ee9f9c136b8583c54e Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 9 Jun 2020 15:10:47 +0200 Subject: [PATCH 625/830] Correct the path to sardana installation dest --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7c002c8276..4eeec1487e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,7 +49,7 @@ script: # run flake8 check on all python files in the project - if [ $TEST == "flake8" ]; then ci/flake8_diff.sh; fi # run the full testsuite - - if [ $TEST == "testsuite" ]; then docker exec sardana-test pytest /usr/local/lib/python3.5/dist-packages/sardana; fi + - if [ $TEST == "testsuite" ]; then docker exec sardana-test pytest /usr/local/lib/python3.5/dist-packages/sardana-*.egg/sardana; fi # build docs - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; fi - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; fi From df3dfbdd5745f73467591425d4e9e2f2c14779a6 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 9 Jun 2020 16:00:43 +0200 Subject: [PATCH 626/830] Execute pytest in bash to GLOB sardana dir name --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4eeec1487e..c9d3790a83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,7 +49,7 @@ script: # run flake8 check on all python files in the project - if [ $TEST == "flake8" ]; then ci/flake8_diff.sh; fi # run the full testsuite - - if [ $TEST == "testsuite" ]; then docker exec sardana-test pytest /usr/local/lib/python3.5/dist-packages/sardana-*.egg/sardana; fi + - if [ $TEST == "testsuite" ]; then docker exec -t sardana-test /bin/bash -c "pytest /usr/local/lib/python3.5/dist-packages/sardana-*.egg/sardana"; fi # build docs - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; fi - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; fi From 5f01ac313a5ecb4ccbc0b6e99bc1f880d03c164b Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 9 Jun 2020 23:16:56 +0200 Subject: [PATCH 627/830] Wait longer for DS to start in tests It has been observed that when switching to execution of tests by pytest sometimes the DS started by tests are taking more then 10 s to start. Usually the start time is 3-4 s. This was especially reproducible when executing all tests, then the workspace/sardana/src/sardana/tango/pool/test/test_Motor.py was failing. When failing we observed that it takes 11 s. --- src/sardana/tango/macroserver/test/base.py | 2 +- src/sardana/tango/pool/test/base.py | 2 +- src/sardana/tango/pool/test/test_persistence.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/tango/macroserver/test/base.py b/src/sardana/tango/macroserver/test/base.py index e6b35425de..1ce7fce16c 100644 --- a/src/sardana/tango/macroserver/test/base.py +++ b/src/sardana/tango/macroserver/test/base.py @@ -82,7 +82,7 @@ def setUp(self, properties=None): db.put_device_property(self.ms_name, {key: values}) # start MS server - self._msstarter.startDs() + self._msstarter.startDs(wait_seconds=20) self.door = PyTango.DeviceProxy(self.door_name) except Exception as e: # force tearDown in order to eliminate the MacroServer diff --git a/src/sardana/tango/pool/test/base.py b/src/sardana/tango/pool/test/base.py index 8027e34ae2..a6708761dc 100644 --- a/src/sardana/tango/pool/test/base.py +++ b/src/sardana/tango/pool/test/base.py @@ -66,7 +66,7 @@ def setUp(self, properties=None): db.put_device_property(self.pool_name, {key: values}) # start Pool server - self._starter.startDs() + self._starter.startDs(wait_seconds=20) # register extensions so the test methods can use them self.pool = PyTango.DeviceProxy(self.pool_name) diff --git a/src/sardana/tango/pool/test/test_persistence.py b/src/sardana/tango/pool/test/test_persistence.py index 6b7d6d2004..cac52dc997 100644 --- a/src/sardana/tango/pool/test/test_persistence.py +++ b/src/sardana/tango/pool/test/test_persistence.py @@ -68,7 +68,7 @@ def check_elems_presistence(self, info): self.elem_name]) # Restart Pool self._starter.stopDs(hard_kill=True) - self._starter.startDs() + self._starter.startDs(wait_seconds=20) # Check if the element exists try: obj = PyTango.DeviceProxy(self.elem_name) From 336f4ea296265acca753436dcf27ab307c037d3c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Jun 2020 23:28:16 +0200 Subject: [PATCH 628/830] Remove redundant protection in PoolElement.start() and waitFinish() A deadlock may happen between a thread using the said methods and the *Tango Event Consumer* thread. The first one locks the `AttrEventWait` object while the second one locks the Tango event system. It was observed when using the Sardana-Taurus model extension of the measurement group which executes `PoolElement.start()` which basically waits for the measurement group being in a state != MOVING, then executes the `Start` command and then wait for state == MOVING. What happens is that while executing the `Start` command the GC enters and destroys some Taurus device created previously. Meanwhile, a state event already arrives and `AttrEventWait.fireEvent()` triggered from the `TangoAttribute.push_event()` waits for the lock. This lock will never get released cause the `DeviceProxy.unsubscribe_event()` from the `TanogAttribute.__del__()` waits for the Tango event callback to finish. And here we have a deadlock. The `AttrEventWait` locking/unlocking in `PoolElement.start()` and `PoolElement.waitFinish()` role is to: 1. Do not process State events from the moment of lock. `waitEvent()` locks in the next instructions but there is still some unprotected period. 2. Do not process State events right after the `Start` command. 3. In case of `PoolElement.start()` protect `__go_time` and `__go_start_time` from being modified by another thread - these however are unprotected as soon as we leave the method. 4. In case of `PoolElement.waitFinish()` protect `__go_time` and `__go_end_time` from being modified by another thread - these however are unprotected as soon as we leave the method. The protections 1. and 2. are not really needed. If we eliminate the lock from `start()` and `waitFinish()` then the eventual events would be processed by `AttrEventWait.fireEvent()`. This processing updates the internal map of events which later on is interpreted by `AttrEventWait.waitEvent()` to determine if a given event already arrived or not. So we will realize anyway that it arrived. Remove the redundant protection. --- src/sardana/taurus/core/tango/sardana/pool.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 20d1b29b85..c1a5e090fd 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -477,7 +477,6 @@ def getInstrument(self): def start(self, *args, **kwargs): evt_wait = self._getEventWait() evt_wait.connect(self.getAttribute("state")) - evt_wait.lock() try: evt_wait.waitEvent(DevState.MOVING, equal=False) self.__go_time = 0 @@ -488,8 +487,6 @@ def start(self, *args, **kwargs): except: evt_wait.disconnect() raise - finally: - evt_wait.unlock() ts2 = evt_wait.getRecordedEvents().get(DevState.MOVING, ts2) return (ts2,) @@ -514,14 +511,12 @@ def waitFinish(self, timeout=None, id=None): if id is not None: id = id[0] evt_wait = self._getEventWait() - evt_wait.lock() try: evt_wait.waitEvent(DevState.MOVING, after=id, equal=False, timeout=timeout, retries=retries) 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 From c0d6416d29956e20029a85ad63fdc2c8850fff9a Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jun 2020 19:35:05 +0200 Subject: [PATCH 629/830] Remove `setup.py test` - it is deprecated https://github.com/pytest-dev/pytest/issues/5534 --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 1573fb3dd1..4d12ca5066 100644 --- a/setup.py +++ b/setup.py @@ -130,6 +130,5 @@ def get_release_info(): entry_points=entry_points, provides=provides, requires=requires, - install_requires=install_requires, - test_suite='sardana.test.testsuite.get_sardana_unitsuite', + install_requires=install_requires ) From d331e8a06157650a880233d54fb39bb8fdd85530 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jun 2020 19:35:53 +0200 Subject: [PATCH 630/830] Remove sardanatestsuite implementation Only leave a script for showing alternative. --- src/sardana/test/testsuite.py | 95 ++--------------------------------- 1 file changed, 4 insertions(+), 91 deletions(-) diff --git a/src/sardana/test/testsuite.py b/src/sardana/test/testsuite.py index 1924164076..7107535093 100644 --- a/src/sardana/test/testsuite.py +++ b/src/sardana/test/testsuite.py @@ -24,102 +24,15 @@ ############################################################################## """ -This module defines the test suite for the whole sardana package -Usage:: - - from sardana.test import testsuite - testsuite.run() - +DEPRECATED """ __docformat__ = 'restructuredtext' -import os -import re -import unittest -import sardana - - -def _filter_suite(suite, exclude_pattern, ret=None): - """removes TestCases from a suite based on regexp matching on the Test id""" - if ret is None: - ret = unittest.TestSuite() - for e in suite: - if isinstance(e, unittest.TestCase): - if re.match(exclude_pattern, e.id()): - print("Excluded %s" % e.id()) - continue - ret.addTest(e) - else: - _filter_suite(e, exclude_pattern, ret=ret) - return ret - - -def get_sardana_suite(exclude_pattern='(?!)'): - """discover all tests in sardana, except those matching `exclude_pattern`""" - loader = unittest.defaultTestLoader - start_dir = os.path.dirname(sardana.__file__) - suite = loader.discover( - start_dir, top_level_dir=os.path.dirname(start_dir)) - return _filter_suite(suite, exclude_pattern) - - -def get_sardana_unitsuite(): - """Provide test suite with only unit tests. These exclude: - - functional tests of macros that requires the "sar_demo environment" - """ - pattern = 'sardana\.macroserver\.macros\.test*|' +\ - 'sardana\.tango\.pool\.test*' - return get_sardana_suite(exclude_pattern=pattern) - - -def run(exclude_pattern='(?!)'): - '''Runs all tests for the sardana package - - :returns: the test runner result - :rtype: unittest.result.TestResult - ''' - # discover all tests within the sardana/src directory - suite = get_sardana_suite(exclude_pattern=exclude_pattern) - # use the basic text test runner that outputs to sys.stderr - runner = unittest.TextTestRunner(descriptions=True, verbosity=2) - # run the test suite - result = runner.run(suite) - return result - def main(): + print("sardanatestsuite was removed in Sardana Jan20. " + "Use pytest to run tests.") import sys - import argparse - from sardana import Release - - parser = argparse.ArgumentParser(description='Main test suite for Sardana') - # TODO: Define the default exclude patterns as a sardanacustomsettings - # variable. - help = """regexp pattern matching test ids to be excluded. - (e.g. 'sardana\.pool\..*' would exclude sardana.pool tests) - """ - parser.add_argument('-e', '--exclude-pattern', - dest='exclude_pattern', - default='(?!)', - help=help) - parser.add_argument('--version', action='store_true', default=False, - help="show program's version number and exit") - args = parser.parse_args() - - if args.version: - print(Release.version) - sys.exit(0) - - ret = run(exclude_pattern=args.exclude_pattern) - - # calculate exit code (0 if OK and 1 otherwise) - if ret.wasSuccessful(): - exit_code = 0 - else: - exit_code = 1 - sys.exit(exit_code) - + sys.exit(1) -if __name__ == '__main__': - main() From 8dedbcc0ec6e76b01602c66fab832b4ce1bceecc Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jun 2020 19:36:34 +0200 Subject: [PATCH 631/830] Update docs with the move from unittest to pytest --- doc/source/devel/howto_test/test_general.rst | 24 +++++------- .../devel/howto_test/test_run_commands.rst | 39 +++++-------------- 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/doc/source/devel/howto_test/test_general.rst b/doc/source/devel/howto_test/test_general.rst index 2d4f54f7ca..9a5336b1d0 100644 --- a/doc/source/devel/howto_test/test_general.rst +++ b/doc/source/devel/howto_test/test_general.rst @@ -29,23 +29,19 @@ revealing the bug. The first tests implemented are focused on Unit Tests, but the same framework should be used for integration and system tests as well. -The sardana.test module includes testsuite.py. This file provides an -auto-discovering suite for all tests implemented in Sardana. - The following are some key points to keep in mind when using this framework: -- The Sardana Test Framework is based on :mod:`unittest` which should be - imported from :mod:`taurus.external` in order to be compatible with all - versions of python supported by Taurus. - -- all test-related code is contained in submodules named `test` which appear +* Most of the tests in the Sardana Test Framework use :mod:`unittest`. + Since Sardana Jan20 we decided to move to `pytest `_ + and we plan to gradually migrate the already existing tests. +* All test-related code is contained in submodules named ``test`` which appear in any module of Sardana. - -- test-related code falls in one of these three categories: - * Actual test code (classes that derive from unittest.TestCase) - * Utility classes/functions (code to simplify development of test code) - * Resources (accessory files required by some test). They are located in - subdirectories named `res` situated inside the folders named `test`. +* Test related code falls in one of these three categories: + + * Actual test code (classes that derive from unittest.TestCase) + * Utility classes/functions (code to simplify development of test code) + * Resources (accessory files required by some test). They are located in + subdirectories named ``res`` situated inside the folders named ``test``. For a more complete description of the conventions on how to write tests with the Sardana Testing Framework, please refer to the diff --git a/doc/source/devel/howto_test/test_run_commands.rst b/doc/source/devel/howto_test/test_run_commands.rst index 83f487a985..484e7b79f0 100644 --- a/doc/source/devel/howto_test/test_run_commands.rst +++ b/doc/source/devel/howto_test/test_run_commands.rst @@ -10,35 +10,13 @@ Run tests from command line Run test suite -------------- -Running the Sardana test suite from command line can be done in two -different ways: +We recommend using pytest to run Sardana tests. Please refer to +`pytest documentation `_ +on how to execute tests. -1) Sardana tests can be executed using the `setuptools` test command prior to - the installation by executing the following command from within the sardana - project directory: - - python setup.py test - - This will execute only a subset of all the sardana tests - the unit test suite. - The functional tests, that require the :ref:`sardana-test-sar_demo`, are - excluded on purpose. - -2) The complete Sardana test suite, that includes the unit and the functional tests - can be executed only after the Sardana installation by executing the - `sardanatestsuite` script. - -Run a single test ------------------ - -Executing a single test from command line is done by doing: - - python -m unittest test_name - -Where test_name is the test module that has to be run. - -That can be done with more verbosity by indicating the option -v. - - python -m unittest -v test_name +.. note:: + Currently the majority of the Sardana tests are written using unittest. + We plan to gradually migrate them to pytest. .. _sardana-test-sar_demo: @@ -46,7 +24,8 @@ sar_demo test environment ------------------------- Some of the Sardana tests e.g. the ones that test the macros, require a running Sardana -instance with the sar_demo macro executed previosly. By default the tests will try to +instance with the sar_demo macro executed previously. By default the tests will try to connect to the `door/demo1/1` door in order to run the macros there. The default door -name can be changed in the `sardanacustomsettings` module. +name can be changed with the `sardana.sardanacustomsettings.UNITTEST_DOOR_NAME` +module. From 2808f3149b885048a51840341cc646329d396e98 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Jun 2020 19:36:58 +0200 Subject: [PATCH 632/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcf2f01aa1..5aa9bae108 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `showscan online_raw` magic command in spock (#1260) * `online` kwarg in `SpockBaseDoor` constructor (#1260) * `sardana.requirements` (#1185) +* `sardanatestsuite` and `sardana.test.testsuite.*` utility functions (#1347) ## [2.8.5] 2020-04-27 From 6c00d5fcb7fa2d390e2547d554fcdf5a331a1560 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jun 2020 21:06:30 +0200 Subject: [PATCH 633/830] Add tests for StateOne methods of counter/timer and motor --- .../pool/test/test_poolcountertimer.py | 33 +++++++++ src/sardana/pool/test/test_poolmotor.py | 68 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 src/sardana/pool/test/test_poolmotor.py diff --git a/src/sardana/pool/test/test_poolcountertimer.py b/src/sardana/pool/test/test_poolcountertimer.py index 29aa921d2e..d4214e764d 100644 --- a/src/sardana/pool/test/test_poolcountertimer.py +++ b/src/sardana/pool/test/test_poolcountertimer.py @@ -25,8 +25,10 @@ import time +import pytest import unittest +from sardana import State from sardana.pool.poolcountertimer import PoolCounterTimer from sardana.pool.test import (FakePool, createPoolController, createPoolCounterTimer, dummyCounterTimerConf01, @@ -61,3 +63,34 @@ def test_acquisition(self): def tearDown(self): unittest.TestCase.tearDown(self) self.pct = None + + +def StateOne_state(self, axis): + return State.On + + +def StateOne_state_status(self, axis): + return State.On, "Status" + + +@pytest.mark.parametrize("mock_StateOne", [StateOne_state, + StateOne_state_status]) +def test_state(monkeypatch, mock_StateOne): + """Test variants of StateOne return value: + - state + - state, status + """ + pool = FakePool() + # when SEP19 gets implemented it should be possible to mock directly + # the imported class + DummyCounterTimerController = pool.ctrl_manager.getControllerClass( + "DummyCounterTimerController") + monkeypatch.setattr(DummyCounterTimerController, "StateOne", + mock_StateOne) + ct_ctrl = createPoolController(pool, dummyPoolCTCtrlConf01) + ct = createPoolCounterTimer(pool, ct_ctrl, dummyCounterTimerConf01) + ct_ctrl.add_element(ct) + pool.add_element(ct_ctrl) + pool.add_element(ct) + assert ct.state == State.On + assert type(ct.status) == str diff --git a/src/sardana/pool/test/test_poolmotor.py b/src/sardana/pool/test/test_poolmotor.py new file mode 100644 index 0000000000..99ade78c6c --- /dev/null +++ b/src/sardana/pool/test/test_poolmotor.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +# Sardana 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. +## +# Sardana 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 Sardana. If not, see . +## +############################################################################## + +import pytest + +from sardana import State +from sardana.pool.test import (FakePool, createPoolController, + createPoolMotor, dummyPoolMotorCtrlConf01, + dummyMotorConf01) + + +def StateOne_state(self, axis): + return State.On + + +def StateOne_state_status(self, axis): + return State.On, "Status" + + +def StateOne_state_status_limits(self, axis): + return State.On, "Status", 0 + + +@pytest.mark.parametrize("mock_StateOne", [StateOne_state, + StateOne_state_status, + StateOne_state_status_limits]) +def test_state(monkeypatch, mock_StateOne): + """Test variants of StateOne return value: + - state + - state, status + - state, status, limit_switches + """ + pool = FakePool() + # when SEP19 gets implemented it should be possible to mock directly + # the imported class + DummyMotorController = pool.ctrl_manager.getControllerClass( + "DummyMotorController") + monkeypatch.setattr(DummyMotorController, "StateOne", mock_StateOne) + mot_ctrl = createPoolController(pool, dummyPoolMotorCtrlConf01) + mot = createPoolMotor(pool, mot_ctrl, dummyMotorConf01) + mot_ctrl.add_element(mot) + pool.add_element(mot_ctrl) + pool.add_element(mot) + assert mot.state == State.On + assert type(mot.status) == str + assert mot.limit_switches.value == (False, ) * 3 From 0d2d4f9624c57bd752dd73ff6ee9985c9ffb9730 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jun 2020 21:11:39 +0200 Subject: [PATCH 634/830] Do not consider ctrl status as part of standard status --- src/sardana/pool/poolbaseelement.py | 7 ++++--- src/sardana/pool/poolmotor.py | 9 ++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sardana/pool/poolbaseelement.py b/src/sardana/pool/poolbaseelement.py index 4eccdd0a81..ffd6db60c5 100644 --- a/src/sardana/pool/poolbaseelement.py +++ b/src/sardana/pool/poolbaseelement.py @@ -239,7 +239,7 @@ def put_status(self, status): # state information # -------------------------------------------------------------------------- - _STD_STATUS = "{name} is {state}\n{ctrl_status}" + _STD_STATUS = "{name} is {state}" def calculate_state_info(self, status_info=None): """Transforms the given state information. This specific base @@ -258,8 +258,9 @@ def calculate_state_info(self, status_info=None): status_info = self._state, self._status state, status = status_info state_str = State[state] - new_status = self._STD_STATUS.format(name=self.name, state=state_str, - ctrl_status=status) + new_status = self._STD_STATUS.format(name=self.name, state=state_str) + if status is not None and len(status) > 0: + new_status += "\n{}".format(status) # append ctrl status return status_info[0], new_status def set_state_info(self, state_info, propagate=1, safe=False): diff --git a/src/sardana/pool/poolmotor.py b/src/sardana/pool/poolmotor.py index 3991190960..5599741b68 100644 --- a/src/sardana/pool/poolmotor.py +++ b/src/sardana/pool/poolmotor.py @@ -274,7 +274,7 @@ def _set_state_info(self, state_info, propagate=1): # state information # ------------------------------------------------------------------------- - _STD_STATUS = "{name} is {state}{limit_switches}{ctrl_status}" + _STD_STATUS = "{name} is {state}{limit_switches}" def calculate_state_info(self, state_info=None): if state_info is None: @@ -307,11 +307,10 @@ def calculate_state_info(self, state_info=None): if ls[2]: limit_switches += ". Hit lower switch" - if len(status) > 0: - status = "\n" + status new_status = self._STD_STATUS.format(name=self.name, state=state_str, - limit_switches=limit_switches, - ctrl_status=status) + limit_switches=limit_switches) + if status is not None and len(status) > 0: + new_status += "\n{}".format(status) # append ctrl status return state, new_status, ls # ------------------------------------------------------------------------- From 120a06326fbb20f4d18b2ac8794f18d4e748fd5a Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jun 2020 21:12:41 +0200 Subject: [PATCH 635/830] Allow to return only state in StateOne Fix #621 --- src/sardana/pool/poolbaseelement.py | 16 +++++++--------- src/sardana/pool/poolmotor.py | 15 ++++++--------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/sardana/pool/poolbaseelement.py b/src/sardana/pool/poolbaseelement.py index ffd6db60c5..22e503e17e 100644 --- a/src/sardana/pool/poolbaseelement.py +++ b/src/sardana/pool/poolbaseelement.py @@ -285,15 +285,13 @@ def put_state_info(self, state_info): self.set_state_info(state_info, propagate=0) def _from_ctrl_state_info(self, state_info): - try: - state_str = State.whatis(state_info) - return int(state_info), "{0} is in {1}".format(self.name, state_str) - except KeyError: - pass - state_info, _ = state_info - state, status = state_info[:2] - state = int(state) - return state, status + state_info, _ = state_info # ignoring exc_info + if state_info in State: + state = state_info + status = None + else: + state, status = state_info + return int(state), status # -------------------------------------------------------------------------- # default attribute diff --git a/src/sardana/pool/poolmotor.py b/src/sardana/pool/poolmotor.py index 5599741b68..3a9d52f3a7 100644 --- a/src/sardana/pool/poolmotor.py +++ b/src/sardana/pool/poolmotor.py @@ -245,19 +245,16 @@ def on_change(self, evt_src, evt_type, evt_value): def _from_ctrl_state_info(self, state_info): state_info, _ = state_info - - try: - state_str = State.whatis(state_info) - return int(state_info), "{0} is in {1}".format(self.name, state_str), 0 - except KeyError: - pass - - if len(state_info) > 2: + if state_info in State: + state = state_info + status = None + ls = 0 + elif len(state_info) > 2: state, status, ls = state_info[:3] else: state, other = state_info[:2] if is_number(other): - ls, status = other, '' + ls, status = other, None else: ls, status = 0, other state, ls = int(state), tuple(map(bool, (ls & 1, ls & 2, ls & 4))) From d6e1a2c833312c4686b4fcdb04ee9c5b69381056 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Jun 2020 21:12:50 +0200 Subject: [PATCH 636/830] Fix flake8 --- src/sardana/pool/poolbaseelement.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/pool/poolbaseelement.py b/src/sardana/pool/poolbaseelement.py index 22e503e17e..d91c17d28a 100644 --- a/src/sardana/pool/poolbaseelement.py +++ b/src/sardana/pool/poolbaseelement.py @@ -250,7 +250,8 @@ def calculate_state_info(self, status_info=None): status information. :param status_info: - given status information [default: None, meaning use current state status. + given status information [default: None, meaning use current state + status. :type status_info: tuple :return: a transformed state information :rtype: tuple""" From b3e1c210a940680d684beb35ea5beeca41ff3b1a Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 12 Jun 2020 10:06:28 +0200 Subject: [PATCH 637/830] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcf2f01aa1..d98963bd60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,10 @@ This file follows the formats and conventions from [keepachangelog.com] * Use `tango.EnsureOmnitThread` to protect Sardana threads (Tango is not thread safe) (#1298) * Avoid using Tango `AttributeProxy` in limits protection to not be affected - by bug tango-controls/pytango#315 (#1302) + by bug tango-controls/pytango#315 (#1302) +* Avoid deadlock in Sardana-Taurus models e.g. `MeasurementGroup.count()` or + `Motor.move()` (#1348) + * Remove redundant protection in PoolElement.start() and waitFinish() * Default macro parameter values in macroexecutor (#1153) * Executing RunMacro Door's command with string parameters containing spaces (#1240) * Setting of environment variables in Python 3.7 (#1195) From d1b3d3d050e851847fe9caec5d4dcbf252607913 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Jun 2020 15:07:00 +0200 Subject: [PATCH 638/830] Print MG state name instead of obj in case ct macros fails --- src/sardana/macroserver/macros/standard.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 28a81c56ab..2a5201ab0b 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -718,7 +718,8 @@ def run(self, integ_time, countable_elem): names = self.countable_elem.ElementList elements = [self.getObj(name) for name in names] self.dump_information(elements) - raise ValueError("Acquisition ended with {}".format(state)) + raise ValueError("Acquisition ended with {}".format( + state.name.capitalize())) for postAcqHook in self.getHooks('post-acq'): postAcqHook() @@ -816,7 +817,8 @@ def run(self, integ_time, countable_elem): names = self.countable_elem.ElementList elements = [self.getObj(name) for name in names] self.dump_information(elements) - raise ValueError("Acquisition ended with {}".format(state)) + raise ValueError("Acquisition ended with {}".format( + state.name.capitalize())) self.setData(Record(data)) self.printAllValues() From 0ba73d43732b32c07b76657b7dc0b9a8fa76de28 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Jun 2020 15:10:41 +0200 Subject: [PATCH 639/830] Reduce nap time in hardware synchronized acquisition Single repetition hardware synchronized measurements e.g. ct macro, can last for 0.2 too long due to this excessive nap time. Use 0.01 nap time (default in other actions). --- src/sardana/pool/poolsynchronization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/poolsynchronization.py b/src/sardana/pool/poolsynchronization.py index d1b88f80f4..7877801ebc 100644 --- a/src/sardana/pool/poolsynchronization.py +++ b/src/sardana/pool/poolsynchronization.py @@ -254,7 +254,7 @@ def action_loop(self): # Triggering loop # TODO: make nap configurable (see motion or acquisition loops) - nap = 0.2 + nap = 0.01 while True: self.read_state_info(ret=states) if not self.is_triggering(states): From 6aa4fd8a2237fc62f1fdf09c9c7ef3cf7abf13a8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Jun 2020 15:11:33 +0200 Subject: [PATCH 640/830] Print exception in cleanup of SatTestTestCase --- src/sardana/tango/pool/test/base_sartest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/tango/pool/test/base_sartest.py b/src/sardana/tango/pool/test/base_sartest.py index dfa32ad2bf..cbd7901501 100644 --- a/src/sardana/tango/pool/test/base_sartest.py +++ b/src/sardana/tango/pool/test/base_sartest.py @@ -169,7 +169,8 @@ def tearDown(self): _cleanup_device(elem_name) try: self.pool.DeleteElement(elem_name) - except: + except Exception as e: + print(e) dirty_elems.append(elem_name) for ctrl_name in self.ctrl_list: From ec296580937ad906985f5e9f501a38036e75b9f7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 4 Jun 2020 19:14:04 +0200 Subject: [PATCH 641/830] Update PseudoCounter Value attribute with exc_info calc catches exceptions coming from the calculations done in the plugin code, however the exc_info is never updated on the Value attribute. This is important when pushing value events - error should be pushed instead of None value. --- src/sardana/pool/poolpseudocounter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/pool/poolpseudocounter.py b/src/sardana/pool/poolpseudocounter.py index 5aa7b4c123..e0eb460273 100644 --- a/src/sardana/pool/poolpseudocounter.py +++ b/src/sardana/pool/poolpseudocounter.py @@ -166,6 +166,8 @@ def calc(self, physical_values=None): "values (you gave %d)" % (obj.name, l_u, l_v)) ctrl, axis = obj.controller, obj.axis result = ctrl.calc(axis, physical_values) + if result.error: + self._exc_info = result.exc_info except SardanaException as se: self._exc_info = se.exc_info result = SardanaValue(exc_info=se.exc_info) From 9dde6b9e543e0d1c1a7bf4de76e69d4e74af7293 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jun 2020 13:30:09 +0200 Subject: [PATCH 642/830] Refactor PoolAcquisitionBase Introduce PoolAcquisitionTimerable concept for timerable only sub-action. --- src/sardana/pool/poolacquisition.py | 52 +++++++++++++++++++---------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index dbe2abaaef..ba68839d3b 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -650,7 +650,7 @@ def read_value(self, ret=None, serial=False): class PoolAcquisitionBase(PoolAction): - """Base class for acquisitions with a generic start_action method. + """Base class for sub-acquisition. .. note:: The PoolAcquisitionBase class has been included in Sardana @@ -659,18 +659,36 @@ class PoolAcquisitionBase(PoolAction): deemed necessary by the core developers. """ - OperationContextClass = AcquisitionBaseContext - def __init__(self, main_element, name): PoolAction.__init__(self, main_element, name) self._channels = [] self._index = None + + +class PoolAcquisitionTimerable(PoolAcquisitionBase): + """Base class for acquisitions of timerable channels. + + Implements a generic start_action method. action_loop method must be + implemented by the sub-class. + + .. note:: + The PoolAcquisitionTimerable class has been included in Sardana + on a provisional basis. Backwards incompatible changes + (up to and including removal of the module) may occur if + deemed necessary by the core developers. + """ + + OperationContextClass = AcquisitionBaseContext + + def __init__(self, main_element, name): + PoolAcquisitionBase.__init__(self, main_element, name) self._nb_states_per_value = None self._acq_sleep_time = None self._pool_ctrl_dict_loop = None self._pool_ctrl_dict_ref = None self._pool_ctrl_dict_value = None + # TODO: for the moment we can not clear value buffers at the end of # the acquisition. This is because of the pseudo counters that are # based on channels synchronized by hardware and software. @@ -980,7 +998,7 @@ def clear_value_buffers(self): channel.clear_value_buffer() -class PoolAcquisitionHardware(PoolAcquisitionBase): +class PoolAcquisitionHardware(PoolAcquisitionTimerable): """Acquisition action for controllers synchronized by hardware .. note:: @@ -994,12 +1012,12 @@ class PoolAcquisitionHardware(PoolAcquisitionBase): """ def __init__(self, main_element, name="AcquisitionHardware"): - PoolAcquisitionBase.__init__(self, main_element, name) + PoolAcquisitionTimerable.__init__(self, main_element, name) def start_action(self, ctrls, value, repetitions, latency, acq_sleep_time=None, nb_states_per_value=None, **kwargs): - PoolAcquisitionBase.start_action(self, ctrls, value, None, + PoolAcquisitionTimerable.start_action(self, ctrls, value, None, repetitions, latency, None, acq_sleep_time, nb_states_per_value, **kwargs) @@ -1078,7 +1096,7 @@ def action_loop(self): self.add_finish_hook(set_state_info, False) -class PoolAcquisitionSoftware(PoolAcquisitionBase): +class PoolAcquisitionSoftware(PoolAcquisitionTimerable): """Acquisition action for controllers synchronized by software .. note:: @@ -1089,7 +1107,7 @@ class PoolAcquisitionSoftware(PoolAcquisitionBase): """ def __init__(self, main_element, name="AcquisitionSoftware", slaves=None): - PoolAcquisitionBase.__init__(self, main_element, name) + PoolAcquisitionTimerable.__init__(self, main_element, name) if slaves is None: slaves = () @@ -1116,7 +1134,7 @@ def get_read_value_loop_ctrls(self): def start_action(self, ctrls, value, master, index, acq_sleep_time=None, nb_states_per_value=None, **kwargs): - PoolAcquisitionBase.start_action(self, ctrls, value, master, 1, 0, + PoolAcquisitionTimerable.start_action(self, ctrls, value, master, 1, 0, index, acq_sleep_time, nb_states_per_value, **kwargs) @@ -1188,7 +1206,7 @@ def action_loop(self): self.add_finish_hook(set_state_info, False) -class PoolAcquisitionSoftwareStart(PoolAcquisitionBase): +class PoolAcquisitionSoftwareStart(PoolAcquisitionTimerable): """Acquisition action for controllers synchronized by software start .. note:: @@ -1202,7 +1220,7 @@ class PoolAcquisitionSoftwareStart(PoolAcquisitionBase): """ def __init__(self, main_element, name="AcquisitionSoftwareStart"): - PoolAcquisitionBase.__init__(self, main_element, name) + PoolAcquisitionTimerable.__init__(self, main_element, name) def get_read_value_ctrls(self): # technical debt in order to work both in case of meas group and @@ -1212,7 +1230,7 @@ def get_read_value_ctrls(self): def start_action(self, ctrls, value, master, repetitions, latency, acq_sleep_time=None, nb_states_per_value=None, **kwargs): - PoolAcquisitionBase.start_action(self, ctrls, value, master, + PoolAcquisitionTimerable.start_action(self, ctrls, value, master, repetitions, latency, None, acq_sleep_time, nb_states_per_value, **kwargs) @@ -1300,7 +1318,7 @@ def action_loop(self): self.add_finish_hook(set_state_info, False) -class PoolCTAcquisition(PoolAcquisitionBase): +class PoolCTAcquisition(PoolAcquisitionTimerable): """..todo:: remove it, still used by pseudo counter""" def __init__(self, main_element, name="CTAcquisition", slaves=None): @@ -1310,7 +1328,7 @@ def __init__(self, main_element, name="CTAcquisition", slaves=None): slaves = () self._slaves = slaves - PoolAcquisitionBase.__init__(self, main_element, name) + PoolAcquisitionTimerable.__init__(self, main_element, name) def get_read_value_loop_ctrls(self): return self._pool_ctrl_dict_loop @@ -1389,12 +1407,10 @@ def action_loop(self): self.add_finish_hook(set_state_info, False) -class Pool0DAcquisition(PoolAction): +class Pool0DAcquisition(PoolAcquisitionBase): def __init__(self, main_element, name="0DAcquisition"): - self._channels = None - self._index = None - PoolAction.__init__(self, main_element, name) + PoolAcquisitionBase.__init__(self, main_element, name) def start_action(self, conf_ctrls, index, acq_sleep_time=None, nb_states_per_value=None, **kwargs): From 292fcea7581aef4115025872e54a2ae6e08bf7d6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jun 2020 15:37:13 +0200 Subject: [PATCH 643/830] Fix problem of measurement group acquisitions Two consecutive measurement group acquisitions executed very fast can overlap. The software trigger of the next acquisition may be skipped cause the previous one is still in progress. Re-introduce the threading.Event for protecting the acquisition action (it was already used in the past). On the first software trigger wait for the previous acquisition to finish. --- src/sardana/pool/poolacquisition.py | 46 ++++++++++--- src/sardana/pool/poolmeasurementgroup.py | 12 ++-- src/sardana/pool/test/test_acquisition.py | 79 ++++++++++------------- 3 files changed, 78 insertions(+), 59 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index ba68839d3b..555d488e4b 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -38,6 +38,7 @@ import datetime import traceback import functools +import threading from taurus.core.util.log import DebugIt from taurus.core.util.enumeration import Enumeration @@ -305,6 +306,7 @@ def __init__(self, main_element, name="Acquisition"): self._0d_acq = Pool0DAcquisition(main_element, name=zerodname) self._hw_acq = PoolAcquisitionHardware(main_element, name=hwname) self._synch = PoolSynchronization(main_element, name=synchname) + self._handled_first_active = False def event_received(self, *args, **kwargs): """Callback executed on event of software synchronizer. @@ -322,46 +324,56 @@ def event_received(self, *args, **kwargs): self.debug(msg) if name == "start": if self._sw_start_acq_args is not None: + self._sw_start_acq._wait() + self._sw_start_acq._set_busy() self.debug('Executing software start acquisition.') - get_thread_pool().add(self._sw_start_acq.run, None, + get_thread_pool().add(self._sw_start_acq.run, + self._sw_start_acq._set_ready, *self._sw_start_acq_args.args, **self._sw_start_acq_args.kwargs) elif name == "active": # this code is not thread safe, but for the moment we assume that # only one EventGenerator will work at the same time + if self._handled_first_active: + timeout = 0 + else: + timeout = None + self._handled_first_active = True if self._sw_acq_args is not None: - if self._sw_acq._is_started() or self._sw_acq.is_running(): + if not self._sw_acq._wait(timeout): msg = ('Skipping trigger: software acquisition is still' ' in progress.') self.debug(msg) return else: + self._sw_acq._set_busy() self.debug('Executing software acquisition.') self._sw_acq_args.kwargs.update({'index': index}) - self._sw_acq._started = True - get_thread_pool().add(self._sw_acq.run, None, + get_thread_pool().add(self._sw_acq.run, + self._sw_acq._set_ready, *self._sw_acq_args.args, **self._sw_acq_args.kwargs) if self._0d_acq_args is not None: - if self._0d_acq._is_started() or self._0d_acq.is_running(): + if not self._0d_acq._wait(timeout): msg = ('Skipping trigger: ZeroD acquisition is still in' ' progress.') self.debug(msg) return else: + self._0d_acq._set_busy() self.debug('Executing ZeroD acquisition.') self._0d_acq_args.kwargs.update({'index': index}) self._0d_acq._started = True self._0d_acq._stopped = False self._0d_acq._aborted = False - get_thread_pool().add(self._0d_acq.run, None, + get_thread_pool().add(self._0d_acq.run, + self._0d_acq._set_ready, *self._0d_acq_args.args, **self._0d_acq_args.kwargs) elif name == "passive": # TODO: _0d_acq_args comparison may not be necessary if (self._0d_acq_args is not None - and (self._0d_acq._is_started() - or self._0d_acq.is_running())): + and not self._0d_acq._is_ready()): self.debug('Stopping ZeroD acquisition.') self._0d_acq.stop_action() @@ -378,6 +390,7 @@ def prepare(self, config, acq_mode, value, synch_description=None, self._0d_acq_args = None self._hw_acq_args = None self._synch_args = None + self._handled_first_active = False ctrls_hw = [] ctrls_sw = [] ctrls_sw_start = [] @@ -663,6 +676,23 @@ def __init__(self, main_element, name): PoolAction.__init__(self, main_element, name) self._channels = [] self._index = None + self._ready = threading.Event() + self._ready.set() + + def _is_ready(self): + return self._ready.is_set() + + def _wait(self, timeout=None): + return self._ready.wait(timeout) + + def _set_ready(self, _=None): + self._ready.set() + + def _is_busy(self): + return not self._ready.is_set() + + def _set_busy(self): + self._ready.clear() class PoolAcquisitionTimerable(PoolAcquisitionBase): diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index f5633338b0..ba1751e7b5 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -1033,13 +1033,15 @@ def _calculate_states(self, state_info=None): # check if software synchronizer is occupied synch_soft = self.acquisition._synch._synch_soft acq_sw = self.acquisition._sw_acq + acq_sw_start = self.acquisition._sw_start_acq acq_0d = self.acquisition._0d_acq - if state in (State.On, State.Unknown) \ - and (synch_soft.is_started() or - acq_sw._is_started() or - acq_0d._is_started()): + if (state == State.Unknown + and (synch_soft.is_started() + or acq_sw._is_busy() + or acq_sw_start._is_busy() + or acq_0d._is_busy())): state = State.Moving - status += "/nSoftware synchronization is in progress" + status += "\nSoftware synchronization is in progress" return state, status def on_element_changed(self, evt_src, evt_type, evt_value): diff --git a/src/sardana/pool/test/test_acquisition.py b/src/sardana/pool/test/test_acquisition.py index b6598aa5d5..cf0c1139e8 100644 --- a/src/sardana/pool/test/test_acquisition.py +++ b/src/sardana/pool/test/test_acquisition.py @@ -84,7 +84,7 @@ def prepare(self, integ_time, repetitions, latency_time, nb_starts): def wait_finish(self): # waiting for acquisition and synchronization to finish - while (self.acquisition.is_running() + while (self.acquisition._is_busy() or self.synchronization.is_running()): time.sleep(.1) @@ -160,16 +160,16 @@ def event_received(self, *args, **kwargs): _, type_, index = args name = type_.name if name == "active": - if self.sw_acq_busy.is_set(): + if self.sw_acq._is_busy(): # skipping acquisition cause the previous on is ongoing return else: - self.sw_acq_busy.set() + self.sw_acq._set_busy() args = self.sw_acq_args kwargs = self.sw_acq_kwargs kwargs['index'] = index get_thread_pool().add(self.sw_acq.run, - None, + self.sw_acq._set_ready, *args, **kwargs) @@ -209,18 +209,8 @@ def continuous_acquisition(self, offset, active_interval, passive_interval, # creating acquisition actions self.hw_acq = self.create_action(PoolAcquisitionHardware, [ct_1_1]) self.sw_acq = self.create_action(PoolAcquisitionSoftware, [ct_2_1]) - # Since we deposit the software acquisition action on the PoolThread's - # queue we can not rely on the action's state - one may still wait - # in the queue (its state has not changed to running yet) and we would - # be depositing another one. This way we may be starting multiple - # times the same action (with the same elements involved), what results - # in "already involved in operation" errors. - # Use an external Event flag to mark if we have any software - # acquisition action pending. - self.sw_acq_busy = threading.Event() - self.sw_acq.add_finish_hook(self.sw_acq_busy.clear) self.sw_acq_args = (sw_ctrls, integ_time, sw_master) - self.sw_acq_kwargs = {} + self.sw_acq_kwargs = dict(synch=True) total_interval = active_interval + passive_interval group = { @@ -236,7 +226,7 @@ def continuous_acquisition(self, offset, active_interval, passive_interval, self.synchronization.run(synch_ctrls, synch_description) # waiting for acquisition and synchronization to finish while (self.hw_acq.is_running() - or self.sw_acq.is_running() + or self.sw_acq._is_busy() or self.synchronization.is_running()): time.sleep(.1) self.do_asserts(repetitions, jobs_before) @@ -260,17 +250,17 @@ def event_received(self, *args, **kwargs): _, type_, value = args name = type_.name if name == "active": - if self.acq_busy.is_set(): + if self.acquisition._is_busy(): # skipping acquisition cause the previous on is ongoing return else: - self.acq_busy.set() + self.acquisition._set_busy() acq_args = list(self.acq_args) acq_kwargs = self.acq_kwargs index = value acq_args[3] = index get_thread_pool().add(self.acquisition.run, - None, + self.acquisition._set_ready, *acq_args, **acq_kwargs) @@ -291,18 +281,8 @@ def acquire(self, integ_time, repetitions, latency_time): # creating acquisition actions self.acquisition = self.create_action(PoolAcquisitionSoftware, [self.channel]) - # Since we deposit the software acquisition action on the PoolThread's - # queue we can not rely on the action's state - one may still wait - # in the queue (its state has not changed to running yet) and we would - # be depositing another one. This way we may be starting multiple - # times the same action (with the same elements involved), what results - # in "already involved in operation" errors. - # Use an external Event flag to mark if we have any software - # acquisition action pending. - self.acq_busy = threading.Event() - self.acquisition.add_finish_hook(self.acq_busy.clear) self.acq_args = (ctrls, integ_time, master, None) - self.acq_kwargs = {} + self.acq_kwargs = dict(synch=True) total_interval = integ_time + latency_time group = { @@ -331,7 +311,6 @@ class BaseAcquisitionSoftwareStartTestCase(AcquisitionTestCase): def setUp(self): """Create test actors (controllers and elements)""" - TestCase.setUp(self) AcquisitionTestCase.setUp(self) def event_received(self, *args, **kwargs): @@ -339,7 +318,9 @@ def event_received(self, *args, **kwargs): _, type_, value = args name = type_.name if name == "start": - get_thread_pool().add(self.acquisition.run, None, + self.acquisition._set_busy() + get_thread_pool().add(self.acquisition.run, + self.acquisition._set_ready, *self.acq_args, **self.acq_kwargs) @@ -361,7 +342,7 @@ def acquire(self, integ_time, repetitions, latency_time): self.acquisition = self.create_action(PoolAcquisitionSoftwareStart, [self.channel]) self.acq_args = (ctrls, integ_time, master, repetitions, latency_time) - self.acq_kwargs = {} + self.acq_kwargs = dict(synch=True) total_interval = integ_time + latency_time group = { @@ -423,6 +404,12 @@ def acquire(self, integ_time, repetitions, latency_time): self.wait_finish() self.do_asserts(repetitions, jobs_before) + def wait_finish(self): + # waiting for acquisition and synchronization to finish + while (self.acquisition.is_running() + or self.synchronization.is_running()): + time.sleep(.1) + def tearDown(self): AcquisitionTestCase.tearDown(self) TestCase.tearDown(self) @@ -452,7 +439,7 @@ class Acquisition2DSoftwareTriggerTestCase(BaseAcquisitionSoftwareTestCase, def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionSoftwareTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuebuffer") @@ -471,7 +458,7 @@ class Acquisition2DSoftwareTriggerRefTestCase(BaseAcquisitionSoftwareTestCase, def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionSoftwareTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuerefbuffer") @@ -494,7 +481,7 @@ class AcquisitionCTSoftwareStartTestCase( def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionSoftwareStartTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuebuffer") @@ -512,7 +499,7 @@ class Acquisition2DSoftwareStartTestCase( def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionSoftwareStartTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuebuffer") @@ -535,7 +522,7 @@ class Acquisition2DSoftwareStartRefTestCase( def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionSoftwareStartTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuerefbuffer") @@ -576,7 +563,7 @@ class Acquisition2DHardwareStartTestCase(BaseAcquisitionHardwareTestCase, def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionHardwareTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuebuffer") @@ -595,7 +582,7 @@ class Acquisition2DHardwareStartRefTestCase( def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionHardwareTestCase.setUp(self) self.channel_ctrl.set_log_level(10) self.data_listener = AttributeListener(dtype=object, attr_name="valuerefbuffer") @@ -619,7 +606,7 @@ class AcquisitionCTHardwareTriggerTestCase(BaseAcquisitionHardwareTestCase, def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionHardwareTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuebuffer") @@ -637,7 +624,7 @@ class Acquisition2DHardwareTriggerTestCase(BaseAcquisitionHardwareTestCase, def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionHardwareTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuebuffer") @@ -656,7 +643,7 @@ class Acquisition2DHardwareTriggerRefTestCase(BaseAcquisitionHardwareTestCase, def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionHardwareTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuerefbuffer") @@ -679,7 +666,7 @@ class AcquisitionCTHardwareGateTestCase(BaseAcquisitionHardwareTestCase, def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionHardwareTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuebuffer") @@ -697,7 +684,7 @@ class Acquisition2DHardwareGateTestCase(BaseAcquisitionHardwareTestCase, def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionHardwareTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuebuffer") @@ -716,7 +703,7 @@ class Acquisition2DHardwareGateRefTestCase(BaseAcquisitionHardwareTestCase, def setUp(self): """Create test actors (controllers and elements)""" TestCase.setUp(self) - AcquisitionTestCase.setUp(self) + BaseAcquisitionHardwareTestCase.setUp(self) self.data_listener = AttributeListener(dtype=object, attr_name="valuerefbuffer") From 054ebab474edd99ecd0800c86273005ddc4f670c Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jun 2020 19:34:57 +0200 Subject: [PATCH 644/830] Do not use state to determine Value quality Value quality was determined based on the channel's state. Since Value events are pushed before the State events the state had to be updated pre-maturely to correctly determine the Value quality. This pre-mature state update was not propagating event and was just updating the channel's internal state. This however was making the measurement group state to switch to ON too early. The first channel's state update with propagation was finding all the channels with internal state set to ON making the measurement group state change to ON as well. This is causing bugs in fast repeated acquisitions. Hack it and use the *acquiring* flag which is independent of the state. Set to to True on start and to False when the acquisition has finished. Also reset it to False when leaving the operation just in case there was some unhandled error or the user stopped the acquisition. --- src/sardana/pool/poolacquisition.py | 43 ++++++++++++++++++++------ src/sardana/tango/pool/CTExpChannel.py | 6 ++-- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 555d488e4b..9dfbe2d974 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -273,6 +273,12 @@ class AcquisitionBaseContext(OperationContext): def exit(self): pool_action = self._pool_action pool_action._reset_ctrl_dicts() + # HACK for properly setting the Value Tango attribute quality + # To remove it either use the concept of quality for + # SardanaValue or use the AcquisitionState (see #1352) + # reset it here as well just in case there was an error or acq was + # stopped by the user + pool_action._set_acquiring(False) return OperationContext.exit(self) @@ -729,6 +735,14 @@ def __init__(self, main_element, name): # acquisition actions, uncomment this line # self.add_finish_hook(self.clear_value_buffers, True) + def _set_acquiring(self, acquiring): + # HACK for properly setting the Value Tango attribute quality + # To remove it either use the concept of quality for + # SardanaValue or use the AcquisitionState (see #1352) + for channel in self._channels: + element = channel.configuration.element + setattr(element, "_acquiring", acquiring) + def get_read_value_ref_ctrls(self): return self._pool_ctrl_dict_ref @@ -959,6 +973,11 @@ def load(channel, value, repetitions, latency=0): self._channels.append(channel) + # HACK for properly setting the Value Tango attribute quality + # To remove it either use the concept of quality for + # SardanaValue or use the AcquisitionState (see #1352) + self._set_acquiring(True) + # set the state of all elements to and inform their listeners for channel in self._channels: channel.set_state(State.Moving, propagate=2) @@ -1089,15 +1108,17 @@ def action_loop(self): time.sleep(nap) i += 1 + # HACK for properly setting the Value Tango attribute quality + # To remove it either use the concept of quality for + # SardanaValue or use the AcquisitionState (see #1352) + self._set_acquiring(False) + with ActionContext(self): self.raw_read_state_info(ret=states) self.raw_read_value(ret=values) self.raw_read_value_ref(ret=value_refs) for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) if acquirable in values: value = values[acquirable] if is_value_error(value): @@ -1201,15 +1222,17 @@ def action_loop(self): slave.getLogName()) self.debug("Details", exc_info=1) + # HACK for properly setting the Value Tango attribute quality + # To remove it either use the concept of quality for + # SardanaValue or use the AcquisitionState (see #1352) + self._set_acquiring(False) + with ActionContext(self): self.raw_read_state_info(ret=states) self.raw_read_value(ret=values) self.raw_read_value_ref(ret=value_refs) for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) if acquirable in values: value = values[acquirable] if is_value_error(value): @@ -1309,15 +1332,17 @@ def action_loop(self): time.sleep(nap) i += 1 + # HACK for properly setting the Value Tango attribute quality + # To remove it either use the concept of quality for + # SardanaValue or use the AcquisitionState (see #1352) + self._set_acquiring(False) + with ActionContext(self): self.raw_read_state_info(ret=states) self.raw_read_value(ret=values) self.raw_read_value_ref(ret=value_refs) for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account - acquirable.set_state_info(state_info, propagate=0) if acquirable in values: value = values[acquirable] if is_value_error(value): diff --git a/src/sardana/tango/pool/CTExpChannel.py b/src/sardana/tango/pool/CTExpChannel.py index 8c193abc58..d9fc18c7fc 100644 --- a/src/sardana/tango/pool/CTExpChannel.py +++ b/src/sardana/tango/pool/CTExpChannel.py @@ -138,8 +138,10 @@ def _on_ct_changed(self, event_source, event_type, event_value): value = "None" elif name == "value": w_value = event_source.get_value_attribute().w_value - state = self.ct.get_state() - if state == State.Moving: + # HACK for properly setting the Value Tango attribute quality + # To remove it either use the concept of quality for + # SardanaValue or use the AcquisitionState (see #1352) + if self.ct._acquiring: quality = AttrQuality.ATTR_CHANGING self.set_attribute(attr, value=value, w_value=w_value, From b3cb1984291ac3ff690d0925a522b9b693c72696 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 12 Jun 2020 23:15:03 +0200 Subject: [PATCH 645/830] Do not state state pre-maturely for 0Ds Other experimental channels were setting state prematurely to determine Value Tango attribute quality. For 0D it is not necessary - Value is not updated at the end of the action loop. --- src/sardana/pool/poolacquisition.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 9dfbe2d974..101c051971 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -1540,10 +1540,7 @@ def action_loop(self): self.raw_read_state_info(ret=states) for acquirable, state_info in list(states.items()): - # first update the element state so that value calculation - # that is done after takes the updated state into account state_info = acquirable._from_ctrl_state_info(state_info) - acquirable.set_state_info(state_info, propagate=0) set_state_info = functools.partial(acquirable.set_state_info, state_info, propagate=2, From 2113a7c11203ced65f86416e1353a1b674f67352 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 13 Jun 2020 00:21:57 +0200 Subject: [PATCH 646/830] Refactor stop_meas_cont_acquisition test This test is following on measurement group State readout which is buggy (#1316). Currently it is better to rely on the State events. - Refactor to rely on State event - Adjust test parameters Fixes #1188 --- .../tango/pool/test/test_measurementgroup.py | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/sardana/tango/pool/test/test_measurementgroup.py b/src/sardana/tango/pool/test/test_measurementgroup.py index 9c0e807b80..d4b16e388f 100644 --- a/src/sardana/tango/pool/test/test_measurementgroup.py +++ b/src/sardana/tango/pool/test/test_measurementgroup.py @@ -228,23 +228,37 @@ def meas_cont_acquisition(self, params, config): repetitions = params['synch_description'][0][SynchParam.Repeats] self._acq_asserts(chn_names, repetitions) + def push_event(self, event): + value = event.attr_value.value + self.meas_state = value + if value == PyTango.DevState.MOVING: + self.meas_started = True + elif self.meas_started and value == PyTango.DevState.ON: + self.meas_finished.set() + def stop_meas_cont_acquisition(self, params, config): '''Helper method to do measurement and stop it''' self.create_meas(config) self.prepare_meas(params) + self.meas_state = None + self.meas_started = False + self.meas_finished = threading.Event() chn_names = self._add_attribute_listener(config) # Do measurement - self.meas.Start() - # starting timer (0.2 s) which will stop the measurement group - threading.Timer(0.2, self.stopMeas).start() - while self.meas.State() == PyTango.DevState.MOVING: - print("Acquiring...") - time.sleep(0.1) - state = self.meas.State() + id_ = self.meas.subscribe_event("State", + PyTango.EventType.CHANGE_EVENT, + self.push_event) + try: + # starting timer (0.2 s) which will stop the measurement group + self.meas.Start() + threading.Timer(0.2, self.stopMeas).start() + self.assertTrue(self.meas_finished.wait(5), "mg has not stopped") + finally: + self.meas.unsubscribe_event(id_) desired_state = PyTango.DevState.ON msg = 'mg state after stop is %s (should be %s)' %\ - (state, desired_state) - self.assertEqual(state, desired_state, msg) + (self.meas_state, desired_state) + self.assertEqual(self.meas_state, desired_state, msg) for name in chn_names: channel = PyTango.DeviceProxy(name) state = channel.state() @@ -276,7 +290,18 @@ def tearDown(self): params_1 = { "synch_description": synch_description1, - "integ_time": 0.01, + "integ_time": 0.1, + "name": '_exp_01' +} + +synch_description2 = [{SynchParam.Delay: {SynchDomain.Time: 0}, + SynchParam.Active: {SynchDomain.Time: 0.1}, + SynchParam.Total: {SynchDomain.Time: 0.15}, + SynchParam.Repeats: 10}] + +params_2 = { + "synch_description": synch_description2, + "integ_time": 0.1, "name": '_exp_01' } doc_1 = 'Synchronized acquisition with two channels from the same controller'\ @@ -361,11 +386,11 @@ def tearDown(self): @insertTest(helper_name='meas_cont_acquisition', test_method_doc=doc_3, params=params_1, config=config_3) @insertTest(helper_name='stop_meas_cont_acquisition', test_method_doc=doc_4, - params=params_1, config=config_1) + params=params_2, config=config_1) @insertTest(helper_name='stop_meas_cont_acquisition', test_method_doc=doc_5, - params=params_1, config=config_2) + params=params_2, config=config_2) @insertTest(helper_name='stop_meas_cont_acquisition', test_method_doc=doc_6, - params=params_1, config=config_3) + params=params_2, config=config_3) class TangoAcquisitionTestCase(MeasSarTestTestCase, unittest.TestCase): """Integration test of TGGeneration and Acquisition actions.""" From 40e4b88cf5176935f9d5e7bf7851d1eb24d0f402 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 13 Jun 2020 00:23:20 +0200 Subject: [PATCH 647/830] Fix calculation of element state when it is None or UNKNOWN --- src/sardana/pool/poolbasegroup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/pool/poolbasegroup.py b/src/sardana/pool/poolbasegroup.py index 6601ef57cf..c7f456bc14 100644 --- a/src/sardana/pool/poolbasegroup.py +++ b/src/sardana/pool/poolbasegroup.py @@ -89,7 +89,8 @@ def _fill_action_cache(self, action_cache=None, physical_elements=None): def _calculate_element_state(self, elem, elem_state_info): u_state, u_status = elem_state_info if u_status is None: - u_status = '%s is None' % elem.name + state_str = "None" if u_state is None else State.whatis(u_state) + u_status = '{} is {}'.format(elem.name, state_str) else: u_status = u_status.split("\n", 1)[0] return u_state, u_status From 3f350de76857c42ffbab001a7a91244c3d7396a1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 13 Jun 2020 00:27:17 +0200 Subject: [PATCH 648/830] Fix flake8 --- src/sardana/pool/poolacquisition.py | 6 +++--- src/sardana/tango/pool/test/test_measurementgroup.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 101c051971..2c9a6ea3e4 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -736,9 +736,9 @@ def __init__(self, main_element, name): # self.add_finish_hook(self.clear_value_buffers, True) def _set_acquiring(self, acquiring): - # HACK for properly setting the Value Tango attribute quality - # To remove it either use the concept of quality for - # SardanaValue or use the AcquisitionState (see #1352) + """HACK for properly setting the Value Tango attribute quality + To remove it either use the concept of quality for + SardanaValue or use the AcquisitionState (see #1352)""" for channel in self._channels: element = channel.configuration.element setattr(element, "_acquiring", acquiring) diff --git a/src/sardana/tango/pool/test/test_measurementgroup.py b/src/sardana/tango/pool/test/test_measurementgroup.py index d4b16e388f..4713360c6f 100644 --- a/src/sardana/tango/pool/test/test_measurementgroup.py +++ b/src/sardana/tango/pool/test/test_measurementgroup.py @@ -295,9 +295,9 @@ def tearDown(self): } synch_description2 = [{SynchParam.Delay: {SynchDomain.Time: 0}, - SynchParam.Active: {SynchDomain.Time: 0.1}, - SynchParam.Total: {SynchDomain.Time: 0.15}, - SynchParam.Repeats: 10}] + SynchParam.Active: {SynchDomain.Time: 0.1}, + SynchParam.Total: {SynchDomain.Time: 0.15}, + SynchParam.Repeats: 10}] params_2 = { "synch_description": synch_description2, From 6d529b0e93ee225a463c9b342396715ccf2d21e0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 13 Jun 2020 00:28:13 +0200 Subject: [PATCH 649/830] Add stress tests for measurement group acquisition --- .../tango/sardana/test/test_measgrpstress.py | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/sardana/taurus/core/tango/sardana/test/test_measgrpstress.py diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measgrpstress.py b/src/sardana/taurus/core/tango/sardana/test/test_measgrpstress.py new file mode 100644 index 0000000000..277e146cc9 --- /dev/null +++ b/src/sardana/taurus/core/tango/sardana/test/test_measgrpstress.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +# Sardana 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. +## +# Sardana 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 Sardana. If not, see . +## +############################################################################## + +import uuid +from unittest import TestCase + +from tango import DevState +from taurus import Device +from taurus.test.base import insertTest + +from .test_pool import is_numerical +from sardana.pool.pooldefs import AcqSynchType +from sardana.taurus.core.tango.sardana.pool import registerExtensions +from sardana.tango.pool.test.base_sartest import SarTestTestCase + + +@insertTest(helper_name="stress_count", + test_method_doc="stress count with CT (hardware trigger) and 0D", + elements=["_test_ct_1_1", "_test_0d_1_1"], repeats=100, + synchronizer="_test_tg_1_1", synchronization=AcqSynchType.Trigger) +@insertTest(helper_name="stress_count", + test_method_doc="stress count with CT (software trigger) and 0D", + elements=["_test_ct_1_1", "_test_0d_1_1"], repeats=100, + synchronizer="software", synchronization=AcqSynchType.Trigger) +@insertTest(helper_name="stress_count", + test_method_doc="stress count with CT (hardware start)", + elements=["_test_ct_1_1"], repeats=100, + synchronizer="_test_tg_1_1", synchronization=AcqSynchType.Start) +@insertTest(helper_name="stress_count", + test_method_doc="stress count with CT (software start)", + elements=["_test_ct_1_1"], repeats=100, + synchronizer="software", synchronization=AcqSynchType.Start) +@insertTest(helper_name="stress_count", + test_method_doc="stress count with CT (hardware trigger)", + elements=["_test_ct_1_1"], repeats=100, + synchronizer="_test_tg_1_1", synchronization=AcqSynchType.Trigger) +@insertTest(helper_name="stress_count", + test_method_doc="count with CT (software trigger)", + elements=["_test_ct_1_1"], repeats=100, + synchronizer="software", synchronization=AcqSynchType.Trigger) +class TestStressMeasurementGroup(SarTestTestCase, TestCase): + + def setUp(self): + SarTestTestCase.setUp(self) + registerExtensions() + + def stress_count(self, elements, repeats, synchronizer, synchronization): + mg_name = str(uuid.uuid1()) + argin = [mg_name] + elements + self.pool.CreateMeasurementGroup(argin) + try: + mg = Device(mg_name) + mg.setSynchronizer(synchronizer, elements[0], apply=False) + mg.setSynchronization(synchronization, elements[0]) + for i in range(repeats): + state, values = mg.count(.001) + self.assertEqual(state, DevState.ON, + "wrong state after measurement") + for channel_name, value in values.items(): + msg = "Value (%s) for %s is not numerical" % \ + (value, channel_name) + self.assertTrue(is_numerical(value), msg) + finally: + mg.cleanUp() + self.pool.DeleteElement(mg_name) + + def tearDown(self): + SarTestTestCase.tearDown(self) From e0224af5588d947fa7d12ab1988fa06706b60e10 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 13 Jun 2020 01:17:10 +0200 Subject: [PATCH 650/830] fix flake8 --- src/sardana/pool/poolacquisition.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 2c9a6ea3e4..902ea30404 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -724,7 +724,6 @@ def __init__(self, main_element, name): self._pool_ctrl_dict_ref = None self._pool_ctrl_dict_value = None - # TODO: for the moment we can not clear value buffers at the end of # the acquisition. This is because of the pseudo counters that are # based on channels synchronized by hardware and software. From 3e12010918153655f78ff1125a36adf1c431abf6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 14 Jun 2020 22:40:34 +0200 Subject: [PATCH 651/830] Avoid extra state readout at the end of acquisition Acquisition's action_loop exists the state poll loop when none of the elements is in Moving or Running state. This indicates that the acquisition has finished. This last state readout should be propagated to the channels. Avoid the extra state readout which actually could change the end acquisition state. This also introduces a small performance improvement. --- src/sardana/pool/poolacquisition.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index dbe2abaaef..dae9ad51f9 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -1042,7 +1042,6 @@ def action_loop(self): i += 1 with ActionContext(self): - self.raw_read_state_info(ret=states) self.raw_read_value(ret=values) self.raw_read_value_ref(ret=value_refs) @@ -1154,7 +1153,6 @@ def action_loop(self): self.debug("Details", exc_info=1) with ActionContext(self): - self.raw_read_state_info(ret=states) self.raw_read_value(ret=values) self.raw_read_value_ref(ret=value_refs) @@ -1262,7 +1260,6 @@ def action_loop(self): i += 1 with ActionContext(self): - self.raw_read_state_info(ret=states) self.raw_read_value(ret=values) self.raw_read_value_ref(ret=value_refs) From d860ed5a0a9470abb3bd84acd450c0e0b95ef467 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 14 Jun 2020 23:24:12 +0200 Subject: [PATCH 652/830] Introduces attribute quality Attribute quality is copied from Tango CS. Attribute quality is a metadata which accompanies the attribute value. It helps to indicate that the attribute is still changing e.g. its element is involved in an operation. --- src/sardana/sardanaattribute.py | 11 ++++++++++- src/sardana/sardanadefs.py | 20 ++++++++++++++++++-- src/sardana/tango/core/util.py | 30 +++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/sardana/sardanaattribute.py b/src/sardana/sardanaattribute.py index c980786b51..4999f12969 100644 --- a/src/sardana/sardanaattribute.py +++ b/src/sardana/sardanaattribute.py @@ -37,7 +37,7 @@ import datetime from .sardanaevent import EventGenerator, EventType -from .sardanadefs import ScalarNumberFilter +from .sardanadefs import ScalarNumberFilter, AttrQuality from .sardanavalue import SardanaValue @@ -54,6 +54,7 @@ def __init__(self, obj, name=None, initial_value=None, **kwargs): self._r_value = None self._last_event_value = None self._w_value = None + self._quality = AttrQuality.Valid self.filter = lambda a, b: True self.config = SardanaAttributeConfiguration() if initial_value is not None: @@ -212,6 +213,12 @@ def _get_write_value_obj(self): if self.has_write_value(): return self._w_value + def get_quality(self): + return self._quality + + def set_quality(self, quality): + self._quality = quality + def get_exc_info(self): """Returns the exception information (like :func:`sys.exc_info`) about last attribute readout or None if last read did not generate an @@ -291,6 +298,8 @@ def fire_read_event(self, propagate=1): "current read value for this attribute") w_value = property(get_write_value, set_write_value, "current write value for this attribute") + quality = property(get_quality, set_quality, + "current quality for this attribute") timestamp = property(get_timestamp, doc="the read timestamp") w_timestamp = property(get_write_timestamp, doc="the write timestamp") error = property(in_error) diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py index 08e5e3d328..621a117db9 100644 --- a/src/sardana/sardanadefs.py +++ b/src/sardana/sardanadefs.py @@ -29,8 +29,8 @@ import collections __all__ = ["EpsilonError", "SardanaServer", "ServerRunMode", "State", - "DataType", "DataFormat", "DataAccess", "DTYPE_MAP", "R_DTYPE_MAP", - "DACCESS_MAP", + "DataType", "DataFormat", "DataAccess", "AttrQuality", + "DTYPE_MAP", "R_DTYPE_MAP", "DACCESS_MAP", "from_dtype_str", "from_access_str", "to_dtype_dformat", "to_daccess", "InvalidId", "InvalidAxis", "ElementType", "Interface", "Interfaces", "InterfacesExpanded", @@ -43,6 +43,7 @@ __docformat__ = 'restructuredtext' import math +from enum import IntEnum from taurus.core.util.enumeration import Enumeration @@ -120,6 +121,21 @@ def __repr__(self): "ReadWrite", "Invalid")) + +class AttrQuality(IntEnum): + """Attribute quality factor""" + + #: Attribute is valid + Valid = 0 + #: Attribute is invalid + Invalid = 1 + #: Attribute is in alarm + Alarm = 2 + #: Attribute is changing e.g. element is in operation + Changing = 3 + #: Attribute is in warning + Warning = 4 + #: dictionary dict DTYPE_MAP = { 'int': DataType.Integer, diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 09d11729a7..7f84681d03 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -61,7 +61,8 @@ import sardana from sardana import State, SardanaServer, DataType, DataFormat, InvalidId, \ - DataAccess, to_dtype_dformat, to_daccess, Release, ServerRunMode + DataAccess, to_dtype_dformat, to_daccess, Release, ServerRunMode, \ + AttrQuality from sardana.sardanaexception import SardanaException, AbortException from sardana.sardanavalue import SardanaValue from sardana.util.wrap import wraps @@ -426,7 +427,21 @@ def from_tango_state_to_state(state): } R_TFORMAT_MAP = dict((v, k) for k, v in list(TFORMAT_MAP.items())) -#: dictionary dict<:class:`sardana.DataAccess`, :class:`PyTango.AttrWriteType`> + +#: dictionary dict<:class:`sardana.AttrQuality`, :class:`PyTango.AttrQuality`> +TQUALITY_MAP = { + AttrQuality.Valid: PyTango.AttrQuality.ATTR_VALID, + AttrQuality.Invalid: PyTango.AttrQuality.ATTR_INVALID, + AttrQuality.Alarm: PyTango.AttrQuality.ATTR_ALARM, + AttrQuality.Changing: PyTango.AttrQuality.ATTR_CHANGING, + AttrQuality.Warning: PyTango.AttrQuality.ATTR_WARNING +} + + +R_TQUALITY_MAP = dict((v, k) for k, v in list(TQUALITY_MAP.items())) + + +#: dictionary dict<:class:`sardana.AttrQuality`, :class:`PyTango.AttrQuality`> TACCESS_MAP = { DataAccess.ReadOnly: READ, DataAccess.ReadWrite: READ_WRITE, @@ -434,7 +449,6 @@ def from_tango_state_to_state(state): R_TACCESS_MAP = dict((v, k) for k, v in list(TACCESS_MAP.items())) - def exception_str(etype=None, value=None, sep='\n'): if etype is None: etype, value = sys.exc_info()[:2] @@ -495,6 +509,16 @@ def from_tango_type_format(dtype, dformat=PyTango.SCALAR): return R_TTYPE_MAP[dtype], R_TFORMAT_MAP[dformat] +def to_tango_quality(quality): + """Transforms a :obj:`~sardana.AttrQuality` into a + :obj:`~PyTango.AttrQuality` + + :param access: the quality to be transformed + :type access: :obj:`~sardana.AttrQuality` + :return: the tango attribute quality + :rtype: :obj:`PyTango.AttrQuality`""" + return TQUALITY_MAP[quality] + def to_tango_attr_info(attr_name, attr_info): if isinstance(attr_info, DataInfo): data_type, data_format = attr_info.dtype, attr_info.dformat From cac679c53a1c3855767b7ef02de38221f0a6dbbe Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 14 Jun 2020 23:28:31 +0200 Subject: [PATCH 653/830] Pass attribute quality together with value for channels In acquisition action it is sometimes necessary to indicated that the attribute's quality is changing - software synchronized counting when intermediate value results are updates. --- src/sardana/pool/poolbasechannel.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sardana/pool/poolbasechannel.py b/src/sardana/pool/poolbasechannel.py index 6a4d9960e7..29f739bad9 100644 --- a/src/sardana/pool/poolbasechannel.py +++ b/src/sardana/pool/poolbasechannel.py @@ -30,6 +30,7 @@ __docformat__ = 'restructuredtext' +from sardana.sardanadefs import AttrQuality from sardana.sardanaattribute import SardanaAttribute from sardana.sardanabuffer import SardanaBuffer from sardana.pool.poolelement import PoolElement @@ -241,19 +242,24 @@ def read_value(self): :class:`~sardana.sardanavalue.SardanaValue`""" return self.acquisition.read_value()[self] - def put_value(self, value, propagate=1): + def put_value(self, value, quality=AttrQuality.Valid, propagate=1): """Sets a value. :param value: the new value :type value: :class:`~sardana.sardanavalue.SardanaValue` + :param quality: + the new quality + :type quality: + :class:`~sardana.AttrQuality` :param propagate: 0 for not propagating, 1 to propagate, 2 propagate with priority :type propagate: int """ val_attr = self._value + val_attr.set_quality(quality) val_attr.set_value(value, propagate=propagate) return val_attr From 90db65725d98168e97fcfff2c7a488a850ce0af6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 15 Jun 2020 00:35:40 +0200 Subject: [PATCH 654/830] Revert hack and use AttrQuality Revert hack from 054ebab4 and use newly introduced concept to Sardana Attribute quality. --- src/sardana/pool/poolacquisition.py | 45 +++++--------------------- src/sardana/tango/pool/CTExpChannel.py | 12 +++---- 2 files changed, 13 insertions(+), 44 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 902ea30404..254155db52 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -43,7 +43,9 @@ from taurus.core.util.log import DebugIt from taurus.core.util.enumeration import Enumeration -from sardana import SardanaValue, State, ElementType, TYPE_TIMERABLE_ELEMENTS +from sardana import AttrQuality, SardanaValue, State, ElementType, \ + TYPE_TIMERABLE_ELEMENTS + from sardana.sardanathreadpool import get_thread_pool from sardana.pool import AcqSynch, AcqMode from sardana.pool.poolaction import ActionContext, PoolAction, \ @@ -273,12 +275,6 @@ class AcquisitionBaseContext(OperationContext): def exit(self): pool_action = self._pool_action pool_action._reset_ctrl_dicts() - # HACK for properly setting the Value Tango attribute quality - # To remove it either use the concept of quality for - # SardanaValue or use the AcquisitionState (see #1352) - # reset it here as well just in case there was an error or acq was - # stopped by the user - pool_action._set_acquiring(False) return OperationContext.exit(self) @@ -734,14 +730,6 @@ def __init__(self, main_element, name): # acquisition actions, uncomment this line # self.add_finish_hook(self.clear_value_buffers, True) - def _set_acquiring(self, acquiring): - """HACK for properly setting the Value Tango attribute quality - To remove it either use the concept of quality for - SardanaValue or use the AcquisitionState (see #1352)""" - for channel in self._channels: - element = channel.configuration.element - setattr(element, "_acquiring", acquiring) - def get_read_value_ref_ctrls(self): return self._pool_ctrl_dict_ref @@ -972,11 +960,6 @@ def load(channel, value, repetitions, latency=0): self._channels.append(channel) - # HACK for properly setting the Value Tango attribute quality - # To remove it either use the concept of quality for - # SardanaValue or use the AcquisitionState (see #1352) - self._set_acquiring(True) - # set the state of all elements to and inform their listeners for channel in self._channels: channel.set_state(State.Moving, propagate=2) @@ -1107,11 +1090,6 @@ def action_loop(self): time.sleep(nap) i += 1 - # HACK for properly setting the Value Tango attribute quality - # To remove it either use the concept of quality for - # SardanaValue or use the AcquisitionState (see #1352) - self._set_acquiring(False) - with ActionContext(self): self.raw_read_state_info(ret=states) self.raw_read_value(ret=values) @@ -1208,7 +1186,7 @@ def action_loop(self): if not i % nb_states_per_value: self.read_value_loop(ret=values) for acquirable, value in list(values.items()): - acquirable.put_value(value) + acquirable.put_value(value, quality=AttrQuality.Changing) time.sleep(nap) i += 1 @@ -1221,11 +1199,6 @@ def action_loop(self): slave.getLogName()) self.debug("Details", exc_info=1) - # HACK for properly setting the Value Tango attribute quality - # To remove it either use the concept of quality for - # SardanaValue or use the AcquisitionState (see #1352) - self._set_acquiring(False) - with ActionContext(self): self.raw_read_state_info(ret=states) self.raw_read_value(ret=values) @@ -1240,7 +1213,10 @@ def action_loop(self): msg = "Details: " + "".join( traceback.format_exception(*value.exc_info)) self.debug(msg) - acquirable.append_value_buffer(value, self._index) + acquirable.get_value_attribute().set_quality( + AttrQuality.Valid) + acquirable.append_value_buffer(value, self._index, + propagate=2) if acquirable in value_refs: value_ref = value_refs[acquirable] if is_value_error(value_ref): @@ -1331,11 +1307,6 @@ def action_loop(self): time.sleep(nap) i += 1 - # HACK for properly setting the Value Tango attribute quality - # To remove it either use the concept of quality for - # SardanaValue or use the AcquisitionState (see #1352) - self._set_acquiring(False) - with ActionContext(self): self.raw_read_state_info(ret=states) self.raw_read_value(ret=values) diff --git a/src/sardana/tango/pool/CTExpChannel.py b/src/sardana/tango/pool/CTExpChannel.py index d9fc18c7fc..c9db626ee0 100644 --- a/src/sardana/tango/pool/CTExpChannel.py +++ b/src/sardana/tango/pool/CTExpChannel.py @@ -39,7 +39,8 @@ from sardana import State, SardanaServer from sardana.sardanaattribute import SardanaAttribute -from sardana.tango.core.util import to_tango_type_format, exception_str +from sardana.tango.core.util import to_tango_type_format, to_tango_quality, \ + exception_str from sardana.tango.pool.PoolDevice import PoolTimerableDevice, \ PoolTimerableDeviceClass @@ -137,12 +138,9 @@ def _on_ct_changed(self, event_source, event_type, event_value): if name == "timer" and value is None: value = "None" elif name == "value": - w_value = event_source.get_value_attribute().w_value - # HACK for properly setting the Value Tango attribute quality - # To remove it either use the concept of quality for - # SardanaValue or use the AcquisitionState (see #1352) - if self.ct._acquiring: - quality = AttrQuality.ATTR_CHANGING + value_attr = event_source.get_value_attribute() + w_value = value_attr.w_value + quality = to_tango_quality(value_attr.quality) self.set_attribute(attr, value=value, w_value=w_value, timestamp=timestamp, quality=quality, From 3e5235e63ef5e25cdfe73404cc890fd4307dbb71 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 17 Jun 2020 22:23:14 +0200 Subject: [PATCH 655/830] Add more verbous assert msg in stress test --- .../taurus/core/tango/sardana/test/test_measgrpstress.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/test/test_measgrpstress.py b/src/sardana/taurus/core/tango/sardana/test/test_measgrpstress.py index 277e146cc9..5f751c7d4c 100644 --- a/src/sardana/taurus/core/tango/sardana/test/test_measgrpstress.py +++ b/src/sardana/taurus/core/tango/sardana/test/test_measgrpstress.py @@ -77,10 +77,10 @@ def stress_count(self, elements, repeats, synchronizer, synchronization): for i in range(repeats): state, values = mg.count(.001) self.assertEqual(state, DevState.ON, - "wrong state after measurement") + "wrong state after measurement {}".format(i)) for channel_name, value in values.items(): - msg = "Value (%s) for %s is not numerical" % \ - (value, channel_name) + msg = ("Value {} for {} is not numerical in " + "measurement {}").format(value, channel_name, i) self.assertTrue(is_numerical(value), msg) finally: mg.cleanUp() From d2aeb3fb9fce31f916e5826daf9b4c0f75b37d86 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 17 Jun 2020 22:27:33 +0200 Subject: [PATCH 656/830] Allow to pass callback to action's run() --- src/sardana/pool/poolaction.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/pool/poolaction.py b/src/sardana/pool/poolaction.py index 63de46d371..1db4e968a9 100644 --- a/src/sardana/pool/poolaction.py +++ b/src/sardana/pool/poolaction.py @@ -356,7 +356,8 @@ def run(self, *args, **kwargs): raise finally: self._started = False - get_thread_pool().add(self._asynch_action_loop, None, context) + cb = kwargs.pop("cb", None) + get_thread_pool().add(self._asynch_action_loop, cb, context) def start_action(self, *args, **kwargs): """Start procedure for this action. Default implementation raises From 2a33fe4ff5524cb30e230aa069a0b40b97faf8dc Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 17 Jun 2020 22:32:14 +0200 Subject: [PATCH 657/830] Add ready/busy methods to PoolSynchronization --- src/sardana/pool/poolsynchronization.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sardana/pool/poolsynchronization.py b/src/sardana/pool/poolsynchronization.py index 7877801ebc..96d68f44b0 100644 --- a/src/sardana/pool/poolsynchronization.py +++ b/src/sardana/pool/poolsynchronization.py @@ -30,6 +30,7 @@ __all__ = ["PoolSynchronization", "SynchDescription", "TGChannel"] import time +import threading from functools import partial from taurus.core.util.log import DebugIt from sardana import State @@ -117,6 +118,8 @@ class PoolSynchronization(PoolAction): """Synchronization action. It coordinates trigger/gate elements and software synchronizer. + + .. todo: Think of moving the ready/busy mechanism to PoolAction """ def __init__(self, main_element, name="Synchronization"): @@ -130,6 +133,23 @@ def __init__(self, main_element, name="Synchronization"): soft_synch_name = main_element.name + "-SoftSynch" self._synch_soft = FunctionGenerator(name=soft_synch_name) self._listener = None + self._ready = threading.Event() + self._ready.set() + + def _is_ready(self): + return self._ready.is_set() + + def _wait(self, timeout=None): + return self._ready.wait(timeout) + + def _set_ready(self, _=None): + self._ready.set() + + def _is_busy(self): + return not self._ready.is_set() + + def _set_busy(self): + self._ready.clear() def add_listener(self, listener): self._listener = listener From f8c2b50ea5521b6f7d4c8e6442519b72c2764dac Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 17 Jun 2020 22:36:10 +0200 Subject: [PATCH 658/830] Wait for hw synch acq and synchronization from previous execution Wait until the hardware synchronized acquisition and synchronization jobs are completed from the eventual previous execution. For this use the post job callback. --- src/sardana/pool/poolacquisition.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 254155db52..ad8376812d 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -549,8 +549,11 @@ def run(self, *args, **kwargs): pseudo_elem.clear_value_buffer() if self._hw_acq_args is not None: + self._hw_acq._wait() + self._hw_acq._set_busy() self._hw_acq.run(*self._hw_acq_args.args, - **self._hw_acq_args.kwargs) + **self._hw_acq_args.kwargs, + cb=self._hw_acq._set_ready) if self._sw_acq_args is not None\ or self._sw_start_acq_args is not None\ @@ -558,8 +561,11 @@ def run(self, *args, **kwargs): self._synch.add_listener(self) if self._synch_args is not None: + self._synch._wait() + self._synch._set_busy() self._synch.run(*self._synch_args.args, - **self._synch_args.kwargs) + **self._synch_args.kwargs, + cb=self._synch._set_ready) def _get_action_for_element(self, element): elem_type = element.get_type() @@ -672,6 +678,8 @@ class PoolAcquisitionBase(PoolAction): on a provisional basis. Backwards incompatible changes (up to and including removal of the module) may occur if deemed necessary by the core developers. + + .. todo: Think of moving the ready/busy mechanism to PoolAction """ def __init__(self, main_element, name): From 076a0157f02bc793b2764ef4e07d9f75ea516937 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 18 Jun 2020 23:33:34 +0200 Subject: [PATCH 659/830] Correct measurement group state calculation 292fce wrongly used sub-actions busy/ready to calculate the measurement group's state. Go back to use the started flag. Otherwise the measurement group was never ending acquisition - sub-actions are marked as ready when all the state events are already handled. --- src/sardana/pool/poolacquisition.py | 2 ++ src/sardana/pool/poolmeasurementgroup.py | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index ad8376812d..02281bb434 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -329,6 +329,7 @@ def event_received(self, *args, **kwargs): self._sw_start_acq._wait() self._sw_start_acq._set_busy() self.debug('Executing software start acquisition.') + self._sw_start_acq._started = True get_thread_pool().add(self._sw_start_acq.run, self._sw_start_acq._set_ready, *self._sw_start_acq_args.args, @@ -351,6 +352,7 @@ def event_received(self, *args, **kwargs): self._sw_acq._set_busy() self.debug('Executing software acquisition.') self._sw_acq_args.kwargs.update({'index': index}) + self._sw_acq._started = True get_thread_pool().add(self._sw_acq.run, self._sw_acq._set_ready, *self._sw_acq_args.args, diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index ba1751e7b5..8faa93e1db 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -1035,11 +1035,11 @@ def _calculate_states(self, state_info=None): acq_sw = self.acquisition._sw_acq acq_sw_start = self.acquisition._sw_start_acq acq_0d = self.acquisition._0d_acq - if (state == State.Unknown + if (state in (State.On, State.Unknown) and (synch_soft.is_started() - or acq_sw._is_busy() - or acq_sw_start._is_busy() - or acq_0d._is_busy())): + or acq_sw._is_started() + or acq_sw_start._is_started() + or acq_0d._is_started())): state = State.Moving status += "\nSoftware synchronization is in progress" return state, status From 0086c8f6d886287800196691f19aaecb90c79aa7 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Fri, 19 Jun 2020 23:04:12 +0200 Subject: [PATCH 660/830] update plotselect according to new measurement API --- src/sardana/macroserver/macros/standard.py | 64 ++++++++++------------ 1 file changed, 28 insertions(+), 36 deletions(-) mode change 100644 => 100755 src/sardana/macroserver/macros/standard.py diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py old mode 100644 new mode 100755 index 4a61ec7609..e51abb1598 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -1052,42 +1052,34 @@ def run(self, ScanFilePath_list, ScanID): class plotselect(Macro): - """ - plotselect counter1 counter2 ... (change plot display of active measurement - group) - - """ + """select channels for plotting in the active measurement group""" param_def = [ - ['plotChs', ParamRepeat( - ['plotChs', Type.String, 'None', ""], min=0), None, ""] + ['channel', + [['channel', Type.ExpChannel, 'None', ""], {'min': 0}], + None, + "List of channels to plot"], ] - def run(self, plotChs): - mntGrp = self.getEnv('ActiveMntGrp') - self.mntGrp = self.getObj(mntGrp, type_class=Type.MeasurementGroup) - cfg = self.mntGrp.getConfiguration() - channels = self.mntGrp.getChannels() - channelNames = [] - - # Enable Plot only in the channels passed. - for channel in channels: - if channel['enabled']: - channelNames.append(channel['name']) - if channel['name'] in plotChs: - # Enable Plot - self.info("Plot channel %s" % channel['name']) - channel['plot_type'] = 1 - channel['plot_axes'] = [''] - else: - # Disable Plot - channel['plot_type'] = 0 - channel['plot_axes'] = [] - - # check if plotChs exists - for plotCh in plotChs: - if plotCh not in channelNames: - self.warning('channel %s is not enabled or does not exist in' - ' the current measurement group' % plotCh) - - # Force set Configuration. - self.mntGrp.setConfiguration(cfg.raw_data) + def run(self, channel): + try: + active_meas_grp = self.getEnv('ActiveMntGrp') + meas_grp = self.getMeasurementGroup(active_meas_grp) + self.output("Active measurement group: {}".format(meas_grp.name)) + except: + self.warning('No active measurement group found') + return + + plot_channels_ok = [] + enabled_channels = meas_grp.getEnabled() + # check channels first + for chan in channel: + enabled = enabled_channels.get(chan.name) + plot_channels_ok.append(chan.name) + if not enabled: + self.warning("{} is disabled".format(chan.name)) + else: + self.output("{} selected for plotting".format(chan.name)) + # set the plot type and plot axis in the meas_group + meas_grp.setPlotType("No", apply=False) + meas_grp.setPlotType("Spectrum", *plot_channels_ok, apply=False) + meas_grp.setPlotAxes([""], *plot_channels_ok) From 84acf50a3d65b15a54b4aeb621e5a78fc76099ac Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Fri, 19 Jun 2020 23:08:08 +0200 Subject: [PATCH 661/830] add case for channel not in meas_grp --- src/sardana/macroserver/macros/standard.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index e51abb1598..526c8120a8 100755 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -1074,11 +1074,14 @@ def run(self, channel): # check channels first for chan in channel: enabled = enabled_channels.get(chan.name) - plot_channels_ok.append(chan.name) - if not enabled: - self.warning("{} is disabled".format(chan.name)) + if enabled is None: + self.warning("{} not in {}".format(chan.name, meas_grp.name)) else: - self.output("{} selected for plotting".format(chan.name)) + plot_channels_ok.append(chan.name) + if not enabled: + self.warning("{} is disabled".format(chan.name)) + else: + self.output("{} selected for plotting".format(chan.name)) # set the plot type and plot axis in the meas_group meas_grp.setPlotType("No", apply=False) meas_grp.setPlotType("Spectrum", *plot_channels_ok, apply=False) From 18bd67be0510395133de3bd160b607b14fb40c37 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 19 Jun 2020 23:18:44 +0200 Subject: [PATCH 662/830] Remove deprecated version_info and revision --- src/sardana/release.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/sardana/release.py b/src/sardana/release.py index 2434494909..b5cdbe744e 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -28,7 +28,7 @@ """ -Release data for the taurus project. It contains the following members: +Release data for the sardana project. It contains the following members: - version : (str) version string - description : (str) brief description @@ -49,16 +49,6 @@ # bumpversion script (https://github.com/peritus/bumpversion) version = '3.0.2-alpha' -# generate version_info and revision (**deprecated** since v 2.1.2--alpha). -if '-' in version: - _v, _rel = version.split('-') -else: - _v, _rel = version, '' - -_v = tuple([int(n) for n in _v.split('.')]) -version_info = _v + (_rel, 0) # deprecated, do not use -revision = str(version_info[4]) # deprecated, do not use - description = "instrument control and data acquisition system" long_description = \ From 03d73558dc754a8c44756e7a61ee4e72c791924a Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 19 Jun 2020 23:44:27 +0200 Subject: [PATCH 663/830] Remove deprecated RepeatParameter Also correct some examples and docstrings. --- src/sardana/macroserver/macro.py | 5 ++--- .../macroserver/macros/examples/parameters.py | 11 +++++----- .../macroserver/macros/examples/scans.py | 10 +++++---- src/sardana/macroserver/msmetamacro.py | 13 ++++------- src/sardana/macroserver/msparameter.py | 22 +------------------ 5 files changed, 19 insertions(+), 42 deletions(-) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index 172f3fc4f7..728ef8a85a 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -32,7 +32,7 @@ __all__ = ["OverloadPrint", "PauseEvent", "Hookable", "ExecMacroHook", "MacroFinder", "Macro", "macro", "iMacro", "imacro", - "MacroFunc", "Type", "ParamRepeat", "Table", "List", "ViewOption", + "MacroFunc", "Type", "Table", "List", "ViewOption", "LibraryError", "Optional"] __docformat__ = 'restructuredtext' @@ -57,8 +57,7 @@ from sardana.util.wrap import wraps from sardana.util.thread import _asyncexc -from sardana.macroserver.msparameter import Type, ParamType, ParamRepeat, \ - Optional +from sardana.macroserver.msparameter import Type, ParamType, Optional from sardana.macroserver.msexception import StopException, AbortException, \ ReleaseException, MacroWrongParameterType, UnknownEnv, UnknownMacro, \ LibraryError diff --git a/src/sardana/macroserver/macros/examples/parameters.py b/src/sardana/macroserver/macros/examples/parameters.py index 47326235a9..53caffb11c 100644 --- a/src/sardana/macroserver/macros/examples/parameters.py +++ b/src/sardana/macroserver/macros/examples/parameters.py @@ -193,7 +193,8 @@ def run(self, *args, **kwargs): class pt7d1(Macro): - """Macro with a list of pair Motor,Float. Default value for last ParamRepeat element. + """Macro with a list of pair Motor,Float. Default value for last + repeat parameter element. Usages from Spock, ex.: pt7d1 [[mot1 1] [mot2 3]] pt7d1 mot1 1 mot2 3 @@ -214,7 +215,8 @@ def run(self, *args, **kwargs): class pt7d2(Macro): - """Macro with a list of pair Motor,Float. Default value for both ParamRepeat elements. + """Macro with a list of pair Motor,Float. Default value for both + repeat parameters elements. Usages from Spock, ex.: pt7d2 [[mot1 1] [mot2 3]] pt7d2 mot1 1 mot2 3 @@ -252,9 +254,8 @@ def run(self, *args, **kwargs): class pt9(Macro): - """Same as macro pt7 but with old style ParamRepeat. If you are writing - a macro with variable number of parameters for the first time don't even - bother to look at this example since it is DEPRECATED. + """Same as macro pt7 but with min and max number of repetitions of the + repeat parameter. Usages from Spock, ex.: pt9 [[mot1 1][mot2 3]] pt9 mot1 1 mot2 3 diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index cbbbf7df12..51e7ef8614 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -429,11 +429,13 @@ def run(self, *args): class reg3scan(Macro): """reg3scan. - Do an absolute scan of the specified motors with different number of intervals for each region. - It uses the gscan framework. All the motors will be drived to the same position in each step + Do an absolute scan of the specified motors with different number of + intervals for each region. It uses the gscan framework. + All the motors will be drived to the same position in each step - NOTE: Due to a ParamRepeat limitation, integration time has to be - specified before the regions. + .. note:: + integration time is specified before the regions to facilitate + input of parameters in Spock. """ hints = {'scan': 'reg3scan'} diff --git a/src/sardana/macroserver/msmetamacro.py b/src/sardana/macroserver/msmetamacro.py index 9d8b583cd2..5aca2fcf28 100644 --- a/src/sardana/macroserver/msmetamacro.py +++ b/src/sardana/macroserver/msmetamacro.py @@ -35,7 +35,7 @@ from sardana import InvalidId, ElementType from sardana.sardanameta import SardanaLibrary, SardanaClass, SardanaFunction -from sardana.macroserver.msparameter import Type, ParamRepeat +from sardana.macroserver.msparameter import Type import collections MACRO_TEMPLATE = """class @macro_name@(Macro): @@ -150,18 +150,13 @@ def _build_parameter(self, param_def): '''Builds a list of parameters, each of them represented by a dictionary containing information: name, type, default_value, description, min and max values. In case of simple parameters, type is the parameter type. - In case of ParamRepeat, type is a list containing definition of the - param repeat. + In case of repeat parameter, type is a list containing its definition. ''' ret = [] param_def = param_def or () for p in param_def: t = p[1] ret_p = {'min': None, 'max': None} - # take care of old ParamRepeat - if isinstance(t, ParamRepeat): - ret_p = {'min': 1, 'max': None} - t = t.obj() if isinstance(t, collections.Sequence) and not isinstance(t, str): ret_p = {'min': 1, 'max': None} @@ -183,7 +178,7 @@ def build_parameter_info(self, param_def=None): info = [str(len(param_def))] for name, type_class, def_val, desc in param_def: - repeat = isinstance(type_class, ParamRepeat) + repeat = isinstance(type_class, list) info.append(name) type_name = (repeat and 'ParamRepeat') or type_class info.append(type_name) @@ -205,7 +200,7 @@ def build_result_info(self, result_def=None): info = [str(len(result_def))] for name, type_class, def_val, desc in result_def: - repeat = isinstance(type_class, ParamRepeat) + repeat = isinstance(type_class, list) info.append(name) type_name = (repeat and 'ParamRepeat') or type_class info.append(type_name) diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py index c2c5ee92eb..763545a0a0 100644 --- a/src/sardana/macroserver/msparameter.py +++ b/src/sardana/macroserver/msparameter.py @@ -29,7 +29,7 @@ __all__ = ["WrongParam", "MissingParam", "SupernumeraryParam", "UnknownParamObj", "WrongParamType", "MissingRepeat", "SupernumeraryRepeat", "TypeNames", "Type", "ParamType", - "ParamRepeat", "ElementParamType", "ElementParamInterface", + "ElementParamType", "ElementParamInterface", "AttrParamType", "AbstractParamTypes", "ParamDecoder"] __docformat__ = 'restructuredtext' @@ -186,26 +186,6 @@ def serialize(self, *args, **kwargs): return kwargs -class ParamRepeat(object): - # opts: min, max - - def __init__(self, *param_def, **opts): - self.param_def = param_def - self.opts = {'min': 1, 'max': None} - self.opts.update(opts) - self._obj = list(param_def) - self._obj.append(self.opts) - - def items(self): - return list(self.opts.items()) - - def __getattr__(self, name): - return self.opts[name] - - def obj(self): - return self._obj - - class ElementParamType(ParamType): capabilities = ParamType.ItemList, ParamType.ItemListEvents From 08e38af3725bdf8a875385d7a086712d7dd841b5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 20 Jun 2020 00:10:05 +0200 Subject: [PATCH 664/830] Remove deprecated hook place: hooks --- src/sardana/macroserver/scan/gscan.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 6330825a2a..f3a42c57d4 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -186,7 +186,6 @@ class GScan(Logger): strict order after finishing acquisition but before recording the step. - 'post-step-hooks' : (optional) a sequence of callables to be called in strict order after finishing recording the step. - - 'hooks' : (deprecated, use post-acq-hooks instead) - 'point_id' : a hashable identifing the scan point. - 'check_func' : (optional) a list of callable objects. callable(moveables, counters) @@ -1185,20 +1184,6 @@ def stepUp(self, n, step, lstep): except Exception: pass - # hooks for backwards compatibility: - if 'hooks' in step: - self.macro.info('Deprecation warning: you should use ' - '"post-acq-hooks" instead of "hooks" in the step ' - 'generator') - for hook in step.get('hooks', ()): - hook() - try: - step['extrainfo'].update(hook.getStepExtraInfo()) - except InterruptException: - raise - except Exception: - pass - # Add final moveable positions data_line['point_nb'] = n data_line['timestamp'] = dt @@ -2150,7 +2135,6 @@ class CTScan(CScan, CAcquisition): in strict order before starting to move - 'post-move-hooks': (optional) a sequence of callables to be called in strict order after finishing the move - - 'hooks' : (deprecated, use post-acq-hooks instead) - 'waypoint_id' : a hashable identifing the waypoint - 'check_func' : (optional) a list of callable objects. callable(moveables, counters) From b173d9613ed17435156e5420eee443d32797151c Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 20 Jun 2020 00:10:32 +0200 Subject: [PATCH 665/830] Remove deprecated hook place: pre-start --- src/sardana/macroserver/scan/gscan.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index f3a42c57d4..f3c0f17c72 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2449,12 +2449,7 @@ def _go_through_waypoints(self): self.data.initial_data = initial_data if hasattr(macro, 'getHooks'): - pre_acq_hooks = macro.getHooks('pre-start') - if len(pre_acq_hooks) > 0: - self.macro.warning("pre-start hook place is deprecated," - "use pre-acq instead") - pre_acq_hooks += waypoint.get('pre-acq-hooks', []) - + pre_acq_hooks = waypoint.get('pre-acq-hooks', []) for hook in pre_acq_hooks: hook() self.macro.checkPoint() From 93b26cf902afa799e96fc62afd4808a0be4ff8af Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 20 Jun 2020 23:25:21 +0200 Subject: [PATCH 666/830] Remove deprecated FileRecorder macro hint --- .../macros/examples/specific_experiments.py | 2 +- src/sardana/macroserver/scan/gscan.py | 2 +- .../macroserver/scan/recorder/storage.py | 25 +++++++------------ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/sardana/macroserver/macros/examples/specific_experiments.py b/src/sardana/macroserver/macros/examples/specific_experiments.py index b74d2393b4..e7a40e5df0 100644 --- a/src/sardana/macroserver/macros/examples/specific_experiments.py +++ b/src/sardana/macroserver/macros/examples/specific_experiments.py @@ -41,7 +41,7 @@ class xas_acq(Macro, Hookable): Perform an X-ray absorption scan experiment. Data is stored in a NXxas-compliant file. """ - hints = {'FileRecorder': 'NXxas_FileRecorder', 'scan': 'xas_acq', 'allowsHooks': ( + hints = {'scan': 'xas_acq', 'allowsHooks': ( 'pre-move', 'post-move', 'pre-acq', 'post-acq', 'post-step')} # env = ('MonochromatorEnergy', )#'AbsorbedBeam', 'IncomingBeam', # 'Monitor') #this hints that the macro requires the ActiveMntGrp diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 6330825a2a..4bf3f103f2 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -213,7 +213,7 @@ class GScan(Logger): - DataHandler with the following recorders: - OutputRecorder (depends on ``OutputCols`` environment variable) - SharedMemoryRecorder (depends on ``SharedMemory`` environment variable) - - FileRecorder (depends on ``ScanDir`` and ``ScanData`` + - FileRecorder (depends on ``ScanDir``, ``ScanData`` and ``ScanRecorder`` environment variables) - ScanDataEnvironment with the following contents: diff --git a/src/sardana/macroserver/scan/recorder/storage.py b/src/sardana/macroserver/scan/recorder/storage.py index 7a6c818c7c..c50634fdc8 100644 --- a/src/sardana/macroserver/scan/recorder/storage.py +++ b/src/sardana/macroserver/scan/recorder/storage.py @@ -304,21 +304,14 @@ def _createBranch(self, path): def FileRecorder(filename, macro, **pars): ext = os.path.splitext(filename)[1].lower() or '.spec' rec_manager = macro.getMacroServer().recorder_manager - - hinted_recorder = getattr(macro, 'hints', {}).get('FileRecorder', None) - if hinted_recorder is not None: - macro.deprecated("FileRecorder macro hints are deprecated. " - "Use ScanRecorder variable instead.") - klass = rec_manager.getRecorderClass(hinted_recorder) + klasses = rec_manager.getRecorderClasses( + filter=BaseFileRecorder, extension=ext) + len_klasses = len(klasses) + if len_klasses == 0: + klass = rec_manager.getRecorderClass('SPEC_FileRecorder') + elif len_klasses == 1: + klass = list(klasses.values())[0] else: - klasses = rec_manager.getRecorderClasses( - filter=BaseFileRecorder, extension=ext) - len_klasses = len(klasses) - if len_klasses == 0: - klass = rec_manager.getRecorderClass('SPEC_FileRecorder') - elif len_klasses == 1: - klass = list(klasses.values())[0] - else: - raise AmbiguousRecorderError('Choice of recorder for %s ' - 'extension is ambiguous' % ext) + raise AmbiguousRecorderError('Choice of recorder for %s ' + 'extension is ambiguous' % ext) return klass(filename=filename, macro=macro, **pars) From 222c60c18355de8bbe733b8b98463a01382943f8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 21 Jun 2020 23:49:24 +0200 Subject: [PATCH 667/830] Remove deprecated ctrl_prop --- src/sardana/pool/controller.py | 5 ----- .../pool/poolcontrollers/HklPseudoMotorController.py | 6 ++++-- src/sardana/pool/poolmetacontroller.py | 7 ------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 4fde8318a8..e7f0fb125e 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -109,11 +109,6 @@ class Controller(object): :keyword kwargs: """ - #: - #: .. deprecated:: 1.0 - #: use :attr:`~Controller.ctrl_properties` instead - class_prop = {} - #: A sequence of :obj:`str` representing the controller features ctrl_features = [] #: diff --git a/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py index f104065e8f..5fcd6666d5 100644 --- a/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py +++ b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py @@ -117,8 +117,10 @@ class DiffracBasis(PseudoMotorController): """ The PseudoMotor controller for the diffractometer""" - class_prop = {'DiffractometerType': {Type: str, - Description: 'Type of the diffractometer, e.g. E6C'}, # noqa + ctrl_properties = { + 'DiffractometerType': { + Type: str, + Description: 'Type of the diffractometer, e.g. E6C'}, } ctrl_attributes = {'Crystal': {Type: str, diff --git a/src/sardana/pool/poolmetacontroller.py b/src/sardana/pool/poolmetacontroller.py index 1278617aec..9e8ee06c4d 100644 --- a/src/sardana/pool/poolmetacontroller.py +++ b/src/sardana/pool/poolmetacontroller.py @@ -272,13 +272,6 @@ def __init__(self, **kwargs): dep_msg = ("Defining the controller property description using a " + "string is deprecated, use " + "sardana.pool.controller.Description constant instead.") - for k, v in list(klass.class_prop.items()): # old member - props[k] = DataInfo.toDataInfo(k, v) - if Description in v: - self.ctrl_properties_descriptions.append(v[Description]) - elif 'Description' in v: - self.warning(dep_msg) - self.ctrl_properties_descriptions.append(v['Description']) for k, v in list(klass.ctrl_properties.items()): props[k] = DataInfo.toDataInfo(k, v) From 8336c2ed7219bd59e32fc0e18513d616d61a93c6 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Mon, 22 Jun 2020 17:50:25 +0200 Subject: [PATCH 668/830] update channel selection --- src/sardana/macroserver/macros/scan.py | 169 ++++++++++++++++--------- 1 file changed, 106 insertions(+), 63 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 59c835575d..6541cd6b88 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1888,71 +1888,114 @@ class scanstats(Macro): the active measurement group for the last scan. Print it and publish it in the env. The macro must be hooked in the post-scan hook place. """ - - def run(self, *args): + param_def = [ + ["channel", + [["channel", Type.ExpChannel, None, ""], {"min":0}], + None, + "List of channels for statistics calculations" + ] + ] + + def run(self, channel): parent = self.getParentMacro() if parent: - mntGrp = self.getEnv('ActiveMntGrp') - self.mntGrp = self.getObj(mntGrp, type_class=Type.MeasurementGroup) - channels = self.mntGrp.getChannels() - select_channel = '' - - for channel in channels: - if channel['enabled'] & channel['plot_type'] == 1: - select_channel = channel['label'] - break - - # in case no channel is enabled and plotted just take the first - if select_channel == '': - select_channel = channels[0]['label'] - - select_motor = str(parent.motors[0]) - - # calculate stats for all enabled channels - data = parent.data - stats = {} - - for channel in channels: - if channel['enabled']: - channel_name = channel['label'] - counter_data = [] - motor_data = [] - - for idx, rc in data.items(): - counter_data.append(rc[channel_name]) - motor_data.append(rc[select_motor]) - - counter_data = numpy.array(counter_data) - counter_data_grad = numpy.gradient(counter_data) - motor_data = numpy.array(motor_data) - - stats[channel_name] = {'min': numpy.min(counter_data), - 'max': numpy.max(counter_data), - 'minpos': motor_data[numpy.argmin(counter_data)], - 'maxpos': motor_data[numpy.argmax(counter_data)], - 'mean': numpy.mean(counter_data), - 'int': numpy.sum(counter_data), - 'cen': numpy.sum(counter_data*motor_data)/numpy.sum(counter_data), - 'edge': numpy.sum(counter_data_grad*motor_data)/numpy.sum(counter_data_grad)} - - self.info('Statistics on channel: %s' % select_channel) - self.info('Statistics for movable: %s' % select_motor) - - # print statistics - self.info('Min: %g' % stats[select_channel]['min']) - self.info('Max: %g' % stats[select_channel]['max']) - self.info('Min at: %g' % stats[select_channel]['minpos']) - self.info('Max at: %g' % stats[select_channel]['maxpos']) - self.info('Mean: %g' % stats[select_channel]['mean']) - self.info('Integral: %g' % stats[select_channel]['int']) - self.info('CEN: %g' % stats[select_channel]['cen']) - self.info('EDGE: %g' % stats[select_channel]['edge']) - # set CEN and PEAK as env variables - # set the motor only in case it is hard to access it from another - # macro like pic or cen - self.setEnv('ScanStats', {'stats': stats, - 'counter': select_channel, - 'motor': select_motor}) + try: + active_meas_grp = self.getEnv("ActiveMntGrp") + meas_grp = self.getMeasurementGroup(active_meas_grp) + except: + self.warning("No active measurement group found") + return + + calc_channels = [] + enabled_channels = meas_grp.getEnabled() + if channel: + stat_channels = [chan.name for chan in channel] + else: + stat_channels = [key for key, value in enabled_channels.items()] + + for chan in stat_channels: + enabled = enabled_channels.get(chan) + if enabled is None: + self.warning("{} not in {}".format(chan, meas_grp.name)) + else: + if not enabled: + self.warning("{} not enabled".format(chan)) + else: + if channel: + # channel was given as parameters + calc_channels.append(chan) + else: + # no channel given + # add all potted channels + if meas_grp.getPlotType(chan)[chan] == 1: + calc_channels.append(chan) + + if calc_channels == []: + # fallback is first enabled channel in meas_grp + calc_channels.append(next(iter(enabled_channels))) + + + # mntGrp = self.getEnv('ActiveMntGrp') + # self.mntGrp = self.getObj(mntGrp, type_class=Type.MeasurementGroup) + # channels = self.mntGrp.getChannels() + # select_channel = '' + + # for channel in channels: + # if channel['enabled'] & channel['plot_type'] == 1: + # select_channel = channel['label'] + # break + + # # in case no channel is enabled and plotted just take the first + # if select_channel == '': + # select_channel = channels[0]['label'] + + # select_motor = str(parent.motors[0]) + + # # calculate stats for all enabled channels + # data = parent.data + # stats = {} + + # for channel in channels: + # if channel['enabled']: + # channel_name = channel['label'] + # counter_data = [] + # motor_data = [] + + # for idx, rc in data.items(): + # counter_data.append(rc[channel_name]) + # motor_data.append(rc[select_motor]) + + # counter_data = numpy.array(counter_data) + # counter_data_grad = numpy.gradient(counter_data) + # motor_data = numpy.array(motor_data) + + # stats[channel_name] = {'min': numpy.min(counter_data), + # 'max': numpy.max(counter_data), + # 'minpos': motor_data[numpy.argmin(counter_data)], + # 'maxpos': motor_data[numpy.argmax(counter_data)], + # 'mean': numpy.mean(counter_data), + # 'int': numpy.sum(counter_data), + # 'cen': numpy.sum(counter_data*motor_data)/numpy.sum(counter_data), + # 'edge': numpy.sum(counter_data_grad*motor_data)/numpy.sum(counter_data_grad)} + + # self.info('Statistics on channel: %s' % select_channel) + # self.info('Statistics for movable: %s' % select_motor) + + # # print statistics + # self.info('Min: %g' % stats[select_channel]['min']) + # self.info('Max: %g' % stats[select_channel]['max']) + # self.info('Min at: %g' % stats[select_channel]['minpos']) + # self.info('Max at: %g' % stats[select_channel]['maxpos']) + # self.info('Mean: %g' % stats[select_channel]['mean']) + # self.info('Integral: %g' % stats[select_channel]['int']) + # self.info('CEN: %g' % stats[select_channel]['cen']) + # self.info('EDGE: %g' % stats[select_channel]['edge']) + # # set CEN and PEAK as env variables + # # set the motor only in case it is hard to access it from another + # # macro like pic or cen + # self.setEnv('ScanStats', {'stats': stats, + # 'counter': select_channel, + # 'motor': select_motor}) else: self.warning('for now the scanstats macro can only be executed as' ' a post-scan hook') From 784ccd2f6f94253e6b6167785d0f3fb68e18c70b Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Mon, 22 Jun 2020 22:53:07 +0200 Subject: [PATCH 669/830] plotting using taurus Table --- src/sardana/macroserver/macros/scan.py | 140 +++++++++++-------------- 1 file changed, 64 insertions(+), 76 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 6541cd6b88..213c92fc99 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1890,7 +1890,7 @@ class scanstats(Macro): """ param_def = [ ["channel", - [["channel", Type.ExpChannel, None, ""], {"min":0}], + [["channel", Type.ExpChannel, None, ""], {"min": 0}], None, "List of channels for statistics calculations" ] @@ -1911,91 +1911,79 @@ def run(self, channel): if channel: stat_channels = [chan.name for chan in channel] else: - stat_channels = [key for key, value in enabled_channels.items()] + stat_channels = [key for key in enabled_channels.keys()] for chan in stat_channels: enabled = enabled_channels.get(chan) if enabled is None: self.warning("{} not in {}".format(chan, meas_grp.name)) else: - if not enabled: + if not enabled and channel: self.warning("{} not enabled".format(chan)) - else: - if channel: - # channel was given as parameters - calc_channels.append(chan) - else: - # no channel given - # add all potted channels - if meas_grp.getPlotType(chan)[chan] == 1: - calc_channels.append(chan) - + elif enabled and channel: + # channel was given as parameters + calc_channels.append(chan) + elif enabled and meas_grp.getPlotType(chan)[chan] == 1: + calc_channels.append(chan) + if calc_channels == []: # fallback is first enabled channel in meas_grp calc_channels.append(next(iter(enabled_channels))) - - - # mntGrp = self.getEnv('ActiveMntGrp') - # self.mntGrp = self.getObj(mntGrp, type_class=Type.MeasurementGroup) - # channels = self.mntGrp.getChannels() - # select_channel = '' - - # for channel in channels: - # if channel['enabled'] & channel['plot_type'] == 1: - # select_channel = channel['label'] - # break - - # # in case no channel is enabled and plotted just take the first - # if select_channel == '': - # select_channel = channels[0]['label'] - - # select_motor = str(parent.motors[0]) - - # # calculate stats for all enabled channels - # data = parent.data - # stats = {} - - # for channel in channels: - # if channel['enabled']: - # channel_name = channel['label'] - # counter_data = [] - # motor_data = [] - - # for idx, rc in data.items(): - # counter_data.append(rc[channel_name]) - # motor_data.append(rc[select_motor]) - - # counter_data = numpy.array(counter_data) - # counter_data_grad = numpy.gradient(counter_data) - # motor_data = numpy.array(motor_data) - - # stats[channel_name] = {'min': numpy.min(counter_data), - # 'max': numpy.max(counter_data), - # 'minpos': motor_data[numpy.argmin(counter_data)], - # 'maxpos': motor_data[numpy.argmax(counter_data)], - # 'mean': numpy.mean(counter_data), - # 'int': numpy.sum(counter_data), - # 'cen': numpy.sum(counter_data*motor_data)/numpy.sum(counter_data), - # 'edge': numpy.sum(counter_data_grad*motor_data)/numpy.sum(counter_data_grad)} - - # self.info('Statistics on channel: %s' % select_channel) - # self.info('Statistics for movable: %s' % select_motor) - - # # print statistics - # self.info('Min: %g' % stats[select_channel]['min']) - # self.info('Max: %g' % stats[select_channel]['max']) - # self.info('Min at: %g' % stats[select_channel]['minpos']) - # self.info('Max at: %g' % stats[select_channel]['maxpos']) - # self.info('Mean: %g' % stats[select_channel]['mean']) - # self.info('Integral: %g' % stats[select_channel]['int']) - # self.info('CEN: %g' % stats[select_channel]['cen']) - # self.info('EDGE: %g' % stats[select_channel]['edge']) - # # set CEN and PEAK as env variables - # # set the motor only in case it is hard to access it from another - # # macro like pic or cen - # self.setEnv('ScanStats', {'stats': stats, - # 'counter': select_channel, - # 'motor': select_motor}) + + select_motor = str(parent.motors[0]) + data = parent.data + stats = {} + col_header = [] + cols = [] + + for channel_name in calc_channels: + counter_data = [] + motor_data = [] + + for idx, rc in data.items(): + counter_data.append(rc[channel_name]) + motor_data.append(rc[select_motor]) + + counter_data = numpy.array(counter_data) + counter_data_grad = numpy.gradient(counter_data) + motor_data = numpy.array(motor_data) + + stats[channel_name] = { + 'min': numpy.min(counter_data), + 'max': numpy.max(counter_data), + 'minpos': motor_data[numpy.argmin(counter_data)], + 'maxpos': motor_data[numpy.argmax(counter_data)], + 'mean': numpy.mean(counter_data), + 'int': numpy.sum(counter_data), + 'cen': numpy.sum(counter_data*motor_data) + / numpy.sum(counter_data), + 'edge': numpy.sum(counter_data_grad*motor_data) + / numpy.sum(counter_data_grad)} + + col_header.append([channel_name]) + cols.append([ + stats[channel_name]['min'], + stats[channel_name]['max'], + stats[channel_name]['minpos'], + stats[channel_name]['maxpos'], + stats[channel_name]['mean'], + stats[channel_name]['int'], + stats[channel_name]['cen'], + stats[channel_name]['edge'] + ]) + self.info('Statistics for movable: %s' % select_motor) + + table = Table(elem_list=cols, elem_fmt=["%*g"], + row_head_str=['MIN', 'MAX', 'MIN@', 'MAX@', + 'MEAN', 'INT', 'CEN', 'EDGE'], + col_head_str=col_header, col_head_sep='-') + out = table.genOutput() + + for line in out: + self.info(line) + self.setEnv('ScanStats', {'Stats': stats, + 'Motor': select_motor, + 'ScanID': self.getEnv('ScanID')}) else: self.warning('for now the scanstats macro can only be executed as' ' a post-scan hook') From 4559ad1eb854667f8f85799ae5696c69de39f6b5 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Tue, 23 Jun 2020 12:21:33 +0200 Subject: [PATCH 670/830] fix flake8 --- src/sardana/macroserver/macros/scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 213c92fc99..33c51e0358 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1901,11 +1901,11 @@ def run(self, channel): if parent: try: active_meas_grp = self.getEnv("ActiveMntGrp") - meas_grp = self.getMeasurementGroup(active_meas_grp) - except: + except UnknownEnv: self.warning("No active measurement group found") return + meas_grp = self.getMeasurementGroup(active_meas_grp) calc_channels = [] enabled_channels = meas_grp.getEnabled() if channel: From fb89997583e9343143acae2935c2bb80295f086e Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Tue, 23 Jun 2020 12:24:52 +0200 Subject: [PATCH 671/830] fix flake8 --- src/sardana/macroserver/macros/standard.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 526c8120a8..a02bed80ac 100755 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -1063,12 +1063,13 @@ class plotselect(Macro): def run(self, channel): try: active_meas_grp = self.getEnv('ActiveMntGrp') - meas_grp = self.getMeasurementGroup(active_meas_grp) - self.output("Active measurement group: {}".format(meas_grp.name)) - except: + except UnknownEnv: self.warning('No active measurement group found') return + meas_grp = self.getMeasurementGroup(active_meas_grp) + self.output("Active measurement group: {}".format(meas_grp.name)) + plot_channels_ok = [] enabled_channels = meas_grp.getEnabled() # check channels first From 5857b6b01c2e27247dbf02d439ffac446ddc5eee Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Tue, 23 Jun 2020 16:37:11 +0200 Subject: [PATCH 672/830] update pic and cen for new calcScanStat PR --- src/sardana/macroserver/macros/standard.py | 119 ++++++++++++--------- 1 file changed, 71 insertions(+), 48 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index d8b4cfbe01..da3d20aa67 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -25,7 +25,8 @@ __all__ = ["ct", "mstate", "mv", "mvr", "pwa", "pwm", "repeat", "set_lim", "set_lm", "set_pos", "settimer", "uct", "umv", "umvr", "wa", "wm", - "tw", "logmacro", "newfile", "pic", "cen", "where"] + "tw", "logmacro", "newfile", "_movetostatspos", "pic", "cen", + "where"] __docformat__ = 'restructuredtext' @@ -1052,72 +1053,94 @@ def run(self, ScanFilePath_list, ScanID): postNewfileHook() -class pic(Macro): - """This macro moves the motor of the last scan to the peak position for a - given counter. If no counter is given, it selects the first plotted counter - from the expconf. - """ +class _movetostatspos(Macro): + """This macro does the logic for pic and cen""" param_def = [ - ['counter', Type.ExpChannel, None, 'name of counter'] + ['channel', Type.ExpChannel, Optional, 'name of channel'], + ['caller', Type.String, None, 'caller (pic or cen)'] ] - def prepare(self, counter): - self.stats = self.getEnv('ScanStats') - if counter is None: - self.counter = self.stats['counter'] + def run(self, channel, caller): + try: + stats = self.getEnv('ScanStats') + except UnknownEnv: + self.warning("No ScanStats available in env") + return + + if channel is None: + # use first channel in stats + channel = next(iter(stats['Stats'])) + else: + if channel.name in stats['Stats']: + channel = channel.name + else: + self.warning("Channel {} not present in ScanStats".format( + channel.name)) + return + + if caller == 'pic': + stats_value = 'maxpos' + stats_str = 'PIC' + elif caller == 'cen': + stats_value = 'cen' + stats_str = 'CEN' else: - self.counter = counter.getName() + self.warning("Caller {} is unkown".format(caller)) + return - self.motor_name = self.stats['motor'] - self.motor = self.getMotion([self.motor_name]) + motor_name = stats['Motor'] + motor = self.getMotion([motor_name]) + current_pos = motor.readPosition()[0] + pos = stats['Stats'][channel][stats_value] - def run(self, counter): - current_pos = self.motor.readPosition()[0] - peak = self.stats['stats'][self.counter]['maxpos'] - self.info('move motor %s from current position\nat %f\nto peak of' - ' counter %s\nat %f' % (self.motor_name, current_pos, - self.counter, peak)) - self.motor.move(peak) + self.info("move motor {:s} from current position\nat {:.4f}\n" + "to {:s} of counter {:s}\nat {:.4f}".format(motor_name, + current_pos, + stats_str, + channel, + pos)) + motor.move(pos) -class cen(Macro): - """This macro moves the motor of the last scan to the cen position for a - given counter. If no counter is given, it selects the first plotted counter - from the expconf. +class pic(Macro): + """This macro moves the motor of the last scan to the PEAK position for a + given channel. If no channel is given, it selects the first channel from + the ScanStats env variable. """ param_def = [ - ['counter', Type.ExpChannel, None, 'name of counter'] + ['channel', Type.ExpChannel, Optional, 'name of channel'] ] - def prepare(self, counter): - self.stats = self.getEnv('ScanStats') - if counter is None: - self.counter = self.stats['counter'] - else: - self.counter = counter.getName() + def run(self, channel): + self.execMacro('_movetostatspos', channel, 'pic') + - self.motor_name = self.stats['motor'] - self.motor = self.getMotion([self.motor_name]) +class cen(Macro): + """This macro moves the motor of the last scan to the CEN position for a + given channel. If no channel is given, it selects the first channel from + the ScanStats env variable. + """ - def run(self, counter): - current_pos = self.motor.readPosition()[0] - cen = self.stats['stats'][self.counter]['cen'] - self.info('move motor %s from current position\nat %f\nto cen of' - ' counter %s\nat %f' % (self.motor_name, current_pos, - self.counter, cen)) - self.motor.move(cen) + param_def = [ + ['channel', Type.ExpChannel, Optional, 'name of channel'] + ] + + def run(self, channel): + self.execMacro('_movetostatspos', channel, 'cen') class where(Macro): """This macro shows the current position of the last scanned motor.""" - def prepare(self): - self.stats = self.getEnv('ScanStats') - self.motor_name = self.stats['motor'] - self.motor = self.getMotion([self.motor_name]) - def run(self): - current_pos = self.motor.readPosition()[0] - self.info('motor %s is\nat %f' % (self.motor_name, current_pos)) + try: + motor_name = self.getEnv('ScanStats')['Motor'] + except UnknownEnv: + self.warning("No motor from last scan available") + return + + motor = self.getMotion([motor_name]) + self.info("motor {:s} is\nat {:.4f}".format(motor_name, + motor.readPosition()[0])) From 7809c5f437f2ec46d32586e426a1281a25495740 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Tue, 23 Jun 2020 16:39:58 +0200 Subject: [PATCH 673/830] remove EDGE stats --- src/sardana/macroserver/macros/scan.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 33c51e0358..553fad6a55 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1956,9 +1956,7 @@ def run(self, channel): 'mean': numpy.mean(counter_data), 'int': numpy.sum(counter_data), 'cen': numpy.sum(counter_data*motor_data) - / numpy.sum(counter_data), - 'edge': numpy.sum(counter_data_grad*motor_data) - / numpy.sum(counter_data_grad)} + / numpy.sum(counter_data)} col_header.append([channel_name]) cols.append([ @@ -1969,13 +1967,12 @@ def run(self, channel): stats[channel_name]['mean'], stats[channel_name]['int'], stats[channel_name]['cen'], - stats[channel_name]['edge'] ]) self.info('Statistics for movable: %s' % select_motor) table = Table(elem_list=cols, elem_fmt=["%*g"], row_head_str=['MIN', 'MAX', 'MIN@', 'MAX@', - 'MEAN', 'INT', 'CEN', 'EDGE'], + 'MEAN', 'INT', 'CEN'], col_head_str=col_header, col_head_sep='-') out = table.genOutput() From f017997daa4306c20edadf20aed52d7a35a0e16b Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Tue, 23 Jun 2020 16:42:04 +0200 Subject: [PATCH 674/830] add comment for CEN calculation, fix flake8 --- src/sardana/macroserver/macros/scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 553fad6a55..8346bda824 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1945,7 +1945,6 @@ def run(self, channel): motor_data.append(rc[select_motor]) counter_data = numpy.array(counter_data) - counter_data_grad = numpy.gradient(counter_data) motor_data = numpy.array(motor_data) stats[channel_name] = { @@ -1957,7 +1956,8 @@ def run(self, channel): 'int': numpy.sum(counter_data), 'cen': numpy.sum(counter_data*motor_data) / numpy.sum(counter_data)} - + # Logic for CEN must be adopted to find center-of-mass and + # edges of erf-like data col_header.append([channel_name]) cols.append([ stats[channel_name]['min'], From 02010a62beb1b26827fc7b3641e29eee52870805 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 24 Jun 2020 09:25:43 +0200 Subject: [PATCH 675/830] add cases for door and macro name in genv macro --- src/sardana/macroserver/macros/env.py | 49 +++++++++++++++++---------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index aef1a849af..1aefe3a1d1 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -202,28 +202,43 @@ def run(self, env, value): class genv(Macro): """Gets the given environment variable""" - param_def = [['name', Type.Env, None, - 'Environment variable name. Can be one of the following:\n' - ' - - global variable\n' - ' - . - variable value for a specific ' - 'door\n' - ' - . - variable value for a specific' - ' macro\n' - ' - .. - variable value' - ' for a specific macro running on a specific door'], + param_def = [ + ["name", Type.Env, None, + "Environment variable name. Can be one of the following:\n" + " - - global variable\n" + " - . - variable value for a specific " + "door\n" + " - . - variable value for a specific" + " macro\n" + " - .. - variable value" + " for a specific macro running on a specific door"], ] def run(self, var): pars = var.split(".") + door_name = None + macro_name = None if len(pars) == 1: - env = self.getAllDoorEnv() - line = '%s = %s' % (str(var), env[var]) - else: - env = self.getEnv(key=None, macro_name=pars[0]) - names_list = list(env.keys()) - names_list.sort(key=str.lower) - line = '%s = %s' % (str(var), str(env[pars[1]])) - self.output(line) + key = pars[0] + elif len(pars) > 1: + if pars[0].find("/") != -1: + # first string is a Door name + door_name = pars[0] + if len(pars) == 3: + macro_name = pars[1] + key = pars[2] + else: + key = pars[1] + else: + # first string is a Macro name + macro_name = pars[0] + key = pars[1] + + env = self.getEnv(key=key, + macro_name=macro_name, + door_name=door_name) + + self.output("{:s} = {:s}".format(str(key), str(env))) class usenv(Macro): From 95f315f67312095e85826b18842619ab1c227431 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 24 Jun 2020 09:39:54 +0200 Subject: [PATCH 676/830] set ScanStats with Doorname and some cleanup --- src/sardana/macroserver/macros/scan.py | 51 +++++++++++++------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 8346bda824..c7166abbbb 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1930,7 +1930,7 @@ def run(self, channel): # fallback is first enabled channel in meas_grp calc_channels.append(next(iter(enabled_channels))) - select_motor = str(parent.motors[0]) + selected_motor = str(parent.motors[0]) data = parent.data stats = {} col_header = [] @@ -1942,45 +1942,46 @@ def run(self, channel): for idx, rc in data.items(): counter_data.append(rc[channel_name]) - motor_data.append(rc[select_motor]) + motor_data.append(rc[selected_motor]) counter_data = numpy.array(counter_data) motor_data = numpy.array(motor_data) stats[channel_name] = { - 'min': numpy.min(counter_data), - 'max': numpy.max(counter_data), - 'minpos': motor_data[numpy.argmin(counter_data)], - 'maxpos': motor_data[numpy.argmax(counter_data)], - 'mean': numpy.mean(counter_data), - 'int': numpy.sum(counter_data), - 'cen': numpy.sum(counter_data*motor_data) + "min": numpy.min(counter_data), + "max": numpy.max(counter_data), + "minpos": motor_data[numpy.argmin(counter_data)], + "maxpos": motor_data[numpy.argmax(counter_data)], + "mean": numpy.mean(counter_data), + "int": numpy.sum(counter_data), + "cen": numpy.sum(counter_data*motor_data) / numpy.sum(counter_data)} # Logic for CEN must be adopted to find center-of-mass and # edges of erf-like data col_header.append([channel_name]) cols.append([ - stats[channel_name]['min'], - stats[channel_name]['max'], - stats[channel_name]['minpos'], - stats[channel_name]['maxpos'], - stats[channel_name]['mean'], - stats[channel_name]['int'], - stats[channel_name]['cen'], + stats[channel_name]["min"], + stats[channel_name]["max"], + stats[channel_name]["minpos"], + stats[channel_name]["maxpos"], + stats[channel_name]["mean"], + stats[channel_name]["int"], + stats[channel_name]["cen"], ]) - self.info('Statistics for movable: %s' % select_motor) + self.info("Statistics for movable: {:s}".format(selected_motor)) table = Table(elem_list=cols, elem_fmt=["%*g"], - row_head_str=['MIN', 'MAX', 'MIN@', 'MAX@', - 'MEAN', 'INT', 'CEN'], - col_head_str=col_header, col_head_sep='-') + row_head_str=["MIN", "MAX", "MIN@", "MAX@", + "MEAN", "INT", "CEN"], + col_head_str=col_header, col_head_sep="-") out = table.genOutput() for line in out: self.info(line) - self.setEnv('ScanStats', {'Stats': stats, - 'Motor': select_motor, - 'ScanID': self.getEnv('ScanID')}) + self.setEnv("{:s}.ScanStats".format(self.getDoorName()), + {"Stats": stats, + "Motor": selected_motor, + "ScanID": self.getEnv("ScanID")}) else: - self.warning('for now the scanstats macro can only be executed as' - ' a post-scan hook') + self.warning("for now the scanstats macro can only be executed as" + " a post-scan hook") From e3c80e4121c47693b68d2301ffba286e3851fbac Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 24 Jun 2020 11:39:29 +0200 Subject: [PATCH 677/830] added _calcStats method, implemented peak detection, cen calculation adopted form ESRF --- src/sardana/macroserver/macros/scan.py | 117 ++++++++++++++++++++++--- 1 file changed, 106 insertions(+), 11 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index c7166abbbb..f4101ac06b 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1947,17 +1947,19 @@ def run(self, channel): counter_data = numpy.array(counter_data) motor_data = numpy.array(motor_data) + (_min, _max, min_at, max_at, half_max, com, mean, _int, + fwhm, cen) = self._calcStats(motor_data, counter_data) stats[channel_name] = { - "min": numpy.min(counter_data), - "max": numpy.max(counter_data), - "minpos": motor_data[numpy.argmin(counter_data)], - "maxpos": motor_data[numpy.argmax(counter_data)], - "mean": numpy.mean(counter_data), - "int": numpy.sum(counter_data), - "cen": numpy.sum(counter_data*motor_data) - / numpy.sum(counter_data)} - # Logic for CEN must be adopted to find center-of-mass and - # edges of erf-like data + "min": _min, + "max": _max, + "minpos": min_at, + "maxpos": max_at, + "mean": mean, + "int": _int, + "com": com, + "fwhm": fwhm, + "cen": cen} + col_header.append([channel_name]) cols.append([ stats[channel_name]["min"], @@ -1966,13 +1968,15 @@ def run(self, channel): stats[channel_name]["maxpos"], stats[channel_name]["mean"], stats[channel_name]["int"], + stats[channel_name]["com"], + stats[channel_name]["fwhm"], stats[channel_name]["cen"], ]) self.info("Statistics for movable: {:s}".format(selected_motor)) table = Table(elem_list=cols, elem_fmt=["%*g"], row_head_str=["MIN", "MAX", "MIN@", "MAX@", - "MEAN", "INT", "CEN"], + "MEAN", "INT", "COM", "FWHM", "CEN"], col_head_str=col_header, col_head_sep="-") out = table.genOutput() @@ -1985,3 +1989,94 @@ def run(self, channel): else: self.warning("for now the scanstats macro can only be executed as" " a post-scan hook") + + @staticmethod + def _calcStats(x, y): + # max and min + _min = numpy.min(y) + _max = numpy.max(y) + + min_idx = numpy.argmin(y) + min_at = x[min_idx] + max_idx = numpy.argmax(y) + max_at = x[max_idx] + + # center of mass (com) + try: + com = numpy.sum(y*x)/numpy.sum(y) + except ZeroDivisionError: + com = 0 + + mean = numpy.mean(y) + _int = numpy.sum(y) + + # determine if it is a peak- or erf-like function + half_max = (_max-_min)/2+_min + + lower_left = False + lower_right = False + + if numpy.any(y[0:max_idx] < half_max): + lower_left = True + if numpy.any(y[max_idx:] < half_max): + lower_right = True + + if lower_left and lower_right: + # it is a peak-like function + y_data = y + else: + # it is an erf-like function + # use the gradient for further calculation + y_data = numpy.gradient(y) + # use also the half maximum of the gradient + half_max = (numpy.max(y_data)-numpy.min(y_data)) \ + / 2+numpy.min(y_data) + + # cen and fwhm + # this part is adapted from: + # + # The PyMca X-Ray Fluorescence Toolkit + # + # Copyright (c) 2004-2014 European Synchrotron Radiation Facility + # + # This file is part of the PyMca X-ray Fluorescence Toolkit developed + # at the ESRF by the Software group. + + max_idx_data = numpy.argmax(y_data) + idx = max_idx_data + try: + while y_data[idx] >= half_max: + idx = idx-1 + + x0 = x[idx] + x1 = x[idx+1] + y0 = y_data[idx] + y1 = y_data[idx+1] + + lhmx = (half_max*(x1-x0) - (y0*x1)+(y1*x0)) / (y1-y0) + except ZeroDivisionError: + lhmx = 0 + except IndexError: + lhmx = x[0] + + idx = max_idx_data + try: + while y_data[idx] >= half_max: + idx = idx+1 + + x0 = x[idx] + x1 = x[idx+1] + y0 = y_data[idx] + y1 = y_data[idx+1] + + uhmx = (half_max*(x1-x0) - (y0*x1)+(y1*x0)) / (y1-y0) + except ZeroDivisionError: + uhmx = 0 + except IndexError: + uhmx = x[-1] + + fwhm = uhmx - lhmx + cen = (uhmx + lhmx)/2 + + return (_min, _max, min_at, max_at, half_max, com, mean, _int, + fwhm, cen) From ae550252c40e7bb59e54abc10125cab850627106 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Wed, 24 Jun 2020 15:04:04 +0200 Subject: [PATCH 678/830] get ScanStats env variabel for current Door only --- src/sardana/macroserver/macros/standard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index da3d20aa67..9de9e04b21 100644 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -1063,7 +1063,7 @@ class _movetostatspos(Macro): def run(self, channel, caller): try: - stats = self.getEnv('ScanStats') + stats = self.getEnv('ScanStats', door_name=self.getDoorName()) except UnknownEnv: self.warning("No ScanStats available in env") return From 382001805d3ee04f1d8a051eaee343e1efae98b7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 24 Jun 2020 22:52:10 +0200 Subject: [PATCH 679/830] Remove deprecated ctrl_extra_attributes --- src/sardana/pool/controller.py | 6 ------ src/sardana/pool/poolmetacontroller.py | 2 -- 2 files changed, 8 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index e7f0fb125e..224a0ab7e8 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -111,10 +111,6 @@ class Controller(object): #: A sequence of :obj:`str` representing the controller features ctrl_features = [] - #: - #: .. deprecated:: 1.0 - #: use :attr:`~Controller.axis_attributes` instead - ctrl_extra_attributes = {} #: A :class:`dict` containing controller properties where: #: @@ -492,9 +488,7 @@ def GetAxisAttributes(self, axis): .. versionadded:: 1.0""" ret = copy.deepcopy(self.standard_axis_attributes) axis_attrs = copy.deepcopy(self.axis_attributes) - old_axis_attrs = copy.deepcopy(self.ctrl_extra_attributes) ret.update(axis_attrs) - ret.update(old_axis_attrs) return ret def SendToCtrl(self, stream): diff --git a/src/sardana/pool/poolmetacontroller.py b/src/sardana/pool/poolmetacontroller.py index 9e8ee06c4d..f0fe0aaf6a 100644 --- a/src/sardana/pool/poolmetacontroller.py +++ b/src/sardana/pool/poolmetacontroller.py @@ -289,8 +289,6 @@ def __init__(self, **kwargs): ctrl_attrs[k] = DataInfo.toDataInfo(k, v) self.axis_attributes = axis_attrs = CaselessDict() - for k, v in list(klass.ctrl_extra_attributes.items()): # old member - axis_attrs[k] = DataInfo.toDataInfo(k, v) for k, v in list(klass.axis_attributes.items()): axis_attrs[k] = DataInfo.toDataInfo(k, v) From 5155cc21e6284c9bf1382490b4ab20f6edbd4077 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 24 Jun 2020 22:53:28 +0200 Subject: [PATCH 680/830] Remove deprecated inst_name --- src/sardana/pool/controller.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 224a0ab7e8..c7fb6e546b 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -331,14 +331,6 @@ def DeleteDevice(self, axis): :param int axis: axis number""" pass - @property - def inst_name(self): - """**Controller API**. The controller instance name. - - .. deprecated:: 1.0 - use :meth:`~Controller.GetName` instead""" - return self._inst_name - def GetName(self): """**Controller API**. The controller instance name. From 652ddef49d5915e72752e4de7fd02d80b1ea19b7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 24 Jun 2020 22:59:37 +0200 Subject: [PATCH 681/830] Remove deprecated SetPar and GetPar --- src/sardana/pool/controller.py | 39 ++++++---------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index c7fb6e546b..3c52425663 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -397,20 +397,20 @@ def GetCtrlPar(self, parameter): def SetAxisPar(self, axis, parameter, value): """**Controller API**. Override is MANDATORY. Called to set a parameter with a value on the given axis. Default - implementation calls deprecated :meth:`~Controller.SetPar` which, by - default, raises :exc:`NotImplementedError`. + implementation raises :exc:`NotImplementedError`. .. versionadded:: 1.0""" - return self.SetPar(axis, parameter, value) + raise NotImplementedError("SetAxisPar must be defined in the " + "controller") def GetAxisPar(self, axis, parameter): """**Controller API**. Override is MANDATORY. Called to get a parameter value on the given axis. Default - implementation calls deprecated :meth:`~Controller.GetPar` which, by - default, raises :exc:`NotImplementedError`. + implementation raises :exc:`NotImplementedError`. .. versionadded:: 1.0""" - return self.GetPar(axis, parameter) + raise NotImplementedError("GetAxisPar must be defined in the " + "controller") def SetAxisExtraPar(self, axis, parameter, value): """**Controller API**. Override if necessary. @@ -430,25 +430,6 @@ def GetAxisExtraPar(self, axis, parameter): .. versionadded:: 1.0""" return self.GetExtraAttributePar(axis, parameter) - def SetPar(self, axis, parameter, value): - """**Controller API**. Called to set a parameter with a value on - the given axis. Default implementation raises - :exc:`NotImplementedError`. - - .. deprecated:: 1.0 - use :meth:`~Controller.SetAxisPar` instead""" - raise NotImplementedError("SetAxisPar must be defined in the " - "controller") - - def GetPar(self, axis, parameter): - """**Controller API**. Called to get a parameter value on the given - axis. Default implementation raises :exc:`NotImplementedError`. - - .. deprecated:: 1.0 - use :meth:`~Controller.GetAxisPar` instead""" - raise NotImplementedError("GetAxisPar must be defined in the " - "controller") - def SetExtraAttributePar(self, axis, parameter, value): """**Controller API**. Called to set a parameter with a value on the given axis. Default implementation raises :exc:`NotImplementedError`. @@ -778,13 +759,7 @@ class MotorController(Controller, Startable, Stopable, Readable): - step_per_unit These parameters are configured through the - :meth:`~Controller.GetAxisPar`/:meth:`~Controller.SetAxisPar` - API (in version <1.0 the methods were called - :meth:`~Controller.GetPar`/:meth:`~Controller.SetPar`. Default - :meth:`~Controller.GetAxisPar` and - :meth:`~Controller.SetAxisPar` still call - :meth:`~Controller.GetPar` and :meth:`~Controller.SetPar` - respectively in order to maintain backward compatibility). + :meth:`~Controller.GetAxisPar`/:meth:`~Controller.SetAxisPar` API. """ #: A constant representing no active switch. From 760e7e337ed89800e7984a72414f8925808f1132 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 24 Jun 2020 23:02:03 +0200 Subject: [PATCH 682/830] Remove deprecated SetExtraAttributePar and GetExtraAttributePar --- src/sardana/pool/controller.py | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 3c52425663..50e623436f 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -415,36 +415,18 @@ def GetAxisPar(self, axis, parameter): def SetAxisExtraPar(self, axis, parameter, value): """**Controller API**. Override if necessary. Called to set a parameter with a value on the given axis. Default - implementation calls deprecated :meth:`~Controller.SetExtraAttributePar` - which, by default, raises :exc:`NotImplementedError`. + implementation raises :exc:`NotImplementedError`. .. versionadded:: 1.0""" - return self.SetExtraAttributePar(axis, parameter, value) + raise NotImplementedError("SetAxisExtraPar must be defined in the " + "controller") def GetAxisExtraPar(self, axis, parameter): """**Controller API**. Override if necessary. Called to get a parameter value on the given axis. Default - implementation calls deprecated :meth:`~Controller.GetExtraAttributePar` - which, by default, raises :exc:`NotImplementedError`. + implementation raises :exc:`NotImplementedError`. .. versionadded:: 1.0""" - return self.GetExtraAttributePar(axis, parameter) - - def SetExtraAttributePar(self, axis, parameter, value): - """**Controller API**. Called to set a parameter with a value on the - given axis. Default implementation raises :exc:`NotImplementedError`. - - .. deprecated:: 1.0 - use :meth:`~Controller.SetAxisExtraPar` instead""" - raise NotImplementedError("SetAxisExtraPar must be defined in the " - "controller") - - def GetExtraAttributePar(self, axis, parameter): - """**Controller API**. Called to get a parameter value on the given - axis. Default implementation raises :exc:`NotImplementedError`. - - .. deprecated:: 1.0 - use :meth:`~Controller.GetAxisExtraPar` instead""" raise NotImplementedError("GetAxisExtraPar must be defined in the " "controller") From 98d3693f64b773e0e0a74d18c4bdfad5d01e6c02 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 24 Jun 2020 23:22:53 +0200 Subject: [PATCH 683/830] Remove deprecated _trigger_type --- src/sardana/pool/controller.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 4fde8318a8..5f76d4af3c 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -954,14 +954,6 @@ def __init__(self, inst, props, *args, **kwargs): self._latency_time = 0 self._synchronization = AcqSynch.SoftwareTrigger - def get_trigger_type(self): - msg = "trigger_type is deprecated since SEP6. " +\ - "Use synchronization instead" - self._log.warning(msg) - return self._synchronization - - _trigger_type = property(get_trigger_type) - def PreStartAllCT(self): """**Counter/Timer Controller API**. Override if necessary. Called to prepare an acquisition of all selected axis. From 6f10a1747ad1ee13f4549ccd11daa6513529e543 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 24 Jun 2020 23:27:48 +0200 Subject: [PATCH 684/830] Remove deprecated Start *CT* methods Remove defined in Startable interface method. This changes one behavior: now it is mandatory to implement StartOne method (before it was not). But at first glance this makes sense. --- src/sardana/pool/controller.py | 85 ---------------------------------- 1 file changed, 85 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 5f76d4af3c..3feaa4a63a 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -954,91 +954,6 @@ def __init__(self, inst, props, *args, **kwargs): self._latency_time = 0 self._synchronization = AcqSynch.SoftwareTrigger - def PreStartAllCT(self): - """**Counter/Timer Controller API**. Override if necessary. - Called to prepare an acquisition of all selected axis. - Default implementation does nothing. - - .. deprecated:: 1.0 - use :meth:`~CounterTimerController.PreStartAll` instead""" - pass - - def PreStartOneCT(self, axis): - """**Counter/Timer Controller API**. Override if necessary. - Called to prepare an acquisition a single axis. - Default implementation returns True. - - :param int axis: axis number - :return: True means a successfull PreStartOneCT or False for a failure - :rtype: bool - - .. deprecated:: 1.0 - use :meth:`~CounterTimerController.PreStartOne` instead""" - return True - - def StartOneCT(self, axis): - """**Counter/Timer Controller API**. Override if necessary. - Called to start an acquisition of a selected axis. - Default implementation does nothing. - - :param int axis: axis number - - .. deprecated:: 1.0 - use :meth:`~CounterTimerController.StartOne` instead""" - pass - - def StartAllCT(self): - """**Counter/Timer Controller API**. - Called to start an acquisition of a group of channels. - Default implementation does nothing. - - .. deprecated:: 1.0 - use :meth:`~CounterTimerController.StartAll` instead""" - pass - - def PreStartAll(self): - """**Controller API**. Override if necessary. - Called to prepare a write of the position of all axis. Default - implementation calls deprecated - :meth:`~CounterTimerController.PreStartAllCT` which, by default, does - nothing. - - .. versionadded:: 1.0""" - return self.PreStartAllCT() - - def PreStartOne(self, axis, value=None): - """**Controller API**. Override if necessary. - Called to prepare a write of the position of a single axis. - Default implementation calls deprecated - :meth:`~CounterTimerController.PreStartOneCT` which, by default, - returns True. - - :param int axis: axis number - :param float value: the value - :return: True means a successfull pre-start or False for a failure - :rtype: bool - - .. versionadded:: 1.0""" - return self.PreStartOneCT(axis) - - def StartOne(self, axis, value=None): - """**Controller API**. Override if necessary. - Called to write the position of a selected axis. Default - implementation calls deprecated - :meth:`~CounterTimerController.StartOneCT` which, by default, does - nothing. - - :param int axis: axis number - :param float value: the value""" - return self.StartOneCT(axis) - - def StartAll(self): - """**Controller API**. - Default implementation calls deprecated - :meth:`~CounterTimerController.StartAllCT` which, by default, does - nothing.""" - return self.StartAllCT() - class TriggerGateController(Controller, Synchronizer, Stopable, Startable): """Base class for a trigger/gate controller. Inherit from this class to From cf74b25fa8ac8a6f4e3b8c2552560ee3b57aa543 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 25 Jun 2020 23:13:40 +0200 Subject: [PATCH 685/830] Remove deprecated PseudoMotorController methods --- src/sardana/pool/controller.py | 74 +--------------------------------- 1 file changed, 2 insertions(+), 72 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 4fde8318a8..98e18425a2 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -1302,7 +1302,8 @@ def CalcPseudo(self, axis, physical_pos, curr_pseudo_pos): :rtype: float .. versionadded:: 1.0""" - return self.calc_pseudo(axis, physical_pos) + raise NotImplementedError("CalcPseudo must be defined in the " + "controller") def CalcPhysical(self, axis, pseudo_pos, curr_physical_pos): """**Pseudo Motor Controller API**. Override is **MANDATORY**. @@ -1319,77 +1320,6 @@ def CalcPhysical(self, axis, pseudo_pos, curr_physical_pos): :rtype: float .. versionadded:: 1.0""" - return self.calc_physical(axis, pseudo_pos) - - def calc_all_pseudo(self, physical_pos): - """**Pseudo Motor Controller API**. Override if necessary. - Calculates the positions of all pseudo motors that belong to the - pseudo motor system from the positions of the physical motors. - Default implementation does a loop calling - :meth:`PseudoMotorController.calc_pseudo` for each pseudo motor role. - - :param sequence physical_pos: a sequence of physical motor - positions - :return: a sequece of pseudo motor positions (one for each pseudo - motor role) - :rtype: sequence - - .. deprecated:: 1.0 - implement :meth:`~PseudoMotorController.CalcAllPseudo` instead""" - ret = [] - for i in range(len(self.pseudo_motor_roles)): - ret.append(self.calc_pseudo(i + 1, physical_pos)) - return ret - - def calc_all_physical(self, pseudo_pos): - """**Pseudo Motor Controller API**. Override if necessary. - Calculates the positions of all motors that belong to the pseudo - motor system from the positions of the pseudo motors. - Default implementation does a loop calling - :meth:`PseudoMotorController.calc_physical` for each motor role. - - :param pseudo_pos: a sequence of pseudo motor positions - :type pseudo_pos: sequence - :return: a sequece of motor positions (one for each motor role) - :rtype: sequence - - .. deprecated:: 1.0 - implement :meth:`~PseudoMotorController.CalcAllPhysical` - instead""" - ret = [] - for i in range(len(self.motor_roles)): - pos = self.calc_physical(i + 1, pseudo_pos) - ret.append(pos) - return ret - - def calc_pseudo(self, axis, physical_pos): - """**Pseudo Motor Controller API**. Override is **MANDATORY**. - Calculate pseudo motor position given the physical motor positions - - :param int axis: the pseudo motor role axis - :param sequence physical_pos: a sequence of motor positions - :return: a pseudo motor position corresponding to the given axis - pseudo motor role - :rtype: float - - .. deprecated:: 1.0 - implement :meth:`~PseudoMotorController.CalcPseudo` instead""" - raise NotImplementedError( - "CalcPseudo must be defined in the controller") - - def calc_physical(self, axis, pseudo_pos): - """**Pseudo Motor Controller API**. Override is **MANDATORY**. - Calculate physical motor position given the pseudo motor positions. - - :param axis: the motor role axis - :type axis: int - :param pseudo_pos: a sequence of pseudo motor positions - :type pseudo_pos: sequence - :return: a motor position corresponding to the given axis motor role - :rtype: float - - .. deprecated:: 1.0 - implement :meth:`~PseudoMotorController.CalcPhysical` instead""" raise NotImplementedError("CalcPhysical must be defined in the " "controller") From 991fc7aecf84dbecca838a706a9542c81d38a3c7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 25 Jun 2020 23:19:21 +0200 Subject: [PATCH 686/830] Remove deprecated PseudoCounterController method --- src/sardana/pool/controller.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 4fde8318a8..6d9c6a9e24 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -1485,21 +1485,6 @@ def Calc(self, axis, values): :rtype: float .. versionadded:: 1.0""" - return self.calc(axis, values) - - def calc(self, axis, values): - """**Pseudo Counter Controller API**. Override is **MANDATORY**. - Calculate pseudo counter value given the counter values. - - :param int axis: the pseudo counter role axis - :param sequence values: a sequence containing current values - of underlying elements - :return: a pseudo counter value corresponding to the given axis - pseudo counter role - :rtype: float - - .. deprecated:: 1.0 - implement :meth:`~PseudoCounterController.Calc` instead""" raise NotImplementedError("Calc must be defined in the controller") def CalcAll(self, values): From f844cbe9a59c6ab79d4c646f72550a365cedeacb Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 25 Jun 2020 23:22:51 +0200 Subject: [PATCH 687/830] Remove deprecated IOR predefined_values --- src/sardana/pool/controller.py | 4 ---- src/sardana/pool/poolcontrollers/DummyIORController.py | 2 -- src/sardana/pool/poolmetacontroller.py | 3 --- 3 files changed, 9 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 4fde8318a8..5a7a865591 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -1523,10 +1523,6 @@ class IORegisterController(Controller, Readable): """Base class for a IORegister controller. Inherit from this class to implement your own IORegister controller for the device pool. """ - #: - #: .. deprecated:: 1.0 - #: use :attr:`~Controller.axis_attributes` instead - predefined_values = () #: A :class:`dict` containing the standard attributes present on each axis #: device diff --git a/src/sardana/pool/poolcontrollers/DummyIORController.py b/src/sardana/pool/poolcontrollers/DummyIORController.py index e649bb3406..181d1905ce 100644 --- a/src/sardana/pool/poolcontrollers/DummyIORController.py +++ b/src/sardana/pool/poolcontrollers/DummyIORController.py @@ -35,8 +35,6 @@ class DummyIORController(IORegisterController): MaxDevice = 1024 - predefined_values = "0", "Online", "1", "Offline", "2", "Standby" - def __init__(self, inst, props, *args, **kwargs): IORegisterController.__init__(self, inst, props, *args, **kwargs) self.myvalue = 1 diff --git a/src/sardana/pool/poolmetacontroller.py b/src/sardana/pool/poolmetacontroller.py index 1278617aec..3f296dcda9 100644 --- a/src/sardana/pool/poolmetacontroller.py +++ b/src/sardana/pool/poolmetacontroller.py @@ -320,9 +320,6 @@ def __init__(self, **kwargs): self.dict_extra['counter_roles'] = self.counter_roles self.dict_extra['pseudo_counter_roles'] = self.pseudo_counter_roles - if ElementType.IORegister in types: - self.dict_extra['predefined_values'] = klass.predefined_values - init_args = inspect.getargspec(klass.__init__) if init_args.varargs is None or init_args.keywords is None: self.api_version = 0 From da56aeacfc39cecc20bac360af4b63616a9c7e37 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 6 Jul 2020 23:44:41 +0200 Subject: [PATCH 688/830] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9e6db0a36..c338e382d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) * Dump info on channels if MG acq fails in step scan, ct and uct (#1308) * Add timestamp to element's dumped information (#1308) +* Quality to `SardanaAttribute` (#1353) * Instruments creation and configuration in sar_demo (#1198) * Allow _experimental channel acquisition_ with PoolChannelTaurusValue (PCTV) widget (#1203) * Documentation to Taurus Extensions of Sardana Devices: MacroServer part @@ -39,6 +40,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Change limit switches indicators from buttons to labels (#210, #1290) * Improve documentation (#1241) * Better macro exception message and hint to use `www` (#1191) +* Stress tests (on the Taurus level) for measurements (#1353) * Add basic information to "how to write custom recorder" to the documentation (#1275) * Register a TaurusValue factory for pool widgets (#1333) @@ -53,6 +55,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Avoid deadlock in Sardana-Taurus models e.g. `MeasurementGroup.count()` or `Motor.move()` (#1348) * Remove redundant protection in PoolElement.start() and waitFinish() +* Fast repetitions of single acqusition measurements (counts) on MeasurementGroup (#1353) +* Pre-mature returning to ON state of MeasurementGroup at the end of measurement (#1353) * Default macro parameter values in macroexecutor (#1153) * Executing RunMacro Door's command with string parameters containing spaces (#1240) * Setting of environment variables in Python 3.7 (#1195) @@ -97,6 +101,7 @@ This file follows the formats and conventions from [keepachangelog.com] (#1320, #1319 * Avoid repeating of positions when `regscan`, `reg2scan` and `reg3scan` pass through start position(s) (#1326) +* `test_stop_meas_cont_acquisition_3` spurious failures (#1188, #1353) * Build docs with Sphinx 3 (#1330) ### Deprecated From 3ed9e1362555874b87974dca6b9b7d4ee8a24f66 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 6 Jul 2020 23:47:26 +0200 Subject: [PATCH 689/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c338e382d8..5b123d9aba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,7 @@ This file follows the formats and conventions from [keepachangelog.com] ### Changed +* Avoid extra state readout at the end of acquisition (#1354) * Renamed _synchronization_ to _synch description_ * Tango MeasurementGroup `Synchronization` attribute to `SynchDescription` * Core MeasurementGroup `synchronization` property to `synch_description` From c6c4403a804424189b5c324c512120571f7622b9 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 17 Jul 2020 16:50:53 +0100 Subject: [PATCH 690/830] Replace deprecated getargspec with getfullargspec The old inspect.getargspec raises an error if it inspects an annotated callable (see #1366) --- src/sardana/macroserver/msmacromanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index b752027603..eccd95678f 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -115,7 +115,7 @@ def is_macro(macro, abs_file=None, logger=None): if not hasattr(macro, 'macro_data'): return False - args, varargs, keywords, _ = inspect.getargspec(macro) + args, varargs, keywords, *_ = inspect.getfullargspec(macro) if len(args) == 0: if logger: logger.debug("Could not add macro %s: Needs at least one " From 3637b8fa7a827e792ed1730135f3151902bb5844 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 17 Jul 2020 16:53:55 +0100 Subject: [PATCH 691/830] Protect is_macro against inspection errors If a member fails to be inspected, is_macro should return False. Previously it was raising the exception. This prevented an entire macro lib from being inspected. --- src/sardana/macroserver/msmacromanager.py | 34 +++++++++++++---------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index eccd95678f..f863b5a695 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -84,7 +84,7 @@ def islambda(f): f.__name__ == (lambda: True).__name__ -def is_macro(macro, abs_file=None, logger=None): +def _is_macro(macro, abs_file=None, logger=None): """Helper function to determine if a certain python object is a valid macro""" if inspect.isclass(macro): @@ -92,24 +92,21 @@ def is_macro(macro, abs_file=None, logger=None): return False # if it is a class defined in some other module forget it to # avoid replicating the same macro in different macro files - try: - # use normcase to treat case insensitivity of paths on - # certain platforms e.g. Windows - if os.path.normcase(inspect.getabsfile(macro)) !=\ - os.path.normcase(abs_file): - return False - except TypeError: + + # use normcase to treat case insensitivity of paths on + # certain platforms e.g. Windows + if os.path.normcase(inspect.getabsfile(macro)) !=\ + os.path.normcase(abs_file): return False + elif callable(macro) and not islambda(macro): # if it is a function defined in some other module forget it to # avoid replicating the same macro in different macro files - try: - # use normcase to treat case insensitivity of paths on - # certain platforms e.g. Windows - if os.path.normcase(inspect.getabsfile(macro)) !=\ - os.path.normcase(abs_file): - return False - except TypeError: + + # use normcase to treat case insensitivity of paths on + # certain platforms e.g. Windows + if os.path.normcase(inspect.getabsfile(macro)) !=\ + os.path.normcase(abs_file): return False if not hasattr(macro, 'macro_data'): @@ -138,6 +135,13 @@ def is_macro(macro, abs_file=None, logger=None): return True +def is_macro(macro, abs_file=None, logger=None): + try: + return _is_macro(macro, abs_file=abs_file, logger=logger) + except Exception: + return False + + def is_flat_list(obj): """Check if a given object is a flat list.""" if not isinstance(obj, list): From 02a5353b673325d8fa3ff236e0c3b82285f971a2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 19 Jul 2020 20:43:26 +0200 Subject: [PATCH 692/830] Adapt TaurusCounterTimerController to new LoadOne API --- src/sardana/pool/poolcontrollers/TaurusController.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/poolcontrollers/TaurusController.py b/src/sardana/pool/poolcontrollers/TaurusController.py index 6186e058b5..15f11c1431 100644 --- a/src/sardana/pool/poolcontrollers/TaurusController.py +++ b/src/sardana/pool/poolcontrollers/TaurusController.py @@ -48,7 +48,7 @@ def AddDevice(self, axis): def DeleteDevice(self, axis): self._axes_taurus_attr.pop(axis) - def LoadOne(self, axis, value): + def LoadOne(self, axis, value, repetitions, latency): pass def StateOne(self, axis): From 39dd8597c03a2dac8c8f832e77e46e3fcade4a9d Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 19 Jul 2020 20:44:49 +0200 Subject: [PATCH 693/830] Adapt DummyOneDController to new LoadOne API --- src/sardana/pool/poolcontrollers/DummyOneDController.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/poolcontrollers/DummyOneDController.py b/src/sardana/pool/poolcontrollers/DummyOneDController.py index d49c342ec9..88f4e38ac5 100644 --- a/src/sardana/pool/poolcontrollers/DummyOneDController.py +++ b/src/sardana/pool/poolcontrollers/DummyOneDController.py @@ -248,7 +248,7 @@ def StartOne(self, axis, value): def StartAll(self): self.start_time = time.time() - def LoadOne(self, axis, value, repetitions): + def LoadOne(self, axis, value, repetitions, latency_time): idx = axis - 1 if value > 0: self.integ_time = value From 1d70082801340f4f580aa6c9e65aa3f9a276f42e Mon Sep 17 00:00:00 2001 From: PhilIJC Date: Mon, 20 Jul 2020 09:55:55 +0000 Subject: [PATCH 694/830] typo in overview? the w seems to be some typo --- doc/source/users/overview.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/users/overview.rst b/doc/source/users/overview.rst index 3f9a774ab8..ac5e88907c 100644 --- a/doc/source/users/overview.rst +++ b/doc/source/users/overview.rst @@ -43,8 +43,8 @@ application should serve as a reminder of this final goal. :align: center :width: 500 -Inside this application we have many features common to other beamline control -applications or w some accelerator applications. The following screen shot +Inside this application we have many features common to other beamline control +applications or to some accelerator applications. The following screen shot shows such a standard application which has been done without programming - just by configuring the application. This illustrates one of the design guidelines in Sardana: Always provide a generic interface which can be From 4eb53fb6aab142f7dd2df6d09a81be88be300b63 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 20 Jul 2020 19:48:34 +0200 Subject: [PATCH 695/830] Remove back-compat for LoadOne API --- src/sardana/pool/poolacquisition.py | 35 ++--------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py index 8967989930..224fb6ce1f 100644 --- a/src/sardana/pool/poolacquisition.py +++ b/src/sardana/pool/poolacquisition.py @@ -886,43 +886,12 @@ def load(channel, value, repetitions, latency=0): pool_ctrl = channel.controller ctrl = pool_ctrl.ctrl ctrl.PreLoadAll() - try: - res = ctrl.PreLoadOne(axis, value, repetitions, - latency) - except TypeError: - try: - res = ctrl.PreLoadOne(axis, value, repetitions) - msg = ("PreLoadOne(axis, value, repetitions) is " - "deprecated since version 2.7.0." - "Use PreLoadOne(axis, value, repetitions, " - "latency_time) instead.") - self.warning(msg) - except TypeError: - res = ctrl.PreLoadOne(axis, value) - msg = ("PreLoadOne(axis, value) is deprecated since " - "version 2.3.0. Use PreLoadOne(axis, value, " - "repetitions, latency_time) instead.") - self.warning(msg) + res = ctrl.PreLoadOne(axis, value, repetitions, latency) if not res: msg = ("%s.PreLoadOne(%d) returned False" % (pool_ctrl.name, axis)) raise Exception(msg) - try: - ctrl.LoadOne(axis, value, repetitions, latency) - except TypeError: - try: - ctrl.LoadOne(axis, value, repetitions) - msg = ("LoadOne(axis, value, repetitions) is" - "deprecated since version Jan18." - "Use LoadOne(axis, value, repetitions, " - "latency_time) instead.") - self.warning(msg) - except TypeError: - ctrl.LoadOne(axis, value) - msg = ("LoadOne(axis, value) is deprecated since " - "version 2.3.0. Use LoadOne(axis, value, " - "repetitions) instead.") - self.warning(msg) + ctrl.LoadOne(axis, value, repetitions, latency) ctrl.LoadAll() with ActionContext(self): From 1af6f16212e15950ffd02d1b27d58b9d779577b9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 21 Jul 2020 16:14:29 +0200 Subject: [PATCH 696/830] Update LoadOne API in docs --- .../devel/howto_controllers/howto_countertimercontroller.rst | 4 ++-- doc/source/devel/howto_controllers/sf_ct_ctrl.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index 16d62f5270..6d4f751265 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -115,7 +115,7 @@ Here is an example of the possible implementation of class SpringfieldCounterTimerController(CounterTimerController): - def LoadOne(self, axis, value, repetitions): + def LoadOne(self, axis, value, repetitions, latency): self.springfield.LoadChannel(axis, value) .. _sardana-countertimercontroller-howto-value: @@ -474,7 +474,7 @@ Here is an example of the possible implementation of class SpringfieldCounterTimerController(CounterTimerController): - def LoadOne(self, axis, value, repetitions): + def LoadOne(self, axis, value, repetitions, latency): self.springfield.LoadChannel(axis, value) self.springfield.SetRepetitions(repetitions) return value diff --git a/doc/source/devel/howto_controllers/sf_ct_ctrl.py b/doc/source/devel/howto_controllers/sf_ct_ctrl.py index 5e3c64b469..7e5df8dd20 100644 --- a/doc/source/devel/howto_controllers/sf_ct_ctrl.py +++ b/doc/source/devel/howto_controllers/sf_ct_ctrl.py @@ -66,7 +66,7 @@ def StartOne(self, axis, value=None): """acquire the specified counter""" self.springfield.StartChannel(axis) - def LoadOne(self, axis, value, repetitions): + def LoadOne(self, axis, value, repetitions, latency): self.springfield.LoadChannel(axis, value) def StopOne(self, axis): @@ -112,7 +112,7 @@ def ReadOne(self, axis): value = self.springfield.getValue(axis) return value - def LoadOne(self, axis, value, repetitions): + def LoadOne(self, axis, value, repetitions, latency): self.springfield.LoadChannel(axis, value) def StartOne(self, axis, position): From db77d43ad295c448e732e221a084cedac1594ae9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 21 Jul 2020 17:41:35 +0200 Subject: [PATCH 697/830] Remove deprecated PQDN-FQDN back-compat in meas grp conf --- src/sardana/pool/poolmeasurementgroup.py | 38 +++---------------- .../pool/test/test_measurementgroup.py | 4 +- 2 files changed, 8 insertions(+), 34 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 8faa93e1db..8517ce414a 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -435,10 +435,7 @@ def __init__(self, parent=None): self._parent = None if parent is not None: self._parent = weakref.proxy(parent) - self._config = None - self._use_fqdn = True - # Structure to store the controllers and their channels self._timerable_ctrls = {} self._zerod_ctrls = [] @@ -600,7 +597,7 @@ def get_configuration_for_user(self): """Return measurement configuration serializable data structure.""" return self._user_config - def set_configuration_from_user(self, cfg, to_fqdn=True): + def set_configuration_from_user(self, cfg): """Set measurement configuration from serializable data structure. Setting of the configuration includes the validation process. Setting @@ -666,8 +663,6 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): if external: ctrl = ctrl_name else: - if to_fqdn: - ctrl_name = _to_fqdn(ctrl_name, logger=self._parent) ctrl = pool.get_element_by_full_name(ctrl_name) assert ctrl.get_type() == ElementType.Controller @@ -693,10 +688,6 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): ctrl_conf['synchronizer'] = 'software' user_config_ctrl['synchronizer'] = 'software' else: - if to_fqdn: - synchronizer = _to_fqdn(synchronizer, - logger=self._parent) - user_config_ctrl['synchronizer'] = synchronizer pool_synch = pool.get_element_by_full_name(synchronizer) pool_synch_ctrl = pool_synch.controller @@ -749,12 +740,8 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): synchronization) ctrl_acq_synch[ctrl] = acq_synch timer = ctrl_data.get("timer") - if timer is not None and to_fqdn: - timer = _to_fqdn(timer, self._parent) ctrl_conf["timer"] = timer monitor = ctrl_data.get("monitor") - if monitor is not None and to_fqdn: - monitor = _to_fqdn(monitor, self._parent) ctrl_conf["monitor"] = monitor ctrl_item = TimerableControllerConfiguration(ctrl, ctrl_conf) else: @@ -771,9 +758,6 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): params['pool'] = pool channel = PoolExternalObject(**params) else: - if to_fqdn: - ch_name = _to_fqdn(ch_name, logger=self._parent) - ch_data['full_name'] = ch_name channel = pool.get_element_by_full_name(ch_name) ch_data = self._fill_channel_data(channel, ch_data) user_config_channel[ch_name] = ch_data @@ -800,17 +784,11 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): msg_error = '' if ctrl_item.timer is None: timer_name = ctrl_data['timer'] - if to_fqdn: - timer_name = _to_fqdn(timer_name, - logger=self._parent) ch_timer = pool.get_element_by_full_name(timer_name) msg_error += 'Channel {0} is not present but used as ' \ 'timer. '.format(ch_timer.name) if ctrl_item.monitor is None: monitor_name = ctrl_data['monitor'] - if to_fqdn: - monitor_name = _to_fqdn(monitor_name, - logger=self._parent) ch_monitor = pool.get_element_by_full_name(monitor_name) msg_error += 'Channel {0} is not present but used as ' \ 'monitor.'.format(ch_monitor.name) @@ -882,8 +860,6 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): else: # Measurement Group with all channel synchronized by hardware mnt_grp_timer = cfg.get('timer') if mnt_grp_timer: - if to_fqdn: - mnt_grp_timer = _to_fqdn(mnt_grp_timer, self._parent) user_config['timer'] = mnt_grp_timer else: # for backwards compatibility use a random monitor @@ -896,8 +872,6 @@ def set_configuration_from_user(self, cfg, to_fqdn=True): else: # Measurement Group with all channel synchronized by hardware mnt_grp_monitor = cfg.get('monitor') if mnt_grp_monitor: - if to_fqdn: - mnt_grp_monitor = _to_fqdn(mnt_grp_monitor, self._parent) user_config['monitor'] = mnt_grp_monitor else: # for backwards compatibility use a random monitor @@ -1100,16 +1074,16 @@ def configuration(self): return self._config # TODO: Check if it needed - def set_configuration(self, config=None, propagate=1, to_fqdn=True): - self._config._use_fqdn = to_fqdn + def set_configuration(self, config=None, propagate=1): self._config.configuration = config self._config_dirty = True if not propagate: return - self.fire_event(EventType("configuration", priority=propagate), config) + self.fire_event(EventType("configuration", priority=propagate), + config) - def set_configuration_from_user(self, cfg, propagate=1, to_fqdn=True): - self._config.set_configuration_from_user(cfg, to_fqdn) + def set_configuration_from_user(self, cfg, propagate=1): + self._config.set_configuration_from_user(cfg) self._config_dirty = True if not propagate: return diff --git a/src/sardana/pool/test/test_measurementgroup.py b/src/sardana/pool/test/test_measurementgroup.py index c5a1aa1c03..49a147bfca 100644 --- a/src/sardana/pool/test/test_measurementgroup.py +++ b/src/sardana/pool/test/test_measurementgroup.py @@ -55,7 +55,7 @@ def prepare_meas(self, config): conf["user_elements"] = channel_ids self.pmg = createPoolMeasurementGroup(pool, conf) pool.add_element(self.pmg) - self.pmg.set_configuration_from_user(mg_conf, to_fqdn=False) + self.pmg.set_configuration_from_user(mg_conf) return channel_names def prepare_attribute_listener(self): @@ -152,7 +152,7 @@ def consecutive_acquisitions(self, pool, config, synch_description): pool, config) # setting mg configuration - this cleans the action cache! - self.pmg.set_configuration_from_user(mg_conf, to_fqdn=False) + self.pmg.set_configuration_from_user(mg_conf) repetitions = 0 for group in synch_description: repetitions += group[SynchParam.Repeats] From b5169b7e5a458fb038cb42ab4c24d99a60eaa5e3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 22 Jul 2020 17:56:32 +0200 Subject: [PATCH 698/830] Remove back-compat for meas grp start without preparation --- src/sardana/pool/poolmeasurementgroup.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 8faa93e1db..e3b9c48f51 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -1342,16 +1342,8 @@ def start_acquisition(self, value=None, multiple=1): ..todo:: remove value and multiple arguments. """ if self._pending_starts == 0: - msg = "starting acquisition without prior preparing is " \ - "deprecated since version Jan18." - self.warning(msg) - self.debug("Preparing with number_of_starts equal to 1") - nb_starts = self.nb_starts - self.set_nb_starts(1, propagate=0) - try: - self.prepare(multiple) - finally: - self.set_nb_starts(nb_starts, propagate=0) + msg = "prepare is mandatory before starting acquisition" + raise RuntimeError(msg) self._aborted = False self._pending_starts -= 1 if not self._simulation_mode: From 59aebded04bc496b6262b918a81eb335c4bb5545 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 22 Jul 2020 18:05:48 +0200 Subject: [PATCH 699/830] Remove deprecated ctrl_properties with "Description" as str --- src/sardana/pool/poolmetacontroller.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/sardana/pool/poolmetacontroller.py b/src/sardana/pool/poolmetacontroller.py index f0fe0aaf6a..6505b54627 100644 --- a/src/sardana/pool/poolmetacontroller.py +++ b/src/sardana/pool/poolmetacontroller.py @@ -269,17 +269,10 @@ def __init__(self, **kwargs): self.ctrl_properties = props = CaselessDict() self.ctrl_properties_descriptions = [] - dep_msg = ("Defining the controller property description using a " - + "string is deprecated, use " - + "sardana.pool.controller.Description constant instead.") - for k, v in list(klass.ctrl_properties.items()): props[k] = DataInfo.toDataInfo(k, v) if Description in v: self.ctrl_properties_descriptions.append(v[Description]) - elif 'Description' in v: - self.warning(dep_msg) - self.ctrl_properties_descriptions.append(v['Description']) self.dict_extra['properties'] = tuple(klass.ctrl_properties) self.dict_extra['properties_desc'] = self.ctrl_properties_descriptions From 4f7b22890c3123e293f41c1f877de605c83ef79b Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 23 Jul 2020 17:54:20 +0200 Subject: [PATCH 700/830] Remove deprecated DiscretePseudMotor Label and Calibration attributes --- .../DiscretePseudoMotorController.py | 135 ++---------------- 1 file changed, 8 insertions(+), 127 deletions(-) diff --git a/src/sardana/pool/poolcontrollers/DiscretePseudoMotorController.py b/src/sardana/pool/poolcontrollers/DiscretePseudoMotorController.py index c3ac6ef5ac..5dc2f7cd64 100644 --- a/src/sardana/pool/poolcontrollers/DiscretePseudoMotorController.py +++ b/src/sardana/pool/poolcontrollers/DiscretePseudoMotorController.py @@ -36,11 +36,6 @@ from sardana.pool.controller import PseudoMotorController from sardana.pool.controller import Type, Access, Description -CALIBRATION = 'Calibration' -LABELS = 'Labels' -MSG_API = 'Configuration attribute is in use. Labels and Calibration ' \ - 'attributes are deprecated since version 2.5.0' - class DiscretePseudoMotorController(PseudoMotorController): """ @@ -55,23 +50,7 @@ class DiscretePseudoMotorController(PseudoMotorController): pseudo_motor_roles = ("DiscreteMoveable",) motor_roles = ("ContinuousMoveable",) - axis_attributes = {CALIBRATION: # type hackish until arrays supported - {Type: str, - Description: 'Flatten list of a list of triples and ' - '[min,cal,max]. Deprecated since ' - 'version 2.5.0.', - Access: DataAccess.ReadWrite, - 'fget': 'get%s' % CALIBRATION, - 'fset': 'set%s' % CALIBRATION}, - LABELS: # type hackish until arrays supported - {Type: str, - Description: 'String list with the meaning of each ' - 'discrete position. Deprecated since ' - 'version 2.5.0.', - Access: DataAccess.ReadWrite, - 'fget': 'get%s' % LABELS, - 'fset': 'set%s' % LABELS}, - 'Configuration': + axis_attributes = {'Configuration': # type hackish until encoded attributes supported {Type: str, Description: 'String dictionary mapping the labels' @@ -96,15 +75,9 @@ def GetAxisAttributes(self, axis): return axis_attrs def CalcPseudo(self, axis, physical_pos, curr_pseudo_pos): - if self._configuration is not None: - positions = self._positions_cfg - calibration = self._calibration_cfg - labels = self._labels_cfg - else: - # TODO: Remove when we drop support to Labels and Calibration - positions = self._positions - calibration = self._calibration - labels = self._labels + positions = self._positions_cfg + calibration = self._calibration_cfg + labels = self._labels_cfg llabels = len(labels) lcalibration = len(calibration) @@ -135,16 +108,9 @@ def CalcPseudo(self, axis, physical_pos, curr_pseudo_pos): raise Exception("Bad configuration on axis attributes.") def CalcPhysical(self, axis, pseudo_pos, curr_physical_pos): - - if self._configuration is not None: - positions = self._positions_cfg - calibration = self._calibration_cfg - labels = self._labels_cfg - else: - # TODO: Remove when we drop support to Labels and Calibration - positions = self._positions - calibration = self._calibration - labels = self._labels + positions = self._positions_cfg + calibration = self._calibration_cfg + labels = self._labels_cfg # If Labels is well defined, the write value must be one this struct llabels = len(labels) @@ -176,93 +142,8 @@ def CalcPhysical(self, axis, pseudo_pos, curr_physical_pos): self._log.debug("calibrated_position = %s", calibrated_position) return calibrated_position - # TODO: Remove when we drop support to Labels and Calibration - def getLabels(self, axis): - if self._configuration is not None: - raise ValueError(MSG_API) - - self._log.warning("Labels attribute is deprecated since version " - "2.5.0. Use Configuration attribute instead.") - - # hackish until we support DevVarDoubleArray in extra attrs - labels = self._labels - positions = self._positions - labels_str = "" - for i in range(len(labels)): - labels_str += "%s:%d " % (labels[i], positions[i]) - return labels_str[:-1] # remove the final space - - # TODO: Remove when we drop support to Labels and Calibration - def setLabels(self, axis, value): - if self._configuration is not None: - raise ValueError(MSG_API) - - self._log.warning("Labels attribute is deprecated since version " - "2.5.0. Use Configuration attribute instead.") - - # hackish until we support DevVarStringArray in extra attrs - labels = [] - positions = [] - for pair in value.split(): - l, p = pair.split(':') - labels.append(l) - positions.append(int(p)) - if len(labels) == len(positions): - self._labels = labels - self._positions = positions - else: - raise Exception("Rejecting labels: invalid structure") - - # TODO: Remove when we drop support to Labels and Calibration - def getCalibration(self, axis): - if self._configuration is not None: - raise ValueError(MSG_API) - self._log.warning("Calibration attribute is deprecated since version " - "2.5.0. Use Configuration attribute instead.") - - return json.dumps(self._calibration) - - # TODO: Remove when we drop support to Labels and Calibration - def setCalibration(self, axis, value): - if self._configuration is not None: - raise ValueError(MSG_API) - self._log.warning("Calibration attribute is deprecated since version " - "2.5.0. Use Configuration attribute instead.") - - try: - self._calibration = json.loads(value) - except Exception: - raise Exception("Rejecting calibration: invalid structure") - def getConfiguration(self, axis): - if self._configuration is None: - # TODO: Remove when we drop support to Labels and Calibration - return self._getConfiguration() - else: - return json.dumps(self._configuration) - - # TODO: Remove when we drop support to Labels and Calibration - def _getConfiguration(self): - mapping = dict() - llab = len(self._labels) - lcal = len(self._calibration) - - if llab == 0: - return json.dumps(mapping) - elif lcal > 0 and lcal != llab: - msg = 'Calibration and Labels have different length' - raise RuntimeError(msg) - - for idx, label in enumerate(self._labels): - pos = self._positions[idx] - mapping[label] = {'pos': int(pos)} - if lcal > 0: - minimum, set, maximum = self._calibration[idx] - mapping[label]['set'] = set - mapping[label]['min'] = minimum - mapping[label]['max'] = maximum - - return json.dumps(mapping) + return json.dumps(self._configuration) def setConfiguration(self, axis, value): try: From d61c0d3ddd8043883929fe3558d3b51cd5bae23a Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 23 Jul 2020 18:00:47 +0200 Subject: [PATCH 701/830] Remove deprecated sardana.spock.release module --- src/sardana/spock/release.py | 70 ------------------------------------ 1 file changed, 70 deletions(-) delete mode 100644 src/sardana/spock/release.py diff --git a/src/sardana/spock/release.py b/src/sardana/spock/release.py deleted file mode 100644 index ec1a9cc1ee..0000000000 --- a/src/sardana/spock/release.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -############################################################################## -## -# This file is part of Sardana -## -# http://www.sardana-controls.org/ -## -# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -# Sardana 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. -## -# Sardana 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 Sardana. If not, see . -## -############################################################################## - -# DEPRECATED MODULE!!!! -# This module is deprecated avoid to use anything from it. -# Use sardana.release module instead -#### - -"""Release data for the Spock project. -""" - -# Name of the package for release purposes. This is the name which labels -# the tarballs and RPMs made by distutils, so it's best to lowercase it. -name = 'spock' - -# For versions with substrings (like 0.6.16.svn), use an extra . to separate -# the new substring. We have to avoid using either dashes or underscores, -# because bdist_rpm does not accept dashes (an RPM) convention, and -# bdist_deb does not accept underscores (a Debian convention). - -revision = '1' - -#version = '0.8.1.svn.r' + revision.rstrip('M') -version = '2.1.1' - -description = "An enhanced interactive Macro Server shell." - -long_description = \ - """ -Spock provides an interactive environment for interacting with the Tango -MacroServer Device. It is completely based on IPython which itself provides a -replacement for the interactive Python interpreter with extra functionality. - """ - -license = 'GNU' - -authors = {'Tiago': ('Tiago Coutinho', 'tiago.coutinho@esrf.fr'), - 'Reszela': ('Zbigniew Reszela', 'zreszela@cells.es'), - 'Pascual-Izarra': ('Carlos Pascual-Izarra', 'cpascual@cells.es')} - -url = '' - -download_url = '' - -platforms = ['Linux', 'Windows XP/2000/NT', 'Windows 95/98/ME'] - -keywords = ['Sardana', 'Interactive', 'MacroServer', 'Tango', 'Shell'] From bb98fa5093d847625e8e829b42724c37dd01bbf4 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 23 Jul 2020 18:18:06 +0200 Subject: [PATCH 702/830] Prepare measurement group in CTScan --- src/sardana/macroserver/scan/gscan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 6330825a2a..f13bac6d68 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2476,7 +2476,8 @@ def _go_through_waypoints(self): self.macro.checkPoint() self.macro.debug("Starting measurement group") - + self.measurement_group.setNbStarts(1) + self.measurement_group.prepare() mg_id = self.measurement_group.start() if i == 0: first_timestamp = time.time() From 830d5afaeda4153622682a412aaeaf53a036109a Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 24 Jul 2020 18:10:36 +0200 Subject: [PATCH 703/830] Prepare measurement group in tests --- src/sardana/tango/pool/test/test_measurementgroup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/tango/pool/test/test_measurementgroup.py b/src/sardana/tango/pool/test/test_measurementgroup.py index 4713360c6f..042db14541 100644 --- a/src/sardana/tango/pool/test/test_measurementgroup.py +++ b/src/sardana/tango/pool/test/test_measurementgroup.py @@ -220,6 +220,8 @@ def meas_cont_acquisition(self, params, config): self.prepare_meas(params) chn_names = self._add_attribute_listener(config) # Do acquisition + self.meas.write_attribute("NbStarts", 1) + self.meas.Prepare() self.meas.Start() while self.meas.State() == PyTango.DevState.MOVING: print("Acquiring...") @@ -250,6 +252,8 @@ def stop_meas_cont_acquisition(self, params, config): self.push_event) try: # starting timer (0.2 s) which will stop the measurement group + self.meas.write_attribute("NbStarts", 1) + self.meas.Prepare() self.meas.Start() threading.Timer(0.2, self.stopMeas).start() self.assertTrue(self.meas_finished.wait(5), "mg has not stopped") From e4e29508990705bf3dc89288617c38ed509526f8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 24 Jul 2020 19:17:28 +0200 Subject: [PATCH 704/830] Remove support to IPython < 1 --- src/sardana/spock/genutils.py | 18 +- src/sardana/spock/ipython_00_10/__init__.py | 27 - src/sardana/spock/ipython_00_10/genutils.py | 1173 ----------------- src/sardana/spock/ipython_00_11/__init__.py | 27 - src/sardana/spock/ipython_00_11/genutils.py | 1313 ------------------- 5 files changed, 5 insertions(+), 2553 deletions(-) delete mode 100644 src/sardana/spock/ipython_00_10/__init__.py delete mode 100644 src/sardana/spock/ipython_00_10/genutils.py delete mode 100644 src/sardana/spock/ipython_00_11/__init__.py delete mode 100644 src/sardana/spock/ipython_00_11/genutils.py diff --git a/src/sardana/spock/genutils.py b/src/sardana/spock/genutils.py index e1c8dcb60b..788581bc01 100644 --- a/src/sardana/spock/genutils.py +++ b/src/sardana/spock/genutils.py @@ -48,13 +48,7 @@ def get_ipython_version(): import IPython v = None try: - try: - v = IPython.Release.version - except: - try: - v = IPython.release.version - except: - pass + v = IPython.release.version except: pass return v @@ -64,10 +58,8 @@ def get_ipython_version_list(): ipv_str = get_ipython_version() return translate_version_str2list(ipv_str) + ipv = get_ipython_version_list() -if ipv >= [0, 10] and ipv < [0, 11]: - from .ipython_00_10.genutils import * # noqa -elif ipv >= [0, 11] and ipv < [1, 0]: - from .ipython_00_11.genutils import * # noqa -else: - from .ipython_01_00.genutils import * # noqa +if ipv < [1, 0]: + raise Exception("IPython > 1 is required") +from .ipython_01_00.genutils import * # noqa diff --git a/src/sardana/spock/ipython_00_10/__init__.py b/src/sardana/spock/ipython_00_10/__init__.py deleted file mode 100644 index 7d0819f924..0000000000 --- a/src/sardana/spock/ipython_00_10/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -############################################################################## -## -# This file is part of Sardana -## -# http://www.sardana-controls.org/ -## -# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -# Sardana 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. -## -# Sardana 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 Sardana. If not, see . -## -############################################################################## - -"""This package provides the spock generic utilities for ipython 0.10""" diff --git a/src/sardana/spock/ipython_00_10/genutils.py b/src/sardana/spock/ipython_00_10/genutils.py deleted file mode 100644 index 586e41d1d4..0000000000 --- a/src/sardana/spock/ipython_00_10/genutils.py +++ /dev/null @@ -1,1173 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -############################################################################## -## -# This file is part of Sardana -## -# http://www.sardana-controls.org/ -## -# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -# Sardana 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. -## -# Sardana 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 Sardana. If not, see . -## -############################################################################## - -"""This package provides the spock generic utilities""" - -__all__ = ['page', 'arg_split', 'get_gui_mode', 'get_pylab_mode', - 'get_color_mode', 'get_ipapi', - 'get_editor', 'ask_yes_no', 'spock_input', - 'translate_version_str2int', 'get_ipython_version', - 'get_ipython_version_number', 'get_python_version', - 'get_python_version_number', 'get_ipython_dir', - 'get_ipython_profiles', 'get_spock_profiles', - 'get_non_spock_profiles', 'get_spock_user_profile_module', - 'get_pytango_version', 'get_pytango_version_number', - 'get_server_for_device', 'get_macroserver_for_door', - 'get_device_from_user', 'get_tango_db', 'get_tango_host_from_user', - 'print_dev_from_class', 'from_name_to_tango', 'clean_up', - 'get_taurus_core_version', 'get_taurus_core_version_number', - 'check_requirements', 'get_door', 'get_macro_server', 'expose_magic', - 'unexpose_magic', 'expose_variable', 'expose_variables', - 'unexpose_variable', - 'create_spock_profile', 'check_for_upgrade', 'get_args', - 'init_console', 'init_magic', 'init_pre_spock', 'init_post_spock', - 'init_spock', 'start', 'mainloop', 'run', - 'load_ipython_extension', 'unload_ipython_extension', 'load_config', - 'MSG_FAILED', 'MSG_FAILED_WR', 'MSG_R', 'MSG_ERROR', - 'MSG_DONE', 'MSG_OK'] - -__docformat__ = 'restructuredtext' - -import sys -import os -import socket -import imp - -import IPython -import IPython.genutils - -try: - import PyTango - import itango -except ImportError: - # postpone error to check_requirements - pass - -from taurus.core.taurushelper import Factory -from taurus.core.util.codecs import CodecFactory - -from sardana.spock import exception -from sardana.spock import colors -from sardana import release - -arg_split = IPython.iplib.arg_split -page = IPython.genutils.page -TermColors = colors.TermColors - - -requirements = { - # module minimum recommended - "IPython": ("0.10.0", "0.10.0"), - "Python": ("2.6.0", "2.6.0"), - "PyTango": ("7.1.2", "7.2.0"), - # for the moment just for reference since itango does not provide version - # when using PyTango < 9 the dependency is >= 0.0.1 and < 0.1.0 - # when using PyTango >= 9 the dependency is >= 0.1.6 - "itango": ("0.0.1", "0.0.1"), - "taurus.core": ("2.0.0", "2.1.0") -} - -ENV_NAME = "_E" - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# IPython utilities -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def get_gui_mode(): - if '-q4thread' in sys.argv: - return 'qt' - elif '-gthread' in sys.argv: - return 'gtk' - elif '-wthread' in sys.argv: - return 'wx' - return '' - - -def get_pylab_mode(): - return get_gui_mode() - - -def get_color_mode(): - return get_ipapi().options.colors - - -def get_ipapi(): - return IPython.ipapi.get() - - -def get_editor(): - return get_ipapi().options.editor - - -def ask_yes_no(prompt, default=None): - """Asks a question and returns a boolean (y/n) answer. - - If default is given (one of 'y','n'), it is used if the user input is - empty. Otherwise the question is repeated until an answer is given. - - An EOF is treated as the default answer. If there is no default, an - exception is raised to prevent infinite loops. - - Valid answers are: y/yes/n/no (match is not case sensitive).""" - - if default: - prompt = '%s [%s]' % (prompt, default) - return IPython.genutils.ask_yes_no(prompt, default) - - -def spock_input(prompt='', ps2='... '): - return IPython.genutils.raw_input_ext(prompt=prompt, ps2=ps2) - - -def translate_version_str2int(version_str): - """Translates a version string in format x[.y[.z[...]]] into a 000000 number""" - import math - # Get the current version number ignoring the release part ("-alpha") - num_version_str = version_str.split('-')[0] - parts = num_version_str.split('.') - i, v, l = 0, 0, len(parts) - if not l: - return v - while i < 3: - try: - v += int(parts[i]) * int(math.pow(10, (2 - i) * 2)) - l -= 1 - i += 1 - except ValueError: - return v - if not l: - return v - return v - - try: - v += 10000 * int(parts[0]) - l -= 1 - except ValueError: - return v - if not l: - return v - - try: - v += 100 * int(parts[1]) - l -= 1 - except ValueError: - return v - if not l: - return v - - try: - v += int(parts[0]) - l -= 1 - except ValueError: - return v - if not l: - return v - - -def get_ipython_version(): - """Returns the current IPython version""" - v = None - try: - try: - v = IPython.Release.version - except Exception: - try: - v = IPython.release.version - except Exception: - pass - except Exception: - pass - return v - - -def get_ipython_version_number(): - """Returns the current IPython version number""" - ipyver_str = get_ipython_version() - if ipyver_str is None: - return None - return translate_version_str2int(ipyver_str) - - -def get_python_version(): - return '.'.join(map(str, sys.version_info[:3])) - - -def get_python_version_number(): - pyver_str = get_python_version() - return translate_version_str2int(pyver_str) - - -def get_ipython_dir(): - """Find the ipython local directory. Usually is /.ipython""" - if hasattr(itango, "get_ipython_dir"): - return itango.get_ipython_dir() - - if hasattr(IPython.iplib, 'get_ipython_dir'): - # Starting from ipython 0.9 they hadded this method - return IPython.iplib.get_ipython_dir() - - # Try to find the profile in the current directory and then in the - # default IPython dir - #userdir = os.path.realpath(os.path.curdir) - home_dir = IPython.genutils.get_home_dir() - - if os.name == 'posix': - ipdir = '.ipython' - else: - ipdir = '_ipython' - ipdir = os.path.join(home_dir, ipdir) - ipythondir = os.path.abspath(os.environ.get('IPYTHONDIR', ipdir)) - return ipythondir - - -def get_ipython_profiles(): - """Helper function to find all ipython profiles""" - if hasattr(itango, "get_ipython_profiles"): - return itango.get_ipython_profiles() - - ret = [] - ipydir = 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 - - -def get_spock_profiles(ipython_profiles=None): - """Helper function to find all spock ipython profiles""" - ret = [] - ipydir = get_ipython_dir() - if not os.path.isdir(ipydir): - return ret - if ipython_profiles is None: - ipython_profiles = get_ipython_profiles() - ret = [] - for profile in ipython_profiles: - profile_f = os.path.join(ipydir, "ipy_profile_%s.py" % profile) - if not os.path.isfile(profile_f): - continue - try: - for i, l in enumerate(open(profile_f)): - if i > 10: - break - if l.find("spock_creation_version") >= 0: - ret.append(profile) - break - except: - pass - return ret - - -def get_non_spock_profiles(ipython_profiles=None): - """Helper function to find all non spock ipython profiles""" - if ipython_profiles is None: - ipython_profiles = get_ipython_profiles() - ipython_profiles = set(ipython_profiles) - spock_profiles = set(get_spock_profiles(ipython_profiles=ipython_profiles)) - return ipython_profiles.difference(spock_profiles) - - -def get_spock_user_profile_module(profile_name): - return 'ipy_profile_%s' % profile_name - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# PyTango utilities -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def get_pytango_version(): - try: - import PyTango - except: - return None - try: - return PyTango.Release.version - except: - return '0.0.0' - - -def get_pytango_version_number(): - tgver_str = get_pytango_version() - if tgver_str is None: - return None - return translate_version_str2int(tgver_str) - - -def get_server_for_device(device_name): - db = get_tango_db() - device_name = device_name.lower() - server_list = db.get_server_list() - for server in server_list: - for dev in db.get_device_class_list(server)[::2]: - if dev.lower() == device_name: - return server - return None - - -def get_macroserver_for_door(door_name): - """Returns the MacroServer device name in the same DeviceServer as the - given door device""" - full_door_name, door_name, door_alias = from_name_to_tango(door_name) - db = get_tango_db() - door_name = door_name.lower() - server_list = list(db.get_server_list('MacroServer/*')) - server_list += list(db.get_server_list('Sardana/*')) - server_devs = None - for server in server_list: - server_devs = db.get_device_class_list(server) - devs, klasses = server_devs[0::2], server_devs[1::2] - for dev in devs: - if dev.lower() == door_name: - for i, klass in enumerate(klasses): - if klass == 'MacroServer': - return "%s:%s/%s" % (db.get_db_host(), db.get_db_port(), devs[i]) - else: - return None - - -def get_device_from_user(expected_class, dft=None): - """Gets a device of the given device class from user input""" - dft = print_dev_from_class(expected_class, dft) - prompt = "%s name from the list" % expected_class - if not dft is None: - prompt += "[%s]" % dft - prompt += "? " - from_user = input(prompt).strip() or dft - - name = '' - try: - full_name, name, alias = from_name_to_tango(from_user) - except: - print("Warning: the given %s does not exist" % expected_class) - return name - - try: - db = get_tango_db() - cl_name = db.get_class_for_device(name) - class_correct = cl_name == expected_class - if not class_correct: - print("Warning: the given name is not a %s (it is a %s)" % - (expected_class, cl_name)) - except Exception as e: - print("Warning: unable to confirm if '%s' is valid" % name) - print(str(e)) - return full_name - - -def get_tango_db(): - tg_host = PyTango.ApiUtil.get_env_var("TANGO_HOST") - - db = None - if tg_host is None: - host, port = get_tango_host_from_user() - tg_host = "%s:%d" % (host, port) - os.environ["TANGO_HOST"] = tg_host - db = PyTango.Database() - else: - try: - db = PyTango.Database() - except: - # tg host is not valid. Find a valid one - host, port = get_tango_host_from_user() - tg_host = "%s:%d" % (host, port) - os.environ["TANGO_HOST"] = tg_host - - db = PyTango.Database() - return db - - -def get_tango_host_from_user(): - - while True: - prompt = "Please enter a valid tango host (:): " - from_user = input(prompt).strip() - - try: - host, port = from_user.split(':') - try: - port = int(port) - try: - socket.gethostbyname(host) - try: - db = PyTango.Database(host, port) - return db.get_db_host(), db.get_db_port() - except: - exp = "No tango database found at %s:%d" % (host, port) - except: - exp = "Invalid host name %s" % host - except: - exp = "Port must be a number > 0" - except: - exp = "Invalid tango host. Must be in format :" - exp = "Invalid tango host. %s " % exp - print(exp) - - -def print_dev_from_class(classname, dft=None): - - db = get_tango_db() - pytg_ver = get_pytango_version_number() - if pytg_ver >= 0o30004: - server_wildcard = '*' - try: - exp_dev_list = db.get_device_exported_for_class(classname) - except: - exp_dev_list = [] - else: - server_wildcard = '%' - exp_dev_list = [] - - res = None - dev_list = list(db.get_device_name(server_wildcard, classname)) - tg_host = "%s:%s" % (db.get_db_host(), db.get_db_port()) - print("Available", classname, "devices from", tg_host, ":") - - list_devices_with_alias = [] - list_devices_with_no_alias = [] - for dev in dev_list: - _, name, alias = from_name_to_tango(dev) - if alias: - dev_alias_name = (alias, name) - list_devices_with_alias.append(dev_alias_name) - else: - dev_alias_name = ("", name) - list_devices_with_no_alias.append(dev_alias_name) - - list_devices_with_alias = sorted(list_devices_with_alias, - key=lambda s: s[0].lower()) - list_devices_with_no_alias = sorted(list_devices_with_no_alias, - key=lambda s: s[0].lower()) - ordered_devices_list = list_devices_with_alias + list_devices_with_no_alias - - for dev in ordered_devices_list: - dev_alias = dev[0] - dev_name = dev[1] - if dev_alias == "": - out = dev_name - else: - out = "%s (a.k.a. %s)" % (dev_alias, dev_name) - out = "%-25s" % out - if dev_name in exp_dev_list: - out += " (running)" - print(out) - - if dft: - if dft.lower() == name.lower(): - res = name - elif alias is not None and dft.lower() == alias.lower(): - res = alias - return res - - -def from_name_to_tango(name): - - db = get_tango_db() - - alias = None - - c = name.count('/') - # if the db prefix is there, remove it first - if c == 3 or c == 1: - name = name[name.index("/") + 1:] - - elems = name.split('/') - l = len(elems) - - if l == 3: - try: - alias = db.get_alias(name) - if alias.lower() == 'nada': - alias = None - except: - alias = None - elif l == 1: - alias = name - name = db.get_device_alias(alias) - else: - raise Exception("Invalid device name '%s'" % name) - - full_name = "%s:%s/%s" % (db.get_db_host(), db.get_db_port(), name) - return full_name, name, alias - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# taurus utilities -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def clean_up(): - taurus.Manager().cleanUp() - - -def get_taurus_core_version(): - try: - import taurus - return taurus.core.release.version - except: - return '0.0.0' - - -def get_taurus_core_version_number(): - tgver_str = get_taurus_core_version() - if tgver_str is None: - return None - return translate_version_str2int(tgver_str) - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# Requirements checking -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def check_requirements(): - r = requirements - minPyTango, recPyTango = list(map(translate_version_str2int, r["PyTango"])) - minIPython, recIPython = list(map(translate_version_str2int, r["IPython"])) - minPython, recPython = list(map(translate_version_str2int, r["Python"])) - minTaurusCore, recTaurusCore = list(map( - translate_version_str2int, r["taurus.core"])) - - currPython = get_python_version_number() - currIPython = get_ipython_version_number() - currPyTango = get_pytango_version_number() - currTaurusCore = get_taurus_core_version_number() - - errMsg = "" - warnMsg = "" - - errPython, errIPython, errPyTango, errTaurusCore = False, False, False, False - if currPython is None: - errMsg += "Spock needs Python version >= %s. No python installation found\n" % requirements[ - "Python"][0] - errPython = True - elif currPython < minPython: - errMsg += "Spock needs Python version >= %s. Current version is %s\n" % ( - requirements["Python"][0], get_python_version()) - errPython = True - - if currIPython is None: - errMsg += "Spock needs IPython version >= %s. No IPython installation found\n" % requirements[ - "IPython"][0] - errIPython = True - elif currIPython < minIPython: - errMsg += "Spock needs IPython version >= %s. Current version is %s\n" % ( - requirements["IPython"][0], get_ipython_version()) - errIPython = True - - if currPyTango is None: - errMsg += "Spock needs PyTango version >= %s. No PyTango installation found\n" % requirements[ - "IPython"][0] - errPyTango = True - elif currPyTango < minPyTango: - errMsg += "Spock needs PyTango version >= %s. " % requirements[ - "PyTango"][0] - if currPyTango > 0: - errMsg += "Current version is %s\n" % get_pytango_version() - else: - errMsg += "Current version is unknown (most surely too old)\n" - errPyTango = True - - # TODO: verify the version whenever itango starts to provide it - try: - import itango - except ImportError: - errMsg += "Spock needs itango version >= 0.0.1, < 0.1.0 (PyTango < 9) or version >= 0.1.6 (PyTanog >= 9). No itango installation found\n" - - if currTaurusCore is None: - errMsg += "Spock needs taurus.core version >= %s. No taurus.core installation found\n" % requirements[ - "taurus.core"][0] - errTaurusCore = True - elif currTaurusCore < minTaurusCore: - errMsg += "Spock needs taurus.core version >= %s. " % requirements[ - "taurus.core"][0] - if currTaurusCore > 0: - errMsg += "Current version is %s\n" % get_taurus_core_version() - else: - errMsg += "Current version is unknown (most surely too old)\n" - errTaurusCore = True - - # Warnings - if not errPython and currPython < recPython: - warnMsg += "Spock recommends Python version >= %s. Current version is %s\n" % ( - requirements["Python"][1], get_python_version()) - - if not errIPython and currIPython < recIPython: - warnMsg += "Spock recommends IPython version >= %s. Current version is %s\n" % ( - requirements["IPython"][1], get_ipython_version()) - - if not errPyTango and currPyTango < recPyTango: - warnMsg += "Spock recommends PyTango version >= %s. Current version is %s\n" % ( - requirements["PyTango"][1], get_pytango_version()) - - if not errTaurusCore and currTaurusCore < recTaurusCore: - warnMsg += "Spock recommends taurus.core version >= %s. Current version is %s\n" % ( - requirements["taurus.core"][1], get_taurus_core_version()) - - if errMsg: - errMsg += warnMsg - raise exception.SpockMissingRequirement(errMsg) - - if warnMsg: - raise exception.SpockMissingRecommended(warnMsg) - - return True - - -def _get_dev(dev_type): - ip = get_ipapi() - ret = ip.user_ns.get("_" + dev_type) - if ret is not None: - return ret - - dev_obj_name = '%s_NAME' % dev_type - # TODO: For Taurus 4 compatibility - dev_name = "tango://%s" % ip.user_ns[dev_obj_name] - factory = Factory() - dev_obj = factory.getDevice(dev_name) - ip.user_ns[dev_type] = PyTango.DeviceProxy(dev_name) - ip.user_ns["_" + dev_type] = dev_obj - setattr(ip, '_%s' % dev_type, dev_obj) - return dev_obj - - -def get_door(): - return _get_dev('DOOR') - - -def get_macro_server(): - return _get_dev('MACRO_SERVER') - - -def _macro_completer(self, event): - """Method called by the IPython autocompleter. It will determine possible - values for macro arguments. - """ - ms = get_macro_server() - - macro_name = event.command.lstrip('%') - - # calculate parameter index - param_idx = len(event.line.split()) - 1 - if not event.line.endswith(' '): - param_idx -= 1 - # get macro info - info = ms.getMacroInfoObj(macro_name) - # if macro doesn't have parameters return - if param_idx < 0 or not info.hasParams(): - return - # get the parameter info - possible_params = info.getPossibleParams(param_idx) - # return the existing elements for the given parameter type - if possible_params: - res = [] - for param in possible_params: - if param['type'].lower() == 'boolean': - res.extend(['True', 'False']) - else: - res.extend(ms.getElementNamesWithInterface(param['type'])) - return res - - -def expose_magic(name, fn, completer_func=_macro_completer): - ip = get_ipapi() - ip.expose_magic(name, fn) - - if completer_func is None: - return - - # enable macro param completion - ip.set_hook('complete_command', completer_func, str_key=name) - - # register also when the command as is typed with the magic prefix '%' - name = str('%') + name - ip.set_hook('complete_command', completer_func, str_key=name) - - -def unexpose_magic(name): - ip = get_ipapi() - mg = 'magic_%s' % name - delattr(ip.IP, mg) - - -def expose_variable(name, value): - get_ipapi().to_user_ns({name: value}) - - -def unexpose_variable(name): - user_ns = get_ipapi().user_ns - del user_ns[name] - - -def expose_variables(d): - get_ipapi().to_user_ns(d) - -# def _expose_device(name): -# ip.to_user_ns({ name : PyTango.DeviceProxy(name) }) - -# def expose_device(name): -# ip = get_ipapi() -# ip.magic("bg _expose_device(%s)" % name) - - -def create_spock_profile(userdir, dft_profile, profile, door_name=None): - """Create a profile file from a profile template file """ - - src_data = """\ -\"\"\"Settings for Spock session\"\"\" - -# -# Please do not delete the next lines has they are used to check the version -# number for possible upgrades -# spock_creation_version = {version} -# door_name = {door_name} -# - -import IPython -from sardana.spock.genutils import init_spock - -def main(): - ip = IPython.ipapi.get() - init_spock(ip, '{macroserver_name}', '{door_name}') - -main() -""" - - # - # Discover door name - # - if door_name is None: - door_name = get_device_from_user("Door", profile) - else: - full_door_name, door_name, door_alias = from_name_to_tango(door_name) - door_name = full_door_name - - # - # Discover macro server name - # - ms_name = get_macroserver_for_door(door_name) - - dest_data = src_data.format(version=release.version, - macroserver_name=ms_name, - door_name=door_name) - - f_name = '%s.py' % get_spock_user_profile_module(profile) - - dest_name = os.path.join(userdir, f_name) - - sys.stdout.write('Storing %s in %s... ' % (f_name, userdir)) - sys.stdout.flush() - res = MSG_FAILED - try: - dest = open(dest_name, "w") - dest.write(dest_data) - dest.flush() - dest.close() - res = MSG_DONE - finally: - sys.stdout.write(res + '\n') - sys.stdout.flush() - - -def check_for_upgrade(ipy_profile_file, ipythondir, session, profile): - # Check if the current profile is up to date with the spock version - spock_profile_ver_str = '0.0.0' - door_name = None - - # search for version and door inside the ipy_profile file - for i, line in enumerate(ipy_profile_file): - if i > 20: - break # give up after 20 lines - if line.startswith('# spock_creation_version = '): - spock_profile_ver_str = line[line.index('=') + 1:].strip() - if line.startswith('# door_name = '): - door_name = line[line.index('=') + 1:].strip() - - # convert version from string to numbers - spock_lib_ver_str = release.version - spocklib_ver = translate_version_str2int(spock_lib_ver_str) - spock_profile_ver = translate_version_str2int(spock_profile_ver_str) - - alpha_in_spock_profile = "-alpha" in spock_profile_ver_str - alpha_in_spock_lib = "-alpha" in spock_lib_ver_str - if spocklib_ver == spock_profile_ver and \ - alpha_in_spock_profile == alpha_in_spock_lib: - return - if spocklib_ver < spock_profile_ver: - print('%sYour spock profile (%s) is newer than your spock version ' - '(%s)!' % (TermColors.Brown, spock_profile_ver_str, - spock_lib_ver_str)) - print('Please upgrade spock or delete the current profile %s' % - TermColors.Normal) - sys.exit(1) - - # there was no version track of spock profiles since spock 0.2.0 so change - # the message - if spock_profile_ver_str == '0.0.0': - spock_profile_ver_str = '<= 0.2.0' - print('Your current spock door extension profile has been created with ' - 'spock %s.\n' - 'Your current spock door extension version is %s, therefore a ' - 'profile upgrade is needed.\n' - % (spock_profile_ver_str, spock_lib_ver_str)) - prompt = ('Do you wish to upgrade now (warn: this will shutdown the ' - 'current spock session) ([y]/n)? ') - r = input(prompt) or 'y' - if r.lower() == 'y': - create_spock_profile(ipythondir, session, profile, door_name) - sys.exit(0) - - -def get_args(argv): - - script_name = argv[0] - script_dir, session = os.path.split(script_name) - script_name = os.path.realpath(script_name) - #script_dir = os.path.dirname(script_name) - - macro_server = None - door = None - - # Define the profile file - profile = "spockdoor" - try: - profile_idx = argv.index('-p') + 1 - if len(argv) > profile_idx: - profile = argv[profile_idx] - except: - pass - - profile_modulename = get_spock_user_profile_module(profile) - - # Try to find the profile in the current directory and then in the - # default IPython dir - #userdir = os.path.realpath(os.path.curdir) - ipythondir = get_ipython_dir() - - if not os.path.isdir(ipythondir): - # Platform-dependent suffix. - if os.name == 'posix': - rc_suffix = '' - else: - rc_suffix = '.ini' - IPython.iplib.user_setup(ipythondir, rc_suffix, mode='install', - interactive=False) - - try: - f, name, t = imp.find_module(profile_modulename, [ipythondir]) - check_for_upgrade(f, ipythondir, session, profile) - except ImportError: - # Create a new profile - r = '' - while not r in ['y', 'n']: - prompt = 'Profile \'%s\' does not exist. Do you want to create '\ - 'one now ([y]/n)? ' % profile - r = input(prompt) or 'y' - if r.lower() == 'y': - create_spock_profile(ipythondir, session, profile) - else: - sys.stdout.write( - 'No spock door extension profile was created. Starting normal spock...\n') - sys.stdout.flush() - profile = '' - - # inform the shell of the profile it should use - if not '-p' in argv and profile: - argv.append('-p') - argv.append(profile) - - user_ns = {'MACRO_SERVER_NAME': macro_server, - 'DOOR_NAME': door, - 'PROFILE': profile} - - return user_ns - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# Useful constants -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - -MSG_G = '[%s%%s%s]' % (TermColors.Green, TermColors.Normal) -MSG_R = '[%s%%s%s]' % (TermColors.Red, TermColors.Normal) -MSG_FAILED = MSG_R % 'FAILED' -MSG_FAILED_WR = MSG_R % 'FAILED: %s' -MSG_ERROR = MSG_R % 'ERROR' -MSG_DONE = MSG_G % 'DONE' -MSG_OK = MSG_G % 'OK' - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# initialization methods -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def init_console(ip): - # Handy tab-completers for %cd, %run, import etc. - # Try commenting this out if you have completion problems/slowness - import ipy_stock_completers - - spockver = release.version - pyver = get_python_version() - ipyver = get_ipython_version() - pytangover = get_pytango_version() - tauruscorever = get_taurus_core_version() - - TermColors = IPython.ColorANSI.TermColors - - d = {"version": spockver, - "pyver": pyver, - "ipyver": ipyver, - "pytangover": pytangover, - "taurusver": tauruscorever, - "profile": ip.user_ns["PROFILE"], - "door": ip.user_ns["DOOR_ALIAS"]} - - d.update(TermColors.__dict__) - - # IPython options - o = ip.options - o.autocall = 1 - o.autoedit_syntax = 0 - o.autoindent = 1 - o.automagic = 1 - o.cache_size = 1000 - o.colors = 'Linux' - o.color_info = 1 - o.confirm_exit = 0 - o.deep_reload = 0 - #o.editor = 'gedit' - o.log = 0 - o.logfile = '' - o.messages = 1 - o.pdb = 0 - o.pprint = 1 - o.quick = 0 - o.readline = 1 - o.screen_length = 0 - o.separate_in = '\n' - o.separate_out = '\n' - o.separate_out2 = '' - o.nosep = 0 - o.wildcards_case_sensitive = 0 - o.object_info_string_level = 0 - o.xmode = 'Context' - o.multi_line_specials = 1 - o.system_header = 'IPython system call: ' - o.system_verbose = 1 - o.wxversion = '0' - o.colors = "GreenTango" - o.prompt_in1 = "$DOOR_ALIAS$DOOR_STATE [\\#]: " - o.prompt_out = "Result [\\#]: " - o.readline_parse_and_bind.append('tab: complete') - #o.readline_parse_and_bind.append('tab: menu-complete') - o.readline_parse_and_bind.append('"\C-l": possible-completions') - o.readline_parse_and_bind.append('set show-all-if-ambiguous on') - o.readline_parse_and_bind.append('"\C-o": tab-insert') - o.readline_parse_and_bind.append('"\M-i": " "') - o.readline_parse_and_bind.append('"\M-o": "\d\d\d\d"') - o.readline_parse_and_bind.append('"\M-I": "\d\d\d\d"') - o.readline_parse_and_bind.append('"\C-r": reverse-search-history') - o.readline_parse_and_bind.append('"\C-s": forward-search-history') - o.readline_parse_and_bind.append('"\C-p": history-search-backward') - o.readline_parse_and_bind.append('"\C-n": history-search-forward') - o.readline_parse_and_bind.append('"\e[A": history-search-backward') - o.readline_parse_and_bind.append('"\e[B": history-search-forward') - o.readline_parse_and_bind.append('"\C-k": kill-line') - o.readline_parse_and_bind.append('"\C-u": unix-line-discard') - o.readline_remove_delims = '-/~' - o.readline_merge_completions = 1 - o.readline_omit__names = 0 - - banner = """\ -%(Purple)sSpock %(version)s%(Normal)s -- An interactive laboratory application. - -help -> Spock's help system. -object? -> Details about 'object'. ?object also works, ?? prints more. -""" - banner = banner % d - banner = banner.format(**d) - - o.banner = banner - - -def init_magic(ip): - import sardana.spock.magic - magic = sardana.spock.magic - expose_magic('debug', magic.debug, magic.debug_completer) - expose_magic('www', magic.www, None) - expose_magic('post_mortem', magic.post_mortem, None) - expose_magic('spsplot', magic.spsplot, None) - expose_magic('macrodata', magic.macrodata, None) - expose_magic('edmac', magic.edmac, None) - expose_magic('showscan', magic.showscan, None) - expose_magic('expconf', magic.expconf, None) - ip.set_hook('late_startup_hook', magic.spock_late_startup_hook) - ip.set_hook('pre_prompt_hook', magic.spock_pre_prompt_hook) - - -def init_pre_spock(ip, macro_server, door): - so = IPython.ipstruct.Struct() - full_door_tg_name, door_tg_name, door_tg_alias = from_name_to_tango(door) - #macro_server = get_ms_for_door(door_tg_name) - full_ms_tg_name, ms_tg_name, ms_tg_alias = from_name_to_tango(macro_server) - ip.user_ns['MACRO_SERVER_NAME'] = full_ms_tg_name - ip.user_ns['MACRO_SERVER_ALIAS'] = ms_tg_alias or ms_tg_name - ip.user_ns['DOOR_NAME'] = full_door_tg_name - ip.user_ns['DOOR_ALIAS'] = door_tg_alias or door_tg_name - ip.user_ns['DOOR_STATE'] = "" - ip.user_ns['spock_options'] = so - - if 'mv' in ip.IP.alias_table: - del ip.IP.alias_table['mv'] - - v = release.version - alias = ip.user_ns['DOOR_ALIAS'] - profile = ip.user_ns['PROFILE'] - - so.spock_banner = """\ -{Blue}Spock's sardana extension %s loaded with profile: %s (linked to door '%s'){Normal} -""" % (v, profile, alias) - - # the CodecFactory is not thread safe. There are two attributes who will - # request for it in the first event at startup in different threads - # therefore this small hack: make sure CodecFactory is initialized. - CodecFactory() - - factory = Factory() - - import sardana.spock.spockms - macroserver = sardana.spock.spockms - - factory.registerDeviceClass('MacroServer', macroserver.SpockMacroServer) - - mode = get_gui_mode() - if mode == 'qt': - factory.registerDeviceClass('Door', macroserver.QSpockDoor) - else: - factory.registerDeviceClass('Door', macroserver.SpockDoor) - - door = get_door() - macro_server = get_macro_server() - - # Initialize the environment - expose_variable(ENV_NAME, macro_server.getEnvironment()) - - -def init_post_spock(ip): - init_console(ip) - init_magic(ip) - - -def init_spock(ip, macro_server, door): - init_pre_spock(ip, macro_server, door) - itango.init_ipython(ip) - init_post_spock(ip) - - -def start(user_ns=None): - if '-pylab' not in sys.argv: - sys.argv.insert(1, '-pylab') - if '-q4thread' not in sys.argv: - sys.argv.insert(1, '-q4thread') - - # Make sure the log level is changed to warning - from taurus.core.taurushelper import setLogLevel, Warning - CodecFactory() - setLogLevel(Warning) - - try: - check_requirements() - except exception.SpockMissingRequirement as requirement: - print(str(requirement)) - sys.exit(-1) - except exception.SpockMissingRecommended as recommended: - print(str(recommended)) - - user_ns = user_ns or {} - try: - user_ns.update(get_args(sys.argv)) - except exception.SpockException as e: - print(e) - print('Starting normal IPython console') - except KeyboardInterrupt: - print("\nUser pressed Ctrl+C. Exiting...") - sys.exit() - except Exception as e: - print('spock exited with an unmanaged exception: %s' % str(e)) - sys.exit(-2) - - return IPython.Shell.start(user_ns=user_ns) - - -def mainloop(shell=None, user_ns=None): - if shell is None: - shell = start(user_ns) - shell.mainloop() - - -def run(user_ns=None): - - # TODO: Temporary solution, available while Taurus3 is being supported. - from taurus import tauruscustomsettings - from sardana import sardanacustomsettings - max_counts = getattr(sardanacustomsettings, - 'TAURUS_MAX_DEPRECATION_COUNTS', 0) - tauruscustomsettings._MAX_DEPRECATIONS_LOGGED = max_counts - # - - # initialize input handler as soon as possible - import sardana.spock.inputhandler - input_handler = sardana.spock.inputhandler.InputHandler() - - try: - mainloop(user_ns=user_ns) - finally: - try: - clean_up() - except Exception: - pass - - # TODO: Temporary solution, available while Taurus3 is being supported. - try: - from taurus.core.util.log import _DEPRECATION_COUNT - from taurus import info - info('\n*********************\n%s', _DEPRECATION_COUNT.pretty()) - except: - pass - -# for compatibility reasons with new IPython API (>=0.11) we add the following -# empty methods - - -def load_ipython_extension(ipython): - pass - - -def unload_ipython_extension(ipython): - pass - - -def load_config(config): - pass diff --git a/src/sardana/spock/ipython_00_11/__init__.py b/src/sardana/spock/ipython_00_11/__init__.py deleted file mode 100644 index 75fadda7c9..0000000000 --- a/src/sardana/spock/ipython_00_11/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -############################################################################## -## -# This file is part of Sardana -## -# http://www.sardana-controls.org/ -## -# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -# Sardana 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. -## -# Sardana 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 Sardana. If not, see . -## -############################################################################## - -"""This package provides the spock generic utilities for ipython > 0.10""" diff --git a/src/sardana/spock/ipython_00_11/genutils.py b/src/sardana/spock/ipython_00_11/genutils.py deleted file mode 100644 index ad1931bb86..0000000000 --- a/src/sardana/spock/ipython_00_11/genutils.py +++ /dev/null @@ -1,1313 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -############################################################################## -## -# This file is part of Sardana -## -# http://www.sardana-controls.org/ -## -# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -# Sardana 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. -## -# Sardana 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 Sardana. If not, see . -## -############################################################################## - -"""This package provides the spock generic utilities""" - -__all__ = ['page', 'arg_split', 'get_gui_mode', 'get_pylab_mode', - 'get_color_mode', 'get_app', - 'get_shell', 'get_ipapi', 'get_config', 'get_editor', 'ask_yes_no', - 'spock_input', - 'translate_version_str2int', 'get_ipython_version', - 'get_ipython_version_number', 'get_python_version', - 'get_python_version_number', 'get_ipython_dir', - 'get_ipython_profiles', - 'get_pytango_version', 'get_pytango_version_number', - 'get_server_for_device', 'get_macroserver_for_door', - 'get_device_from_user', 'get_tango_db', 'get_tango_host_from_user', - 'print_dev_from_class', 'from_name_to_tango', 'clean_up', - 'get_taurus_core_version', 'get_taurus_core_version_number', - 'check_requirements', 'get_door', 'get_macro_server', - 'expose_magic', 'unexpose_magic', 'expose_variable', - 'expose_variables', 'unexpose_variable', - 'create_spock_profile', 'check_for_upgrade', 'get_args', - 'start', 'mainloop', 'run', - 'load_ipython_extension', 'unload_ipython_extension', 'load_config', - 'MSG_FAILED', 'MSG_FAILED_WR', 'MSG_R', 'MSG_ERROR', - 'MSG_DONE', 'MSG_OK'] - -__docformat__ = 'restructuredtext' - -import sys -import os -import socket - -import IPython -import IPython.core.magic -from IPython.core.page import page -from IPython.core.profiledir import ProfileDirError, ProfileDir -from IPython.core.application import BaseIPythonApplication -from IPython.core.interactiveshell import InteractiveShell -from IPython.utils.io import ask_yes_no as _ask_yes_no -from IPython.utils.io import raw_input_ext as _raw_input_ext -from IPython.utils.path import get_ipython_dir -from IPython.utils.process import arg_split -from IPython.utils.coloransi import TermColors -from IPython.config.application import Application -from IPython.frontend.terminal.ipapp import TerminalIPythonApp, \ - launch_new_instance - -from taurus.core.taurushelper import Factory, Manager, Warning -from taurus.core.util.codecs import CodecFactory -from taurus.core.taurushelper import setLogLevel - - -# make sure Qt is properly initialized -from taurus.external.qt import Qt - -from sardana.spock import exception -from sardana.spock import colors -from sardana import release - -SpockTermColors = colors.TermColors - -requirements = { - # module minimum recommended - "IPython": ("0.11.0", "0.12.0"), - "Python": ("2.6.0", "2.6.0"), - "PyTango": ("7.2.0", "7.2.3"), - # for the moment just for reference since itango does not provide version - # when using PyTango < 9 the dependency is >= 0.0.1 and < 0.1.0 - # when using PyTango >= 9 the dependency is >= 0.1.6 - "itango": ("0.0.1", "0.0.1"), - "taurus.core": ("3.0.0", "3.0.0") -} - -ENV_NAME = "_E" - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# IPython utilities -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def get_gui_mode(): - return 'qt' - - -def get_pylab_mode(): - return get_app().pylab - - -def get_color_mode(): - return get_config().InteractiveShell.colors - - -def get_app(): - # return TerminalIPythonApp.instance() - return Application.instance() - - -def get_shell(): - """Get the global InteractiveShell instance.""" - return get_app().shell - - -def get_ipapi(): - """Get the global InteractiveShell instance.""" - return InteractiveShell.instance() - - -def get_config(): - return get_app().config - - -def get_editor(): - return get_ipapi().editor - - -def ask_yes_no(prompt, default=None): - """Asks a question and returns a boolean (y/n) answer. - - If default is given (one of 'y','n'), it is used if the user input is - empty. Otherwise the question is repeated until an answer is given. - - An EOF is treated as the default answer. If there is no default, an - exception is raised to prevent infinite loops. - - Valid answers are: y/yes/n/no (match is not case sensitive).""" - - if default: - prompt = '%s [%s]' % (prompt, default) - return _ask_yes_no(prompt, default) - - -def spock_input(prompt='', ps2='... '): - return _raw_input_ext(prompt=prompt, ps2=ps2) - - -def translate_version_str2int(version_str): - """Translates a version string in format x[.y[.z[...]]] into a 000000 number""" - import math - # Get the current version number ignoring the release part ("-alpha") - num_version_str = version_str.split('-')[0] - parts = num_version_str.split('.') - i, v, l = 0, 0, len(parts) - if not l: - return v - while i < 3: - try: - v += int(parts[i]) * int(math.pow(10, (2 - i) * 2)) - l -= 1 - i += 1 - except ValueError: - return v - if not l: - return v - return v - - try: - v += 10000 * int(parts[0]) - l -= 1 - except ValueError: - return v - if not l: - return v - - try: - v += 100 * int(parts[1]) - l -= 1 - except ValueError: - return v - if not l: - return v - - try: - v += int(parts[0]) - l -= 1 - except ValueError: - return v - if not l: - return v - - -def get_ipython_version(): - """Returns the current IPython version""" - v = None - try: - try: - v = IPython.Release.version - except Exception: - try: - v = IPython.release.version - except Exception as e2: - print(e2) - except Exception as e3: - print(e3) - return v - - -def get_ipython_version_number(): - """Returns the current IPython version number""" - ipyver_str = get_ipython_version() - if ipyver_str is None: - return None - return translate_version_str2int(ipyver_str) - - -def get_python_version(): - return '.'.join(map(str, sys.version_info[:3])) - - -def get_python_version_number(): - pyver_str = get_python_version() - return translate_version_str2int(pyver_str) - - -def get_ipython_profiles(path=None): - """list profiles in a given root directory""" - if path is None: - path = get_ipython_dir() - files = os.listdir(path) - profiles = [] - for f in files: - full_path = os.path.join(path, f) - if os.path.isdir(full_path) and f.startswith('profile_'): - profiles.append(f.split('_', 1)[-1]) - return profiles - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# PyTango utilities -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def get_pytango_version(): - try: - import PyTango - try: - return PyTango.Release.version - except: - return '0.0.0' - except: - return None - - -def get_pytango_version_number(): - tgver_str = get_pytango_version() - if tgver_str is None: - return None - return translate_version_str2int(tgver_str) - - -def get_server_for_device(device_name): - db = get_tango_db() - device_name = device_name.lower() - server_list = db.get_server_list() - for server in server_list: - for dev in db.get_device_class_list(server)[::2]: - if dev.lower() == device_name: - return server - return None - - -def get_macroserver_for_door(door_name): - """Returns the MacroServer device name in the same DeviceServer as the - given door device""" - _, door_name, _ = from_name_to_tango(door_name) - db = get_tango_db() - door_name = door_name.lower() - server_list = list(db.get_server_list('MacroServer/*')) - server_list += list(db.get_server_list('Sardana/*')) - server_devs = None - for server in server_list: - server_devs = db.get_device_class_list(server) - devs, klasses = server_devs[0::2], server_devs[1::2] - for dev in devs: - if dev.lower() == door_name: - for i, klass in enumerate(klasses): - if klass == 'MacroServer': - return "%s:%s/%s" % (db.get_db_host(), db.get_db_port(), devs[i]) - else: - return None - - -def get_device_from_user(expected_class, dft=None): - """Gets a device of the given device class from user input""" - dft = print_dev_from_class(expected_class, dft) - prompt = "%s name from the list" % expected_class - if not dft is None: - prompt += "[%s]" % dft - prompt += "? " - from_user = input(prompt).strip() or dft - - name = '' - try: - full_name, name, _ = from_name_to_tango(from_user) - except: - print("Warning: the given %s does not exist" % expected_class) - return name - - try: - db = get_tango_db() - cl_name = db.get_class_for_device(name) - class_correct = cl_name == expected_class - if not class_correct: - print("Warning: the given name is not a %s (it is a %s)" % - (expected_class, cl_name)) - except Exception as e: - print("Warning: unable to confirm if '%s' is valid" % name) - print(str(e)) - return full_name - - -def get_tango_db(): - import PyTango - tg_host = PyTango.ApiUtil.get_env_var("TANGO_HOST") - - db = None - if tg_host is None: - host, port = get_tango_host_from_user() - tg_host = "%s:%d" % (host, port) - os.environ["TANGO_HOST"] = tg_host - db = PyTango.Database() - else: - try: - db = PyTango.Database() - except: - # tg host is not valid. Find a valid one - host, port = get_tango_host_from_user() - tg_host = "%s:%d" % (host, port) - os.environ["TANGO_HOST"] = tg_host - - db = PyTango.Database() - return db - - -def get_tango_host_from_user(): - import PyTango - while True: - prompt = "Please enter a valid tango host (:): " - from_user = input(prompt).strip() - - try: - host, port = from_user.split(':') - try: - port = int(port) - try: - socket.gethostbyname(host) - try: - PyTango.Database(host, port) - return (host, port) - except: - exp = "No tango database found at %s:%d" % (host, port) - except: - exp = "Invalid host name %s" % host - except: - exp = "Port must be a number > 0" - except: - exp = "Invalid tango host. Must be in format :" - exp = "Invalid tango host. %s " % exp - print(exp) - - -def print_dev_from_class(classname, dft=None): - - db = get_tango_db() - pytg_ver = get_pytango_version_number() - if pytg_ver >= 0o30004: - server_wildcard = '*' - try: - exp_dev_list = db.get_device_exported_for_class(classname) - except: - exp_dev_list = [] - else: - server_wildcard = '%' - exp_dev_list = [] - - res = None - dev_list = list(db.get_device_name(server_wildcard, classname)) - tg_host = "%s:%s" % (db.get_db_host(), db.get_db_port()) - print("Available", classname, "devices from", tg_host, ":") - - list_devices_with_alias = [] - list_devices_with_no_alias = [] - for dev in dev_list: - _, name, alias = from_name_to_tango(dev) - if alias: - dev_alias_name = (alias, name) - list_devices_with_alias.append(dev_alias_name) - else: - dev_alias_name = ("", name) - list_devices_with_no_alias.append(dev_alias_name) - - list_devices_with_alias = sorted(list_devices_with_alias, - key=lambda s: s[0].lower()) - list_devices_with_no_alias = sorted(list_devices_with_no_alias, - key=lambda s: s[0].lower()) - ordered_devices_list = list_devices_with_alias + list_devices_with_no_alias - - for dev in ordered_devices_list: - dev_alias = dev[0] - dev_name = dev[1] - if dev_alias == "": - out = dev_name - else: - out = "%s (a.k.a. %s)" % (dev_alias, dev_name) - out = "%-25s" % out - if dev_name in exp_dev_list: - out += " (running)" - print(out) - - if dft: - if dft.lower() == name.lower(): - res = name - elif alias is not None and dft.lower() == alias.lower(): - res = alias - return res - - -def from_name_to_tango(name): - - db = get_tango_db() - - alias = None - - c = name.count('/') - # if the db prefix is there, remove it first - if c == 3 or c == 1: - name = name[name.index("/") + 1:] - - elems = name.split('/') - l = len(elems) - - if l == 3: - try: - alias = db.get_alias(name) - if alias.lower() == 'nada': - alias = None - except: - alias = None - elif l == 1: - alias = name - name = db.get_device_alias(alias) - else: - raise Exception("Invalid device name '%s'" % name) - - full_name = "%s:%s/%s" % (db.get_db_host(), db.get_db_port(), name) - return full_name, name, alias - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# taurus utilities -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def clean_up(): - Manager().cleanUp() - - -def get_taurus_core_version(): - try: - import taurus - return taurus.core.release.version - except: - import traceback - traceback.print_exc() - return '0.0.0' - - -def get_taurus_core_version_number(): - tgver_str = get_taurus_core_version() - if tgver_str is None: - return None - return translate_version_str2int(tgver_str) - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# Requirements checking -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def check_requirements(): - r = requirements - minPyTango, recPyTango = list(map(translate_version_str2int, r["PyTango"])) - minIPython, recIPython = list(map(translate_version_str2int, r["IPython"])) - minPython, recPython = list(map(translate_version_str2int, r["Python"])) - minTaurusCore, recTaurusCore = list(map( - translate_version_str2int, r["taurus.core"])) - - currPython = get_python_version_number() - currIPython = get_ipython_version_number() - currPyTango = get_pytango_version_number() - currTaurusCore = get_taurus_core_version_number() - - errMsg = "" - warnMsg = "" - - errPython, errIPython, errPyTango, errTaurusCore = False, False, False, False - if currPython is None: - errMsg += "Spock needs Python version >= %s. No python installation found\n" % requirements[ - "Python"][0] - errPython = True - elif currPython < minPython: - errMsg += "Spock needs Python version >= %s. Current version is %s\n" % ( - requirements["Python"][0], get_python_version()) - errPython = True - - if currIPython is None: - errMsg += "Spock needs IPython version >= %s. No IPython installation found\n" % requirements[ - "IPython"][0] - errIPython = True - elif currIPython < minIPython: - errMsg += "Spock needs IPython version >= %s. Current version is %s\n" % ( - requirements["IPython"][0], get_ipython_version()) - errIPython = True - - if currPyTango is None: - errMsg += "Spock needs PyTango version >= %s. No PyTango installation found\n" % requirements[ - "IPython"][0] - errPyTango = True - elif currPyTango < minPyTango: - errMsg += "Spock needs PyTango version >= %s. " % requirements[ - "PyTango"][0] - if currPyTango > 0: - errMsg += "Current version is %s\n" % get_pytango_version() - else: - errMsg += "Current version is unknown (most surely too old)\n" - errPyTango = True - - # TODO: verify the version whenever itango starts to provide it - try: - import itango - except ImportError: - errMsg += "Spock needs itango version >= 0.0.1, < 0.1.0 (PyTango < 9) or version >= 0.1.6 (PyTanog >= 9). No itango installation found\n" - - if currTaurusCore is None: - errMsg += "Spock needs taurus.core version >= %s. No taurus.core installation found\n" % requirements[ - "taurus.core"][0] - errTaurusCore = True - elif currTaurusCore < minTaurusCore: - errMsg += "Spock needs taurus.core version >= %s. " % requirements[ - "taurus.core"][0] - if currTaurusCore > 0: - errMsg += "Current version is %s\n" % get_taurus_core_version() - else: - errMsg += "Current version is unknown (most surely too old)\n" - errTaurusCore = True - - # Warnings - if not errPython and currPython < recPython: - warnMsg += "Spock recommends Python version >= %s. Current version " \ - "is %s\n" % (requirements["Python"][1], - get_python_version()) - - if not errIPython and currIPython < recIPython: - warnMsg += "Spock recommends IPython version >= %s. Current version " \ - "is %s\n" % (requirements["IPython"][1], - get_ipython_version()) - - if not errPyTango and currPyTango < recPyTango: - warnMsg += "Spock recommends PyTango version >= %s. Current version " \ - "is %s\n" % (requirements["PyTango"][1], - get_pytango_version()) - - if not errTaurusCore and currTaurusCore < recTaurusCore: - warnMsg += "Spock recommends taurus.core version >= %s. Current " \ - "version is %s\n" % (requirements["taurus.core"][1], - get_taurus_core_version()) - - if errMsg: - errMsg += warnMsg - raise exception.SpockMissingRequirement(errMsg) - - if warnMsg: - raise exception.SpockMissingRecommended(warnMsg) - - return True - - -def _get_dev(dev_type): - spock_config = get_config().Spock - taurus_dev = None - taurus_dev_var = "_" + dev_type - if hasattr(spock_config, taurus_dev_var): - taurus_dev = getattr(spock_config, taurus_dev_var) - if taurus_dev is None: - # TODO: For Taurus 4 compatibility - dev_name = "tango://%s" % getattr(spock_config, dev_type + '_name') - factory = Factory() - taurus_dev = factory.getDevice(dev_name) - import PyTango - dev = PyTango.DeviceProxy(dev_name) - setattr(spock_config, dev_type, dev) - setattr(spock_config, taurus_dev_var, taurus_dev) - shell = get_shell() - dev_type_upper = dev_type.upper() - shell.user_ns[dev_type_upper] = dev - shell.user_ns["_" + dev_type_upper] = taurus_dev - return taurus_dev - - -def get_door(): - return _get_dev('door') - - -def get_macro_server(): - return _get_dev('macro_server') - - -def _macro_completer(self, event): - """Method called by the IPython autocompleter. It will determine possible - values for macro arguments. - """ - ms = get_macro_server() - - macro_name = event.command.lstrip('%') - # calculate parameter index - param_idx = len(event.line.split()) - 1 - if not event.line.endswith(' '): - param_idx -= 1 - # get macro info - info = ms.getMacroInfoObj(macro_name) - # if macro doesn't have parameters return - if param_idx < 0 or not info.hasParams(): - return - # get the parameter info - possible_params = info.getPossibleParams(param_idx) - # return the existing elements for the given parameter type - if possible_params: - res = [] - for param in possible_params: - if param['type'].lower() == 'boolean': - res.extend(['True', 'False']) - else: - res.extend(ms.getElementNamesWithInterface(param['type'])) - return res - - -def expose_magic(name, fn, completer_func=_macro_completer): - shell = get_shell() - fn.old_magic = shell.define_magic(name, fn) - fn.old_completer = completer_func - - if completer_func is None: - return - - # enable macro param completion - if completer_func is not None: - shell.set_hook('complete_command', completer_func, str_key=name) - shell.set_hook('complete_command', completer_func, str_key='%' + name) - - -def unexpose_magic(name): - shell = get_shell() - mg_name = 'magic_' + name - if hasattr(shell, mg_name): - magic_fn = getattr(shell, mg_name) - delattr(shell, mg_name) - if hasattr(magic_fn, 'old_magic') and magic_fn.old_magic is not None: - expose_magic(name, magic_fn.old_magic, magic_fn.old_completer) - - -def expose_variable(name, value): - get_shell().user_ns[name] = value - - -def expose_variables(d): - get_shell().user_ns.update(d) - - -def unexpose_variable(name): - user_ns = get_shell().user_ns - del user_ns[name] - - -def _create_config_file(location, door_name=None): - config_file_name = BaseIPythonApplication.config_file_name.default_value - abs_config_file_name = os.path.join(location, config_file_name) - - src_data = """\ -\"\"\"Settings for Spock session\"\"\" - -# -# Please do not delete the next lines has they are used to check the version -# number for possible upgrades -# spock_creation_version = {version} -# door_name = {door_name} -# - -import itango - -import sardana.spock.genutils -from sardana.spock.config import Spock - -config = get_config() -config.Spock.macro_server_name = '{macroserver_name}' -config.Spock.door_name = '{door_name}' - -load_subconfig('ipython_config.py', profile='default') -sardana.spock.load_config(config) - -# Put any additional environment here and/or overwrite default sardana config -config.IPKernelApp.pylab = 'inline' - -""" - # - # Discover door name - # - if door_name is None: - door_name = get_device_from_user("Door") - else: - full_door_name, door_name, _ = from_name_to_tango(door_name) - door_name = full_door_name - - # - # Discover macro server name - # - ms_name = get_macroserver_for_door(door_name) - - dest_data = src_data.format(version=release.version, - macroserver_name=ms_name, - door_name=door_name) - - sys.stdout.write('Storing %s in %s... ' % (config_file_name, location)) - sys.stdout.flush() - - with open(abs_config_file_name, "w") as f: - f.write(dest_data) - f.close() - sys.stdout.write(MSG_DONE + '\n') - - -def create_spock_profile(userdir, profile, door_name=None): - """Create spock profile directory and configuration file from a template - file - - :param userdir: directory where the spock profile will be created - :param profile: profile name - :param door_name: door name, if None, user will be asked for the door name - """ - - if not os.path.isdir(userdir): - ProfileDir.create_profile_dir(userdir) - p_dir = ProfileDir.create_profile_dir_by_name(userdir, profile) - ipy_profile_dir = p_dir.location - - _create_config_file(ipy_profile_dir) - - -def upgrade_spock_profile(ipy_profile_dir, door_name): - """Upgrade spock profile by recreating configuration file from scratch - - :param ipy_profile_dir: directory with the spock profile - :param door_name: door name - """ - _create_config_file(ipy_profile_dir, door_name) - - -def check_for_upgrade(ipy_profile_dir): - """Check if the current profile is up to date with the spock version - - :param ipy_profile_dir: directory with the spock profile - """ - spock_profile_ver_str = '0.0.0' - door_name = None - - config_file_name = BaseIPythonApplication.config_file_name.default_value - abs_config_file_name = os.path.join(ipy_profile_dir, config_file_name) - - # search for version and door inside the ipy_profile file - with open(abs_config_file_name, "r") as ipy_config_file: - for i, line in enumerate(ipy_config_file): - if i > 20: - break # give up after 20 lines - if line.startswith('# spock_creation_version = '): - spock_profile_ver_str = line[line.index('=') + 1:].strip() - if line.startswith('# door_name = '): - door_name = line[line.index('=') + 1:].strip() - - # convert version from string to numbers - spock_lib_ver_str = release.version - spocklib_ver = translate_version_str2int(spock_lib_ver_str) - spock_profile_ver = translate_version_str2int(spock_profile_ver_str) - - alpha_in_spock_profile = "-alpha" in spock_profile_ver_str - alpha_in_spock_lib = "-alpha" in spock_lib_ver_str - if spocklib_ver == spock_profile_ver and \ - alpha_in_spock_profile == alpha_in_spock_lib: - return - if spocklib_ver < spock_profile_ver: - print('%sYour spock profile (%s) is newer than your spock version ' - '(%s)!' % (SpockTermColors.Brown, - spock_profile_ver_str, spock_lib_ver_str)) - print('Please upgrade spock or delete the current profile %s' % - SpockTermColors.Normal) - sys.exit(1) - - # there was no version track of spock profiles since spock 0.2.0 so change - # the message - if spock_profile_ver_str == '0.0.0': - spock_profile_ver_str = '<= 0.2.0' - print('Your current spock door extension profile has been created with ' - 'spock %s.\n' - 'Your current spock door extension version is %s, therefore a ' - 'profile upgrade is needed.\n' % (spock_profile_ver_str, - spock_lib_ver_str)) - prompt = ('Do you wish to upgrade now (warn: this will shutdown the ' - 'current spock session) ([y]/n)? ') - r = input(prompt) or 'y' - if r.lower() == 'y': - upgrade_spock_profile(ipy_profile_dir, door_name) - sys.exit(0) - - -def get_args(argv): - - script_name = argv[0] - _, session = os.path.split(script_name) - script_name = os.path.realpath(script_name) - - macro_server = None - door = None - - # Define the profile file - profile = "spockdoor" - try: - for _, arg in enumerate(argv[1:]): - if arg.startswith('--profile='): - profile = arg[10:] - break - else: - argv.append("--profile=" + profile) - except: - pass - - ipython_dir = get_ipython_dir() - try: - ProfileDir.find_profile_dir_by_name(ipython_dir, profile) - except ProfileDirError: - r = '' - while not r in ('y', 'n'): - prompt = 'Profile \'%s\' does not exist. Do you want to create '\ - 'one now ([y]/n)? ' % profile - r = input(prompt) or 'y' - if r.lower() == 'y': - create_spock_profile(ipython_dir, profile) - else: - sys.stdout.write( - 'No spock door extension profile was created. Starting normal spock...\n') - sys.stdout.flush() - profile = '' - - # inform the shell of the profile it should use - if not '--profile=' in argv and profile: - argv.append('--profile=' + profile) - - user_ns = {'MACRO_SERVER_NAME': macro_server, - 'DOOR_NAME': door, - 'PROFILE': profile} - - return user_ns - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# Useful constants -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - -MSG_G = '[%s%%s%s]' % (SpockTermColors.Green, SpockTermColors.Normal) -MSG_R = '[%s%%s%s]' % (SpockTermColors.Red, SpockTermColors.Normal) -MSG_FAILED = MSG_R % 'FAILED' -MSG_FAILED_WR = MSG_R % 'FAILED: %s' -MSG_ERROR = MSG_R % 'ERROR' -MSG_DONE = MSG_G % 'DONE' -MSG_OK = MSG_G % 'OK' - -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# initialization methods -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - - -def init_taurus(): - # the CodecFactory is not thread safe. There are two attributes who will - # request for it in the first event at startup in different threads - # therefore this small hack: make sure CodecFactory is initialized. - CodecFactory() - - factory = Factory() - - import sardana.spock.spockms - macroserver = sardana.spock.spockms - - factory.registerDeviceClass('MacroServer', macroserver.SpockMacroServer) - - mode = get_gui_mode() - if mode == 'qt': - factory.registerDeviceClass('Door', macroserver.QSpockDoor) - else: - factory.registerDeviceClass('Door', macroserver.SpockDoor) - - -def load_ipython_extension(ipython): - import sardana.spock.magic - magic = sardana.spock.magic - - init_taurus() - - config = ipython.config - user_ns = ipython.user_ns - user_ns['MACRO_SERVER_NAME'] = config.Spock.macro_server_name - user_ns['MACRO_SERVER_ALIAS'] = config.Spock.macro_server_alias - user_ns['DOOR_NAME'] = config.Spock.door_name - user_ns['DOOR_ALIAS'] = config.Spock.door_alias - user_ns['DOOR_STATE'] = "" - - #shell.set_hook('late_startup_hook', magic.spock_late_startup_hook) - ipython.set_hook('pre_prompt_hook', magic.spock_pre_prompt_hook) - - # if ip.IP.alias_table.has_key('mv'): - # del ip.IP.alias_table['mv'] - - door = get_door() - macro_server = get_macro_server() - - # Initialize the environment - expose_variable(ENV_NAME, macro_server.getEnvironment()) - - new_style_magics = hasattr(IPython.core.magic, 'Magics') and hasattr( - IPython.core.magic, 'magics_class') - - if new_style_magics: - @IPython.core.magic.magics_class - class Sardana(IPython.core.magic.Magics): - debug = IPython.core.magic.line_magic(magic.debug) - www = IPython.core.magic.line_magic(magic.www) - post_mortem = IPython.core.magic.line_magic(magic.post_mortem) - spsplot = IPython.core.magic.line_magic(magic.spsplot) - macrodata = IPython.core.magic.line_magic(magic.macrodata) - edmac = IPython.core.magic.line_magic(magic.edmac) - showscan = IPython.core.magic.line_magic(magic.showscan) - expconf = IPython.core.magic.line_magic(magic.expconf) - - ipython.register_magics(Sardana) - else: - expose_magic('debug', magic.debug, magic.debug_completer) - expose_magic('www', magic.www, None) - expose_magic('post_mortem', magic.post_mortem, None) - expose_magic('spsplot', magic.spsplot, None) - expose_magic('macrodata', magic.macrodata, None) - expose_magic('edmac', magic.edmac, None) - expose_magic('showscan', magic.showscan, None) - expose_magic('expconf', magic.expconf, None) - - door.setConsoleReady(True) - - -def unload_ipython_extension(ipython): - pass - - -def load_config(config): - spockver = release.version - pyver = get_python_version() - ipyver = get_ipython_version() - pytangover = get_pytango_version() - tauruscorever = get_taurus_core_version() - - door = config.Spock.door_name - - if not hasattr(config.Spock, 'macro_server_name'): - macro_server = get_macroserver_for_door(door) - else: - macro_server = config.Spock.macro_server_name - - full_door_tg_name, door_tg_name, door_tg_alias = from_name_to_tango(door) - door_alias = door_tg_alias or door_tg_name - full_ms_tg_name, ms_tg_name, ms_tg_alias = from_name_to_tango(macro_server) - ms_alias = ms_tg_alias or ms_tg_name - - config.Spock.macro_server_name = full_ms_tg_name - config.Spock.door_name = full_door_tg_name - config.Spock.macro_server_alias = ms_alias - config.Spock.door_alias = door_alias - - d = {"version": spockver, - "pyver": pyver, - "ipyver": ipyver, - "pytangover": pytangover, - "taurusver": tauruscorever, - #"profile" : ip.user_ns["PROFILE"], - "door": door_alias} - - d.update(TermColors.__dict__) - - gui_mode = get_gui_mode() - - banner = """\ -%(Purple)sSpock %(version)s%(Normal)s -- An interactive laboratory application. - -help -> Spock's help system. -object? -> Details about 'object'. ?object also works, ?? prints more. -""" - banner = banner % d - banner = banner.format(**d) - - ipy_ver = get_ipython_version_number() - - # ------------------------------------ - # Application - # ------------------------------------ - app = config.Application - app.log_level = 30 - - # ------------------------------------ - # BaseIPythonApplication - # ------------------------------------ - i_app = config.BaseIPythonApplication - extensions = getattr(i_app, 'extensions', []) - extensions.extend(["itango", "sardana.spock"]) - i_app.extensions = extensions - - # ------------------------------------ - # InteractiveShell - # (IPython.core.interactiveshell) - # ------------------------------------ - i_shell = config.InteractiveShell - i_shell.autocall = 0 - i_shell.automagic = True - i_shell.color_info = True - i_shell.colors = 'Linux' - i_shell.deep_reload = True - i_shell.confirm_exit = False - - if ipy_ver >= 1200: - # ------------------------------------ - # PromptManager (ipython >= 0.12) - # ------------------------------------ - prompt = config.PromptManager - prompt.in_template = '{DOOR_ALIAS} [\\#]: ' - prompt.in2_template = ' .\\D.: ' - prompt.out_template = 'Result [\\#]: ' - prompt.color_scheme = 'Linux' - else: - # (Deprecated in ipython >= 0.12 use PromptManager.in_template) - i_shell.prompt_in1 = config.Spock.door_alias + ' [\\#]: ' - - # (Deprecated in ipython >= 0.12 use PromptManager.in2_template) - i_shell.prompt_in2 = ' .\\D.: ' - - # (Deprecated in ipython >= 0.12 use PromptManager.out_template) - i_shell.prompt_out = 'Result [\\#]: ' - - # (Deprecated in ipython >= 0.12 use PromptManager.justify) - i_shell.prompts_pad_left = True - - # ------------------------------------ - # IPCompleter - # ------------------------------------ - completer = config.IPCompleter - completer.omit__names = 2 - completer.greedy = False - - # ------------------------------------ - # InteractiveShellApp - # ------------------------------------ - i_shell_app = config.InteractiveShellApp - i_shell_app.ignore_old_config = True - - # ------------------------------------ - # TerminalIPythonApp: options for the IPython terminal (and not Qt Console) - # ------------------------------------ - term_app = config.TerminalIPythonApp - term_app.display_banner = True - term_app.gui = gui_mode - term_app.pylab = 'qt' - term_app.pylab_import_all = False - #term_app.nosep = False - #term_app.classic = True - - # ------------------------------------ - # IPKernelApp: options for the Qt Console - # ------------------------------------ - #kernel_app = config.IPKernelApp - ipython_widget = config.IPythonWidget - ipython_widget.in_prompt = ' Spock [%i]: ' - ipython_widget.out_prompt = 'Result [%i]: ' - ipython_widget.input_sep = '\n' - ipython_widget.output_sep = '' - ipython_widget.output_sep2 = '' - ipython_widget.enable_calltips = True - if ipy_ver >= 1300: - ipython_widget.gui_completion = 'droplist' - else: - ipython_widget.gui_completion = True - ipython_widget.ansi_codes = True - ipython_widget.paging = 'inside' - #ipython_widget.pylab = 'inline' - - # ------------------------------------ - # ConsoleWidget - # ------------------------------------ - # console_widget = config.ConsoleWidget - - # ------------------------------------ - # FrontendWidget - # ------------------------------------ - frontend_widget = config.FrontendWidget - frontend_widget.banner = banner - - # ------------------------------------ - # TerminalInteractiveShell - # ------------------------------------ - term_i_shell = config.TerminalInteractiveShell - term_i_shell.autocall = 2 - term_i_shell.automagic = True - #term_i_shell.editor = 'gedit' - #term_i_shell.editor = 'nano' - - term_i_shell.banner1 = banner - term_i_shell.banner2 = "Connected to " + door_alias + "\n" - #term_app.banner1 = banner - #term_app.banner2 = "Connected to " + door_alias + "\n" - - # ------------------------------------ - # InlineBackend - # ------------------------------------ - inline_backend = config.InlineBackend - inline_backend.figure_format = 'svg' - - # ------------------------------------ - # InteractiveShellEmbed - # ------------------------------------ - #i_shell_embed = config.InteractiveShellEmbed - - # ------------------------------------ - # NotebookApp - # ------------------------------------ - #notebook_app = config.NotebookApp - - # ------------------------------------ - # NotebookManager - # ------------------------------------ - #notebook_manager = config.NotebookManager - - # ------------------------------------ - # ZMQInteractiveShell - # ------------------------------------ - #zmq_i_shell = config.ZMQInteractiveShell - - # Tell console everything is ready. - config.Spock.ready = True - return config - - -def start(user_ns=None): - # Make sure the log level is changed to warning - CodecFactory() - setLogLevel(Warning) - - try: - check_requirements() - except exception.SpockMissingRequirement as requirement: - print(str(requirement)) - sys.exit(-1) - except exception.SpockMissingRecommended as recommended: - print(str(recommended)) - - user_ns = user_ns or {} - try: - user_ns.update(get_args(sys.argv)) - except exception.SpockException as e: - print(e) - print('Starting normal IPython console') - except KeyboardInterrupt: - print("\nUser pressed Ctrl+C. Exiting...") - sys.exit() - except Exception as e: - print('spock exited with an unmanaged exception: %s' % str(e)) - sys.exit(-2) - - app = TerminalIPythonApp.instance() - app.initialize() - #config = get_config() - return app - - -def mainloop(app=None, user_ns=None): - if app is None: - app = start(user_ns) - app.start() - - -def prepare_input_handler(): - # initialize input handler as soon as possible - import sardana.spock.inputhandler - _ = sardana.spock.inputhandler.InputHandler() - - -def prepare_cmdline(argv=None): - if argv is None: - argv = sys.argv - - script_name = argv[0] - _, session = os.path.split(script_name) - script_name = os.path.realpath(script_name) - - # Define the profile file - profile, append_profile = "spockdoor", True - try: - # in ipython the last option in the list takes precedence - # so reversing order for searching of the profile - reversed_argv = reversed(argv[1:]) - for _, arg in enumerate(reversed_argv): - if arg.startswith('--profile='): - profile = arg[10:] - append_profile = False - break - except: - pass - - ipython_dir = get_ipython_dir() - try: - pd = ProfileDir.find_profile_dir_by_name(ipython_dir, profile) - except ProfileDirError: - r = '' - while not r in ('y', 'n'): - prompt = "Profile '%s' does not exist. Do you want to create "\ - "one now ([y]/n)? " % profile - r = input(prompt) or 'y' - if r.lower() == 'y': - create_spock_profile(ipython_dir, profile) - else: - sys.stdout.write('No spock profile was created. ' - 'Starting ipython with default profile...\n') - sys.stdout.flush() - # removing all options refering to profile - for _, arg in enumerate(argv[1:]): - if arg.startswith('--profile='): - argv.remove(arg) - return - else: - ipy_profile_dir = pd.location # directory with the spock profile - check_for_upgrade(ipy_profile_dir) - - if append_profile: - argv.append("--profile=" + profile) - - -def run(): - - # TODO: Temporary solution, available while Taurus3 is being supported. - from taurus import tauruscustomsettings - from sardana import sardanacustomsettings - max_counts = getattr(sardanacustomsettings, - 'TAURUS_MAX_DEPRECATION_COUNTS', 0) - tauruscustomsettings._MAX_DEPRECATIONS_LOGGED = max_counts - # - - try: - from IPython.utils.traitlets import Unicode - from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget - - class SpockConsole(RichIPythonWidget): - - banner = Unicode(config=True) - - def _banner_default(self): - config = get_config() - return config.FrontendWidget.banner - - import IPython.frontend.qt.console.qtconsoleapp - IPythonQtConsoleApp = IPython.frontend.qt.console.qtconsoleapp.IPythonQtConsoleApp - IPythonQtConsoleApp.widget_factory = SpockConsole - IPythonQtConsoleApp.version.default_value = release.version - except ImportError: - pass - - try: - check_requirements() - except exception.SpockMissingRequirement as requirement: - print(str(requirement)) - sys.exit(-1) - except exception.SpockMissingRecommended as recommended: - print(str(recommended)) - - prepare_input_handler() - prepare_cmdline() - - launch_new_instance() - - # TODO: Temporary solution, available while Taurus3 is being supported. - try: - from taurus.core.util.log import _DEPRECATION_COUNT - from taurus import info - info('\n*********************\n%s', _DEPRECATION_COUNT.pretty()) - except: - pass From 62718135262b5abe9ed152bb57e7c768fb23c665 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 26 Jul 2020 17:19:40 +0200 Subject: [PATCH 705/830] Remove deprecated Door's Abort command --- src/sardana/tango/macroserver/Door.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py index 76ec7538a8..cfee3e75f3 100644 --- a/src/sardana/tango/macroserver/Door.py +++ b/src/sardana/tango/macroserver/Door.py @@ -354,10 +354,6 @@ def read_RecordData(self, attr): def read_MacroStatus(self, attr): attr.set_value('', '') - def Abort(self): - self.debug("Abort is deprecated. Use StopMacro instead") - return self.StopMacro() - def AbortMacro(self): macro = self.getRunningMacro() if macro is None: @@ -365,9 +361,6 @@ def AbortMacro(self): self.debug("aborting %s" % macro._getDescription()) self.macro_executor.abort() - def is_Abort_allowed(self): - return True - def ReleaseMacro(self): macro = self.getRunningMacro() if macro is None: @@ -472,9 +465,6 @@ class DoorClass(SardanaDeviceClass): # Command definitions cmd_list = { - 'Abort': - [[DevVoid, ""], - [DevVoid, ""]], 'PauseMacro': [[DevVoid, ""], [DevVoid, ""]], From a42bfda89b0e86f55058b37282108146c998e1b4 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 26 Jul 2020 17:33:18 +0200 Subject: [PATCH 706/830] Remove Controller getUsedAxis --- src/sardana/taurus/core/tango/sardana/pool.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index c1a5e090fd..a33d002514 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -670,12 +670,6 @@ def getUsedAxes(self): axes.append(elem.getAxis()) return sorted(axes) - def getUsedAxis(self): - msg = ("getUsedAxis is deprecated since version 2.5.0. ", - "Use getUsedAxes instead.") - self.warning(msg) - self.getUsedAxes() - def getLastUsedAxis(self): """Return the last used axis (the highest axis) in this controller From a61b746c245bf747457639242112f0081f03b452 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 26 Jul 2020 17:46:15 +0200 Subject: [PATCH 707/830] Remove deprecated DoorAttrListener class --- .../qtgui/extra_macroexecutor/dooroutput.py | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py index 9ca657d932..ffaf1d160d 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py @@ -210,39 +210,6 @@ def contextMenuEvent(self, event): menu.exec_(event.globalPos()) -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- -# Door attributes listeners -#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - -class DoorAttrListener(Qt.QObject): - """Deprecated. Do not use""" - - def __init__(self, attrName): - Qt.QObject.__init__(self) - from taurus.core.util.log import deprecated - deprecated(dep="DoorAttrListener", rel="2.5.1") - self.attrName = attrName - self.attrObj = None - - def setDoorName(self, doorName): - if not self.attrObj is None: - self.attrObj.removeListener(self) - self.attrObj = taurus.Attribute(doorName, self.attrName) - self.attrObj.addListener(self) - - def eventReceived(self, src, type, value): - if (type == taurus.core.taurusbasetypes.TaurusEventType.Error or - type == taurus.core.taurusbasetypes.TaurusEventType.Config): - return - - # The old code (using old-style signals) emitted a signal called - # doorChanged . Emulating this with new-style signasl - # is problematic, and in this case it is not worth it since this class - # is unused, deprecated and will disappear soon - # self.emit(Qt.SIGNAL('door%sChanged' % self.attrName), value.value) - - - if __name__ == "__main__": import sys import taurus From 4c7efd401118f4058a665e4e0c72a38833689acb Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 26 Jul 2020 18:02:22 +0200 Subject: [PATCH 708/830] Remove deprecated methods from MacroButton widget --- .../qtgui/extra_macroexecutor/macrobutton.py | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 332622b2c7..11b4ab5c9d 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -100,11 +100,6 @@ def __init__(self, parent=None, designMode=False): def handleEvent(self, evt_src, evt_type, evt_value): pass - def toggleProgress(self, visible): - '''deprecated''' - self.warning('toggleProgress is deprecated. Use showProgress') - self.showProgress(visible) - def showProgress(self, visible): '''Set whether the progress bar is shown @@ -247,29 +242,6 @@ def updateMacroArgument(self, index, value): # update tooltip self.setToolTip(self.macro_name + ' ' + ' '.join(self.macro_args)) - def updateMacroArgumentFromSignal(self, index, obj, signal): - '''deprecated''' - msg = 'updateMacroArgumentFromSignal is deprecated. connectArgEditors' - self.warning(msg) - self.connect(obj, signal, - functools.partial(self.updateMacroArgument, index)) - - def connectArgEditors(self, signals): - """ - Associate signals to argument changes. - - :param signals: (seq) An ordered sequence of signals - """ - - for i, signal in enumerate(signals): - if not self.__isSignal(signal): - # bck-compat: (sender, sig) tuples used instead of pyqtsignals - sender, sig = signal - self.deprecated(dep='Passing (sender, signature) tuples', - alt='pyqtSignal objects', rel='2.5.1') - signal = getattr(sender, sig.split('(')[0]) - signal.connect(functools.partial(self.updateMacroArgument, i)) - @staticmethod def __isSignal(obj): if not hasattr(obj, 'emit'): From ee8e662978a5734c968a631bbc04f7d9cd1a2293 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 26 Jul 2020 18:16:03 +0200 Subject: [PATCH 709/830] Remove deprecated PoolMotorSlim widget --- .../taurus/qt/qtgui/extra_pool/__init__.py | 2 +- .../taurus/qt/qtgui/extra_pool/poolmotor.py | 511 ------------------ .../qt/qtgui/extra_pool/ui/PoolMotorSlim.ui | 354 ------------ 3 files changed, 1 insertion(+), 866 deletions(-) delete mode 100644 src/sardana/taurus/qt/qtgui/extra_pool/ui/PoolMotorSlim.ui diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py b/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py index 8950af34d6..5fdd4123dc 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/__init__.py @@ -28,7 +28,7 @@ """ from .motor import TaurusMotorH, TaurusMotorH2, TaurusMotorV, TaurusMotorV2 # noqa -from .poolmotor import PoolMotorSlim, LabelWidgetDragsDeviceAndAttribute # noqa +from .poolmotor import LabelWidgetDragsDeviceAndAttribute # noqa from .poolmotor import (PoolMotorTV, PoolMotorTVLabelWidget, # noqa PoolMotorTVReadWidget, PoolMotorTVWriteWidget, PoolMotorTVUnitsWidget, # noqa PoolMotor) # noqa diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py index 1ad01bb66d..a31d5db83c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py +++ b/src/sardana/taurus/qt/qtgui/extra_pool/poolmotor.py @@ -384,499 +384,6 @@ def setModel(self, modelName): att.name.lower()) if att.name.lower() in attributes else 1) -@UILoadable(with_ui='ui') -class PoolMotorSlim(TaurusWidget, PoolMotorClient): - - __pyqtSignals__ = ("modelChanged(const QString &)",) - - def __init__(self, parent=None, designMode=False): - TaurusWidget.__init__(self, parent) - msg = ("PoolMotorSlim is deprecated since 2.5.0. Use PoolMotorTV " - "instead.") - self.deprecated(msg) - - #self.call__init__wo_kw(Qt.QWidget, parent) - #self.call__init__(TaurusBaseWidget, str(self.objectName()), designMode=designMode) - PoolMotorClient.__init__(self) - self.loadUi() - self.recheckTaurusParent() - - self.show_context_menu = True - - self.setAcceptDrops(True) - - if designMode: - self.__setTaurusIcons() - return - - # CREATE THE TaurusValue that can not be configured in the Designer - self.taurus_value = TaurusValue(self.ui.taurusValueContainer) - - # Use a DragDevAndAttributeLabelWidget to provide a richer QMimeData - # content - self.taurus_value.setLabelWidgetClass( - LabelWidgetDragsDeviceAndAttribute) - - # Make the label to be the device alias - self.taurus_value.setLabelConfig('dev_alias') - - self.taurus_value_enc = TaurusValue(self.ui.taurusValueContainer) - - # THIS WILL BE DONE IN THE DESIGNER - # Config Button will launch a PoolMotorConfigurationForm -# 19.08.2011 after discussion between cpascual, gcui and zreszela, Configuration Panel was rolled back to -# standard TaurusAttrForm - list of all attributes alphabetically ordered -# taurus_attr_form = PoolMotorConfigurationForm() - taurus_attr_form = TaurusAttrForm() - - taurus_attr_form.setMinimumSize(Qt.QSize(470, 800)) - self.ui.btnCfg.setWidget(taurus_attr_form) - self.ui.btnCfg.setUseParentModel(True) - - # ADD AN EVENT FILTER FOR THE STATUS LABEL IN ORDER TO PROVIDE JUST THE - # STRING FROM THE CONTROLLER (LAST LINE) - def just_ctrl_status_line(evt_src, evt_type, evt_value): - if evt_type not in [TaurusEventType.Change, TaurusEventType.Periodic]: - return evt_src, evt_type, evt_value - try: - status = evt_value.value - last_line = status.split('\n')[-1] - new_evt_value = PyTango.DeviceAttribute(evt_value) - new_evt_value.value = last_line - return evt_src, evt_type, new_evt_value - except: - return evt_src, evt_type, evt_value - self.ui.lblStatus.insertEventFilter(just_ctrl_status_line) - - # These buttons are just for showing if the limit is active or not - self.ui.btnMin.setEnabled(False) - self.ui.btnMax.setEnabled(False) - - # HOMING NOT IMPLMENTED YET - self.ui.btnHome.setEnabled(False) - - # DEFAULT VISIBLE COMPONENTS - self.toggleHideAll() - self.toggleMoveAbsolute(True) - self.toggleStopMove(True) - - # SET TAURUS ICONS - self.__setTaurusIcons() - - self.ui.motorGroupBox.setContextMenuPolicy(Qt.Qt.CustomContextMenu) - - self.ui.motorGroupBox.customContextMenuRequested.connect( - self.buildContextMenu) - self.ui.btnGoToNeg.clicked.connect(self.jogNeg) - self.ui.btnGoToNegPress.pressed.connect(self.jogNeg) - self.ui.btnGoToNegPress.released.connect(self.abort) - self.ui.btnGoToNegInc.clicked.connect(self.goToNegInc) - self.ui.btnGoToPos.clicked.connect(self.jogPos) - self.ui.btnGoToPosPress.pressed.connect(self.jogPos) - self.ui.btnGoToPosPress.released.connect(self.abort) - self.ui.btnGoToPosInc.clicked.connect(self.goToPosInc) - - self.ui.btnHome.clicked.connect(self.goHome) - self.ui.btnStop.clicked.connect(self.abort) - - # ALSO UPDATE THE WIDGETS EVERYTIME THE FORM HAS TO BE SHOWN - self.ui.btnCfg.clicked.connect(taurus_attr_form._updateAttrWidgets) - self.ui.btnCfg.clicked.connect(self.buildBetterCfgDialogTitle) - - ####################################################################### - ######################################## - # LET TAURUS CONFIGURATION MECANISM SHINE! - ######################################## - self.registerConfigProperty( - self.ui.inc.isVisible, self.toggleMoveRelative, 'MoveRelative') - self.registerConfigProperty( - self.ui.btnGoToNegPress.isVisible, self.toggleMoveContinuous, 'MoveContinuous') - self.registerConfigProperty( - self.ui.btnGoToNeg.isVisible, self.toggleMoveToLimits, 'MoveToLimits') - self.registerConfigProperty( - self.ui.btnStop.isVisible, self.toggleStopMove, 'StopMove') - self.registerConfigProperty( - self.ui.btnHome.isVisible, self.toggleHoming, 'Homing') - self.registerConfigProperty( - self.ui.btnCfg.isVisible, self.toggleConfig, 'Config') - self.registerConfigProperty( - self.ui.lblStatus.isVisible, self.toggleStatus, 'Status') - ####################################################################### - - def __setTaurusIcons(self): - self.ui.btnMin.setText('') - self.ui.btnMin.setIcon(Qt.QIcon("actions:list-remove.svg")) - self.ui.btnMax.setText('') - self.ui.btnMax.setIcon(Qt.QIcon("actions:list-add.svg")) - - self.ui.btnGoToNeg.setText('') - self.ui.btnGoToNeg.setIcon( - Qt.QIcon("actions:media_skip_backward.svg")) - self.ui.btnGoToNegPress.setText('') - self.ui.btnGoToNegPress.setIcon( - Qt.QIcon("actions:media_seek_backward.svg")) - self.ui.btnGoToNegInc.setText('') - self.ui.btnGoToNegInc.setIcon( - Qt.QIcon("actions:media_playback_backward.svg")) - self.ui.btnGoToPos.setText('') - self.ui.btnGoToPos.setIcon(Qt.QIcon("actions:media_skip_forward.svg")) - self.ui.btnGoToPosPress.setText('') - self.ui.btnGoToPosPress.setIcon( - Qt.QIcon("actions:media_seek_forward.svg")) - self.ui.btnGoToPosInc.setText('') - self.ui.btnGoToPosInc.setIcon( - Qt.QIcon("actions:media_playback_start.svg")) - self.ui.btnStop.setText('') - self.ui.btnStop.setIcon(Qt.QIcon("actions:media_playback_stop.svg")) - self.ui.btnHome.setText('') - self.ui.btnHome.setIcon(Qt.QIcon("actions:go-home.svg")) - self.ui.btnCfg.setText('') - self.ui.btnCfg.setIcon(Qt.QIcon("categories:preferences-system.svg")) - ####################################################################### - - #@Qt.pyqtSlot(list) - def updateLimits(self, limits): - if isinstance(limits, dict): - limits = limits["limits"] - pos_lim = limits[1] - pos_btnstylesheet = '' - enabled = True - if pos_lim: - pos_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( - PyTango.DevState.ALARM) - enabled = False - self.ui.btnMax.setStyleSheet(pos_btnstylesheet) - self.ui.btnGoToPos.setEnabled(enabled) - self.ui.btnGoToPosPress.setEnabled(enabled) - self.ui.btnGoToPosInc.setEnabled(enabled) - - neg_lim = limits[2] - neg_btnstylesheet = '' - enabled = True - if neg_lim: - neg_btnstylesheet = 'QPushButton{%s}' % DEVICE_STATE_PALETTE.qtStyleSheet( - PyTango.DevState.ALARM) - enabled = False - self.ui.btnMin.setStyleSheet(neg_btnstylesheet) - self.ui.btnGoToNeg.setEnabled(enabled) - self.ui.btnGoToNegPress.setEnabled(enabled) - self.ui.btnGoToNegInc.setEnabled(enabled) - - # def sizeHint(self): - # return Qt.QSize(300,30) - - @Qt.pyqtSlot() - def goToNegInc(self): - self.moveInc(-1 * self.ui.inc.value()) - - @Qt.pyqtSlot() - def goToPosInc(self): - self.moveInc(self.ui.inc.value()) - - def buildContextMenu(self, point): - if not self.show_context_menu: - return - menu = Qt.QMenu(self) - - action_hide_all = Qt.QAction(self) - action_hide_all.setText('Hide All') - menu.addAction(action_hide_all) - - action_show_all = Qt.QAction(self) - action_show_all.setText('Show All') - menu.addAction(action_show_all) - - action_move_absolute = Qt.QAction(self) - action_move_absolute.setText('Move Absolute') - action_move_absolute.setCheckable(True) - action_move_absolute.setChecked( - self.taurus_value.writeWidget().isVisible()) - menu.addAction(action_move_absolute) - - action_move_relative = Qt.QAction(self) - action_move_relative.setText('Move Relative') - action_move_relative.setCheckable(True) - action_move_relative.setChecked(self.ui.inc.isVisible()) - menu.addAction(action_move_relative) - - action_move_continuous = Qt.QAction(self) - action_move_continuous.setText('Move Continuous') - action_move_continuous.setCheckable(True) - action_move_continuous.setChecked(self.ui.btnGoToNegPress.isVisible()) - menu.addAction(action_move_continuous) - - action_move_to_limits = Qt.QAction(self) - action_move_to_limits.setText('Move to Limits') - action_move_to_limits.setCheckable(True) - action_move_to_limits.setChecked(self.ui.btnGoToNeg.isVisible()) - menu.addAction(action_move_to_limits) - - action_encoder = Qt.QAction(self) - action_encoder.setText('Encoder Read') - action_encoder.setCheckable(True) - action_encoder.setChecked(self.taurus_value_enc.isVisible()) - if self.has_encoder: - menu.addAction(action_encoder) - - action_stop_move = Qt.QAction(self) - action_stop_move.setText('Stop Movement') - action_stop_move.setCheckable(True) - action_stop_move.setChecked(self.ui.btnStop.isVisible()) - menu.addAction(action_stop_move) - - action_homing = Qt.QAction(self) - action_homing.setText('Homing') - action_homing.setCheckable(True) - action_homing.setChecked(self.ui.btnHome.isVisible()) - menu.addAction(action_homing) - - action_config = Qt.QAction(self) - action_config.setText('Config') - action_config.setCheckable(True) - action_config.setChecked(self.ui.btnCfg.isVisible()) - menu.addAction(action_config) - - action_status = Qt.QAction(self) - action_status.setText('Status') - action_status.setCheckable(True) - action_status.setChecked(self.ui.lblStatus.isVisible()) - menu.addAction(action_status) - - action_hide_all.triggered.connect(self.toggleHideAll) - action_show_all.triggered.connect(self.toggleShowAll) - action_move_absolute.toggled.connect(self.toggleMoveAbsolute) - action_move_relative.toggled.connect(self.toggleMoveRelative) - action_move_continuous.toggled.connect(self.toggleMoveContinuous) - action_move_to_limits.toggled.connect(self.toggleMoveToLimits) - action_encoder.toggled.connect(self.toggleEncoder) - action_stop_move.toggled.connect(self.toggleStopMove) - action_homing.toggled.connect(self.toggleHoming) - action_config.toggled.connect(self.toggleConfig) - action_status.toggled.connect(self.toggleStatus) - - menu.popup(self.cursor().pos()) - - def toggleHideAll(self): - self.toggleAll(False) - - def toggleShowAll(self): - self.toggleAll(True) - - def toggleAll(self, visible): - self.toggleMoveAbsolute(visible) - self.toggleMoveRelative(visible) - self.toggleMoveContinuous(visible) - self.toggleMoveToLimits(visible) - self.toggleEncoder(visible) - self.toggleStopMove(visible) - self.toggleHoming(visible) - self.toggleConfig(visible) - self.toggleStatus(visible) - - def toggleMoveAbsolute(self, visible): - if self.taurus_value.writeWidget() is not None: - self.taurus_value.writeWidget().setVisible(visible) - - def toggleMoveRelative(self, visible): - self.ui.btnGoToNegInc.setVisible(visible) - self.ui.inc.setVisible(visible) - self.ui.btnGoToPosInc.setVisible(visible) - - def toggleMoveContinuous(self, visible): - self.ui.btnGoToNegPress.setVisible(visible) - self.ui.btnGoToPosPress.setVisible(visible) - - def toggleMoveToLimits(self, visible): - self.ui.btnGoToNeg.setVisible(visible) - self.ui.btnGoToPos.setVisible(visible) - - def toggleEncoder(self, visible): - self.taurus_value_enc.setVisible(visible) - - def toggleStopMove(self, visible): - self.ui.btnStop.setVisible(visible) - - def toggleHoming(self, visible): - self.ui.btnHome.setVisible(visible) - - def toggleConfig(self, visible): - self.ui.btnCfg.setVisible(visible) - - def toggleStatus(self, visible): - self.ui.lblStatus.setVisible(visible) - - def dragEnterEvent(self, event): - event.accept() - - def dropEvent(self, event): - mimeData = event.mimeData() - if mimeData.hasFormat(TAURUS_DEV_MIME_TYPE): - model = str(mimeData.data(TAURUS_DEV_MIME_TYPE)) - elif mimeData.hasFormat(TAURUS_ATTR_MIME_TYPE): - model = str(mimeData.data(TAURUS_ATTR_MIME_TYPE)) - else: - model = str(mimeData.text()) - self.setModel(model) - - def keyPressEvent(self, key_event): - if key_event.key() == Qt.Qt.Key_Escape: - self.abort() - key_event.accept() - TaurusWidget.keyPressEvent(self, key_event) - - def buildBetterCfgDialogTitle(self): - while self.ui.btnCfg._dialog is None: - pass - model = self.getModel() - self.ui.btnCfg._dialog.setWindowTitle( - '%s config' % taurus.Factory().getDevice(model).getSimpleName()) - - @classmethod - def getQtDesignerPluginInfo(cls): - ret = TaurusWidget.getQtDesignerPluginInfo() - ret['module'] = 'taurus.qt.qtgui.extra_pool' - ret['group'] = 'Taurus Sardana' - ret['icon'] = ':/designer/extra_motor.png' - ret['container'] = False - return ret - - def showEvent(self, event): - TaurusWidget.showEvent(self, event) - try: - self.motor_dev.getAttribute('Position').enablePolling(force=True) - except AttributeError as e: - self.debug('Error in showEvent: %s', repr(e)) - - def hideEvent(self, event): - TaurusWidget.hideEvent(self, event) - try: - self.motor_dev.getAttribute('Position').disablePolling() - except AttributeError as e: - self.debug('Error in hideEvent: %s', repr(e)) - - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - # QT properties - #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- - @Qt.pyqtSlot() - def getModel(self): - return self.ui.motorGroupBox.getModel() - - @Qt.pyqtSlot("QString") - def setModel(self, model): - # DUE TO A BUG IN TAUGROUPBOX, WE NEED THE FULL MODEL NAME - try: - # In case the model is an attribute of a motor, get the device name - # TODO: When sardana is moved to Taurus 4 replace this line by - # taurushelper.isValidName(model, [TaurusElementType.Device]) - if not TangoDeviceNameValidator().isValid(model): - model = model.rpartition('/')[0] - model = taurus.Factory().getDevice(model).getFullName() - self.setMotor(model) - self.ui.motorGroupBox.setModel(model) - self.ui.motorGroupBox.setEnabled(True) - - self.taurus_value.setModel(model + '/Position') - - # DUE TO A BUG IN TAURUSVALUE, THAT DO NOT USE PARENT MODEL WE NEED - # TO ALWAYS SET THE MODEL - self.taurus_value.setUseParentModel(False) - - # THE FORCED APPLY HAS TO BE DONE AFTER THE MODEL IS SET, SO THE - # WRITEWIDGET IS AVAILABLE - if self.taurus_value.writeWidget() is not None: - self.taurus_value.writeWidget().setForcedApply(True) - - show_enc = self.taurus_value_enc.isVisible() - if self.has_encoder: - self.taurus_value_enc.setModel(model + '/Encoder') - self.taurus_value_enc.setUseParentModel(False) - self.taurus_value_enc.readWidget().setBgRole('none') - else: - self.taurus_value_enc.setModel(None) - show_enc = False - if not show_enc: - self.toggleEncoder(False) - - try: - self.unregisterConfigurableItem('MoveAbsolute') - self.unregisterConfigurableItem('Encoder') - except: - pass - self.registerConfigProperty(self.taurus_value.writeWidget( - ).isVisible, self.toggleMoveAbsolute, 'MoveAbsolute') - self.registerConfigProperty( - self.taurus_value_enc.isVisible, self.toggleEncoder, 'Encoder') - - # SINCE TAURUSLAUNCHERBUTTON HAS NOT THIS PROPERTY IN THE - # DESIGNER, WE MUST SET IT HERE - self.ui.btnCfg.setUseParentModel(True) - - # CONFIGURE A LISTENER IN ORDER TO UPDATE LIMIT SWITCHES STATES - self.limits_listener = LimitsListener() - self.limits_listener.updateLimits.connect( - self.updateLimits) - limits_visible = False - if self.has_limits: - self._limits_switches = self.motor_dev.getAttribute( - 'Limit_switches') - self._limits_switches.addListener(self.limits_listener) - # self.updateLimits(limits_attribute.read().rvalue) - limits_visible = True - self.ui.btnMin.setVisible(limits_visible) - self.ui.btnMax.setVisible(limits_visible) - except Exception as e: - self.ui.motorGroupBox.setEnabled(False) - self.info('Error setting model "%s". Reason: %s' % - (model, repr(e))) - self.traceback() - - @Qt.pyqtSlot() - def resetModel(self): - self.ui.motorGroupBox.resetModel() - - @Qt.pyqtSlot() - def getShowContextMenu(self): - return self.show_context_menu - - @Qt.pyqtSlot() - def setShowContextMenu(self, showContextMenu): - self.show_context_menu = showContextMenu - - @Qt.pyqtSlot() - def resetShowContextMenu(self): - self.show_context_menu = True - - @Qt.pyqtSlot() - def getStepSize(self): - return self.ui.inc.value() - - @Qt.pyqtSlot(float) - def setStepSize(self, stepSize): - self.ui.inc.setValue(stepSize) - - @Qt.pyqtSlot() - def resetStepSize(self): - self.setStepSize(1) - - @Qt.pyqtSlot() - def getStepSizeIncrement(self): - return self.ui.inc.singleStep() - - @Qt.pyqtSlot(float) - def setStepSizeIncrement(self, stepSizeIncrement): - self.ui.inc.setSingleStep(stepSizeIncrement) - - @Qt.pyqtSlot() - def resetStepSizeIncrement(self): - self.setStepSizeIncrement(1) - - model = Qt.pyqtProperty("QString", getModel, setModel, resetModel) - stepSize = Qt.pyqtProperty( - "double", getStepSize, setStepSize, resetStepSize) - stepSizeIncrement = Qt.pyqtProperty( - "double", getStepSizeIncrement, setStepSizeIncrement, resetStepSizeIncrement) - - ########################################################################## # NEW APPROACH TO OPERATE POOL MOTORS FROM A TAURUS FORM INHERITTING DIRECTLY FROM TaurusVALUE # # AND USING PARTICULAR CLASSES THAT KNOW THEY ARE PART OF A TAURUSVALUE AND CAN INTERACT # @@ -1847,17 +1354,6 @@ def main(): # tests.append(3) # tests.append(4) - # 1) Test PoolMotorSlim motor widget - form_pms = TaurusForm() - pms_widget_class = 'sardana.taurus.qt.qtgui.extra_pool.PoolMotorSlim' - pms_tgclass_map = {'SimuMotor': (pms_widget_class, (), {}), - 'Motor': (pms_widget_class, (), {}), - 'PseudoMotor': (pms_widget_class, (), {})} - form_pms.setCustomWidgetMap(pms_tgclass_map) - if 1 in tests: - form_pms.setModel(models) - w.layout().addWidget(form_pms) - # 2) Test PoolMotorTV motor widget form_tv = TaurusForm() form_tv.setModifiableByUser(True) @@ -1881,13 +1377,6 @@ def main(): motor_widget.setModel(motor) w.layout().addWidget(motor_widget) - # 4) Test Stand-Alone PoolMotorSlim widget - if 4 in tests: - for motor in models: - motor_widget = PoolMotorSlim() - motor_widget.setModel(motor) - w.layout().addWidget(motor_widget) - w.show() sys.exit(app.exec_()) diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/ui/PoolMotorSlim.ui b/src/sardana/taurus/qt/qtgui/extra_pool/ui/PoolMotorSlim.ui deleted file mode 100644 index 9233390342..0000000000 --- a/src/sardana/taurus/qt/qtgui/extra_pool/ui/PoolMotorSlim.ui +++ /dev/null @@ -1,354 +0,0 @@ - - - PoolMotorSlim - - - - 0 - 0 - 549 - 212 - - - - Dialog - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - false - - - - 0 - - - 0 - - - - - - 30 - 16777215 - - - - Stops the motor - - - S - - - - - - - 0 - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - Negative limit - - - - - - - true - - - false - - - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - Moves the motor towards negative limit - - - |< - - - false - - - false - - - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - Moves the motor towards negative limit while pressed - - - « - - - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - Decrements motor position <inc> units - - - < - - - - - - - - 80 - 16777215 - - - - 10000000000000000159028911097599180468360808563945281389781327557747838772170381060813469985856815104.000000000000000 - - - 1.000000000000000 - - - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - Increments motor position <inc> units - - - > - - - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - Moves the motor towards positive limit while pressed - - - » - - - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - Moves the motor towards positive limit - - - >| - - - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - Positive limit - - - + - - - false - - - - - - - - - - 0 - 0 - - - - - - - - - 30 - 16777215 - - - - Configures the motor - - - Cfg - - - - - - - /Status - - - true - - - - - - - - 30 - 16777215 - - - - Goes Home - - - H - - - - - - - - - - - TaurusLauncherButton - QPushButton -
taurus.qt.qtgui.button
-
- - TaurusWidget - QWidget -
taurus.qt.qtgui.container
- 1 -
- - TaurusLabel - QLabel -
taurus.qt.qtgui.display
-
- - TaurusGroupBox - QGroupBox -
taurus.qt.qtgui.container
- 1 -
-
- - -
From 3000d9fcfe5def9d3f1d7dbb60395dee4de2ec4a Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 21 Jul 2020 17:47:10 +0200 Subject: [PATCH 710/830] Remove deprecated trigger_type back-compat in meas grp conf --- src/sardana/pool/controller.py | 10 +--------- src/sardana/pool/poolmeasurementgroup.py | 13 +------------ .../qt/qtgui/extra_sardana/measurementgroup.py | 11 +---------- 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 4fde8318a8..a035c70bf4 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -925,7 +925,7 @@ class CounterTimerController(Controller, Readable, Startable, Stopable, - timer - monitor - - trigger_type""" + """ #: A :class:`dict` containing the standard attributes present on each axis #: device @@ -954,14 +954,6 @@ def __init__(self, inst, props, *args, **kwargs): self._latency_time = 0 self._synchronization = AcqSynch.SoftwareTrigger - def get_trigger_type(self): - msg = "trigger_type is deprecated since SEP6. " +\ - "Use synchronization instead" - self._log.warning(msg) - return self._synchronization - - _trigger_type = property(get_trigger_type) - def PreStartAllCT(self): """**Counter/Timer Controller API**. Override if necessary. Called to prepare an acquisition of all selected axis. diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 8517ce414a..af93e4972d 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -713,21 +713,10 @@ def set_configuration_from_user(self, cfg): conf_synch_ctrl.add_channel(conf_synch) ctrl_conf['synchronizer'] = conf_synch - try: synchronization = ctrl_data['synchronization'] except KeyError: - # backwards compatibility for configurations before SEP6 - try: - synchronization = ctrl_data['trigger_type'] - msg = ("trigger_type configuration parameter " - "is deprecated" - " in favor of synchronization. Re-apply " - "configuration in order to upgrade.") - self._parent.warning(msg) - except KeyError: - synchronization = AcqSynchType.Trigger - + synchronization = AcqSynchType.Trigger ctrl_conf['synchronization'] = synchronization user_config_ctrl['synchronization'] = synchronization diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index 6089412f32..4e72057d88 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -528,16 +528,7 @@ def data(self, index, role=Qt.Qt.DisplayRole): ch_name, ch_data = index.internalPointer().itemData() unitdict = self.getPyData(ctrlname=ch_data['_controller_name']) key = self.data_keys_map[taurus_role] - try: - synchronization = unitdict[key] - except KeyError: - # backwards compatibility for configurations before SEP6 - synchronization = unitdict.get('trigger_type', None) - if synchronization is not None: - msg = ("trigger_type configuration parameter is deprecated" - " in favor of synchronization. Re-apply" - " configuration in order to upgrade.") - self.warning(msg) + synchronization = unitdict[key] return AcqSynchType[synchronization] elif taurus_role in (ChannelView.Timer, ChannelView.Monitor): ch_name, ch_data = index.internalPointer().itemData() From eddafdc252ffed100a3a80e0d17519c233e48a99 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 11 Aug 2020 10:34:55 +0200 Subject: [PATCH 711/830] Update CHANGELOG with 2.8.6 hotfix release --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b123d9aba..e619ee8910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,7 +83,6 @@ This file follows the formats and conventions from [keepachangelog.com] server started afterwards (#599, #1278) * `TaurusMacroExecutorWidget` does not use _parent model_ feature (#599, #1278) * `TaurusSequencerWidget` does not use _parent model_ feature (#1284) -* MacroButton with repeat parameters (#1172, #1314) * Macro plotting in new versions of ipython and matplotlib require extra call to `pyplot.draw()` to make sure that the plot is refreshed (#1280) * Allow MacroButton widget to be smaller - minimum size to show the macro name (#1265) @@ -141,6 +140,11 @@ This file follows the formats and conventions from [keepachangelog.com] * `sardana.requirements` (#1185) * `sardanatestsuite` and `sardana.test.testsuite.*` utility functions (#1347) +## [2.8.6] 2020-08-10 + +### Fixed + +* MacroButton with repeat parameters (#1172, #1314) ## [2.8.5] 2020-04-27 @@ -879,7 +883,8 @@ Main improvements since sardana 1.5.0 (aka Jan15): [keepachangelog.com]: http://keepachangelog.com -[Unreleased]: https://github.com/sardana-org/sardana/compare/2.8.5...HEAD +[Unreleased]: https://github.com/sardana-org/sardana/compare/2.8.6...HEAD +[2.8.6]: https://github.com/sardana-org/sardana/compare/2.8.6...2.8.5 [2.8.5]: https://github.com/sardana-org/sardana/compare/2.8.5...2.8.4 [2.8.4]: https://github.com/sardana-org/sardana/compare/2.8.4...2.8.3 [2.8.3]: https://github.com/sardana-org/sardana/compare/2.8.3...2.8.2 From 6cc94d92eb7060c79b379af851ef135969d02ace Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 12 Aug 2020 12:33:46 +0200 Subject: [PATCH 712/830] Use taurus validator to identify a door --- src/sardana/macroserver/macros/env.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py index 1aefe3a1d1..e267ae432a 100644 --- a/src/sardana/macroserver/macros/env.py +++ b/src/sardana/macroserver/macros/env.py @@ -29,7 +29,7 @@ __docformat__ = 'restructuredtext' -import taurus +from taurus.core.tango.tangovalidator import TangoDeviceNameValidator from taurus.console.list import List from sardana.macroserver.macro import Macro, Type from sardana.macroserver.msexception import UnknownEnv @@ -221,17 +221,13 @@ def run(self, var): if len(pars) == 1: key = pars[0] elif len(pars) > 1: - if pars[0].find("/") != -1: - # first string is a Door name - door_name = pars[0] - if len(pars) == 3: - macro_name = pars[1] - key = pars[2] - else: - key = pars[1] - else: - # first string is a Macro name + _, door_name, _ = TangoDeviceNameValidator().getNames(pars[0]) + if door_name is None: # first string is a Macro name macro_name = pars[0] + if len(pars) == 3: + macro_name = pars[1] + key = pars[2] + else: key = pars[1] env = self.getEnv(key=key, From e4ea7acb1dc3c188d947682f166142675657fc3e Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 12 Aug 2020 13:00:12 +0200 Subject: [PATCH 713/830] Add genv macro to the catalogue --- doc/source/users/standard_macro_catalog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/users/standard_macro_catalog.rst b/doc/source/users/standard_macro_catalog.rst index 0c7254a950..3e613ed625 100644 --- a/doc/source/users/standard_macro_catalog.rst +++ b/doc/source/users/standard_macro_catalog.rst @@ -88,6 +88,7 @@ environment related macros :columns: 5 * :class:`~sardana.macroserver.macros.env.lsenv` + * :class:`~sardana.macroserver.macros.env.genv` * :class:`~sardana.macroserver.macros.env.senv` * :class:`~sardana.macroserver.macros.env.usenv` * :class:`~sardana.macroserver.macros.env.dumpenv` From da2d0335cd28cba57cc17bff5de493688c98fea2 Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Wed, 12 Aug 2020 17:11:41 +0200 Subject: [PATCH 714/830] m, typo --- doc/source/devel/howto_controllers/howto_controller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_controllers/howto_controller.rst b/doc/source/devel/howto_controllers/howto_controller.rst index f1c743b9a3..eef0b5028f 100644 --- a/doc/source/devel/howto_controllers/howto_controller.rst +++ b/doc/source/devel/howto_controllers/howto_controller.rst @@ -547,7 +547,7 @@ It is a very common pattern that when integrating a new hardware (or eventually a software component) in Sardana you start from a Tango Device Server (either already existing one or you develop one as an intermediate layer). In this case your controller will need to access Tango and there are -two ways od doing that, either with Taurus_ (using `taurus.Device`) or +two ways of doing that, either with Taurus_ (using `taurus.Device`) or with PyTango_ (using `tango.DeviceProxy`). Please consult a similar discussion :ref:`sardana-macro-accessing-tango` on which one to use. From f9b8318956922e017cfddf710264206ea6a2a05b Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Wed, 12 Aug 2020 17:16:36 +0200 Subject: [PATCH 715/830] m, typo --- doc/source/devel/howto_macros/macros_general.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst index 3246f02e37..f8e1cb08c7 100644 --- a/doc/source/devel/howto_macros/macros_general.rst +++ b/doc/source/devel/howto_macros/macros_general.rst @@ -935,7 +935,7 @@ measurement group, etc. There exists different :term:`API` to access to Tango_ devices. -First, to access to Sardana elements it is recommended to use the Sardana +First, to access Sardana elements it is recommended to use the Sardana :term:`API`: e.g.: `~sardana.macroserver.macro.Macro.getMoveable` to obtain any moveable (motor or pseudo motor), `~sardana.macroserver.macro.Macro.getMeasurementGroup` to obtain a measurement From d4c383b64be14d21bcbb48f6f6078f8424e59e7c Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Wed, 12 Aug 2020 17:21:42 +0200 Subject: [PATCH 716/830] m, typo --- src/sardana/pool/poolutil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/poolutil.py b/src/sardana/pool/poolutil.py index f5c6fa2be2..5a30035a8f 100644 --- a/src/sardana/pool/poolutil.py +++ b/src/sardana/pool/poolutil.py @@ -43,7 +43,7 @@ def __call__(self, *args, **kwargs): return self def get_device(self, *args, **kwargs): - """Factory method to create a single instance the `tango.DeviceProxy` + """Factory method to create a single tango.DeviceProxy` instance per controller instance. :param ctrl_name: Controller name to which assign the proxy object From c1e332ff500781093ae17a2fd58dc456d278cd9f Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Wed, 12 Aug 2020 17:22:26 +0200 Subject: [PATCH 717/830] m, typo --- src/sardana/pool/poolutil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/pool/poolutil.py b/src/sardana/pool/poolutil.py index 5a30035a8f..309af9c682 100644 --- a/src/sardana/pool/poolutil.py +++ b/src/sardana/pool/poolutil.py @@ -43,7 +43,7 @@ def __call__(self, *args, **kwargs): return self def get_device(self, *args, **kwargs): - """Factory method to create a single tango.DeviceProxy` instance + """Factory method to create a single `tango.DeviceProxy` instance per controller instance. :param ctrl_name: Controller name to which assign the proxy object From adab5688e957d65f71c6cbdbe68002d51f8cda07 Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Wed, 12 Aug 2020 17:38:08 +0200 Subject: [PATCH 718/830] PEP8, avoid backslash continuation This use of backslash for line breaking is discouraged and in this case it is not even needed --- src/sardana/pool/poolutil.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sardana/pool/poolutil.py b/src/sardana/pool/poolutil.py index 309af9c682..d29b94af91 100644 --- a/src/sardana/pool/poolutil.py +++ b/src/sardana/pool/poolutil.py @@ -62,8 +62,7 @@ def get_device(self, *args, **kwargs): dev = ctrl_devs.get(device_name) if dev is None: import PyTango - ctrl_devs[device_name] = dev = \ - PyTango.DeviceProxy(device_name) + ctrl_devs[device_name] = dev = PyTango.DeviceProxy(device_name) return dev get_motor = get_phy_motor = get_pseudo_motor = get_motor_group = \ From d60cb2d9e706756bf04018c0b982e551896af85e Mon Sep 17 00:00:00 2001 From: Carlos Pascual Date: Wed, 12 Aug 2020 18:19:16 +0200 Subject: [PATCH 719/830] Update intersphinx links --- doc/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index a26c2a43f2..9392b1c373 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -291,13 +291,13 @@ def type_getattr(self, name): intersphinx_mapping = { 'python': ('https://docs.python.org/3', None), 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), - 'numpy': ('https://docs.scipy.org/doc/numpy', None), + 'numpy': ('https://numpy.org/doc/stable', None), 'ipython': ('http://ipython.org/ipython-doc/stable/', None), 'pytango': ('https://pytango.readthedocs.io/en/stable/', None), 'taurus': ('http://taurus-scada.org', None), 'pyqt': ('https://www.riverbankcomputing.com/static/Docs/PyQt5/', None), 'matplotlib': ('https://matplotlib.org/', None), - 'guiqwt': ('https://pythonhosted.org/guiqwt/', None), + 'guiqwt': ('https://guiqwt.readthedocs.io/en/latest/', None), } From 8e409f716b4a953b1c4ae6beae5d6e89088bfcfb Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 13 Aug 2020 10:11:37 +0200 Subject: [PATCH 720/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e619ee8910..f4717cefef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Measurement group configuration macros: `set_meas_conf` and `get_meas_conf` (#690) * Active measurement group selection macros: `set_meas` and `get_meas` (#690) * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) +* Add `genv` macro for printing environment variable values (#888) * Dump info on channels if MG acq fails in step scan, ct and uct (#1308) * Add timestamp to element's dumped information (#1308) * Quality to `SardanaAttribute` (#1353) From 2c8c6c1bb7c5b1d369bc66d712c0ba241b59d1eb Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 13 Aug 2020 13:55:30 +0200 Subject: [PATCH 721/830] Document scan statistics calculation --- .../users/environment_variable_catalog.rst | 47 +++++++++++++++++++ doc/source/users/scan.rst | 18 +++++++ doc/source/users/standard_macro_catalog.rst | 1 + 3 files changed, 66 insertions(+) diff --git a/doc/source/users/environment_variable_catalog.rst b/doc/source/users/environment_variable_catalog.rst index e7f72174fb..e776521b89 100644 --- a/doc/source/users/environment_variable_catalog.rst +++ b/doc/source/users/environment_variable_catalog.rst @@ -334,6 +334,53 @@ Example 2: .. seealso:: More about the extension to recorder map in :ref:`sardana-writing-recorders`. +.. _scanstats: + +ScanStats +~~~~~~~~~ +*Not mandatory, set by* :class:`~sardana.macroserver.macros.scan.scanstats` *macro* + +Stores the last calculated scan statistics. Its value is a dictionary with +the following key - value: + +* Motor - motor name on which the statistics were calculated +* ScanID - scan ID +* Stats - dictionary with channel(s) name as key and value being a dictionary + with the channel's scan statistics: + + * cen + * com + * fwhm + * int + * max + * maxpos + * mean + * min + * minpos + +For example:: + + {'Motor': 'mot01', + 'ScanID': 288, + 'Stats': {'ct01': {'cen': 5.0, + 'com': 5.000000000000002, + 'fwhm': 10.0, + 'int': 10.099999999999998, + 'max': 0.1, + 'maxpos': 0.0, + 'mean': 0.09999999999999998, + 'min': 0.1, + 'minpos': 0.0}, + 'gct01': {'cen': 4.999999999585752, + 'com': 5.000000000000002, + 'fwhm': 1.9999999568277493, + 'int': 21.289340331309955, + 'max': 1.0, + 'maxpos': 5.0, + 'mean': 0.21078554783475204, + 'min': 2.9802322387695312e-08, + 'minpos': 0.0}}} + .. _sharedmemory: SharedMemory diff --git a/doc/source/users/scan.rst b/doc/source/users/scan.rst index 6a98d7f4f4..193628f1a4 100644 --- a/doc/source/users/scan.rst +++ b/doc/source/users/scan.rst @@ -206,5 +206,23 @@ The snapshot is saved only once during a scan, on the very beginning. The exact way the snapshot data is saved depends on the :ref:`recorder ` and scan file format being used. +Scan statistics +--------------- + +Sardana may automatically calculate some basic statistics over the scan +results e.g., max, mean, FWHM, etc. + +In order to enable the statistic calculation you just need to attach +the :class:`~sardana.macroserver.macros.scan.scanstats` macro to the +``post-scan`` hook place (see :ref:`hook documentation ` +for more info). + +Apart from printing statistics by the scanstats macro these are stored in +the door's :ref:`scanstats` environment variable. This way some other macro +can use them e.g. move the scanned motor to the position where a given channel +reached the maximum value (*go to peak*). + + + .. _SEP6: http://www.sardana-controls.org/sep/?SEP6.md .. _Tango: http://www.tango-controls.org diff --git a/doc/source/users/standard_macro_catalog.rst b/doc/source/users/standard_macro_catalog.rst index 0c7254a950..b6a6dfbff0 100644 --- a/doc/source/users/standard_macro_catalog.rst +++ b/doc/source/users/standard_macro_catalog.rst @@ -218,3 +218,4 @@ scan related macros :columns: 5 * :class:`~sardana.macroserver.macros.standard.newfile` + * :class:`~sardana.macroserver.macros.scan.scanstats` From 336bb46d72bde889cf9ca26337252aedda742f34 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 14 Aug 2020 09:39:31 +0200 Subject: [PATCH 722/830] Make ActiveMntGrp mandatory --- src/sardana/macroserver/macros/standard.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index a02bed80ac..d47c107724 100755 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -1053,6 +1053,8 @@ def run(self, ScanFilePath_list, ScanID): class plotselect(Macro): """select channels for plotting in the active measurement group""" + + env = ("ActiveMntGrp", ) param_def = [ ['channel', [['channel', Type.ExpChannel, 'None', ""], {'min': 0}], @@ -1061,12 +1063,7 @@ class plotselect(Macro): ] def run(self, channel): - try: - active_meas_grp = self.getEnv('ActiveMntGrp') - except UnknownEnv: - self.warning('No active measurement group found') - return - + active_meas_grp = self.getEnv('ActiveMntGrp') meas_grp = self.getMeasurementGroup(active_meas_grp) self.output("Active measurement group: {}".format(meas_grp.name)) From cc2c231fd6738912b41aec87b905eab571e2e05e Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 14 Aug 2020 09:39:47 +0200 Subject: [PATCH 723/830] Add plotselect to the catalogue --- doc/source/users/standard_macro_catalog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/users/standard_macro_catalog.rst b/doc/source/users/standard_macro_catalog.rst index 0c7254a950..4cd0940472 100644 --- a/doc/source/users/standard_macro_catalog.rst +++ b/doc/source/users/standard_macro_catalog.rst @@ -134,6 +134,7 @@ experiment configuration macros * :class:`~sardana.macroserver.macros.expconf.defsnap` * :class:`~sardana.macroserver.macros.expconf.udefsnap` * :class:`~sardana.macroserver.macros.expconf.lssnap` + * :class:`~sardana.macroserver.macros.standard.plotselect` general hooks macros -------------------- From 93df2891c3d7bc7f9f400f7ef4145b0d53ba4642 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 14 Aug 2020 10:49:10 +0200 Subject: [PATCH 724/830] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4717cefef..ca5f54f4ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Measurement group configuration macros: `set_meas_conf` and `get_meas_conf` (#690) * Active measurement group selection macros: `set_meas` and `get_meas` (#690) * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) -* Add `genv` macro for printing environment variable values (#888) +* `plotselect` macro for configuring channels for online scan plotting (#824) +* `genv` macro for printing environment variable values (#888) * Dump info on channels if MG acq fails in step scan, ct and uct (#1308) * Add timestamp to element's dumped information (#1308) * Quality to `SardanaAttribute` (#1353) From 1e5ebaf3d07f7c82f64e85d82d1e28db342b5a16 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 14 Aug 2020 11:05:05 +0200 Subject: [PATCH 725/830] Correct the macro docsting --- src/sardana/macroserver/macros/scan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index f4101ac06b..a5dbda6cbe 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1884,10 +1884,11 @@ def _get_nr_points(self): class scanstats(Macro): - """Calculate basic statistics of the first enabled and plotted counter in + """Calculate basic statistics of the enabled and plotted channels in the active measurement group for the last scan. Print it and publish it in the env. The macro must be hooked in the post-scan hook place. """ + param_def = [ ["channel", [["channel", Type.ExpChannel, None, ""], {"min": 0}], From 160d9dfee55062639fc9c84a21c25b3a5d2b0fa7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 14 Aug 2020 11:06:54 +0200 Subject: [PATCH 726/830] Make ActiveMntGrp mandatory for the scanstats macro --- src/sardana/macroserver/macros/scan.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index a5dbda6cbe..5bdb8d1570 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1889,6 +1889,8 @@ class scanstats(Macro): in the env. The macro must be hooked in the post-scan hook place. """ + env = ("ActiveMntGrp", ) + param_def = [ ["channel", [["channel", Type.ExpChannel, None, ""], {"min": 0}], @@ -1900,12 +1902,7 @@ class scanstats(Macro): def run(self, channel): parent = self.getParentMacro() if parent: - try: - active_meas_grp = self.getEnv("ActiveMntGrp") - except UnknownEnv: - self.warning("No active measurement group found") - return - + active_meas_grp = self.getEnv("ActiveMntGrp") meas_grp = self.getMeasurementGroup(active_meas_grp) calc_channels = [] enabled_channels = meas_grp.getEnabled() From f702858745d673f68eabc49631ba91ca2127fc07 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 14 Aug 2020 11:51:50 +0200 Subject: [PATCH 727/830] Refactor extraction of scan data Refactor extraction of scan data in order to avoid extracting motor's data multiple times. --- src/sardana/macroserver/macros/scan.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 5bdb8d1570..02675f566f 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1929,24 +1929,26 @@ def run(self, channel): calc_channels.append(next(iter(enabled_channels))) selected_motor = str(parent.motors[0]) - data = parent.data stats = {} col_header = [] cols = [] + motor_data = [] + channels_data = {} for channel_name in calc_channels: - counter_data = [] - motor_data = [] + channels_data[channel_name] = [] - for idx, rc in data.items(): - counter_data.append(rc[channel_name]) - motor_data.append(rc[selected_motor]) + for idx, rc in parent.data.items(): + motor_data.append(rc[selected_motor]) + for channel_name in calc_channels: + channels_data[channel_name].append(rc[channel_name]) - counter_data = numpy.array(counter_data) - motor_data = numpy.array(motor_data) + motor_data = numpy.array(motor_data) + for channel_name, data in channels_data.items(): + channel_data = numpy.array(data) (_min, _max, min_at, max_at, half_max, com, mean, _int, - fwhm, cen) = self._calcStats(motor_data, counter_data) + fwhm, cen) = self._calcStats(motor_data, channel_data) stats[channel_name] = { "min": _min, "max": _max, From f1022c642009682feca84f768eec949007def22f Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 14 Aug 2020 11:54:02 +0200 Subject: [PATCH 728/830] Invert logic for scanstats execution protection --- src/sardana/macroserver/macros/scan.py | 173 +++++++++++++------------ 1 file changed, 87 insertions(+), 86 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 02675f566f..18d7015bfa 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1901,94 +1901,95 @@ class scanstats(Macro): def run(self, channel): parent = self.getParentMacro() - if parent: - active_meas_grp = self.getEnv("ActiveMntGrp") - meas_grp = self.getMeasurementGroup(active_meas_grp) - calc_channels = [] - enabled_channels = meas_grp.getEnabled() - if channel: - stat_channels = [chan.name for chan in channel] - else: - stat_channels = [key for key in enabled_channels.keys()] - - for chan in stat_channels: - enabled = enabled_channels.get(chan) - if enabled is None: - self.warning("{} not in {}".format(chan, meas_grp.name)) - else: - if not enabled and channel: - self.warning("{} not enabled".format(chan)) - elif enabled and channel: - # channel was given as parameters - calc_channels.append(chan) - elif enabled and meas_grp.getPlotType(chan)[chan] == 1: - calc_channels.append(chan) - - if calc_channels == []: - # fallback is first enabled channel in meas_grp - calc_channels.append(next(iter(enabled_channels))) - - selected_motor = str(parent.motors[0]) - stats = {} - col_header = [] - cols = [] - - motor_data = [] - channels_data = {} - for channel_name in calc_channels: - channels_data[channel_name] = [] - - for idx, rc in parent.data.items(): - motor_data.append(rc[selected_motor]) - for channel_name in calc_channels: - channels_data[channel_name].append(rc[channel_name]) - - motor_data = numpy.array(motor_data) - for channel_name, data in channels_data.items(): - channel_data = numpy.array(data) - - (_min, _max, min_at, max_at, half_max, com, mean, _int, - fwhm, cen) = self._calcStats(motor_data, channel_data) - stats[channel_name] = { - "min": _min, - "max": _max, - "minpos": min_at, - "maxpos": max_at, - "mean": mean, - "int": _int, - "com": com, - "fwhm": fwhm, - "cen": cen} - - col_header.append([channel_name]) - cols.append([ - stats[channel_name]["min"], - stats[channel_name]["max"], - stats[channel_name]["minpos"], - stats[channel_name]["maxpos"], - stats[channel_name]["mean"], - stats[channel_name]["int"], - stats[channel_name]["com"], - stats[channel_name]["fwhm"], - stats[channel_name]["cen"], - ]) - self.info("Statistics for movable: {:s}".format(selected_motor)) - - table = Table(elem_list=cols, elem_fmt=["%*g"], - row_head_str=["MIN", "MAX", "MIN@", "MAX@", - "MEAN", "INT", "COM", "FWHM", "CEN"], - col_head_str=col_header, col_head_sep="-") - out = table.genOutput() - - for line in out: - self.info(line) - self.setEnv("{:s}.ScanStats".format(self.getDoorName()), - {"Stats": stats, - "Motor": selected_motor, - "ScanID": self.getEnv("ScanID")}) - else: + if not parent: self.warning("for now the scanstats macro can only be executed as" " a post-scan hook") + return + + active_meas_grp = self.getEnv("ActiveMntGrp") + meas_grp = self.getMeasurementGroup(active_meas_grp) + calc_channels = [] + enabled_channels = meas_grp.getEnabled() + if channel: + stat_channels = [chan.name for chan in channel] + else: + stat_channels = [key for key in enabled_channels.keys()] + + for chan in stat_channels: + enabled = enabled_channels.get(chan) + if enabled is None: + self.warning("{} not in {}".format(chan, meas_grp.name)) + else: + if not enabled and channel: + self.warning("{} not enabled".format(chan)) + elif enabled and channel: + # channel was given as parameters + calc_channels.append(chan) + elif enabled and meas_grp.getPlotType(chan)[chan] == 1: + calc_channels.append(chan) + + if calc_channels == []: + # fallback is first enabled channel in meas_grp + calc_channels.append(next(iter(enabled_channels))) + + selected_motor = str(parent.motors[0]) + stats = {} + col_header = [] + cols = [] + + motor_data = [] + channels_data = {} + for channel_name in calc_channels: + channels_data[channel_name] = [] + + for idx, rc in parent.data.items(): + motor_data.append(rc[selected_motor]) + for channel_name in calc_channels: + channels_data[channel_name].append(rc[channel_name]) + + motor_data = numpy.array(motor_data) + for channel_name, data in channels_data.items(): + channel_data = numpy.array(data) + + (_min, _max, min_at, max_at, half_max, com, mean, _int, + fwhm, cen) = self._calcStats(motor_data, channel_data) + stats[channel_name] = { + "min": _min, + "max": _max, + "minpos": min_at, + "maxpos": max_at, + "mean": mean, + "int": _int, + "com": com, + "fwhm": fwhm, + "cen": cen} + + col_header.append([channel_name]) + cols.append([ + stats[channel_name]["min"], + stats[channel_name]["max"], + stats[channel_name]["minpos"], + stats[channel_name]["maxpos"], + stats[channel_name]["mean"], + stats[channel_name]["int"], + stats[channel_name]["com"], + stats[channel_name]["fwhm"], + stats[channel_name]["cen"], + ]) + self.info("Statistics for movable: {:s}".format(selected_motor)) + + table = Table(elem_list=cols, elem_fmt=["%*g"], + row_head_str=["MIN", "MAX", "MIN@", "MAX@", + "MEAN", "INT", "COM", "FWHM", "CEN"], + col_head_str=col_header, col_head_sep="-") + out = table.genOutput() + + for line in out: + self.info(line) + self.setEnv("{:s}.ScanStats".format(self.getDoorName()), + {"Stats": stats, + "Motor": selected_motor, + "ScanID": self.getEnv("ScanID")}) @staticmethod def _calcStats(x, y): From 4cfe940a871511f1ba7a9d84fbc0a06a0b88a51a Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 14 Aug 2020 12:23:12 +0200 Subject: [PATCH 729/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca5f54f4ee..58410efc8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Measurement group configuration macros: `set_meas_conf` and `get_meas_conf` (#690) * Active measurement group selection macros: `set_meas` and `get_meas` (#690) * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) +* Automatic scan statistics calculation with the `scanstats` macro as the `post-scan` + hook stored in the `ScanStats` environment variable * `plotselect` macro for configuring channels for online scan plotting (#824) * `genv` macro for printing environment variable values (#888) * Dump info on channels if MG acq fails in step scan, ct and uct (#1308) From 3d6e7d843b78c0d246372a5191cc002479a1bd09 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 15 Aug 2020 16:44:22 +0200 Subject: [PATCH 730/830] Use getMoveable instead of getMotion Moveable is enough when you don't want to move. --- src/sardana/macroserver/macros/standard.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 144d867844..14db088701 100755 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -1178,6 +1178,6 @@ def run(self): self.warning("No motor from last scan available") return - motor = self.getMotion([motor_name]) + motor = self.getMoveable(motor_name) self.info("motor {:s} is\nat {:.4f}".format(motor_name, - motor.readPosition()[0])) + motor.getPosition())) From 6c921721457722ab8dc863787db08d8b6c167b96 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 15 Aug 2020 16:45:39 +0200 Subject: [PATCH 731/830] Make ScanStats mandatory env var --- src/sardana/macroserver/macros/standard.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 14db088701..7d0f37b5b6 100755 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -1093,17 +1093,15 @@ def run(self, channel): class _movetostatspos(Macro): """This macro does the logic for pic and cen""" + env = ("ScanStats", ) + param_def = [ ['channel', Type.ExpChannel, Optional, 'name of channel'], ['caller', Type.String, None, 'caller (pic or cen)'] ] def run(self, channel, caller): - try: - stats = self.getEnv('ScanStats', door_name=self.getDoorName()) - except UnknownEnv: - self.warning("No ScanStats available in env") - return + stats = self.getEnv('ScanStats', door_name=self.getDoorName()) if channel is None: # use first channel in stats From 7b0ca18eb7d30b7b2573de4191eccbeea5a4719d Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 15 Aug 2020 16:46:18 +0200 Subject: [PATCH 732/830] Raise exceptions instead of returning --- src/sardana/macroserver/macros/standard.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index 7d0f37b5b6..fde0f5030a 100755 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -1110,9 +1110,8 @@ def run(self, channel, caller): if channel.name in stats['Stats']: channel = channel.name else: - self.warning("Channel {} not present in ScanStats".format( - channel.name)) - return + raise Exception("channel {} not present in ScanStats".format( + channel.name)) if caller == 'pic': stats_value = 'maxpos' @@ -1121,8 +1120,7 @@ def run(self, channel, caller): stats_value = 'cen' stats_str = 'CEN' else: - self.warning("Caller {} is unkown".format(caller)) - return + raise Exception("caller {} is unknown".format(caller)) motor_name = stats['Motor'] motor = self.getMotion([motor_name]) From 4eb995d0789d23b9f4c043eb7ac36a9ed8eb4071 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 15 Aug 2020 16:49:36 +0200 Subject: [PATCH 733/830] Make ScanStats env var mandatory for where macro --- src/sardana/macroserver/macros/standard.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index fde0f5030a..cd8a9163f8 100755 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -1167,13 +1167,10 @@ def run(self, channel): class where(Macro): """This macro shows the current position of the last scanned motor.""" - def run(self): - try: - motor_name = self.getEnv('ScanStats')['Motor'] - except UnknownEnv: - self.warning("No motor from last scan available") - return + env = ("ScanStats", ) + def run(self): + motor_name = self.getEnv('ScanStats')['Motor'] motor = self.getMoveable(motor_name) self.info("motor {:s} is\nat {:.4f}".format(motor_name, motor.getPosition())) From 105f9f57229b8d9ccaf3f60e2b8de0754f336c8b Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 15 Aug 2020 16:54:41 +0200 Subject: [PATCH 734/830] Add where, pic and cen to the catalogue --- doc/source/users/standard_macro_catalog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/source/users/standard_macro_catalog.rst b/doc/source/users/standard_macro_catalog.rst index 86ab6c83b6..bd9f1e2731 100644 --- a/doc/source/users/standard_macro_catalog.rst +++ b/doc/source/users/standard_macro_catalog.rst @@ -221,3 +221,6 @@ scan related macros * :class:`~sardana.macroserver.macros.standard.newfile` * :class:`~sardana.macroserver.macros.scan.scanstats` + * :class:`~sardana.macroserver.macros.standard.where` + * :class:`~sardana.macroserver.macros.standard.pic` + * :class:`~sardana.macroserver.macros.standard.cen` From 0329f3a8fd7ee1ffe28d9c84bd1e360e34464c6a Mon Sep 17 00:00:00 2001 From: zreszela Date: Sat, 15 Aug 2020 17:24:27 +0200 Subject: [PATCH 735/830] Document pic and cen --- .../users/environment_variable_catalog.rst | 18 +++++++++--------- doc/source/users/scan.rst | 12 ++++++++---- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/doc/source/users/environment_variable_catalog.rst b/doc/source/users/environment_variable_catalog.rst index e776521b89..e66214594d 100644 --- a/doc/source/users/environment_variable_catalog.rst +++ b/doc/source/users/environment_variable_catalog.rst @@ -348,15 +348,15 @@ the following key - value: * Stats - dictionary with channel(s) name as key and value being a dictionary with the channel's scan statistics: - * cen - * com - * fwhm - * int - * max - * maxpos - * mean - * min - * minpos + * cen - center of FWHM + * com - center of mass of channel data + * fwhm - full-width at half-max of channel data + * int - sum/integral of channel data + * max - maximum of channel data + * maxpos - motor position where the channel reached the maximum + * mean - average of channel data + * min - minimum of channel data + * minpos - motor position where the channel reached the minimum For example:: diff --git a/doc/source/users/scan.rst b/doc/source/users/scan.rst index 193628f1a4..29c7236c12 100644 --- a/doc/source/users/scan.rst +++ b/doc/source/users/scan.rst @@ -212,15 +212,19 @@ Scan statistics Sardana may automatically calculate some basic statistics over the scan results e.g., max, mean, FWHM, etc. -In order to enable the statistic calculation you just need to attach +In order to enable the statistics calculation you just need to attach the :class:`~sardana.macroserver.macros.scan.scanstats` macro to the ``post-scan`` hook place (see :ref:`hook documentation ` for more info). -Apart from printing statistics by the scanstats macro these are stored in +Apart from printing the statistics by the scanstats macro these are stored in the door's :ref:`scanstats` environment variable. This way some other macro -can use them e.g. move the scanned motor to the position where a given channel -reached the maximum value (*go to peak*). +can use them e.g., + +* move the scanned motor to the position where a given channel + reached the maximum value (:class:`~sardana.macroserver.macros.standard.pic`) +* move the scanned motor to center position of FWHM + (:class:`~sardana.macroserver.macros.standard.cen`) From 8b0ed851714a8c30810452c7754feae6e7d80bd5 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 08:59:46 +0200 Subject: [PATCH 736/830] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58410efc8d..d2c7d19469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,10 @@ This file follows the formats and conventions from [keepachangelog.com] * Active measurement group selection macros: `set_meas` and `get_meas` (#690) * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) * Automatic scan statistics calculation with the `scanstats` macro as the `post-scan` - hook stored in the `ScanStats` environment variable + hook stored in the `ScanStats` environment variable (#880) +* `pic`, `cen` to move the scanned motor to the peak and center of FWHM values + respectively (#890) +* `where` macro to print the scanned motor position (#890) * `plotselect` macro for configuring channels for online scan plotting (#824) * `genv` macro for printing environment variable values (#888) * Dump info on channels if MG acq fails in step scan, ct and uct (#1308) From f901a791a826fbf1bb4b63a47114742fe220b043 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 17 Aug 2020 12:41:48 +0200 Subject: [PATCH 737/830] Always return state_info as a tuple Early unification of the PoolController.raw_read_axis_states() return value facilitates downstream interpretations. Always return a tuple with state_info even if it just contains a state (in this case fill status with None). --- src/sardana/pool/poolbaseelement.py | 6 +----- src/sardana/pool/poolcontroller.py | 2 ++ src/sardana/pool/poolmotor.py | 6 +----- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/sardana/pool/poolbaseelement.py b/src/sardana/pool/poolbaseelement.py index d91c17d28a..68d3e88813 100644 --- a/src/sardana/pool/poolbaseelement.py +++ b/src/sardana/pool/poolbaseelement.py @@ -287,11 +287,7 @@ def put_state_info(self, state_info): def _from_ctrl_state_info(self, state_info): state_info, _ = state_info # ignoring exc_info - if state_info in State: - state = state_info - status = None - else: - state, status = state_info + state, status = state_info return int(state), status # -------------------------------------------------------------------------- diff --git a/src/sardana/pool/poolcontroller.py b/src/sardana/pool/poolcontroller.py index 299dd233da..5ac8ab3213 100644 --- a/src/sardana/pool/poolcontroller.py +++ b/src/sardana/pool/poolcontroller.py @@ -595,6 +595,8 @@ def raw_read_axis_states(self, axes=None, ctrl_states=None): if state_info is None: raise Exception("%s.StateOne(%s(%d)) returns 'None'" % (self.name, element.name, axis)) + if state_info in State: + state_info = (state_info, None) state_info = state_info, None except: exc_info = sys.exc_info() diff --git a/src/sardana/pool/poolmotor.py b/src/sardana/pool/poolmotor.py index 3a9d52f3a7..da281c21d3 100644 --- a/src/sardana/pool/poolmotor.py +++ b/src/sardana/pool/poolmotor.py @@ -245,11 +245,7 @@ def on_change(self, evt_src, evt_type, evt_value): def _from_ctrl_state_info(self, state_info): state_info, _ = state_info - if state_info in State: - state = state_info - status = None - ls = 0 - elif len(state_info) > 2: + if len(state_info) > 2: state, status, ls = state_info[:3] else: state, other = state_info[:2] From 0001a3cffa5a8cb3c3cfce71a9406ba22e62b6ff Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 12:50:52 +0200 Subject: [PATCH 738/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2c7d19469..dd67cf94a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -146,6 +146,8 @@ This file follows the formats and conventions from [keepachangelog.com] * `online` kwarg in `SpockBaseDoor` constructor (#1260) * `sardana.requirements` (#1185) * `sardanatestsuite` and `sardana.test.testsuite.*` utility functions (#1347) +* `sardana.release` attributes: `version_info` and `revision` (#1315, #1357) +* `sardana.spock.release` module (#1315, #1357) ## [2.8.6] 2020-08-10 From 648e938daf30dfcfe54035cb571606e69a86f5c1 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 12:53:57 +0200 Subject: [PATCH 739/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd67cf94a5..d3a1152876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,7 @@ This file follows the formats and conventions from [keepachangelog.com] ### Removed * Support to Python < 3.5 (#1089, #1173, #1201, #1263) +* `sardana.macroserver.macro.ParamRepeat` class (#1315, #1358) * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From 0801e5feb672f3b457f2c4ffd78058a05367d3c7 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 12:56:37 +0200 Subject: [PATCH 740/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3a1152876..4c974bd195 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,6 +147,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `online` kwarg in `SpockBaseDoor` constructor (#1260) * `sardana.requirements` (#1185) * `sardanatestsuite` and `sardana.test.testsuite.*` utility functions (#1347) +* Hook places: `hooks` and `pre-start` (#1315, #1359) * `sardana.release` attributes: `version_info` and `revision` (#1315, #1357) * `sardana.spock.release` module (#1315, #1357) From 87fd1efcc892134f69e54a9921076a9ee11a6f8e Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 12:57:48 +0200 Subject: [PATCH 741/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c974bd195..4850b6498c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -148,6 +148,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `sardana.requirements` (#1185) * `sardanatestsuite` and `sardana.test.testsuite.*` utility functions (#1347) * Hook places: `hooks` and `pre-start` (#1315, #1359) +* `FileRecorder` macro hint (#1315, #1360) * `sardana.release` attributes: `version_info` and `revision` (#1315, #1357) * `sardana.spock.release` module (#1315, #1357) From ea561e2db560958ea7b5e1f5200859a7d50e0874 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:01:57 +0200 Subject: [PATCH 742/830] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4850b6498c..55a9d14e13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -141,6 +141,13 @@ This file follows the formats and conventions from [keepachangelog.com] * Support to Python < 3.5 (#1089, #1173, #1201, #1263) * `sardana.macroserver.macro.ParamRepeat` class (#1315, #1358) +* Controller API (#1315, #1361) + * `class_prop` + * `ctrl_extra_attributes` + * `inst_name` + * `SetPar()` and `GerPar()` + * `SetExtraAttributePar()` and `GetExtraAttributePar()` + * `ctrl_properties` with "Description" as `str` * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From 790aaa460c0da0c79c40deacd089c952f8c933e9 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:04:44 +0200 Subject: [PATCH 743/830] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55a9d14e13..4077ad2796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -141,13 +141,16 @@ This file follows the formats and conventions from [keepachangelog.com] * Support to Python < 3.5 (#1089, #1173, #1201, #1263) * `sardana.macroserver.macro.ParamRepeat` class (#1315, #1358) -* Controller API (#1315, #1361) +* Controller API (#1315, #1361): * `class_prop` * `ctrl_extra_attributes` * `inst_name` * `SetPar()` and `GerPar()` * `SetExtraAttributePar()` and `GetExtraAttributePar()` * `ctrl_properties` with "Description" as `str` +* `CounterTimerController` controller API (#1315, #1362): + * `_trigger_type` + * `PreStartAllCT()`, `PreStartOneCT()`, `StartAllCT()` and `StartOneCT()` * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From fe3d35bb0fa0d84b5a775691f5708b83ea3f64a8 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:06:55 +0200 Subject: [PATCH 744/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4077ad2796..489f382480 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,6 +151,8 @@ This file follows the formats and conventions from [keepachangelog.com] * `CounterTimerController` controller API (#1315, #1362): * `_trigger_type` * `PreStartAllCT()`, `PreStartOneCT()`, `StartAllCT()` and `StartOneCT()` +* `PseudoMotorController` controller API (#1315, #1363) + * `calc_all_pseudo()`, `calc_all_physical()`, `calc_pseudo()` and `calc_physical()` * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From 56d2b1e74c1261f8bb4597ddebefacfd8cf2ba7e Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:10:19 +0200 Subject: [PATCH 745/830] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 489f382480..975aacd2a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -153,6 +153,10 @@ This file follows the formats and conventions from [keepachangelog.com] * `PreStartAllCT()`, `PreStartOneCT()`, `StartAllCT()` and `StartOneCT()` * `PseudoMotorController` controller API (#1315, #1363) * `calc_all_pseudo()`, `calc_all_physical()`, `calc_pseudo()` and `calc_physical()` +* `PseudoCounterController` controller API (#1315, #1364) + * `calc()` +* `IORegisterController` controller API (#1315, #1365): + * `predefined_values` * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From 7a201c121ce3bcb104a574761c39d41af83dec81 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:13:46 +0200 Subject: [PATCH 746/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 975aacd2a0..373a2fdcb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -157,6 +157,8 @@ This file follows the formats and conventions from [keepachangelog.com] * `calc()` * `IORegisterController` controller API (#1315, #1365): * `predefined_values` +* `Loadable` backawards compatibility (without `repetitions` and `latency` arguments) + (#1315, #1370) * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From 4fc59c07c0084a80d2701f0cf9a13733c1b9fafe Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:19:05 +0200 Subject: [PATCH 747/830] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 373a2fdcb2..067fcf2d54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -159,6 +159,10 @@ This file follows the formats and conventions from [keepachangelog.com] * `predefined_values` * `Loadable` backawards compatibility (without `repetitions` and `latency` arguments) (#1315, #1370) +* Backwards compatibility in measurement group configuration (#1315, #1372) + * not complete names (without the scheme and PQDN) + * `trigger_type` +* * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From a49d4598e25c44cd4a2accd4ec3d23af7e8cafa7 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:20:57 +0200 Subject: [PATCH 748/830] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 067fcf2d54..870d9152c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -141,6 +141,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Support to Python < 3.5 (#1089, #1173, #1201, #1263) * `sardana.macroserver.macro.ParamRepeat` class (#1315, #1358) +* Backwards compatibility for measurement group start without preparation + (#1315, #1373) * Controller API (#1315, #1361): * `class_prop` * `ctrl_extra_attributes` @@ -162,7 +164,6 @@ This file follows the formats and conventions from [keepachangelog.com] * Backwards compatibility in measurement group configuration (#1315, #1372) * not complete names (without the scheme and PQDN) * `trigger_type` -* * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From e4d465ef2e2dda8c9f0b02b79d98f26e6e8009fd Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:23:12 +0200 Subject: [PATCH 749/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 870d9152c1..9d3baad33c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -164,6 +164,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Backwards compatibility in measurement group configuration (#1315, #1372) * not complete names (without the scheme and PQDN) * `trigger_type` +* `Label` and `Calibration` attributes of `DiscretePseudMotor` controller + (#1315, #1374) * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From 3bd46ca43f543394d7f8687c342f938ee18cd12b Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:24:16 +0200 Subject: [PATCH 750/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d3baad33c..ba57bcd95e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -176,6 +176,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `FileRecorder` macro hint (#1315, #1360) * `sardana.release` attributes: `version_info` and `revision` (#1315, #1357) * `sardana.spock.release` module (#1315, #1357) +* Support to IPython < 1 (#1315, #1375) ## [2.8.6] 2020-08-10 From 0cab436e58f3caeae786254630fe46fcb8d15299 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:25:21 +0200 Subject: [PATCH 751/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba57bcd95e..466951bf00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -161,6 +161,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `predefined_values` * `Loadable` backawards compatibility (without `repetitions` and `latency` arguments) (#1315, #1370) +* Door's Tango device `Abort` command (#1315, #1376) * Backwards compatibility in measurement group configuration (#1315, #1372) * not complete names (without the scheme and PQDN) * `trigger_type` From 66ac0d0d5c09cc9c0b3b87f92eb9c4de0b40ca6e Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:27:02 +0200 Subject: [PATCH 752/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 466951bf00..2b0cc0d77a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -167,6 +167,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `trigger_type` * `Label` and `Calibration` attributes of `DiscretePseudMotor` controller (#1315, #1374) +* `Controller`'s (Taurus extension) `getUsedAxis` method (#1315, #1377) * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From b3522e005886b8f0d5b1be5a1fbb4503c76b4c94 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:28:44 +0200 Subject: [PATCH 753/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b0cc0d77a..bb6f8a66bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -168,6 +168,8 @@ This file follows the formats and conventions from [keepachangelog.com] * `Label` and `Calibration` attributes of `DiscretePseudMotor` controller (#1315, #1374) * `Controller`'s (Taurus extension) `getUsedAxis` method (#1315, #1377) +* `sardana.taurus.qt.qtgui.extra_macroexecutor.dooroutput.DoorAttrListener` class + (#1315, #1378) * "Show/hide plots" button in `expconf` (#960, #1255, #1257) * `plotsButton` argument in `ExpDescriptionEditor` constructor (#960, #1255, #1257) * `showscan online_raw` magic command in spock (#1260) From 07ebe80edfc17245e395ea652543eb027c718ad3 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:30:52 +0200 Subject: [PATCH 754/830] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb6f8a66bb..9519a5b141 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -167,6 +167,10 @@ This file follows the formats and conventions from [keepachangelog.com] * `trigger_type` * `Label` and `Calibration` attributes of `DiscretePseudMotor` controller (#1315, #1374) +* MacroButton's methods (#1315, #1379) + * `toggleProgress()` + * `updateMacroArgumentFromSignal()` + * `connectArgEditors()` * `Controller`'s (Taurus extension) `getUsedAxis` method (#1315, #1377) * `sardana.taurus.qt.qtgui.extra_macroexecutor.dooroutput.DoorAttrListener` class (#1315, #1378) From 529c34d7909b4e8ca93949bb79cc6757c17e78f0 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 17 Aug 2020 13:32:00 +0200 Subject: [PATCH 755/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9519a5b141..4b6a5d8084 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -161,6 +161,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `predefined_values` * `Loadable` backawards compatibility (without `repetitions` and `latency` arguments) (#1315, #1370) +* `PoolMotorSlim` widget (#1315, #1380) * Door's Tango device `Abort` command (#1315, #1376) * Backwards compatibility in measurement group configuration (#1315, #1372) * not complete names (without the scheme and PQDN) From 959ebc2ae638e47d8679384c895dffeb76f3528f Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 18 Aug 2020 11:35:52 +0200 Subject: [PATCH 756/830] Split migration guide in multiple rst files --- .../0to1.rst} | 74 ++++++++----------- doc/source/devel/guide_migration/2to3.rst | 5 ++ doc/source/devel/guide_migration/index.rst | 16 ++++ doc/source/devel/index.rst | 2 +- 4 files changed, 54 insertions(+), 43 deletions(-) rename doc/source/devel/{guide_migration.rst => guide_migration/0to1.rst} (91%) create mode 100644 doc/source/devel/guide_migration/2to3.rst create mode 100644 doc/source/devel/guide_migration/index.rst diff --git a/doc/source/devel/guide_migration.rst b/doc/source/devel/guide_migration/0to1.rst similarity index 91% rename from doc/source/devel/guide_migration.rst rename to doc/source/devel/guide_migration/0to1.rst index e85bcbb54a..61258c095f 100644 --- a/doc/source/devel/guide_migration.rst +++ b/doc/source/devel/guide_migration/0to1.rst @@ -1,20 +1,13 @@ .. currentmodule:: sardana.pool.controller -.. _sardana-migration-guide: +.. _0to1: - -=================================== -Sardana migration guide -=================================== - -This chapter describes how to migrate different sardana components between the -different API versions. +======== +v0 -> v1 +======== How to migrate your macro code -=================================== - -API v0 -> v1 -------------- +============================== This chapter describes the necessary steps to fully migrate your macros from *API v0* ( sardana 0.x ) to *API v1* ( sardana 1.x ) @@ -26,11 +19,11 @@ The following are the 2 necessary changes to make your macros work in sardana *API v1*: 1. from:: - + from macro import Macro, Type, Table, List - + to:: - + from sardana.macroserver.macro import Macro, Type, Table, List 2. Parameter type ``Type.Motor`` should be changed ``Type.Moveable``. @@ -39,8 +32,8 @@ sardana *API v1*: and `Moveable` means all moveable elements (including physical motor, pseudo motor). -New features in API v1 -""""""""""""""""""""""" +New features +"""""""""""" This chapter is a summary of all new features in *API v1*. @@ -49,9 +42,6 @@ This chapter is a summary of all new features in *API v1*. How to migrate your controller code =================================== -API v0 -> v1 -------------- - This chapter describes the necessary steps to fully migrate your controller from *API v0* ( sardana 0.x ) to *API v1* ( sardana 1.x ) @@ -62,23 +52,23 @@ The following are the 2 necessary changes to make your controller work in sardana *API v1*: 1. from:: - + import pool from pool import /PoolUtil - + to:: - + from sardana import pool from sardana.pool import PoolUtil from sardana.pool.controller import - + 2. change contructor from:: def __init__(self, inst, props): code - + to:: - + def __init__(self, inst, props, *args, **kwargs): MotorController.__init__(self, inst, props, *args, **kwargs) code @@ -89,31 +79,31 @@ sardana *API v1*: The following change is not mandatory but is necessary in order for your controller to be recognized by the pool to be a *API v1* controller: -3. _log member changed from :class:`logging.Logger` to +3. _log member changed from :class:`logging.Logger` to :class:`taurus.core.util.Logger`. This means that you need to change code from:: - + self._log.setLevel(logging.INFO) - + to:: - + self._log.setLogLevel(logging.INFO) - + or:: - + self._log.setLogLevel(taurus.Info) - + since taurus.Info == logging.INFO. - + Optional changes """""""""""""""" -The following changes are not necessary to make your controller work. The +The following changes are not necessary to make your controller work. The *API v1* supports the *API v0* on these matters. 1. **class members**: - + #. from: :attr:`~Controller.class_prop` to: :attr:`~Controller.ctrl_properties` #. from: :attr:`~Controller.ctrl_extra_attributes` to: :attr:`~Controller.axis_attributes` #. new feature in *API v1*: :attr:`~Controller.ctrl_attributes` @@ -121,7 +111,7 @@ The following changes are not necessary to make your controller work. The 3. **data types**: #. :meth:`~Controller.StateOne` **return type**: Previously - :meth:`~Controller.StateOne` had to return a member of + :meth:`~Controller.StateOne` had to return a member of :class:`PyTango.DevState`. Now it **can** instead return a member of :class:`~sardana.sardanadefs.State`. This eliminates the need to import :mod:`PyTango`. @@ -137,7 +127,7 @@ The following changes are not necessary to make your controller work. The #. from: :meth:`~Controller.GetExtraAttributePar` to: :meth:`~Controller.GetAxisExtraPar` #. from: :meth:`~Controller.SetExtraAttributePar` to: :meth:`~Controller.SetAxisExtraPar` #. new feature in *API v1*: :meth:`~Controller.GetCtrlPar`, :meth:`~Controller.SetCtrlPar` - #. new feature in *API v1*: :meth:`~Stopable.AbortAll` (has default + #. new feature in *API v1*: :meth:`~Stopable.AbortAll` (has default implementation which calls :meth:`~Stopable.AbortOne` for each axis) 5. **pseudo motor controller method names**: @@ -149,8 +139,8 @@ The following changes are not necessary to make your controller work. The #. new feature in *API v1*: :meth:`~PseudoMotorController.GetMotor` #. new feature in *API v1*: :meth:`~PseudoMotorController.GetPseudoMotor` -New features in API v1 -""""""""""""""""""""""" +New features +"""""""""""" This chapter is a summary of all new features in *API v1*. @@ -161,7 +151,7 @@ This chapter is a summary of all new features in *API v1*. :meth:`~Controller.GetCtrlPar`, :meth:`~Controller.SetCtrlPar`) 2. For :attr:`~Controller.ctrl_properties`, :attr:`~Controller.axis_attributes` and :attr:`~Controller.ctrl_extra_attributes`: - + - new (more pythonic) syntax. Old syntax is still supported: - can replace data type strings for python type ('PyTango.DevDouble' -> float) - Default behavior. Example: before data access needed to be described explicitly. @@ -170,7 +160,7 @@ This chapter is a summary of all new features in *API v1*. - new keys 'fget' and 'fset' override default method calls 3. no need to import :mod:`PyTango` (:meth:`~Controller.StateOne` can return sardana.State.On instead of PyTango.DevState.ON) -4. PseudoMotorController has new :meth:`~PseudoMotorController.GetMotor` and +4. PseudoMotorController has new :meth:`~PseudoMotorController.GetMotor` and :meth:`~PseudoMotorController.GetPseudoMotor` 5. new :meth:`~Stopable.AbortAll` (with default implementation which calls :meth:`~Stopable.AbortOne` for each axis) diff --git a/doc/source/devel/guide_migration/2to3.rst b/doc/source/devel/guide_migration/2to3.rst new file mode 100644 index 0000000000..374243c40c --- /dev/null +++ b/doc/source/devel/guide_migration/2to3.rst @@ -0,0 +1,5 @@ +.. _2to3: + +======== +v2 -> v3 +======== \ No newline at end of file diff --git a/doc/source/devel/guide_migration/index.rst b/doc/source/devel/guide_migration/index.rst new file mode 100644 index 0000000000..01e0012088 --- /dev/null +++ b/doc/source/devel/guide_migration/index.rst @@ -0,0 +1,16 @@ +.. _sardana-migration-guide: + + +======================= +Sardana migration guide +======================= + +This chapter describes how to migrate different sardana components between the +different API versions. + + +.. toctree:: + :maxdepth: 1 + + 2to3 + 0to1 \ No newline at end of file diff --git a/doc/source/devel/index.rst b/doc/source/devel/index.rst index 7b1aee922f..1f04436aa1 100644 --- a/doc/source/devel/index.rst +++ b/doc/source/devel/index.rst @@ -13,6 +13,6 @@ Developer's Guide Writing recorders howto_test/index API - Migration guide + Migration guide Examples Development guidelines From d6484171592805cad53a254ca9c23b975702a516 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 18 Aug 2020 10:50:36 +0200 Subject: [PATCH 757/830] Remove StartMultiple MeasurementGroup's command Remove all the underneath implementation from the core. --- src/sardana/pool/poolmeasurementgroup.py | 11 +++-------- src/sardana/tango/pool/MeasurementGroup.py | 10 +--------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index c9c435f187..cd95086b37 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -1250,12 +1250,10 @@ def set_nb_starts(self, nb_starts, propagate=1): # acquisition # ------------------------------------------------------------------------- - def prepare(self, multiple=1): + def prepare(self): """Prepare for measurement. Delegate measurement preparation to the acquisition action. - - ..todo:: remove multiple argument """ if len(self.get_user_elements()) == 0: # All channels were disabled @@ -1284,8 +1282,7 @@ def prepare(self, multiple=1): value = self._get_value() self._pending_starts = self.nb_starts - kwargs = {'head': self, - 'multiple': multiple} + kwargs = {'head': self} self.acquisition.prepare(self.configuration, self.acquisition_mode, @@ -1296,13 +1293,11 @@ def prepare(self, multiple=1): self.nb_starts, **kwargs) - def start_acquisition(self, value=None, multiple=1): + def start_acquisition(self, value=None): """Start measurement. Delegate start measurement to the acquisition action. Provide backwards compatibility for starts without previous prepare. - - ..todo:: remove value and multiple arguments. """ if self._pending_starts == 0: msg = "prepare is mandatory before starting acquisition" diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py index a194d56373..0225d826ef 100644 --- a/src/sardana/tango/pool/MeasurementGroup.py +++ b/src/sardana/tango/pool/MeasurementGroup.py @@ -309,13 +309,6 @@ def Start(self): def Stop(self): self.measurement_group.stop() - def StartMultiple(self, n): - try: - self.wait_for_operation() - except: - raise Exception("Cannot acquire: already involved in an operation") - self.measurement_group.start_acquisition(multiple=n) - class MeasurementGroupClass(PoolGroupDeviceClass): @@ -331,8 +324,7 @@ class MeasurementGroupClass(PoolGroupDeviceClass): # Command definitions cmd_list = { 'Prepare': [[DevVoid, ""], [DevVoid, ""]], - 'Start': [[DevVoid, ""], [DevVoid, ""]], - 'StartMultiple': [[DevLong, ""], [DevVoid, ""]], + 'Start': [[DevVoid, ""], [DevVoid, ""]] } cmd_list.update(PoolGroupDeviceClass.cmd_list) From ffffe09471733c43541715b8f50ba3e3912d9fb6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 18 Aug 2020 13:55:57 +0200 Subject: [PATCH 758/830] Add link to macrobutton --- doc/source/users/taurus/macrobutton.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/users/taurus/macrobutton.rst b/doc/source/users/taurus/macrobutton.rst index f302876770..cb62e6211e 100644 --- a/doc/source/users/taurus/macrobutton.rst +++ b/doc/source/users/taurus/macrobutton.rst @@ -1,3 +1,5 @@ +.. _macrobutton: + MacroButton User’s Interface ----------------------------- From 6b4ee80a4fff1d0370e9b9126b96bbea6cb9f89e Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 18 Aug 2020 13:56:23 +0200 Subject: [PATCH 759/830] Document Sardana v2 -> v3 migration --- doc/source/devel/guide_migration/2to3.rst | 49 ++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/doc/source/devel/guide_migration/2to3.rst b/doc/source/devel/guide_migration/2to3.rst index 374243c40c..8e4f3939c3 100644 --- a/doc/source/devel/guide_migration/2to3.rst +++ b/doc/source/devel/guide_migration/2to3.rst @@ -2,4 +2,51 @@ ======== v2 -> v3 -======== \ No newline at end of file +======== + +Sardana v3 is the first Sardana release which **only** supports Python 3.5 or +higher. Due to the dropping of support to Python 2 Sardana v3 is not backwards +compatible with the previous versions. Below you can find +the incompatibilities and the necessary migration steps. + +MacroServer environment +======================= + +Environments created with Python 2 needs to be ported to Python 3. +In order to do that you can use the +`upgrade_env.py `_ script. + +This requires a choice of the shelve backend (either gnu or dumb) +at the moment of migration. The same decision is also important for new +Sardana systems. In this last case it can be tuned with this +`~sardana.sardanacustomsettings.MS_ENV_SHELVE_BACKEND`. + +Plugins migration to Python 3 +============================= + +Sardana plugins (macros, controllers and recorders) and all their dependencies +e.g. external libraries, needs to be migrated to Python 3. You can find hints +on how to pass this process in this +`ALBA's internal presentation `_ +(slides 4 - 34). + +GUIs migration to Python 3 +========================== + +Sardana-Taurus GUIs which use the :ref:`MacroButton` and all +their dependencies e.g. external libraries, needs to be migrated to Python 3. +It has not been observed that other +:ref:`Sardana-Taurus widgets` require their GUIs to be migrated. + +Other (non Python 3 related) incompatibilities +============================================== + +Sardana v3 was reduced by long time ago deprecated features. You can +find a list of them together with the suggested substitutes in this +`table `_. + + + + + + From b8b3248c8ad56ec52429cda5e78f24eba324b1fa Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 21 Aug 2020 17:45:08 +0200 Subject: [PATCH 760/830] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b6a5d8084..63c189b341 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Remove deprecation warning revealed when running test suite (#1267) * Remove event filtering in `DynamicPlotManager` (showscan online) (#1299) * Avoid unnecessary creations of DeviceProxies in `ascanct` (#1281) +* Macro modules with annotated functions are properly interpreted by the MacroServer + (#1366, #1367) * Adapt to new taurus behavior of `cmd_line_parser` kwarg of `TaurusApplication` (#1306) * Fix dummy C/T and 2D controller classes in the case the start sequence was interrupted (#1188, #1309) @@ -104,7 +106,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Make handling of `Macro.sendRecordData()` with arbitrary data more robust in Spock (#1320, #1319) * Use `utf8_json` as default codec (in Tango) if `Macro.sendRecordData()` does not specify one - (#1320, #1319 + (#1320, #1319) * Avoid repeating of positions when `regscan`, `reg2scan` and `reg3scan` pass through start position(s) (#1326) * `test_stop_meas_cont_acquisition_3` spurious failures (#1188, #1353) From 4b1ed825153f474761fc1459b2fb74e3c21abc64 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 21 Aug 2020 18:39:20 +0200 Subject: [PATCH 761/830] Consider door/macro environment variables Macro's env class member specifies which environment variables are mandatory for macro executor. This just checks the global environment and not the eventual door's or macro's environment. Fix it to consider first these environments and as the fallback consider the global one. --- src/sardana/macroserver/msmacromanager.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index b752027603..cac47b4ace 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -792,10 +792,14 @@ def createMacroObj(self, macro_class, par_list, init_opts={}): macro_name = macro_class.__name__ environment = init_opts.get('environment') + executor = init_opts.get('executor') + door_name = executor.door.name r = [] for env in macro_env: - if not environment.has_env(env): + if not environment.has_env(env, + macro_name=macro_name, + door_name=door_name): r.append(env) if r: raise MissingEnv("The macro %s requires the following missing " @@ -820,13 +824,16 @@ def createMacroObjFromMeta(self, meta, par_list, init_opts={}): macro_env = code.env or () environment = init_opts.get('environment') - + executor = init_opts.get('executor') + door_name = executor.door.name + macro_name = meta.name r = [] for env in macro_env: - if not environment.has_env(env): + if not environment.has_env(env, + macro_name=macro_name, + door_name=door_name): r.append(env) if r: - macro_name = meta.name raise MissingEnv("The macro %s requires the following missing " "environment to be defined: %s" % (macro_name, str(r))) From 923f60c2b235703abfc980191140ab8e017d4cd9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 21 Aug 2020 18:42:48 +0200 Subject: [PATCH 762/830] Fix _has* methods of the environment manager Pass the property to _get* methods. --- src/sardana/macroserver/msenvmanager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index ee2921d73f..ac43dd5c20 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -227,7 +227,7 @@ def _getDoorMacroPropertyEnv(self, prop): def _hasDoorMacroPropertyEnv(self, prop): """Determines if the environment contains a property with the format ..""" - return not self._getDoorMacroPropertyEnv() is None + return not self._getDoorMacroPropertyEnv(prop) is None def _getMacroPropertyEnv(self, prop): """Returns the property value for a property which must have the @@ -244,7 +244,7 @@ def _getMacroPropertyEnv(self, prop): def _hasMacroPropertyEnv(self, prop): """Determines if the environment contains a property with the format .""" - return not self._getMacroPropertyEnv() is None + return not self._getMacroPropertyEnv(prop) is None def _getDoorPropertyEnv(self, prop): """Returns the property value for a property which must have the @@ -261,7 +261,7 @@ def _getDoorPropertyEnv(self, prop): def _hasDoorPropertyEnv(self, prop): """Determines if the environment contains a property with the format .""" - return not self._getDoorPropertyEnv() is None + return not self._getDoorPropertyEnv(prop) is None def _getEnv(self, prop): """Returns the property value for a property which must have the From 689b8b3143bf448c45cbfce26be805ebd0a13e35 Mon Sep 17 00:00:00 2001 From: aureocarneiro Date: Sat, 22 Aug 2020 18:27:23 +0200 Subject: [PATCH 763/830] Bump version 3.0.2-alpha to 3.0.2-alpha --- src/sardana/release.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/release.py b/src/sardana/release.py index b5cdbe744e..79e4488198 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = '3.0.2-alpha' +version = 'version = '3.0.2-alpha'' description = "instrument control and data acquisition system" From 61a5ec237008f0c64c031641ce07c42ae8b34044 Mon Sep 17 00:00:00 2001 From: aureocarneiro Date: Sat, 22 Aug 2020 18:27:31 +0200 Subject: [PATCH 764/830] Bump version 3.0.2-alpha to 3.0.2 --- .bumpversion.cfg | 2 +- src/sardana/release.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4a26a4a2a0..790a882a6a 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,7 +3,7 @@ commit = True message = Bump version {current_version} to {new_version} tag = False tag_name = {new_version} -current_version = 3.0.2-alpha +current_version = 3.0.2 parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? serialize = {major}.{minor}.{patch}-{release} diff --git a/src/sardana/release.py b/src/sardana/release.py index 79e4488198..a6583f82e5 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = 'version = '3.0.2-alpha'' +version = 'version = '3.0.2'' description = "instrument control and data acquisition system" From a6ca34a5a865765f471d8f9a97d5933d73b85454 Mon Sep 17 00:00:00 2001 From: aureocarneiro Date: Sat, 22 Aug 2020 18:33:50 +0200 Subject: [PATCH 765/830] change release ref to version 3.0.2 instead Jan/Jul20 --- doc/source/devel/howto_test/test_general.rst | 2 +- src/sardana/macroserver/macros/examples/scans.py | 6 +++--- .../macros/examples/specific_experiments.py | 2 +- src/sardana/macroserver/macros/scan.py | 8 ++++---- src/sardana/pool/poolmeasurementgroup.py | 2 +- src/sardana/taurus/core/tango/sardana/pool.py | 16 ++++++++-------- .../qt/qtgui/extra_macroexecutor/dooroutput.py | 2 +- src/sardana/test/testsuite.py | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/source/devel/howto_test/test_general.rst b/doc/source/devel/howto_test/test_general.rst index 9a5336b1d0..3c9f18c744 100644 --- a/doc/source/devel/howto_test/test_general.rst +++ b/doc/source/devel/howto_test/test_general.rst @@ -32,7 +32,7 @@ should be used for integration and system tests as well. The following are some key points to keep in mind when using this framework: * Most of the tests in the Sardana Test Framework use :mod:`unittest`. - Since Sardana Jan20 we decided to move to `pytest `_ + Since Sardana 3.0.2 we decided to move to `pytest `_ and we plan to gradually migrate the already existing tests. * All test-related code is contained in submodules named ``test`` which appear in any module of Sardana. diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index 51e7ef8614..5d2af31a36 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -99,7 +99,7 @@ def data(self): return self._gScan.data # the GScan provides scan data def _get_nr_points(self): - msg = ("nr_points is deprecated since version Jan20. " + msg = ("nr_points is deprecated since version 3.0.2. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -184,7 +184,7 @@ def data(self): return self._gScan.data def _get_nr_points(self): - msg = ("nr_points is deprecated since version Jan20. " + msg = ("nr_points is deprecated since version 3.0.2. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -296,7 +296,7 @@ def data(self): return self._gScan.data def _get_nr_points(self): - msg = ("nr_points is deprecated since version Jan20. " + msg = ("nr_points is deprecated since version 3.0.2. " "Use nb_points instead.") self.warning(msg) return self.nb_points diff --git a/src/sardana/macroserver/macros/examples/specific_experiments.py b/src/sardana/macroserver/macros/examples/specific_experiments.py index e7a40e5df0..53363e7f4c 100644 --- a/src/sardana/macroserver/macros/examples/specific_experiments.py +++ b/src/sardana/macroserver/macros/examples/specific_experiments.py @@ -123,7 +123,7 @@ def data(self): return self._gScan.data # the GScan provides scan data def _get_nr_points(self): - msg = ("nr_points is deprecated since version Jan20. " + msg = ("nr_points is deprecated since version 3.0.2. " "Use nb_points instead.") self.warning(msg) return self.nb_points diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 18d7015bfa..efd2bb4501 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -336,7 +336,7 @@ def _fill_missing_records(self): scan.data.initRecords(missing_records) def _get_nr_points(self): - msg = ("nr_points is deprecated since version Jan20. " + msg = ("nr_points is deprecated since version 3.0.2. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -964,7 +964,7 @@ def run(self, *args): yield step def _get_nr_points(self): - msg = ("nr_points is deprecated since version Jan20. " + msg = ("nr_points is deprecated since version 3.0.2. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -1826,7 +1826,7 @@ def _fill_missing_records(self): scan.data.initRecords(missing_records) def _get_nr_points(self): - msg = ("nr_points is deprecated since version Jan20. " + msg = ("nr_points is deprecated since version 3.0.2. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -1875,7 +1875,7 @@ def getIntervalEstimation(self): return self.nr_interv def _get_nr_points(self): - msg = ("nr_points is deprecated since version Jan20. " + msg = ("nr_points is deprecated since version 3.0.2. " "Use nb_points instead.") self.warning(msg) return self.nb_points diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index cd95086b37..95e1eb63c9 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -924,7 +924,7 @@ def _fill_channel_data(self, channel, channel_data): channel_data: if self._value_ref_compat: msg = 'value_ref_pattern/value_ref_enabled is deprecated ' \ - 'for non-referable channels since Jul20. Re-apply ' \ + 'for non-referable channels since 3.0.2. Re-apply ' \ 'configuration in order to upgrade.' self._parent.warning(msg) channel_data.pop('value_ref_enabled') diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index a33d002514..badcc0c15d 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2333,7 +2333,7 @@ def setTimer(self, timer, apply_cfg=True): :param timer: timer name """ - self._mg().warning("setTimer() is deprecated since Jul20. " + self._mg().warning("setTimer() is deprecated since 3.0.2. " "Global measurement group timer does not exist") result = self._get_ctrl_for_channel([timer], unique=True) @@ -2344,13 +2344,13 @@ def setTimer(self, timer, apply_cfg=True): def getTimer(self): """DEPRECATED""" - self._mg().warning("getTimer() is deprecated since Jul20. " + self._mg().warning("getTimer() is deprecated since 3.0.2. " "Global measurement group timer does not exist") return self._getTimer() def getMonitor(self): """DEPRECATED""" - self._mg().warning("getMonitor() is deprecated since Jul20. " + self._mg().warning("getMonitor() is deprecated since 3.0.2. " "Global measurement group monitor does not exist") return self._getMonitor() @@ -3080,19 +3080,19 @@ def getChannelsInfo(self): def getMonitorName(self): """DEPRECATED""" - self.warning("getMonitorName() is deprecated since Jul20. " + self.warning("getMonitorName() is deprecated since 3.0.2. " "Global measurement group monitor does not exist.") return self.getConfiguration()._getMonitorName() def getTimerName(self): """DEPRECATED""" - self.warning("getTimerName() is deprecated since Jul20. " + self.warning("getTimerName() is deprecated since 3.0.2. " "Global measurement group timer does not exist.") return self.getConfiguration()._getTimerName() def getTimerValue(self): """DEPRECATED""" - self.warning("getTimerValue() is deprecated since Jul20. " + self.warning("getTimerValue() is deprecated since 3.0.2. " "Global measurement group timer does not exist.") return self.getConfiguration()._getTimerValue() @@ -3102,7 +3102,7 @@ def enableChannels(self, channels): :param channels: (seq) a sequence of strings indicating channel names ''' - self.warning("enableChannels() in deprecated since Jul20. " + self.warning("enableChannels() in deprecated since 3.0.2. " "Use setEnabled() instead.") self.setEnabled(True, *channels) @@ -3112,7 +3112,7 @@ def disableChannels(self, channels): :param channels: (seq) a sequence of strings indicating channel names ''' - self.warning("enableChannels() in deprecated since Jul20. " + self.warning("enableChannels() in deprecated since 3.0.2. " "Use setEnabled() instead.") self.setEnabled(False, *channels) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py index ffaf1d160d..4c51da40a3 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py @@ -153,7 +153,7 @@ def __init__(self, parent=None): from taurus.core.util.log import warning - msg = ("DoorDebug is deprecated since version Jan20. " + msg = ("DoorDebug is deprecated since version 3.0.2. " "Use DoorOutput 'Show debug details' feature instead.") warning(msg) diff --git a/src/sardana/test/testsuite.py b/src/sardana/test/testsuite.py index 7107535093..015cc52f42 100644 --- a/src/sardana/test/testsuite.py +++ b/src/sardana/test/testsuite.py @@ -31,7 +31,7 @@ def main(): - print("sardanatestsuite was removed in Sardana Jan20. " + print("sardanatestsuite was removed in Sardana 3.0.2. " "Use pytest to run tests.") import sys sys.exit(1) From c87ac9fd012c87c28437959c14d75f6b4789e97b Mon Sep 17 00:00:00 2001 From: aureocarneiro Date: Sat, 22 Aug 2020 18:42:53 +0200 Subject: [PATCH 766/830] update release reference in CHANGELOG --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c189b341..017a25a022 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). This file follows the formats and conventions from [keepachangelog.com] -## [Unreleased] +## [3.0.2] ### Added @@ -932,7 +932,7 @@ Main improvements since sardana 1.5.0 (aka Jan15): [keepachangelog.com]: http://keepachangelog.com -[Unreleased]: https://github.com/sardana-org/sardana/compare/2.8.6...HEAD +[3.0.2]: https://github.com/sardana-org/sardana/compare/3.0.2...2.8.6 [2.8.6]: https://github.com/sardana-org/sardana/compare/2.8.6...2.8.5 [2.8.5]: https://github.com/sardana-org/sardana/compare/2.8.5...2.8.4 [2.8.4]: https://github.com/sardana-org/sardana/compare/2.8.4...2.8.3 From d8a9b26e7a742a2f02f5edfa49e18496381ee320 Mon Sep 17 00:00:00 2001 From: aureocarneiro Date: Sat, 22 Aug 2020 18:58:35 +0200 Subject: [PATCH 767/830] Add new Unreleased section on CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 017a25a022..fd2294beaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). This file follows the formats and conventions from [keepachangelog.com] +## [Unreleased] + ## [3.0.2] ### Added @@ -932,6 +934,7 @@ Main improvements since sardana 1.5.0 (aka Jan15): [keepachangelog.com]: http://keepachangelog.com +[Unreleased]: https://github.com/sardana-org/sardana/compare/3.0.2...HEAD [3.0.2]: https://github.com/sardana-org/sardana/compare/3.0.2...2.8.6 [2.8.6]: https://github.com/sardana-org/sardana/compare/2.8.6...2.8.5 [2.8.5]: https://github.com/sardana-org/sardana/compare/2.8.5...2.8.4 From 9736f48cb9409cf209c714095ec727e4f7ea547e Mon Sep 17 00:00:00 2001 From: aureocarneiro Date: Sat, 22 Aug 2020 19:01:24 +0200 Subject: [PATCH 768/830] add temporary release date on CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd2294beaf..13962f04d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ This file follows the formats and conventions from [keepachangelog.com] ## [Unreleased] -## [3.0.2] +## [3.0.2] 2020-08-22 ### Added From 6379230e52f9e5c444d061abcc59891dd86d5603 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 24 Aug 2020 10:24:43 +0200 Subject: [PATCH 769/830] Fix version --- src/sardana/release.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/release.py b/src/sardana/release.py index a6583f82e5..e4d7c0dc96 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = 'version = '3.0.2'' +version = '3.0.2' description = "instrument control and data acquisition system" From f1381a67de233267567b6bc63c33c6c11c4b99de Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 24 Aug 2020 10:39:06 +0200 Subject: [PATCH 770/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c189b341..fd490545e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,8 @@ This file follows the formats and conventions from [keepachangelog.com] (#1260) * Fix fast operations (motion & acq) by propertly clearing operation context and resetting of acq ctrls dicts (#1300) +* Environment variables validation before macro execution when these are defined + on door's or macro's level (#1390) * Use more efficient way to get terminal size for better printing spock output (#1245, #1268) * Measurement groups renaming with `renameelem` macro(#951) * `macroexecutor` correctly loads macro combo box if it was started with server down and From 306fbde449aa6efb61b0c130f1012bec015ed4a4 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 24 Aug 2020 10:43:22 +0200 Subject: [PATCH 771/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd490545e4..11800059c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `TaurusSequencerWidget` does not use _parent model_ feature (#1284) * Macro plotting in new versions of ipython and matplotlib require extra call to `pyplot.draw()` to make sure that the plot is refreshed (#1280) +* Controller's `StateOne()` that returns only state (#621, #1342) * Allow MacroButton widget to be smaller - minimum size to show the macro name (#1265) * Remove TangoAttribute controllers from Sardana (#181, #1279) * Remove deprecation warning revealed when running test suite (#1267) From ccfd7066db744a9475c84a19b5ce21a4ca83559c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 24 Aug 2020 12:03:20 +0200 Subject: [PATCH 772/830] Adapt appveyor configuration to Python3 --- ci/appveyor.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ci/appveyor.yml b/ci/appveyor.yml index 8c1991a639..643185aeb2 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -7,28 +7,28 @@ environment: VENV_TEST_DIR: "venv_test" matrix: - # Python 2.7 (64) - - PYTHON_DIR: "C:\\Python27-x64" - PYTHON_VERSION: "2.7" + # Python 3.5 (64) + - PYTHON_DIR: "C:\\Python35-x64" + PYTHON_VERSION: "3.5" PYTHON_ARCH: "64" - CONDA_PY: "27" + CONDA_PY: "35" - # Python 2.7 - - PYTHON_DIR: "C:\\Python27" - PYTHON_VERSION: "2.7" + # Python 3.5 + - PYTHON_DIR: "C:\\Python35" + PYTHON_VERSION: "3.5" PYTHON_ARCH: "32" - CONDA_PY: "27" + CONDA_PY: "35" install: # Add Python to PATH - "SET PATH=%PYTHON_DIR%;%PYTHON_DIR%\\Scripts;%PATH%" # Upgrade/install distribution modules - - "pip install --upgrade setuptools" - - "python -m pip install --upgrade pip" + - "pip3 install --upgrade setuptools" + - "python3 -m pip install --upgrade pip" # Install virtualenv - - "pip install --upgrade virtualenv" + - "pip3 install --upgrade virtualenv" - "virtualenv --version" build_script: @@ -37,10 +37,10 @@ build_script: - "%VENV_BUILD_DIR%\\Scripts\\activate.bat" # Install wheel - - "pip install --upgrade wheel" + - "pip3 install --upgrade wheel" # Build sardana sdist, msi and wheel - - "python setup.py sdist bdist_wheel bdist_msi" + - "python3 setup.py sdist bdist_wheel bdist_msi" - ps: "ls dist" # Leave build virtualenv From c0c60d2ab09dd1d3c123205968af7b1ecea0ecc4 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 24 Aug 2020 12:11:02 +0200 Subject: [PATCH 773/830] Fix appveyor configuration (python3) --- ci/appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/appveyor.yml b/ci/appveyor.yml index 643185aeb2..260b9f4d07 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -25,7 +25,7 @@ install: # Upgrade/install distribution modules - "pip3 install --upgrade setuptools" - - "python3 -m pip install --upgrade pip" + - "python -m pip install --upgrade pip" # Install virtualenv - "pip3 install --upgrade virtualenv" @@ -40,7 +40,7 @@ build_script: - "pip3 install --upgrade wheel" # Build sardana sdist, msi and wheel - - "python3 setup.py sdist bdist_wheel bdist_msi" + - "python setup.py sdist bdist_wheel bdist_msi" - ps: "ls dist" # Leave build virtualenv From b58f06d06a73bcad6c550b88f8ff00b586022507 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 24 Aug 2020 12:51:45 +0200 Subject: [PATCH 774/830] Add Python 3.6 and 3.7 to appveyor builds --- ci/appveyor.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ci/appveyor.yml b/ci/appveyor.yml index 260b9f4d07..613e447107 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -19,6 +19,30 @@ environment: PYTHON_ARCH: "32" CONDA_PY: "35" + # Python 3.6 (64) + - PYTHON_DIR: "C:\\Python36-x64" + PYTHON_VERSION: "3.6" + PYTHON_ARCH: "64" + CONDA_PY: "36" + + # Python 3.6 + - PYTHON_DIR: "C:\\Python36" + PYTHON_VERSION: "3.6" + PYTHON_ARCH: "32" + CONDA_PY: "36" + + # Python 3.7 (64) + - PYTHON_DIR: "C:\\Python37-x64" + PYTHON_VERSION: "3.7" + PYTHON_ARCH: "64" + CONDA_PY: "37" + + # Python 3.7 + - PYTHON_DIR: "C:\\Python37" + PYTHON_VERSION: "3.7" + PYTHON_ARCH: "32" + CONDA_PY: "37" + install: # Add Python to PATH - "SET PATH=%PYTHON_DIR%;%PYTHON_DIR%\\Scripts;%PATH%" From 9b10166218d4f3427479b4acf764119005e537c9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 25 Aug 2020 11:11:12 +0200 Subject: [PATCH 775/830] Add Python 3.8 to appveyor --- ci/appveyor.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ci/appveyor.yml b/ci/appveyor.yml index 613e447107..d871492322 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -43,6 +43,18 @@ environment: PYTHON_ARCH: "32" CONDA_PY: "37" + # Python 3.8 (64) + - PYTHON_DIR: "C:\\Python38-x64" + PYTHON_VERSION: "3.8" + PYTHON_ARCH: "64" + CONDA_PY: "38" + + # Python 3.8 + - PYTHON_DIR: "C:\\Python38" + PYTHON_VERSION: "3.8" + PYTHON_ARCH: "32" + CONDA_PY: "38" + install: # Add Python to PATH - "SET PATH=%PYTHON_DIR%;%PYTHON_DIR%\\Scripts;%PATH%" From c6817cce1dd578ed6309d7d883f19267ea77f34c Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 26 Aug 2020 20:13:21 +0200 Subject: [PATCH 776/830] Clear event set before starting opertion of PoolElement Clear event set to not confuse the value coming from the connection with the event of of end of the operation in the next wait event. This was observed on Windows where the time stamp resolution is very poor. --- src/sardana/taurus/core/tango/sardana/pool.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index badcc0c15d..b8c8bea69e 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -479,6 +479,11 @@ def start(self, *args, **kwargs): evt_wait.connect(self.getAttribute("state")) try: evt_wait.waitEvent(DevState.MOVING, equal=False) + # Clear event set to not confuse the value coming from the + # connection with the event of of end of the operation + # in the next wait event. This was observed on Windows where + # the time stamp resolution is very poor. + evt_wait.clearEventSet() self.__go_time = 0 self.__go_start_time = ts1 = time.time() self._start(*args, **kwargs) From fcde1468d26b69a22353c8c9dadabd4abe7d7462 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 27 Aug 2020 10:48:31 +0200 Subject: [PATCH 777/830] Add which_python_executable util --- src/sardana/util/whichpython.py | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/sardana/util/whichpython.py diff --git a/src/sardana/util/whichpython.py b/src/sardana/util/whichpython.py new file mode 100644 index 0000000000..198cb76b62 --- /dev/null +++ b/src/sardana/util/whichpython.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +# Sardana 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. +## +# Sardana 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 Sardana. If not, see . +## +############################################################################## + +"""""" + +__all__ = ["which_python_executable"] + + +from taurus.core.util.whichexecutable import whichfile + + +def which_python_executable(): + """Return full path to python executable. + + On some OS Python 3 is executed with python3 but in conda environments it + is executed with python. Return Python 3 executable regardless of the + Python installation. + """ + executable = whichfile("python3") + if executable is None: + executable = whichfile("python") + return executable From 9c6d318f71ccb05ccd88f6ff7959b2c93cdbb0ab Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 27 Aug 2020 10:52:45 +0200 Subject: [PATCH 778/830] Use Python 3 regardless of installation in magic commands --- src/sardana/spock/magic.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index d38c1f6ffe..20a9696601 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -31,6 +31,7 @@ 'post_mortem', 'macrodata', 'edmac', 'spock_late_startup_hook', 'spock_pre_prompt_hook'] +from sardana.util.whichpython import which_python_executable from .genutils import MSG_DONE, MSG_FAILED from .genutils import get_ipapi from .genutils import page, get_door, get_macro_server, ask_yes_no, arg_split @@ -60,7 +61,8 @@ def expconf(self, parameter_s=''): import subprocess import sys fname = sys.modules[ExpDescriptionEditor.__module__].__file__ - args = ['python3', fname, doorname] + python_executable = which_python_executable() + args = [python_executable, fname, doorname] if parameter_s == '--auto-update': args.insert(2, parameter_s) subprocess.Popen(args) @@ -108,7 +110,9 @@ def showscan(self, parameter_s=''): import subprocess import sys fname = sys.modules[ShowScanOnline.__module__].__file__ - args = ['python3', fname, doorname, '--taurus-log-level=error'] + python_executable = which_python_executable() + args = [python_executable, fname, doorname, + '--taurus-log-level=error'] subprocess.Popen(args) return else: From 6662cf2b31b410676bf3da160cc26f20ff798ba8 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 27 Aug 2020 11:37:09 +0200 Subject: [PATCH 779/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ae786425..c33474ea7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,7 @@ This file follows the formats and conventions from [keepachangelog.com] (#1260) * Fix fast operations (motion & acq) by propertly clearing operation context and resetting of acq ctrls dicts (#1300) +* Premature end of acquisition on Windows (#1397) * Environment variables validation before macro execution when these are defined on door's or macro's level (#1390) * Use more efficient way to get terminal size for better printing spock output (#1245, #1268) From 71e34e64b9eddac8d4e87e6694858fc8a8894241 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 27 Aug 2020 11:41:26 +0200 Subject: [PATCH 780/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c33474ea7b..c89034fac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Fix fast operations (motion & acq) by propertly clearing operation context and resetting of acq ctrls dicts (#1300) * Premature end of acquisition on Windows (#1397) +* Use proper python3 executable regardeless of installation (#1398) * Environment variables validation before macro execution when these are defined on door's or macro's level (#1390) * Use more efficient way to get terminal size for better printing spock output (#1245, #1268) From 013a333c6222bd5bf3e0c1fbe1b996413082308d Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 27 Aug 2020 12:24:46 +0200 Subject: [PATCH 781/830] Fix showscan offline broken in #1260 Showscan without arguments should open showscan offline. When scan ID is given as argument it should open the corresponding scan. --- src/sardana/spock/magic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py index 20a9696601..0f4b1c6126 100644 --- a/src/sardana/spock/magic.py +++ b/src/sardana/spock/magic.py @@ -83,7 +83,7 @@ def showscan(self, parameter_s=''): """ params = parameter_s.split() door = get_door() - online, scan_nb = False, None + scan_nb = None if len(params) > 0: if params[0].lower() == 'online': try: @@ -117,7 +117,7 @@ def showscan(self, parameter_s=''): return else: scan_nb = int(params[0]) - door.show_scan(scan_nb) + door.show_scan(scan_nb) def spsplot(self, parameter_s=''): From ae565d67fe34ac42230e8d736c4e7c4fc0275097 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 28 Aug 2020 10:42:10 +0200 Subject: [PATCH 782/830] Subscribe to ValueRefBuffer events in timescan timescan and TScan does not handle the referable channels. Subscribe to them to make tiemscan compatible with referable channels. Fix #1399. --- src/sardana/macroserver/scan/gscan.py | 3 ++- src/sardana/taurus/core/tango/sardana/pool.py | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index c12630771b..eacd779395 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -2769,7 +2769,8 @@ def scan_loop(self): yield 0 measurement_group.setNbStarts(1) measurement_group.count_continuous(synch_description, - self.value_buffer_changed) + self.value_buffer_changed, + self.value_ref_buffer_changed) self.debug("Waiting for value buffer events to be processed") self.wait_value_buffer() self.join_thread_pool() diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index b8c8bea69e..bdf9d86e15 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -3367,7 +3367,8 @@ def go(self, *args, **kwargs): self.prepare() return self.count_raw(start_time) - def count_continuous(self, synch_description, value_buffer_cb=None): + def count_continuous(self, synch_description, value_buffer_cb=None, + value_ref_buffer_cb=None): """Execute measurement process according to the given synchronization description. @@ -3376,6 +3377,9 @@ def count_continuous(self, synch_description, value_buffer_cb=None): synchronizations :param value_buffer_cb: callback on value buffer updates :type value_buffer_cb: callable + :param value_ref_buffer_cb: callback on value reference + buffer updates + :type value_ref_buffer_cb: callable :return: state and eventually value buffers if no callback was passed :rtype: tuple,> @@ -3392,10 +3396,12 @@ class on a provisional basis. Backwards incompatible changes self.setSynchDescription(synch_description) self.prepare() self.subscribeValueBuffer(value_buffer_cb) + self.subscribeValueRefBuffer(value_ref_buffer_cb) try: self.count_raw(start_time) finally: self.unsubscribeValueBuffer(value_buffer_cb) + self.unsubscribeValueRefBuffer(value_ref_buffer_cb) state = self.getStateEG().readValue() if state == Fault: msg = "Measurement group ended acquisition with Fault state" From 97d0965c8c0fcf78254d1ac465f757d57c485474 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 28 Aug 2020 11:31:23 +0200 Subject: [PATCH 783/830] Prevent scanstats when no moveable involved in the scan --- src/sardana/macroserver/macros/scan.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index efd2bb4501..da43fc162e 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1905,6 +1905,10 @@ def run(self, channel): self.warning("for now the scanstats macro can only be executed as" " a post-scan hook") return + if not hasattr(parent, "motors"): + self.warning("scan must involve at least one moveable " + "to calculate statistics") + return active_meas_grp = self.getEnv("ActiveMntGrp") meas_grp = self.getMeasurementGroup(active_meas_grp) From 5953e5f787381c17b6c6f451e7870256fcbf5397 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 28 Aug 2020 12:21:14 +0200 Subject: [PATCH 784/830] Do not calculate stats on 1D and 2D channels 1D and 2D channels, apart their value attribute type (spectrum and image) may be configured to return value references. In this case it is not possible to calculate statistics. Remove the 1D and 2D channels earlier from the calculation. --- src/sardana/macroserver/macros/scan.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index da43fc162e..c80e7d607b 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -1885,8 +1885,10 @@ def _get_nr_points(self): class scanstats(Macro): """Calculate basic statistics of the enabled and plotted channels in - the active measurement group for the last scan. Print it and publish it - in the env. The macro must be hooked in the post-scan hook place. + the active measurement group for the last scan. If no channel is selected + for plotting it fallbacks to the first enabled channel. Print stats and + publish them in the env. + The macro must be hooked in the post-scan hook place. """ env = ("ActiveMntGrp", ) @@ -1932,10 +1934,22 @@ def run(self, channel): elif enabled and meas_grp.getPlotType(chan)[chan] == 1: calc_channels.append(chan) - if calc_channels == []: + if len(calc_channels) == 0: # fallback is first enabled channel in meas_grp calc_channels.append(next(iter(enabled_channels))) + scalar_channels = [] + for _, chan in self.getExpChannels().items(): + if chan.type in ("OneDExpChannel", "TwoDExpChannel"): + continue + scalar_channels.append(chan.name) + calc_channels = [ch for ch in calc_channels if ch in scalar_channels] + + if len(calc_channels) == 0: + self.warning("measurement group must contain at least one " + "enabled scalar channel to calculate statistics") + return + selected_motor = str(parent.motors[0]) stats = {} col_header = [] From 9c45760f981dabe353120b58dbcc44c5470d1358 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 28 Aug 2020 18:17:22 +0200 Subject: [PATCH 785/830] Do not force StartOne re-implementation for CT Controller #1362 wrongly made StartOne() re-implementation mandatory. Make it optional again. --- src/sardana/pool/controller.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index e4f6e00ff4..fc24d05030 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -892,6 +892,14 @@ def __init__(self, inst, props, *args, **kwargs): self._latency_time = 0 self._synchronization = AcqSynch.SoftwareTrigger + def StartOne(self, axis, value): + """**Controller API**. Override if necessary. + Called to do a start of the given axis (whatever start means). + + :param int axis: axis number + :param float value: new value""" + pass + class TriggerGateController(Controller, Synchronizer, Stopable, Startable): """Base class for a trigger/gate controller. Inherit from this class to From a4ec255a33b82340662b6ad268fe6db379b99f7a Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 31 Aug 2020 08:18:30 +0200 Subject: [PATCH 786/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c89034fac3..a5833d0ce4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -159,7 +159,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `SetPar()` and `GerPar()` * `SetExtraAttributePar()` and `GetExtraAttributePar()` * `ctrl_properties` with "Description" as `str` -* `CounterTimerController` controller API (#1315, #1362): +* `CounterTimerController` controller API (#1315, #1362, #1403): * `_trigger_type` * `PreStartAllCT()`, `PreStartOneCT()`, `StartAllCT()` and `StartOneCT()` * `PseudoMotorController` controller API (#1315, #1363) From f1545c16a6c55ab9c8739f20a250dfbba59f71c6 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 1 Sep 2020 14:23:13 +0200 Subject: [PATCH 787/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5833d0ce4..21c6dba301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Fix fast operations (motion & acq) by propertly clearing operation context and resetting of acq ctrls dicts (#1300) * Premature end of acquisition on Windows (#1397) +* `timescan` with referable channels (#1399, #1401) * Use proper python3 executable regardeless of installation (#1398) * Environment variables validation before macro execution when these are defined on door's or macro's level (#1390) From 792c2427672d35d2c854dc244c4bd947b860cc82 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 2 Sep 2020 11:43:25 +0200 Subject: [PATCH 788/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21c6dba301..e014a96495 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,7 +83,7 @@ This file follows the formats and conventions from [keepachangelog.com] configurable with `MACROEXECUTOR_MAX_HISTORY` (#1307) * OutputBlock view option when macros produce outputs at high rate (#1245) * `showscan online` shows only the online trend and not erroneously online and offline - (#1260) + (#1260, #1400) * Fix fast operations (motion & acq) by propertly clearing operation context and resetting of acq ctrls dicts (#1300) * Premature end of acquisition on Windows (#1397) From 9a0128ce67006b0b094b56128261f81ae823fc95 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 2 Sep 2020 15:31:04 +0200 Subject: [PATCH 789/830] Use showProgress() instead of toggleProgress() --- src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index 11b4ab5c9d..c3c5e5520c 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -388,7 +388,7 @@ def update_result(self, result): def toggle_progress(self, toggle): visible = self.show_progress.isChecked() - self.mb.toggleProgress(visible or toggle) + self.mb.showProgress(visible or toggle) def getMacroInfo(self, macro_name): From 3af5db659e4026dcbc201513527dcef85eae3f2c Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 2 Sep 2020 15:31:54 +0200 Subject: [PATCH 790/830] Connect to editors' signals instead of using connectArgEditors() --- .../taurus/qt/qtgui/extra_macroexecutor/macrobutton.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py index c3c5e5520c..d4f73f08f3 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py @@ -468,9 +468,9 @@ def create_layout(self, macro_name): # Toggle progressbar self.show_progress.stateChanged.connect(self.toggle_progress) # connect the argument editors - # signals = [(e, 'textChanged(QString)') for e in _argEditors] - signals = [getattr(e, 'textChanged') for e in _argEditors] - self.mb.connectArgEditors(signals) + for i, editor in enumerate(_argEditors): + slot = functools.partial(self.mb.updateMacroArgument, i) + editor.textChanged.connect(slot) self.setLayout(Qt.QVBoxLayout()) self.layout().addWidget(self.w_arg) From 38b9620cc07d226ecef7d5578903563744d0e813 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 2 Sep 2020 16:18:00 +0200 Subject: [PATCH 791/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e014a96495..b435ed931c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -178,7 +178,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `trigger_type` * `Label` and `Calibration` attributes of `DiscretePseudMotor` controller (#1315, #1374) -* MacroButton's methods (#1315, #1379) +* MacroButton's methods (#1315, #1379, #1405) * `toggleProgress()` * `updateMacroArgumentFromSignal()` * `connectArgEditors()` From f8bd432a7a8719f3eac4137d96d83e5e01e25a00 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 4 Sep 2020 19:10:25 +0200 Subject: [PATCH 792/830] Add note about experimental feature --- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index cf44e10cab..bdc23eb195 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -1,4 +1,10 @@ """A RichJupyterWidget that loads a spock profile. + +.. note:: + The `qtspock` module has been included in Sardana + on a provisional basis. Backwards incompatible changes + (up to and including its removal) may occur if + deemed necessary by the core developers. """ import sys import pickle @@ -47,6 +53,12 @@ class SpockKernelManager(QtKernelManager): """ A kernel manager that checks the spock profile before starting a kernel. + .. note:: + The `SpockKernelManager` class has been included in Sardana + on a provisional basis. Backwards incompatible changes + (up to and including its removal) may occur if + deemed necessary by the core developers. + If the check fails, i.e., the profile does not exist or has a different version, an ipython kernel without spock functionality is started instead and the attribute `valid_spock_profile` is set to `False`. @@ -78,6 +90,12 @@ def _launch_kernel(self, kernel_cmd, **kw): class QtSpockWidget(RichJupyterWidget, TaurusBaseWidget): """A RichJupyterWidget that starts a kernel with a spock profile. + .. note:: + The `QtSpockWidget` class has been included in Sardana + on a provisional basis. Backwards incompatible changes + (up to and including its removal) may occur if + deemed necessary by the core developers. + It is important to call `shutdown_kernel` to gracefully clean up the started subprocesses. @@ -326,7 +344,14 @@ def _handle_kernel_info_reply(self, rep): class QtSpock(TaurusMainWindow): - """A standalone QtSpock window""" + """A standalone QtSpock window + + .. note:: + The `QtSpock` class has been included in Sardana + on a provisional basis. Backwards incompatible changes + (up to and including its removal) may occur if + deemed necessary by the core developers. + """ def __init__(self, parent=None, designMode=False): super().__init__(parent, designMode) self.spockWidget = QtSpockWidget(parent=self) From da6376b3d497c62271c0de3102d218147e65b389 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 4 Sep 2020 19:40:59 +0200 Subject: [PATCH 793/830] Remove legacy qtconsole code from spock --- src/sardana/spock/ipython_01_00/genutils.py | 28 --------------------- 1 file changed, 28 deletions(-) diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py index 5ed1560454..1c26d28af8 100644 --- a/src/sardana/spock/ipython_01_00/genutils.py +++ b/src/sardana/spock/ipython_01_00/genutils.py @@ -1342,34 +1342,6 @@ def run(): max_counts = getattr(sardanacustomsettings, 'TAURUS_MAX_DEPRECATION_COUNTS', 0) tauruscustomsettings._MAX_DEPRECATIONS_LOGGED = max_counts - # - - try: - try: - # IPython 4.x - from traitlets import Unicode - from qtconsole.rich_jupyter_widget import RichIPythonWidget - from qtconsole.qtconsoleapp import IPythonQtConsoleApp - # TODO: check if we can/should set IPythonQtConsoleApp.version - except: - # IPython <4.x - from IPython.utils.traitlets import Unicode - from IPython.qt.console.rich_ipython_widget import RichIPythonWidget - from IPython.qt.console.qtconsoleapp import IPythonQtConsoleApp - IPythonQtConsoleApp.version.default_value = release.version - - class SpockConsole(RichIPythonWidget): - - banner = Unicode(config=True) - - def _banner_default(self): - config = get_config() - return config.FrontendWidget.banner - - IPythonQtConsoleApp.widget_factory = SpockConsole - - except ImportError: - pass try: check_requirements() From 27c4d5800c57c7048a496c6c0dd776841ba0f8a3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Sun, 6 Sep 2020 13:29:09 +0200 Subject: [PATCH 794/830] Add copyright header --- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 26 +++++++++++++++++++ .../qt/qtgui/extra_sardana/qtspock_ext.py | 26 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index bdc23eb195..3e9171c6fe 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -1,3 +1,29 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2020 DESY +## +# Sardana 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. +## +# Sardana 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 Sardana. If not, see . +## +############################################################################## + """A RichJupyterWidget that loads a spock profile. .. note:: diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock_ext.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock_ext.py index 547a5dbe8d..6013f51ac9 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock_ext.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock_ext.py @@ -1,3 +1,29 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2020 DESY +## +# Sardana 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. +## +# Sardana 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 Sardana. If not, see . +## +############################################################################## + from IPython.core.magic import register_line_magic From f5e3cdad356efe19bf152b7b503bcddbb70ddb6d Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 7 Sep 2020 11:13:15 +0200 Subject: [PATCH 795/830] Add edmac to API documentation --- doc/source/devel/api/sardana/spock/magic.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/devel/api/sardana/spock/magic.rst b/doc/source/devel/api/sardana/spock/magic.rst index 1ff09cb076..40f1ab5491 100644 --- a/doc/source/devel/api/sardana/spock/magic.rst +++ b/doc/source/devel/api/sardana/spock/magic.rst @@ -7,4 +7,5 @@ .. rubric:: Functions +.. autofunction:: sardana.spock.magic.edmac .. autofunction:: sardana.spock.magic.showscan \ No newline at end of file From bf0ba79a0d951e1082bc2b95cbcccccf62b99d5c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 7 Sep 2020 11:14:09 +0200 Subject: [PATCH 796/830] Add QtSpock classes to API documentation --- .../devel/api/sardana/taurus/qt/qtgui.rst | 7 ++++ .../sardana/taurus/qt/qtgui/extra_sardana.rst | 14 +++++++ .../taurus/qt/qtgui/extra_sardana/qtspock.rst | 37 +++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 doc/source/devel/api/sardana/taurus/qt/qtgui/extra_sardana.rst create mode 100644 doc/source/devel/api/sardana/taurus/qt/qtgui/extra_sardana/qtspock.rst diff --git a/doc/source/devel/api/sardana/taurus/qt/qtgui.rst b/doc/source/devel/api/sardana/taurus/qt/qtgui.rst index 667ef04817..a5ff2b0b2d 100644 --- a/doc/source/devel/api/sardana/taurus/qt/qtgui.rst +++ b/doc/source/devel/api/sardana/taurus/qt/qtgui.rst @@ -4,3 +4,10 @@ ================================ .. automodule:: sardana.taurus.qt.qtgui + +.. rubric:: Modules + +.. toctree:: + :maxdepth: 1 + + extra_sardana \ No newline at end of file diff --git a/doc/source/devel/api/sardana/taurus/qt/qtgui/extra_sardana.rst b/doc/source/devel/api/sardana/taurus/qt/qtgui/extra_sardana.rst new file mode 100644 index 0000000000..7e02d405ee --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/qt/qtgui/extra_sardana.rst @@ -0,0 +1,14 @@ +.. currentmodule:: sardana.taurus.qt.qtgui.extra_sardana + +:mod:`~sardana.taurus.qt.qtgui.extra_sardana` +============================================= + +.. automodule:: sardana.taurus.qt.qtgui.extra_sardana + +.. rubric:: Modules + +.. toctree:: + :maxdepth: 1 + + qtspock + diff --git a/doc/source/devel/api/sardana/taurus/qt/qtgui/extra_sardana/qtspock.rst b/doc/source/devel/api/sardana/taurus/qt/qtgui/extra_sardana/qtspock.rst new file mode 100644 index 0000000000..229a5c7b2e --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/qt/qtgui/extra_sardana/qtspock.rst @@ -0,0 +1,37 @@ +.. currentmodule:: sardana.taurus.qt.qtgui.extra_sardana.qtspock + + +:mod:`~sardana.taurus.qt.qtgui.extra_sardana.qtspock` +===================================================== + +.. automodule:: sardana.taurus.qt.qtgui.extra_sardana.qtspock + +.. rubric:: Classes + +.. hlist:: + :columns: 4 + + * :class:`QtSpockWidget` + * :class:`QtSpock` + +QtSpockWidget +------------- + +.. inheritance-diagram:: QtSpockWidget + :parts: 1 + +.. autoclass:: QtSpockWidget + :show-inheritance: + :members: + :undoc-members: + +QtSpock +------- + +.. inheritance-diagram:: QtSpock + :parts: 1 + +.. autoclass:: QtSpock + :show-inheritance: + :members: + :undoc-members: From 2905b5445842e077b3c11178d06fd4f71acfe123 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 7 Sep 2020 11:14:36 +0200 Subject: [PATCH 797/830] Correct sphinx warnings --- .../taurus/qt/qtgui/extra_sardana/qtspock.py | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 3e9171c6fe..54d8535170 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -128,27 +128,28 @@ class QtSpockWidget(RichJupyterWidget, TaurusBaseWidget): Useful methods of the base class include execute, interrupt_kernel, restart_kernel, and clear. - Parameters - ---------- - profile : string + :param profile: The name of the spock profile to use. The default is 'spockdoor'. - kernel : string + :type profile: str + :param kernel: The name of the kernel to use. The default is 'python3'. - use_model_from_profile : bool + :type kernel: str + :param use_model_from_profile: If true, the door name is taken from the spock profile, otherwise it has to be set via setModel. - **kwargs + :type use_model_from_profile: bool + :param kwargs: All remaining keywords are passed to the RichJupyterWidget base class - Examples - -------- - >>> from taurus.external.qt import Qt - >>> from sardana.taurus.qt.qtgui.extra_sardana.qtspock import QtSpockWidget - >>> app = Qt.QApplication([]) - ... widget = QtSpockWidget() - ... widget.show() - ... app.aboutToQuit.connect(widget.shutdown_kernel) - ... app.exec_() + Examples:: + + from taurus.external.qt import Qt + from sardana.taurus.qt.qtgui.extra_sardana.qtspock import QtSpockWidget + app = Qt.QApplication([]) + widget = QtSpockWidget() + widget.show() + app.aboutToQuit.connect(widget.shutdown_kernel) + app.exec_() """ def __init__( self, @@ -316,18 +317,16 @@ def get_value(self, var, timeout=None): The command will import the pickle module in the user namespace. This may overwrite a user defined variable with the same name. - Parameters - ---------- - var : str + :param var: The name of the variable to be retrieved - timeout : int or None + :type var: str + :param timeout: Number of seconds to wait for reply. If no reply is recieved, a `Queue.Empty` exception is thrown. The default is to wait indefinitely - - Returns - ------- - The value of the variable from the user namespace + :type timeout: int or None + :return: + The value of the variable from the user namespace """ pickle_dumps = 'pickle.dumps({})'.format(var) msg_id = self.blocking_client.execute( From a8a79526a33f5a4914066c9f70a2aa1411262850 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 7 Sep 2020 11:14:59 +0200 Subject: [PATCH 798/830] Add QtSpock user's documentation --- doc/source/users/taurus/index.rst | 1 + doc/source/users/taurus/qtspock.rst | 37 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 doc/source/users/taurus/qtspock.rst diff --git a/doc/source/users/taurus/index.rst b/doc/source/users/taurus/index.rst index ee87836b43..2511cea7b5 100644 --- a/doc/source/users/taurus/index.rst +++ b/doc/source/users/taurus/index.rst @@ -17,6 +17,7 @@ Sardana provides several :mod:`taurus`-based widgets for being used in GUIs Sardana Editor Showscan PoolMotorTV + QtSpock If you do not find a widget which meets your requirements and plan to develop a new one consider using diff --git a/doc/source/users/taurus/qtspock.rst b/doc/source/users/taurus/qtspock.rst new file mode 100644 index 0000000000..c5579cd2bc --- /dev/null +++ b/doc/source/users/taurus/qtspock.rst @@ -0,0 +1,37 @@ +.. _qtspock: + +QtSpock +------- + +.. note:: + The QtSpock widget has been included in Sardana + on a provisional basis. Backwards incompatible changes + (up to and including its removal) may occur if + deemed necessary by the core developers. + +Sardana provides `qtconsole `_-based widget to +run :ref:`sardana-spock`. + +.. image:: /_static/qtspock.png + :align: center + +It provides most of the Spock features and can be launched either +as a standalone application + +.. code-block:: console + + python3 -m sardana.taurua.qt.qtgui.extran_sardana.qtspock + +or embedded in the :ref:`taurusgui_ui`: (when :ref:`panelcreation` use: +`sardana.taurus.qt.qtgui.extra_sardana.qtspock` module and +`~sardana.taurus.qt.qtgui.extra_sardana.qtspock.QtSpockWidget` +class). + +QtSpock requires a spock profile *spockdoor* to be created and upgraded +beforehand (use ``spock`` command to create/upgrade profile). + +Below you can find a list of features still not supported by QtSpock: + +* block update macros e.g. `~sardana.macroserver.macros.standard.umv` are not + updated in block but their output is simply appended +* `~sardana.spock.magic.edmac` magic command From 5b1547a5c89a41dda566e5fbb2d04034922c660c Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 7 Sep 2020 12:31:40 +0200 Subject: [PATCH 799/830] Add QtSpock screenshot --- doc/source/_static/qtspock.png | Bin 0 -> 27559 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/source/_static/qtspock.png diff --git a/doc/source/_static/qtspock.png b/doc/source/_static/qtspock.png new file mode 100644 index 0000000000000000000000000000000000000000..638fc8f2108ad98aeb13bb15a45e16c5e05752cc GIT binary patch literal 27559 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 Date: Mon, 7 Sep 2020 12:54:04 +0200 Subject: [PATCH 800/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11800059c9..08232b3297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `where` macro to print the scanned motor position (#890) * `plotselect` macro for configuring channels for online scan plotting (#824) * `genv` macro for printing environment variable values (#888) +* QtSpock widget (`QtSpock` and `QtSpockWidget`) - *experimental* (#1109) * Dump info on channels if MG acq fails in step scan, ct and uct (#1308) * Add timestamp to element's dumped information (#1308) * Quality to `SardanaAttribute` (#1353) From a1536ab59c51e2276547def775045746bdb0b1e1 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 7 Sep 2020 13:04:27 +0200 Subject: [PATCH 801/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f0dd589e6..ebd329f025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Active measurement group selection macros: `set_meas` and `get_meas` (#690) * Pre-scan snapshot macros: `lssnap`, `defsnap` and `udefsnap` (#1199) * Automatic scan statistics calculation with the `scanstats` macro as the `post-scan` - hook stored in the `ScanStats` environment variable (#880) + hook stored in the `ScanStats` environment variable (#880, #1402) * `pic`, `cen` to move the scanned motor to the peak and center of FWHM values respectively (#890) * `where` macro to print the scanned motor position (#890) From db5dca612b063885a43cf3d1767a7c84b2097770 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 7 Sep 2020 13:18:05 +0200 Subject: [PATCH 802/830] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebd329f025..9918e6e3ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Pre-mature returning to ON state of MeasurementGroup at the end of measurement (#1353) * Default macro parameter values in macroexecutor (#1153) * Executing RunMacro Door's command with string parameters containing spaces (#1240) +* `macroxecutor` and `sequencer` now react on added/removed macros #295 * Setting of environment variables in Python 3.7 (#1195) * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) From 12be55a6e049a9d702f6dd8ea0b97fa875f24318 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 11:30:03 +0200 Subject: [PATCH 803/830] Substitute travis deploy ti PyPI by GH action --- .github/workflows/publish_pypi.yml | 29 +++++++++++++++++++++++++++++ .travis.yml | 21 --------------------- 2 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/publish_pypi.yml diff --git a/.github/workflows/publish_pypi.yml b/.github/workflows/publish_pypi.yml new file mode 100644 index 0000000000..4e6df42256 --- /dev/null +++ b/.github/workflows/publish_pypi.yml @@ -0,0 +1,29 @@ +name: Publish to PyPI + +on: + release: + types: [created] + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+*' + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* diff --git a/.travis.yml b/.travis.yml index 5712e295d8..00a476ccca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,24 +53,3 @@ script: # build docs - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "cd /sardana ; sphinx-build -W doc/source/ build/sphinx/html" ; fi - if [ $TEST == "doc" ]; then docker exec -t sardana-test /bin/bash -c "touch /sardana/build/sphinx/html/.nojekyll" ; fi - -deploy: - # deploy to pypi when a version tag is pushed to the official repo - - provider: pypi - user: sardana_bot - password: - secure: "HvZGtw8qFlacssi7FE92+gFgQPRRPvurpPxi/Gq74TeKWU0X4EbWVT3XMdi7sb7yA7JQlOGIGtY3ofzEdrKgKcEsrxxKbeSW7foDf3+AlmMF7c31ePxkqBCGMSAxsaCjKJR2sVtBNiycp0I7LWYeKlzFNY2W8aZW9dnpkC9aD/oGdNRJlCVGq912xaTnXRxmUrh+2IeUqsXKqfih7E0Qw99VXOLFdHIHtoPGN5ka+tvLp+zNFMi1q2HUyix4P/aQ10BwE5t1onfdSBBh7bzZTINoUVuN1bstNXYcoqfVMAbOoeArIIr7z41eYd8G8WMTXJp2MFrO61AW6xK8htB07RX2eaEWq7KT4zazG5vP/Skayr7ofnB/d3Rs1BOre9ttScJIxwyQLhL60WeM9NyCoHVjNdKYK5gNHX4se/6FOzmHm1VgQgI9bzyfIIAoSSyUL/5KOGdOwhMPSij5AT1YIy8RSe7efm+xw3md+wcmEsbaMX9VEy2YgTL0/nmFHrEA+9HV0I5xkFBQ8BHuK0YFubQ9rG99B1GwF0Vl85M+Ylp5D1/p70sXCHEUk3SbOcg9Kz0TTisDMuDT2ajJYGylg7/OskI5OwOBbEndP8OUPesm62V1ciQcKjH2L81yWajRPSfd/OPjoMwG+XdaG5rR7m2FACXvyhEOIeK1Mt41MvM=" - on: - repo: sardana-org/sardana - tags: true - condition: "$TEST == testsuite && $DOCKER_IMG == reszelaz/sardana-test && $TRAVIS_TAG =~ ^[0-9]+.[0-9]+.[0-9]+$" - - provider: pages - local_dir: build/sphinx/html - repo: sardana-org/sardana-doc - skip_cleanup: true - github_token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable - keep_history: true - fqdn: sardana-controls.org # Set custom domain - on: - branch: develop - condition: "$TEST == doc" From 222c4065be81b0275bd04276b632e4ad05efd83d Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 18:55:46 +0200 Subject: [PATCH 804/830] Make certain columns editable for timerable channels only Timer, Monitor, Synchronizer and Synchronization columns make sense only for the timerable channels. Disable their edition for all the resting channels. --- .../qtgui/extra_sardana/measurementgroup.py | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index 4e72057d88..d11dd801da 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -83,7 +83,7 @@ DEFAULT_STRING_LENGTH = 80 -def createChannelDict(channel, index=None, **kwargs): +def createChannelDict(channel, index=None, **kwargs): from taurus.core.tango import FROM_TANGO_TO_STR_TYPE import PyTango import numpy @@ -494,20 +494,18 @@ def flags(self, index): taurus_role = self.role(index.column()) if taurus_role == ChannelView.Channel: # channel column is not editable return flags - elif taurus_role == ChannelView.Synchronization: + elif taurus_role in (ChannelView.Timer, + ChannelView.Monitor, + ChannelView.Synchronizer, + ChannelView.Synchronization): 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'): + # only timerable channels accept these configurations + 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 From c17dd21c0ee321e6de05dc67fb69011117df5875 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 22:27:40 +0200 Subject: [PATCH 805/830] Avoid Synchronizer and Synchronization problems with non-timerable channels --- .../taurus/qt/qtgui/extra_sardana/measurementgroup.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index d11dd801da..c929f06d76 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -524,7 +524,15 @@ def data(self, index, role=Qt.Qt.DisplayRole): taurus_role = self.role(index.column()) if taurus_role == ChannelView.Synchronization: ch_name, ch_data = index.internalPointer().itemData() - unitdict = self.getPyData(ctrlname=ch_data['_controller_name']) + ctrlname = ch_data['_controller_name'] + if ctrlname.startswith("__"): + return None + ch_info = self.getAvailableChannels()[ch_name] + if ch_info['type'] not in ('CTExpChannel', + 'OneDExpChannel', + 'TwoDExpChannel'): + return None + unitdict = self.getPyData(ctrlname=ctrlname) key = self.data_keys_map[taurus_role] synchronization = unitdict[key] return AcqSynchType[synchronization] From d213407b409b2aef29d61442902202bbfc10e82a Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 22:49:54 +0200 Subject: [PATCH 806/830] fix flake8 --- src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index c929f06d76..e7c16f7e01 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -83,7 +83,7 @@ DEFAULT_STRING_LENGTH = 80 -def createChannelDict(channel, index=None, **kwargs): +def createChannelDict(channel, index=None, **kwargs): from taurus.core.tango import FROM_TANGO_TO_STR_TYPE import PyTango import numpy From 3d5a849baf27ac0452dd19d89753606017522542 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 23:28:11 +0200 Subject: [PATCH 807/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9918e6e3ec..17ee048f39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ This file follows the formats and conventions from [keepachangelog.com] the documentation (#1275) * Register a TaurusValue factory for pool widgets (#1333) * Direct links to Sardana-Taurus model API (#1335) +* Use GitHub workflows to upload to PyPI (#1253, #1166, #1408, #1189) ### Fixed @@ -102,6 +103,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Macro plotting in new versions of ipython and matplotlib require extra call to `pyplot.draw()` to make sure that the plot is refreshed (#1280) * Controller's `StateOne()` that returns only state (#621, #1342) +* Fix problems with non-timerable channels in expconf (#1409) * Allow MacroButton widget to be smaller - minimum size to show the macro name (#1265) * Remove TangoAttribute controllers from Sardana (#181, #1279) * Remove deprecation warning revealed when running test suite (#1267) From 688ebce9988ee1cf4b273a69221b9b0e403ae361 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 23:31:42 +0200 Subject: [PATCH 808/830] Bump version 3.0.2 to 3.0.3-alpha --- .bumpversion.cfg | 2 +- src/sardana/release.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 790a882a6a..17d59976e1 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,7 +3,7 @@ commit = True message = Bump version {current_version} to {new_version} tag = False tag_name = {new_version} -current_version = 3.0.2 +current_version = 3.0.3-alpha parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? serialize = {major}.{minor}.{patch}-{release} diff --git a/src/sardana/release.py b/src/sardana/release.py index e4d7c0dc96..29f61e2661 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = '3.0.2' +version = '3.0.3-alpha' description = "instrument control and data acquisition system" From d720a958fa26a83833e9d742e2f31d23ba6d7d1c Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 23:31:47 +0200 Subject: [PATCH 809/830] Bump version 3.0.3-alpha to 3.0.3 --- .bumpversion.cfg | 2 +- src/sardana/release.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 17d59976e1..11c0d4b9f9 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,7 +3,7 @@ commit = True message = Bump version {current_version} to {new_version} tag = False tag_name = {new_version} -current_version = 3.0.3-alpha +current_version = 3.0.3 parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? serialize = {major}.{minor}.{patch}-{release} diff --git a/src/sardana/release.py b/src/sardana/release.py index 29f61e2661..c4d742b604 100644 --- a/src/sardana/release.py +++ b/src/sardana/release.py @@ -47,7 +47,7 @@ # we use semantic versioning (http://semver.org/) and we update it using the # bumpversion script (https://github.com/peritus/bumpversion) -version = '3.0.3-alpha' +version = '3.0.3' description = "instrument control and data acquisition system" From 1c495962aaa5de7f41ff0a6b2af7489d4605019e Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 23:33:29 +0200 Subject: [PATCH 810/830] Update version in deprecation messages --- src/sardana/macroserver/macros/examples/scans.py | 6 +++--- .../macros/examples/specific_experiments.py | 2 +- src/sardana/macroserver/macros/scan.py | 8 ++++---- src/sardana/pool/poolmeasurementgroup.py | 2 +- src/sardana/taurus/core/tango/sardana/pool.py | 16 ++++++++-------- .../qt/qtgui/extra_macroexecutor/dooroutput.py | 2 +- src/sardana/test/testsuite.py | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py index 5d2af31a36..72f120d431 100644 --- a/src/sardana/macroserver/macros/examples/scans.py +++ b/src/sardana/macroserver/macros/examples/scans.py @@ -99,7 +99,7 @@ def data(self): return self._gScan.data # the GScan provides scan data def _get_nr_points(self): - msg = ("nr_points is deprecated since version 3.0.2. " + msg = ("nr_points is deprecated since version 3.0.3. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -184,7 +184,7 @@ def data(self): return self._gScan.data def _get_nr_points(self): - msg = ("nr_points is deprecated since version 3.0.2. " + msg = ("nr_points is deprecated since version 3.0.3. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -296,7 +296,7 @@ def data(self): return self._gScan.data def _get_nr_points(self): - msg = ("nr_points is deprecated since version 3.0.2. " + msg = ("nr_points is deprecated since version 3.0.3. " "Use nb_points instead.") self.warning(msg) return self.nb_points diff --git a/src/sardana/macroserver/macros/examples/specific_experiments.py b/src/sardana/macroserver/macros/examples/specific_experiments.py index 53363e7f4c..65be312089 100644 --- a/src/sardana/macroserver/macros/examples/specific_experiments.py +++ b/src/sardana/macroserver/macros/examples/specific_experiments.py @@ -123,7 +123,7 @@ def data(self): return self._gScan.data # the GScan provides scan data def _get_nr_points(self): - msg = ("nr_points is deprecated since version 3.0.2. " + msg = ("nr_points is deprecated since version 3.0.3. " "Use nb_points instead.") self.warning(msg) return self.nb_points diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index c80e7d607b..53cab53338 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -336,7 +336,7 @@ def _fill_missing_records(self): scan.data.initRecords(missing_records) def _get_nr_points(self): - msg = ("nr_points is deprecated since version 3.0.2. " + msg = ("nr_points is deprecated since version 3.0.3. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -964,7 +964,7 @@ def run(self, *args): yield step def _get_nr_points(self): - msg = ("nr_points is deprecated since version 3.0.2. " + msg = ("nr_points is deprecated since version 3.0.3. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -1826,7 +1826,7 @@ def _fill_missing_records(self): scan.data.initRecords(missing_records) def _get_nr_points(self): - msg = ("nr_points is deprecated since version 3.0.2. " + msg = ("nr_points is deprecated since version 3.0.3. " "Use nb_points instead.") self.warning(msg) return self.nb_points @@ -1875,7 +1875,7 @@ def getIntervalEstimation(self): return self.nr_interv def _get_nr_points(self): - msg = ("nr_points is deprecated since version 3.0.2. " + msg = ("nr_points is deprecated since version 3.0.3. " "Use nb_points instead.") self.warning(msg) return self.nb_points diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 95e1eb63c9..6776a6f045 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -924,7 +924,7 @@ def _fill_channel_data(self, channel, channel_data): channel_data: if self._value_ref_compat: msg = 'value_ref_pattern/value_ref_enabled is deprecated ' \ - 'for non-referable channels since 3.0.2. Re-apply ' \ + 'for non-referable channels since 3.0.3. Re-apply ' \ 'configuration in order to upgrade.' self._parent.warning(msg) channel_data.pop('value_ref_enabled') diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index bdf9d86e15..40c25fff22 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -2338,7 +2338,7 @@ def setTimer(self, timer, apply_cfg=True): :param timer: timer name """ - self._mg().warning("setTimer() is deprecated since 3.0.2. " + self._mg().warning("setTimer() is deprecated since 3.0.3. " "Global measurement group timer does not exist") result = self._get_ctrl_for_channel([timer], unique=True) @@ -2349,13 +2349,13 @@ def setTimer(self, timer, apply_cfg=True): def getTimer(self): """DEPRECATED""" - self._mg().warning("getTimer() is deprecated since 3.0.2. " + self._mg().warning("getTimer() is deprecated since 3.0.3. " "Global measurement group timer does not exist") return self._getTimer() def getMonitor(self): """DEPRECATED""" - self._mg().warning("getMonitor() is deprecated since 3.0.2. " + self._mg().warning("getMonitor() is deprecated since 3.0.3. " "Global measurement group monitor does not exist") return self._getMonitor() @@ -3085,19 +3085,19 @@ def getChannelsInfo(self): def getMonitorName(self): """DEPRECATED""" - self.warning("getMonitorName() is deprecated since 3.0.2. " + self.warning("getMonitorName() is deprecated since 3.0.3. " "Global measurement group monitor does not exist.") return self.getConfiguration()._getMonitorName() def getTimerName(self): """DEPRECATED""" - self.warning("getTimerName() is deprecated since 3.0.2. " + self.warning("getTimerName() is deprecated since 3.0.3. " "Global measurement group timer does not exist.") return self.getConfiguration()._getTimerName() def getTimerValue(self): """DEPRECATED""" - self.warning("getTimerValue() is deprecated since 3.0.2. " + self.warning("getTimerValue() is deprecated since 3.0.3. " "Global measurement group timer does not exist.") return self.getConfiguration()._getTimerValue() @@ -3107,7 +3107,7 @@ def enableChannels(self, channels): :param channels: (seq) a sequence of strings indicating channel names ''' - self.warning("enableChannels() in deprecated since 3.0.2. " + self.warning("enableChannels() in deprecated since 3.0.3. " "Use setEnabled() instead.") self.setEnabled(True, *channels) @@ -3117,7 +3117,7 @@ def disableChannels(self, channels): :param channels: (seq) a sequence of strings indicating channel names ''' - self.warning("enableChannels() in deprecated since 3.0.2. " + self.warning("enableChannels() in deprecated since 3.0.3. " "Use setEnabled() instead.") self.setEnabled(False, *channels) diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py index 4c51da40a3..9ad0104b54 100644 --- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py +++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py @@ -153,7 +153,7 @@ def __init__(self, parent=None): from taurus.core.util.log import warning - msg = ("DoorDebug is deprecated since version 3.0.2. " + msg = ("DoorDebug is deprecated since version 3.0.3. " "Use DoorOutput 'Show debug details' feature instead.") warning(msg) diff --git a/src/sardana/test/testsuite.py b/src/sardana/test/testsuite.py index 015cc52f42..cceb776e6b 100644 --- a/src/sardana/test/testsuite.py +++ b/src/sardana/test/testsuite.py @@ -31,7 +31,7 @@ def main(): - print("sardanatestsuite was removed in Sardana 3.0.2. " + print("sardanatestsuite was removed in Sardana 3.0.3. " "Use pytest to run tests.") import sys sys.exit(1) From e4302dae584e070d4c44b487c475c06387029101 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 23:35:27 +0200 Subject: [PATCH 811/830] Update version and data in CHANGELOG --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17ee048f39..f81bcf1df2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). This file follows the formats and conventions from [keepachangelog.com] -## [Unreleased] - -## [3.0.2] 2020-08-22 +## [3.0.3] 2020-09-10 ### Added From 3d641ec71d793518120dd745507f605c6cc6dd11 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 8 Sep 2020 23:43:31 +0200 Subject: [PATCH 812/830] Bump taurus requirement to >= 4.7 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 4d12ca5066..faa673efb5 100644 --- a/setup.py +++ b/setup.py @@ -53,14 +53,14 @@ def get_release_info(): requires = [ 'PyTango (>=9.2.5)', 'itango (>=0.1.6)', - 'taurus (> 4.5.5)', + 'taurus (>= 4.7.0)', 'lxml (>=2.3)', ] install_requires = [ 'PyTango>=9.2.5', 'itango>=0.1.6', - 'taurus>4.5.4', + 'taurus>=4.7.0', 'lxml>=2.3', 'click', ] From bb20838354aa47244054a92ceae8806353938bb0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 9 Sep 2020 18:45:16 +0200 Subject: [PATCH 813/830] Update CHANGELOG --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f81bcf1df2..73f7474440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,9 @@ This file follows the formats and conventions from [keepachangelog.com] ### Fixed +* Improve macro aborting in Spock (2nd, 3rd and eventual 4th Ctrl+C now act + on the macro). Also print additional information on what is happening while + stopping and aborting (#1256, #978, #34) * Use `tango.EnsureOmnitThread` to protect Sardana threads (Tango is not thread safe) (#1298) * Avoid using Tango `AttributeProxy` in limits protection to not be affected @@ -64,7 +67,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Avoid deadlock in Sardana-Taurus models e.g. `MeasurementGroup.count()` or `Motor.move()` (#1348) * Remove redundant protection in PoolElement.start() and waitFinish() -* Fast repetitions of single acqusition measurements (counts) on MeasurementGroup (#1353) +* Fast repetitions of single acquisition measurements (counts) on MeasurementGroup (#1353) * Pre-mature returning to ON state of MeasurementGroup at the end of measurement (#1353) * Default macro parameter values in macroexecutor (#1153) * Executing RunMacro Door's command with string parameters containing spaces (#1240) From d4561a0010a1af28fce6cb56c1c28436009d68bd Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 9 Sep 2020 22:25:56 +0200 Subject: [PATCH 814/830] Update showscan online documentation with pyqtgraph based widget --- doc/source/_static/showscan-online-multi.png | Bin 0 -> 27875 bytes doc/source/_static/showscan-online.png | Bin 30556 -> 19485 bytes doc/source/users/taurus/showscan.rst | 23 ++++++++++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 doc/source/_static/showscan-online-multi.png diff --git a/doc/source/_static/showscan-online-multi.png b/doc/source/_static/showscan-online-multi.png new file mode 100644 index 0000000000000000000000000000000000000000..96a3bdfe432d0555b617ba6982c92aae43c9e988 GIT binary patch literal 27875 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 literal 0 HcmV?d00001 diff --git a/doc/source/_static/showscan-online.png b/doc/source/_static/showscan-online.png index cf25cf9c1becceecae70a078a8be534d64c44554..5b559d8e66339cad47f80b2a2f657a035f16ecf6 100644 GIT binary patch literal 19485 zcmbun1z6Qt*FJm@l@SCa#Gv#bB_$ypDlOfT2c)|@6_rvDL6GhiP`W`;=`LxcyBiLC z3+I{nKl8lr`~BbR`_45pFo*N2z4yA;z3z4I6Cf`uj*ER08-YOJN=k?*A`qDP2n0sa zALrnmBgWSh@Z*w=gt|Qff%om~UySz*c%%r#Erg`VLuHrPg4mlVE^XVq9hL=h(9D#zW7k&%2iQ< zw|aCzk8qgM(ij|)R?ui$ORvz>$RZhg`<}JAU%w=+Rs~$`S9f<~!-$l0yq>}BBwoZo zDypbB&UYrUQ&YQPVImL{49|UIXmsA1aK>PYOm`*;Gdc?9D1ub&&OMMap! zLk35?lx*yUB6n`piF!v@++$}qKicEtz}|RokyTJopf*lgP#`-`{s&xHR6K+N-EoMk zS?cM&J+HvU#q&NgqvY;|^XEtG_u@7-Y=iTEG&GFD?`B_%*1V78W@h@7gO@B!;erM4R;%OL_a14tG*L+_ z#I4s22N;?_<@Rrju#&EMs2m zzu1o|jOTOX7_E_42&avASAVOV;EB!jdQ$$eXT0ak z`!(<_nreL!IM4O{OZl_TD8+0PsPbFc4HO#z^NSi!gzcD; zYK91=Y{hWn1aC=^A0vF51;LTQ-G(!9NlWsAvjnmr4kQLALrT~>+J0O@L^U#Ar#qNBJ+G`vp41FDo=U2agO`Rs}Wmw zX=(ey!I9FA;#zXsu0n2?&RqEDvuDqy8r}#-$J>6>i~C44D6}>tpm)rj(|41QjNfz; zJ#Kj_$hD`OD+mF+j-RVJ*nT6&fU-Vy^wS;W?JqLq3EBGZIW5NuOt;j6^*?CkEjR8g zhyVKZ3txa+@7Pmc)q8tmT0EUueZM1&hlhZmjgcg1%FJ9yyL443K}}gXvyhlhMP+dl z?t2ka>Vc2vdU%Sz{~tTg`pj@~aQGY-l^;2N8ypq{ z1mY%D@sbRA&K)FNy6Ahcm5RK4rl6`4_0bdRqw6&8-db8q{rBIe-1qglPiHbeDoi*z zby8{Vt*or<<@IH-(0C>PWWT4ql!P&w)zfTINXL73A%l#IBdWcp?tNHHjv%*vdTQpU zPp%F{=(O3k6uAUJZ7r>jf=rBzj7&_H1VmKGIR+S){frkcUI-Ph z+L{dtW4RV3?Qy&{f~UFc#*f=$>0Qxn-a3P5>|#B7-n)ynM<*F{k5wG0NDv5^na=88|k1_mNBGiCDu3`|U( z4357xS{c)_vt?pl&}PV34wSs{=Ra+2RYSQ?mGG_NVPB3u?!*+xKj=wsL*dq#yGYoq zuj@IBS8Sv>IH3=Bqj)XFjQRsozv0A7x^BjnWU8|#csP#5_^#a~AtrVaEzOgYO-N>@ zAdSh(ii20n%vY#_{ zL%*}+|K&KbaHh5dp>f|d-0S!1eA8SL+KhVmUbsoh%J#z#RtL1w!C~^rjwpIbDnG`W zs`>1ki$4E^t3$fG3cLh;nBC_M# z?k0v&=W={kb8)Qt=<7?I!otEGyz@RR(M)Wz)A8)aoD1Za5c-!o`4Dm%pP#<;|Il_n z-3RmH<@H|oV(alScqWvIiHWW4=P`v|Z3hR3qurIA;N;cI+NWY!$2ASXmS&?GBFuD* z{A3i&NXpcVjMXuw3l}DB?%U|JHQY!$F@HMmi^}w_cHNylTF6MKs5H~@`ZCL;859`Q zaWI!9UuFIE#UAbR}hy`cN*zH~^JG%n-5tOtvPWL|HHk+)$>qdHlv_n*q zglZTK6?p`Ap3xs-S*Ow04=1iIqO5T}w;W~tIIRbxBT*A-mQn~^E@6W_|Dh)agan}u zp;5>rJw5xaEt$R?dSn*z1~P2>5|IzaeqR|i;uCw&iy*RVH&m((vKx`1_|cpzQ=f*O zgd#(^^Ijp$#D)nGr`sis*F&*G%aKx|A_k&6Qk7(?QUpWvjR@=0Ma1P?j*cI&p9zgg-Dny_!Ynswr~v zQ>3V6hoqJ-|Ace$s%l% zLO=jrf9;uPnO}UDheld@9hc*g2Q8k@m!BCcy9Ksq|F3%X=^wU3R{46*trJ;O&i?9R zGqkEvO;6;3`G$m65aBCHK!7hZtewLZ$A}#BR6+rf_)dxWEN$p(kii3G^MLbvXogY^@|uB!7KoPP)Sb>9#U#w-(^ zb2qQTvk11u5IGIt){{+1S>Dwy<(sJH z_F!DxI3;;`UC!aB`b((9jJrY%e41GwJyO*RGXevdH$`-&8oz&$SEH&UzjVo zn7c?=Z-?__jpgZqjD!T^IG24I?&sOUa2uCX)Fv(A?Lr?8=;b3s#EYJt`D3QnC#ep)MXT=zY8bi_e^2^8wy$jA^lR5@3?bpa zZ(VLZ<`wnTHYpz=)}PDa8yg$-e)y$>7Ew`AEfGvDjg2-QJMW~JZ*Z8%7aFw%1_t)$ zX;<3M$yQH74)Lm&u#}<+GhP1qna_DOmd&s^n4IrN2&FaWsrNAk2FBEnH%Id+Ar$V* zpWid6jk&K?tLCUQm;i(HzNc6c{`Tz^GO{W!^dT^dH>6xZ*{Nx0hET;%fRRxP2xyCR z_w=+G+3V^idVM*Uc|mCDl~UIYtbJUnop0ZN{4g>$W(Y(g1CtF54FTCPvG6kUBf`Ti zhKfv&_cxaMbC-HDIu3W1aBy%s=x_7z3@UvQ5f^v=#IY6>9E^v9)58~75fl`3mVF@Y za7oN$q`9uV*fg8rs+;>i%eSYydqk^8V({Jfr}Qi=r9wUq_!oubSJa z$D3XvH60zR&rg=+a#hXM)uYg+CMNUxAp#|CJB##6=@x?phRxyh1Ox+e-!082YNnc)?WdGM!qo@eeobMqP zf571S8yFeY>`w*-OJQSUr^_Ye362#Q!ur$P_Q!dU+Xj(>M20!wrRud^QNQzUa*dK` z-CArh0CPgF75NLd3Gup2pi!%;~$%grV&mHWD@lnw~>8BEC)+Piaz5hmj7f) zye2P36_2s8b%8PegDkC=i2cOPNM^^oNB1{~{7I%Q(95BcZsN(6H;^xp)=F1>Y|qf)qW#XPmP2cwZ)qlW9UZqOhKq2ZzrQq5RE{;7 z;H|R}k%v!m04#FGv?+9wCmKdz(u4-VS-Y5rBu17kAXKmKFXO&&PZ$6brF^;x6(E8YD`Z663Hk>dGy--37J zSRF2{+rokT7B9vyu4@=qVreck-MXDg}MUuCJ&)4z5yq2wFxKzc?@UKHd`S_*zOOu2ylF*b< ziHeA%E13GoB?FlI!5U+JO1O~feCHyYior|&uXNURv|WJF^{UE3KhunfFAAq&)V%&X z!{F;C+@6B>up|)2a3HG&NoL@rKm*`Ex=dqLuvUwpmi^ zD%+-a)C&s>fytm$EndG1H8`o5nI-^7TYI}Iv8V{KXj5}@!Jq};gFMQzFZ;c}!&3js zGc1ZLp}3@+9tSg#bK!4eW0$jZfl~-PiQW?x7x#Pla)DfQ^!xYk3*?WguUDMhzw7O- zp@Eek#OhJI$>&dJYD#XFDe>o$*<5bpVq$7*yZ6(sZ1f#q80^O2M98(nzYHCrh z6%StJ8Q_X#dE@rh25(m!+{~iQtEw7#`uS&qMs9yW0b9e>&8@B4lS6Y+g@VFDxZt~Y z@2V)M;PJV{nj=7k(&YTWMfLUdnCHB#nfNkpV?Pj{iqWF@ z#_`@qjup~hXsCjg)<`}|DCm1P%wl$SHee-)30*1TSQjrE85#n7TXZHqG%Dm_W!-p= z!~C<I)YyMnp!!J9w0W z$1{;AW;(hj1qM)K1^?@C#gaQn$ncOclBeE1fBt--CnG0hYjaZvmXW~ZxtsX}$QBXe zP-ZvrNnRq1&xLw><-LqIokFmN-PtW7J znL6lUg?aT1mDfofJ0jrmcLIoLy-p71{O~Dvbsqr23#P43MX<56Ut`hnn0c?}cJC(Q z;-kH{1M6+v4^TUYDxt?**vuV50 z?2SOow6OU2n<{m{^?*Gt$3T)k%VJNlvA6M3L!+6HNM~YV{=0-}_KXjv6!UMKCl)PV z%+3LJC~!nrILL4D^ON(qXX)58jCCG%k=ei(3EZrc$4g(gdJnyW53#E9bbv2tISm>H z557yL)2?vX7_?|IDs;^TS?nHu#H=TTy_=srOIKG{Z|$CFJdZvzBe7^ep#P`)mf6nS zjs~60*A~aQY&N^kO*PCa5qRXh4m~nl&?)ejc<`Xk2Lsa!y`v^~{vz;4qD8*(?xVF@A+O_o z^Ze?PvNCQC4h~w{xrEc>3~?mpgwp)0Eqa>WKPE)sOm_D6ps`pM4nmC$s(%QjkdBJV zOlTu4Q;&z9h13KJ#50hGAO!Ng72wU9^qRr$tq!5Js`c%Ko}<0B)9Em|$5K*D>VgQ= zgNzV-U{&kUhelH7{T~6TIv@-}BO|X7655SA4y=sV3JC~Qd!L>_AnMe3l#3_B6Lm^? z7N(sgD@ck5eYH;<_Sb)jQ@BnSb_ntTWEPqBERsX}%BOvi&-a0dhzQ6vXj4ha0N!F} z1qB7A`V$dOgt)`rK4W^;`KlDSJpg6v%9WD3x(5WGpD@B4*}J(__;Z-|FT*WU{BZ>$ zTWx10K(of<;OA%k)04eghzA;OZf*t!hWqzbK%xOkKFm*W8{zr-ih8>9WgC=nXu?h~ z#=07em~1@%!B-rXZ{NPb89!!b&VLIa3QYEItnmc(Xm~E*4~%*J5`UFDDtF}8Bocpp z@JF(#8U#y(k9|R$G_!9p85*Cx25RXLHTtg_wFn5JQtfYbvI!f6N?=!BGLhIJ2V_P? zkX^xydVQ|5s%2nQ2G$^-W3Hw0;hVZXPRr+OvBy)SZd^>v7!HfgvH3f*xgUMlG$a zT7YR{Vz0J%RdaR%XybpD+s`fbS%NxFwa#gL^+ClrkzuMu)<0;(Q4fWH+Y`o!wVET@l=JwzooAY1C)#sS!nHaj^V(wV zLsK=D_xUWsYvi^0-jv4MhXYHq=BXC+3=BB{=QLrG3dCVe#H6I|liw~KpXVTbXMn+$*KWJaaNxONEjK-LOD8LmYSHTd$_$Y_i^_N7NrX-bPy*x_R|Fr z1fc{-WU{|Q7tfX6fyTvvNZuu~KPH4O_m`YceAWt;qJJYzC|#WX!|H$0i=Xd({5ng{ zKM*l^)!6bUE0;&Hi-_ELseQ=>4-z;5L7_xhqb)P`596~+P+#p_eElGN{>~X64`}V< z!t2!xY&0OyJNpADm`J2;U)c6?_t_1cL2%H1sCJ#biRQU=ECVUA(nnAooc=A6AZp+L zN|1)dMe!d>c<0Y9+_?rQ;P{{a#o;?MyFN|tfwHz3^4D8`s!WL+$)tIqiR^fpv<1|O zT{n`P5$kM66l))iiG7}J^=CXcP}{G56qV7rQVBl~AK?_S)mCJy~AYfmRuQ|J1TyM3EGMUd8SGPp{>%Rd)ibVy%U~pAMOG7 zW@qndyHBcCGVN6zs|Ni#EUBKCNmKYZIP4C`$>!+VtlgI0`{(Z37Cs&L%L{-)IcA<7 zAm1ghEGnOiimH$!?fSVmK5(7a3-Tq@RDYH_H?J9`T10 zD}R?oH*s!8=!$O<$}<>9`6Q6qo~U$YI`;D+>h|CxUREWIWsBnpSj)jMi25RDlKsRH zkWe8m;b}vHUwd;K;=dOUCZ-MMAmPN^KH9a+Anzmr6O)@omZ+{H&HqKfYr?1;mWi~adATPx4?*Zxegb@4GWQ|$CSSsFq~$8Yn_QfB-QOMMtTII2ui&_ z%h7dX4$_G#crkI4N^<~RVK#qIm*GM*v0eLKh9jv6!LmP;b@e1VuZlLx51-|BmVsPGbmAz`~3fc z?wM#f2NayZpvQ2pbdC?hji+y%UHada_`vn7o$pUShet*ZCqzUc!(~0u z{?eJ^{jZi#0Wl!-rph8;MTm^`)lB+7)Th$d`PL`gO@c}=$G12A6)j%WE?Ti4|Eu{p;4@g7M?!xl)%^HBIP!40bJ z!1~K?NCj+t6uzsJGOYl@?-ynv8AM1})v0fCfo@h$rTM5{9pKl81w#6AfI8-c;V}vJ zP4)unIv0uei8zEb92$S?O!~UBR{Q6S_Qa+@wZC3(5o(JJl!zY-O$_BblQ9VfLEP~f zfO!3J?+jb?9vjYEzVi1^wV_L9h4#?vFN}oDcCle3ZtOwC0pD!lT|KAAb=Sv8Zcr)S zW>YiB8vByS`me?qo*(iiF5CrzI8?81>pa^v>h1e|zvQR+uuSflNM3mTMKFzRkZ^m( zg^mrA=;kH())UikZ&WePVj4b}jvk6zV!UiLmy`3jHPT$Qp!?g(6nvLw-&nTR;yp zx%DQ;@_#6?ytGa`42=C$R0W9%So1&G2wQv{NVW^}R;yXt`f}(^!{=^fVv^Rht>Jaz zs-JSQWB9+wA?l}`j%A8oCu)M_F~^mgo(cHU7h z%Nu{CkecrsQZ~}L=Y@4I(wy~i9vHV?LkOkNNC#ta>N>p1p~YK&2=AQ2JB!cSiq5TL zLgHxewMO}Et(fmec7e#i2C4=5C3$a1NW8xroO2d0yjg!wn{GV|(2x0LOAslpBJD*Z z7RfJ9KxzmRy(=&MO$?XBotqXMt+_DU}@TO|&+hbkd*^b}42@{j!1)WOyx(`+yoQq{^t&>lGc-Gs% zim!T1-s1`0fOdhiKUixTQVY>y)&B~_4IUSi~0A>om7*~nAN3KsObsYm(hJ5es z$1jngctWOj?VW0g8#WYsOvpf>TrZ`cUK$sZBkPoJMbdDbu4{RovQ&v>NEw?-=-H|V^+Pkm8x^)4oUuSd$T|yurYOU+>JfJ z;7m3DK}VhFr6@uZ^qR0K$I8rX#pcH#Yt^MOkzqm`igxdT>@8bAwbNfY(am(!bwuqv z@1-HljO7VVjpgdRxk&3mG)TCP)lPM1xrau^Zr+xZN^zsX=njqIIsUH)%8zZirHjMcT+*nN<1pKYlQM z!7nH4oT#!XY2=256+@<~)NrR-TL7a}QQ(#&xoA={97hvr96J?|rHpU=_^RtsNPSGK zoc){>hH#>dj@Nr8(1qn?JTn1#x@JEhLlVD#$Fr3G9M6KQPSxd30{XkNUe)Ba?wY7-m_80t+PVY&$s3}_8LjE8qlB`+?*#i zn^#iSHYpr*crBE2IONc*Cp`D^W{ynAo4zM2I-%#lT<{Bz^zQvr(uGeN6^$mLq23-! zA%%k$xauDDkNQm6+i zV;@my$>`Vn#oX^<^@<#zIlDc>i{~#7RQYHv`R}X2A`tzPL2;?P)*U}nJBlz>Cwt1 z6yD<6EC!fW54X2%Ptn$r59;3Gk#m0k9?Z# zT!n$aRPD*Wxl0)r6+SL56X(SEo$UGf`H>MVC@lGe)oSEVi>E!J`B2zeI;^oQj0rB2 zc+cIM@o}|3gl`)z^;6Kd@i5p}F&%DX#KkdJwt5dmT<*23$j@nsBoWs_avl*K}x`@j6$cNS;Lo@W$L z@059`DbYGxEjn_)ebT&hjARtsm3= z&7>558*fn?IptMQC-DdWv#JCo<9}t_3PPbbn_M^QD-ek5(OA|sL^RSi_HE$CDo24_ zfN=V|2>aXal}7MJbJKr+|Ezy9Y*7NV4mz^XM4l&C$c2787Nv*HXoaI@sZ}3e#1Lyt z8yb4;pt-xa?0?p881p_wg9mh;T$$-fZa=A2SrYh?LBXMwjeSyV2AX&D*u%b;eP*7M z0klr5Bjs*8X4U#xaj@7jp$W1$1&ghAcgXzkhZ0sbbh$<=oed18L6zpU9^3UJQ=rO@16l31@T+e>exzq)u%K%A85pG2{6{7|I=bh? zV*d@|T&Jqv{4ih>fUY&ym`h5Z8EA{A>>d32oTMLZnd#{l%ID(U7qc?lc9*l98H-6M zC}ri`vG^W_|@NJjdff=yB?-JJ3n<_~J4;u$W{k#eem~PmO zWj#EqVB{WwKs!=y&r&{3NMJf%gNEL~N7bCOKR_0kYmMs3)hvah;HGq;3VOx;B6dw3 z*OSWWFT0oSr(c$K&#Aj4^00~43`ca^uCeuNS#7$h84}KW-RX8lX13^AMKF?XcSQm6w zlAxPsI_sbX-sQ`^e3=>&($ZjbJ>F=b1X)$+Xqi3eJNOP#o#p4|U^bV;EK+0MtUlG( z2cZgf4-i1BuAl&vSLl#!2j`HeetdEp`O2KoJ^~yL@$ote3Q5kl9Km5>+rm_OygwBJ z4G*^lMZTn@Bq)@QV1Qx8xz&&6il&(1&7h&Y zojV7ezAA?WMOZGy?yKd-5RA}%a>G&-XE$o4V`5Svo{2WkyCLM|@%Zs$FCc#p%Nal( z`>I+Z{B9o>?zn zghoq8M*@dwH}smIv%1tk>CJ!l?&CFovoo|H37V-~tLfo;TfqQx&cY%D&K4M5b!Fw= zJZ(N0{J7)eW0)Wfh4AI&WvI!8w^O-6As#=Qf)d>8691E(21^B}yu6%2Esu=LY6OR+ z%I4S0YZO9WBM-dqa>E*u9#&)mOVOVW8~VVfY*DbFv)d!Nc>Ws{ z6ckskBsx7v*t%At0KJHG$EAMjnuBzh(V_29Ja0}*7E4+{!vMNl3qL=ne6CSH9(5ki z@?HUnfq$!&9lDeKe90A^aJMDOIMB(1&N)o^FyO%b0WGE98(?l%` zFtBdTwgGNgz>3~kf>1a7SShNi+7BRq5%*@1Szor(%8<6rmkThUXCYd-TCr3EZE~uT zW@t&2n)N-d=4t>0{{9^>Np>yKtl{bBSOIrVUS7AKpY*`Dumf8Ec5o*1b_GEgQ{VId zsyE~EHvddyu07s_Tm&zG@=Ph!f2j{Cb|`uI&WSI+`cI$2T|=LC+-)iMjPv+rUua0t zRtV3|zN=4u3jM)}x@TZ;%KrF~A3*y|`7U#JGBaCT|5cCjI+%etq?U?+7=-k;A|fj4 zxY$ciO)U!eHwjpvS?RO_x>ddt+T(B`rb-WN;J2!uB}64C$x>5Qv~T z7t>}$0K!i+x9w9?(=0E&bL&h+U@!e}O~*xVrujXr60j6;yR1)StK?k7r-=RV;fcKb zr>EZoArc8L;owwG1@rMb%-=!+{XSZ+$A_H?p3v8HUKuKe3k<--LzV$w$}spfc9sSh z0t+AoouH3sX=o_8Z4!Yk<6L8|0#w*JNkn*DI5WCtfMLZRvMHyf2C$420A$Fx0>|t1 zz^3cZM5(JkUf)pC&^YW>l1ocX{UDvLkRle)NSokc2MrHMQL+hw=<2<(p#S1kK^DM` zLz6vcMH+Dnq@UL&U_Q%`OMs+5SnGWX`2+CvBV99$5G*v2JFu+@I&+ks+X}9(u3(Zm z8`*`lNKOFf*hl|tDya!wMEHWvq{|P0yyy$9*U8DJJGRhnfo)(Sci7kpii&y#*XKbh zo0*vj4Go+2yyo~mKbpP$R=1E^KnF$J5BuCA_*&KM6Vm_`26 z5hx-ohFg~j$L+_&#>T<}jJVFlspj;V6_43Aki+n*#H4`PZm7i4oGq%=$X-lL48+h{ zFErf#+E{f?b~ZGGQ=OL)9%rsKp>41wXfj3s0b+)*5Q$(^Pi8(n-s;dSHrv^3VZkTo zRaOYUfJ5T2GPLtvO{W|bzdu_=4zF+cbg=!OjSzEHS4eJVOFe+7sG!THz%t#NlH=pk zcDeyG{J-3M@Q>vWPk6RBuXp?T*7P|1`sT|2bfTvup%WA78Fq4K27ZJOep6E$uGNsT zh_vg$1(*&jd~z^_bn$_s34tOYBa3zUa>1x@Fgy~K2sTCNQU`%c@PB)UwQUh69xW?w zehA7L^M)Vss9n4pykDPC*UL}{{WL3<<0(4+kgwgaXu7T~ymwrnwnkTUzD(;7jW5Kk zb==;yog$Mf!&3FBKCCAl6m|D??T+y9*A#mLdt;t+TezrWr|Cvdeenc(di9!3G%jf^ zI5cGaLn^(G%i80Z?aSpwyatcPVPBfK+mFw?k^%T~@x3&+$9z8@1b3Vi)>M5wF44rw zmcu9G-`*UbJ8_ye8RoQ1=jY`Cn^EsfEjbUH40WAvR< zYm+pQerO)|FXWCtr^!NuHhR+{wKzQH1r~dbXa$L{-Z&Uxr(f*(zQ)LRyp-RO%CA!T zm2`5spPL$1)Yz$RA8kS&-+Sl9=T0j$GO?Yv%d1;n24Cneiaf2}?FkV)ao%l;4PvRV zX~*f{5w}ZEHCXl}a@(mo%-?&HE3C^rK7_tMn*SM#25mWhx;vb3;@OU~_&qnt)`VxR z`28B%{7W)*@X~8i`!xaQ(X7vsGJ7;{>a-$yuJ}bP#u>JRP{<@~e4E;-C37vA9%Q+>P~I!=&E2)924SOx)7qH* zU|M8gV@IVEK)k33u&L+|ZPr!0dvZcLj`cfeqjoo01>#5?fIIYbtGe^J=a>hfFLS?O8I3O5E;D{9aISy>RB1#7iEW}qm5 zFTa9ukne)qvG#~^kKS6@wGm}MnReJ|m?yL{k|rqVcz5kvz_NDb9#F(=DW{XF9L<3| zM~3p-?N%#F1&N9G1O%oIj1x}w7V#@kwyw?W9eqWedFjpwT9;txM9VxzpbRvO(K#yD&`dl%Tf`0NZ8 zw{M)?i`{lvg7b>%KvPGD_v*{Y)iU>=aN{-i=H4eKBMH=x+Y3OaVsFD zG4w1sc{0fv|B}^^LnFDb6p-`5BNS8;8!b2F_{YG!HYcaiL$g}7SYWV5Jip;=8`e^P z5?g)iKG{Vs+$zfs7I!}w7yS^SUsaM$yG;!VA8d!ookPmkypCjroR0VFLj=59R-A(q z)^*`x_Rkmkb6dtvS7D)D3$-rw9{)U*c1^2u^@!4oI225jrz>)mRhk18@3ndcR|+z3 z3yXWy{0ee!2(CTo|AH^TKV#xn(URRG2>g*Mo&T_u^U?-MP}-@ZXJMfnbRGL=zK$qm ztWt1hCnk2z=;VF;=;1u$-jl&UBuL+6z;@;>^7ka#sjfj=x$Ah{Dt{?@i)VX09mdl8Db{UQ;x4AG~2B`JT{mnZ?HNsSY~7 zDU7D%^_!huKb2gK>e)8ee6Rhn$_&{VnZx0flvns@h}D6e1@w<&?`(!vx7C*u>0jV# zXGcGNzj++Q64Y3oBIZ!F{jMuT?8N+gfgeTeTd(CO9$Mx*@66buzgfqMr_TT055MrpQY)BU*7cF`;r^ifZTj_bn9KFW{M zk1Hl(g%VdrgsAtHM~4OJyPke7)A|V|lN3w&PcvM*@+5PUa<1spTI$ z5m}D$mw5Q_mypsmLAHa3M`QWIx^n_`Bd9{F*nHaiD@u4w;yXM=WGQHyzGyzIab`Tf zh+P_H>nF_m>$6EaO~$7;Y{e4 zee<+=YgIkj1%Q=)YYG_wJn-C;8`+qWE6&F1unT2^1rMo#hslzOmVdvrDMVVKaA-W3 zOdvX*)oB~`NLcu3G*?JqAaN+CC80*C88vbgu%C--U`))dA#oV~$Q9G(>9)sf*ZbY7 zy}8-y2C4g|f$Y|7*X~Omo^r0sWsZ%V1P$!$YvlKn(_db5k~rBZr8*t1MJa^mqO6A# zoE`{dk4%%MGqkQVSRey2WM%gPyJZLn0c6e8woq0sne*FCEbYd~Z9CjKOyG&d#^?{X%xz^S@Bum2-J~GBiqi3>EN=5357fv{OGn*MpunI~EE6 z;<`2erF4AuSS-?cWwWO}p(9@4sHa_9ZMSSPo59_q6zvDRIl*fz?PLd4`cB(RI#M)i z3BSTnS6SHrik`AAZ%Mfh8`l?=u8(Kqd5ibs42e$MP%=2aF`S-uhrJ+6MY{7G8OZ7> zCzEeWsPAS6ehMuzjAPxtgGuTAIEaos7d88}r( z8?Tw!*2MP~0c+D*y%vd6jL&q`V_iE8uEw2=9JjKA1`E(=C7f8 zu!l#~|tB{`v(u&^aB z6_azPwp8Bik`KjU%>8O(R+yiig60}9fg_T3j_CDDBbrmk*-&lU&sm zx$NqI4wAFL`TN&A#hHuh^VkIO?7qGflr~qtaG#R*!cM78r4186m=jj ztM7C*X!AM;nve4G-n|R|aBxgVR-d8|=Ct=l1X2a5D|sumiPHrA{rA!S#TvN)MF5mM z-ipq$O~B#cODtaBWL<_YFEC>XkKVJBmfngv^$7F-R&3Rmwq;nkcl?yHu2OF*y`aF( zeN8S7vxgfnqDSTo8q-Ph)F_9o@3aquMeox4MKn_ZgG5p0QSi_A+Y=T5V7k_K|Iuph z*I%)dus81|dbTH>!DRfMGV@-r*q>mbQfZpvYJZJO>1Y_2!eMDKL~yLqdc0U5zw4vV zt4Ur>MW_fz1*U`F#MF%J)uJH}bJiwKfK0keHklD8#PXzQn$o(pxEMC$y}xfUZF2PL z=Kam)5!bWSCseEZGqdaa_YClL2~eG|=^svALpda&Wvu7NtvB8o9`)e;#ILa@8FTUU zzF!Sr4?xB{IR>7*#x^-krPfF-kFI6o1dy-ir{n~$4pgYba9*M~__X@5lb;t1d+c!H zKBE8_!|ray`vDFk_io=l0bgriGW69%6}s8kCZ{Hg#?{UO{~jY_YvG!rX0V^9mZN*w zRMZ@F}P50g2*(j$V?O7iHeGb z2%JnOYFdA$#k$X=Tt3nQrk?WhjAdg(Q`4v+j0}y^lP%z4tM2GiZyY=Cp=W9+ug@jV z`W((;`$(Fma0@lu9vPIGDG_-TE-c0GV3NhMBFAU5KY)s>6-ca3(H{Gx^BsH`J2f$f+_0@E-CgO?oEq&Q{W%y|csu^2J?cnn`y097@zS2q za$`#i3qAe8Zsm%4l={jrZHf7Ce?Obk244$!o42;MLdb>UQS!RQ7ezVDRlykbB|Buq zqgI?oE_$)Hsq#-=*tYjk9-y-5pm2*+@}dy zFrM&*H&q86y{l?kd7O+eq;P&OQ3ap`?+97^P5PH%z29YEKvmC<%S3Z5xGYc)QCE&k zw??sdw0mWJ{Mc%Y)5|Q*cX2O!u9nim1^9&tPO^ty`PM+#@6v6_Buj+;JO~kVVZQ6}6MOt&U+@>TE2J(Z@~) ztlA`LdGN^w$aY{U_jLa?2}JPDx5^qb^Vr{uK-P0Io%C>TWi%%>Rm2kqz&RuhE)#IA zNXpY)K}Dqx2>yxl_$l++P+v`xRAEA~ckMD1zj!rhPDih2Ll=M3&#fxIsFdvH=1ntZ z@s-TqHmoVtt4C|bg~zV&N~F7Zr9mX$ z(7~8nyXsuEgRzkzx@c>zpkNca4~8vjux(0e#ENW#`!JRzDm}Z@XHz)|$^ZLWEjrS4t-w zW>$F2YYWH6p}Uz%@fRH1`a%TQTxVY|u+$tce*G>*G&vRTwHRQ}iyapwO*1Eh9Td2` zd;n%PMmoAiE*xTlH+Jo$^#?hX7w?nr2D|@C!lEs+zaCpSWz^=expQz@<-U))d-v`T zEwvz*dHNweB;B3ii{pKuWV^4{*#DYzj%_)sSzf(C-BEMq)b8Kt&ThWzXb_-r)se0| zJ!C;Eyuu55F&P3p>`UJQzTpg|wYnsy3!QMtHl5vK-4|ahJc`)8*|mDkQ(h}+UHP|L zUzhThQal+21h$%Z@>{D7JL3R5deQ~W3X59>93PLuK6aZUK2Fw{m=A4>k+Wu`Ki~3U z@zw5REcI8Gr?)g`H9=MB;yAX3BJTWPot5sXQHuFs$9rW|2>a{3>wbFQ;zhUbJjH_7 z!-x2{pXdA$|4_bn^x=93hDeCs9Phjl?UL%9tb~_lAAfo7zIeMPPDp4H5TST~Txboy z;lP2+(F^BXlH~F$YW!B|qW;rN|LOYS+{**#WGQqC^;w1qF7 zH#dWo?vjYrKXiz_&*EHOQzef!w<<1CXYv-nHXs<3$Q(1-b=THUI9EWZ7bIHLeWX*A zDa&Yn>T&Ltz^|8oNcfr<-stPH)qwBBc>Cmt5DV<%1S4nr4<)#N|Cb`lU}aHzND}r3 PA`p_IvLbnpbf5n}_?;#Z literal 30556 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 OVKrs#2W9tdUi=RMv>odJ diff --git a/doc/source/users/taurus/showscan.rst b/doc/source/users/taurus/showscan.rst index 47a0cc3889..df040ed9e4 100644 --- a/doc/source/users/taurus/showscan.rst +++ b/doc/source/users/taurus/showscan.rst @@ -17,6 +17,15 @@ Showscan online is a simplified Taurus application which provides live scan plots. The number of plots and their configuration depends on the :ref:`measurement group plotting configuration `. +It can be launched from Spock using the :class:`~sardana.spock.magic.showscan` +command: + +.. sourcecode:: spock + + LAB-01-D01 [1]: showscan online + +or with the console command: ``showscan``. + When started it gets populated with empty plots of the :ref:`active measurement group `. As soon as you start scanning the curves and the legend will start to appear. Even if you change @@ -26,10 +35,18 @@ behavior is to keep the plotted data available for inspection after the scan execution. .. figure:: /_static/showscan-online.png - :height: 600 - Showscan online plotting two physical counters against the point - number and a pseudo counter against the moveable. + Showscan online plotting one physical counter against the motor's + position. + +Another interesting feature of this widget is its multi plot organization when +you ask for plotting multiple curves. You can choose between having a separate +plot per curve or group curves by the selected x axis + +.. figure:: /_static/showscan-online-multi.png + + Showscan online plotting three physical counters against the motor's + position on separate plots. ---------------- Showscan offline From 16b847d11d3668c13be083ac5632c856c827f6f0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 9 Sep 2020 23:10:43 +0200 Subject: [PATCH 815/830] Document stopping and aborting in Spock --- doc/source/users/spock.rst | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/doc/source/users/spock.rst b/doc/source/users/spock.rst index 10420b55d3..13652d1125 100644 --- a/doc/source/users/spock.rst +++ b/doc/source/users/spock.rst @@ -190,11 +190,42 @@ Stopping macros --------------- Some macros may take a long time to execute. To stop a macro in the middle of -its execution type :kbd:`Control+c`. +its execution type :kbd:`Control+c`. If the stopping process last too long, +you may trigger the aborting process with a second :kbd:`Control+c`. +Here be patient, further issuing of :kbd:`Control+c` may leave your macro +in an uncontrolled way. Use them only if you are sure that the aborting +process will not bring your system to a safe state. Macros that move motors or acquire data from sensors will automatically stop all motion and/or all acquisition. +While stopping and aborting macros Spock reports you what happens behind the +scene with informative messages: + +.. sourcecode:: spock + + LAB-01-D01 [1]: ascan mot01 0 10 100 0.1 + Operation will be saved in /tmp/test.h5 (HDF5::NXscan from NXscanH5_FileRecorder) + Scan #342 started at Wed Sep 9 23:01:14 2020. It will take at least 0:00:10.174246 + tg_test + #Pt No mot01 ct01 gct01 double_scalar dt + 0 0 0.1 2.98023e-08 243.47 0.0967791 + 1 0.1 0.1 5.91929e-08 243.47 0.239136 + 2 0.2 0.1 1.1595e-07 243.47 0.384191 + ^C + Ctrl-C received: Stopping... + Stopping Motion(['mot01']) reserved by ascan + Motion(['mot01']) stopped + Stopping mntgrp_expconf reserved by ascan + mntgrp_expconf stopped + Stopping /monitor reserved by ascan + Stopping /mirror reserved by ascan + Stopping /slit reserved by ascan + Operation saved in /tmp/test.h5 (HDF5::NXscan) + Scan #342 ended at Wed Sep 9 23:01:15 2020, taking 0:00:01.055814. Dead time 33.7% (motion dead time 12.8%) + Executing ascan.on_stop method... + Stopping done! + Exiting spock ------------- From 785678e38fd9516ffc6a5f1bce2a1d2ca257e869 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 9 Sep 2020 23:14:06 +0200 Subject: [PATCH 816/830] Update PMTV screenshot --- doc/source/_static/pmtv.png | Bin 16269 -> 14659 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/source/_static/pmtv.png b/doc/source/_static/pmtv.png index 42e2d9b94095d0b0bb63a861ffbd0c1cd1cd0e05..b358e4111f9dbe1bb30c73f83c12b62e0ceec0b9 100644 GIT binary patch literal 14659 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 QDiS0sr6^e@ZV>oC0D!AX%m4rY literal 16269 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 Date: Wed, 9 Sep 2020 23:28:21 +0200 Subject: [PATCH 817/830] Document PCTV widget --- doc/source/_static/pctv.png | Bin 0 -> 26582 bytes doc/source/_static/pctv_attr_editor.png | Bin 0 -> 21335 bytes doc/source/users/taurus/index.rst | 1 + doc/source/users/taurus/poolchanneltv.rst | 30 ++++++++++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 doc/source/_static/pctv.png create mode 100644 doc/source/_static/pctv_attr_editor.png create mode 100644 doc/source/users/taurus/poolchanneltv.rst diff --git a/doc/source/_static/pctv.png b/doc/source/_static/pctv.png new file mode 100644 index 0000000000000000000000000000000000000000..3589205dba6c68502cf8a52a0bce3e82e9a1b393 GIT binary patch literal 26582 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 literal 0 HcmV?d00001 diff --git a/doc/source/_static/pctv_attr_editor.png b/doc/source/_static/pctv_attr_editor.png new file mode 100644 index 0000000000000000000000000000000000000000..490e9b00ccbf080cd0d5a9ad0c8d76307e8f20bd GIT binary patch literal 21335 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 Showscan PoolMotorTV + PoolChannelTV QtSpock If you do not find a widget which meets your requirements and plan to develop diff --git a/doc/source/users/taurus/poolchanneltv.rst b/doc/source/users/taurus/poolchanneltv.rst new file mode 100644 index 0000000000..848c042238 --- /dev/null +++ b/doc/source/users/taurus/poolchanneltv.rst @@ -0,0 +1,30 @@ +PoolChannelTV User’s Interface +------------------------------ + +Sardana provides a widget to display and operate any Sardana channel. +As all Taurus widget it needs at least a model, but several can be given +to this widget. The widget exports the Sardana channel models as Taurus attributes. + +The :class:`~sardana.taurus.qt.qtgui.extra_pool.poolchannel.PoolChannelTV` +allows: + + - Start :ref:`experimental channel acquisition` + after prior setting of integration time + - Stop the acquisition + - Monitor the channel's value while acquiring or after the acquisition + +.. image:: /_static/pctv.png + :align: center + +Moreover, this widget allows you to access to the channel's configuration via a +context menu (right-click over the channel name) - see the image bellow. + +.. image:: /_static/pctv_attr_editor.png + :align: center + + + + + + + From a7d021133c23273ca30f5d1862423eb59cc2d157 Mon Sep 17 00:00:00 2001 From: Abdullah Amjad Date: Mon, 14 Sep 2020 10:54:27 +0200 Subject: [PATCH 818/830] parse dial position to nan in wa macro if motor position is None --- src/sardana/macroserver/macros/standard.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py index cd8a9163f8..7be0178ebd 100755 --- a/src/sardana/macroserver/macros/standard.py +++ b/src/sardana/macroserver/macros/standard.py @@ -101,6 +101,8 @@ def run(self, motor_list): value = float('NaN') if attr.name == 'dialposition': value = motor.getDialPosition() + if value is None: + value = float('NaN') data[name].append(value) req2delete.append(name) except PyTango.AsynReplyNotArrived: From 24bf99b8c113c481f54548a3fdd643349eef9753 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Mon, 14 Sep 2020 15:00:01 +0200 Subject: [PATCH 819/830] fix FWHM calculation --- src/sardana/macroserver/macros/scan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 53cab53338..5fe1647524 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -2083,10 +2083,10 @@ def _calcStats(x, y): while y_data[idx] >= half_max: idx = idx+1 - x0 = x[idx] - x1 = x[idx+1] - y0 = y_data[idx] - y1 = y_data[idx+1] + x0 = x[idx-1] + x1 = x[idx] + y0 = y_data[idx-1] + y1 = y_data[idx] uhmx = (half_max*(x1-x0) - (y0*x1)+(y1*x0)) / (y1-y0) except ZeroDivisionError: From 517136bd6c806fe76ae5318e14dcbe3253ff10a3 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Mon, 14 Sep 2020 15:32:46 +0200 Subject: [PATCH 820/830] fix FWHM for erf-like function with negative derivative --- src/sardana/macroserver/macros/scan.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py index 5fe1647524..57e9aa22fa 100644 --- a/src/sardana/macroserver/macros/scan.py +++ b/src/sardana/macroserver/macros/scan.py @@ -2043,13 +2043,20 @@ def _calcStats(x, y): if lower_left and lower_right: # it is a peak-like function y_data = y - else: + elif lower_left: # it is an erf-like function # use the gradient for further calculation y_data = numpy.gradient(y) # use also the half maximum of the gradient half_max = (numpy.max(y_data)-numpy.min(y_data)) \ / 2+numpy.min(y_data) + else: + # it is an erf-like function + # use the gradient for further calculation + y_data = -1*numpy.gradient(y) + # use also the half maximum of the gradient + half_max = (numpy.max(y_data)-numpy.min(y_data)) \ + / 2+numpy.min(y_data) # cen and fwhm # this part is adapted from: From 026096fc36a7c05b8be702a0889002ba1809aba5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 14 Sep 2020 22:52:20 +0200 Subject: [PATCH 821/830] Consider different DS (MacroServer and Sardana) Do not hard-code the DS name to MacroServer. The upgrade_env.py script should be able to migrate also the Sardana DS environments. --- scripts/upgrade/upgrade_env.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/upgrade/upgrade_env.py b/scripts/upgrade/upgrade_env.py index 4baa49b5b2..46d5264890 100644 --- a/scripts/upgrade/upgrade_env.py +++ b/scripts/upgrade/upgrade_env.py @@ -32,7 +32,7 @@ def get_ds_full_name(dev_name): return db_dev.DbGetDeviceInfo(dev_name)[1][3] -def get_ms_properties(ms_name, ms_ds_name): +def get_ms_properties(ms_name, ds_name): db = PyTango.Database() prop = db.get_device_property(ms_name, "EnvironmentDb") ms_properties = prop["EnvironmentDb"] @@ -40,9 +40,9 @@ def get_ms_properties(ms_name, ms_ds_name): dft_ms_properties = os.path.join( DefaultEnvBaseDir, DefaultEnvRelDir) - ds_inst_name = ms_ds_name.split("/")[1] + ds_exec_name, ds_inst_name = ds_name.split("/") ms_properties = dft_ms_properties % { - "ds_exec_name": "MacroServer", + "ds_exec_name": ds_exec_name, "ds_inst_name": ds_inst_name} else: ms_properties = ms_properties[0] @@ -75,10 +75,10 @@ def upgrade_env(ms_name, backend): db = PyTango.Database() try: ms_info = db.get_device_info(ms_name) - ms_ds_name = ms_info.ds_full_name + ds_name = ms_info.ds_full_name except AttributeError: - ms_ds_name = get_ds_full_name(ms_name) - env_filename = get_ms_properties(ms_name, ms_ds_name) + ds_name = get_ds_full_name(ms_name) + env_filename = get_ms_properties(ms_name, ds_name) migrate_file(env_filename, backend) From d904a4b2e081eda91b37b75da5a0a3b59cbd3c94 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 14 Sep 2020 22:56:20 +0200 Subject: [PATCH 822/830] Add warning about write permission to the newly created environment --- scripts/upgrade/upgrade_env.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/upgrade/upgrade_env.py b/scripts/upgrade/upgrade_env.py index 46d5264890..2035a8c7f8 100644 --- a/scripts/upgrade/upgrade_env.py +++ b/scripts/upgrade/upgrade_env.py @@ -7,8 +7,11 @@ 3. a new env file with an additional .db extension will be created. You should **NOT** change the macroserver EnvironmentDb property. The dbm will figure out automatically the file extension -4. a backup will of the original environment will be available with the +4. a backup of the original environment will be available with the extension .py2 +5. run the script with the same OS user as you run the Sardana/MacroServer + server or after running the script give write permission to the newly + created environment file to the OS user that you run the server Usage: python upgrade_env.py """ From e29d40c4314dfe71959a7b26aff7169f4739af5b Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 15 Sep 2020 00:08:26 +0200 Subject: [PATCH 823/830] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f7474440..5feccf9ff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Default macro parameter values in macroexecutor (#1153) * Executing RunMacro Door's command with string parameters containing spaces (#1240) * `macroxecutor` and `sequencer` now react on added/removed macros #295 +* Avoid printing `None` in `wm` and `wa` macros for `DialPosition` attribute and print + the `Position` attribute twice for pseudo motors (#929, #953, #1411, #1412) * Setting of environment variables in Python 3.7 (#1195) * Use `taurus.external.qt.compat.PY_OBJECT` in singal signatures instead of `object` to avoid problems when using `builtins` from `future` (#1082) From 021061d6ce7ea8e617ae802ddeca74d95ea12a16 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 16 Sep 2020 17:44:36 +0200 Subject: [PATCH 824/830] Fix merge from master to release-Jul20 The merge accidently reintroduced to_fqdn conversion. Remove it. --- src/sardana/pool/poolmeasurementgroup.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 68b3ae6e3d..6776a6f045 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -773,17 +773,11 @@ def set_configuration_from_user(self, cfg): msg_error = '' if ctrl_item.timer is None: timer_name = ctrl_data['timer'] - if to_fqdn: - timer_name = _to_fqdn(timer_name, - logger=self._parent) ch_timer = pool.get_element_by_full_name(timer_name) msg_error += 'Channel {0} is not present but used as ' \ 'timer. '.format(ch_timer.name) if ctrl_item.monitor is None: monitor_name = ctrl_data['monitor'] - if to_fqdn: - monitor_name = _to_fqdn(monitor_name, - logger=self._parent) ch_monitor = pool.get_element_by_full_name(monitor_name) msg_error += 'Channel {0} is not present but used as ' \ 'monitor.'.format(ch_monitor.name) From d5399fcbc103d6ccb939b8e1151adf929dbf8e52 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 16 Sep 2020 22:35:03 +0200 Subject: [PATCH 825/830] Remove unused initialized flag --- src/sardana/taurus/core/tango/sardana/pool.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index 40c25fff22..cc8832e7fb 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1499,8 +1499,6 @@ def set_data(self, data, force=False): # 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 From 7ae544f3f4229355775c5bf70e0a74ec8c5ce401 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 16 Sep 2020 22:43:29 +0200 Subject: [PATCH 826/830] Reset tango dicts on set_data set_data is assuming that if we were the object on which the changes were applied then there is no need to reset the tango dictionaries. This is wrong cause these dictionaries contains the configuration as well and this configuration must be updated. Fixes #1415 --- src/sardana/taurus/core/tango/sardana/pool.py | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py index cc8832e7fb..91c2bfa124 100644 --- a/src/sardana/taurus/core/tango/sardana/pool.py +++ b/src/sardana/taurus/core/tango/sardana/pool.py @@ -1393,6 +1393,34 @@ def __init__(self, mg, data): self.set_data(data) def set_data(self, data, force=False): + # dict]> + # where key is a device name and value is a list with two elements: + # - A device proxy or None if there was an error building it + # - 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 + # DeviceProxy, probably) + self.tango_dev_channels_in_error = 0 + + # dict> + # where key is a channel name and value is a tuple of three elements: + # - device name + # - attribute name + # - attribute information or None if there was an error trying to get + # the information + self.tango_channels_info = None + + # 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 + # 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 + # object each time if isinstance(data, str): data = CodecFactory().decode(('json', data)) @@ -1471,33 +1499,6 @@ def set_data(self, data, force=False): ctrl = self._get_ctrl_for_element(channel_name) if ctrl not in self.controller_list_name: self.controller_list_name.append(ctrl) - # dict]> - # where key is a device name and value is a list with two elements: - # - A device proxy or None if there was an error building it - # - 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 - # DeviceProxy, probably) - self.tango_dev_channels_in_error = 0 - - # dict> - # where key is a channel name and value is a tuple of three elements: - # - device name - # - attribute name - # - attribute information or None if there was an error trying to get - # the information - self.tango_channels_info = None - - # 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 - # 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 def _build(self): # internal channel structure that groups channels by tango device so From 07a9bb079271de17c2cb34bb772742a1f68b99fd Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 17 Sep 2020 22:12:01 +0200 Subject: [PATCH 827/830] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5feccf9ff7..75fc7a581f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ This file follows the formats and conventions from [keepachangelog.com] * better curve colors and symbols * Measurement group (Taurus extension) configuration API with methods to set/get: enabled, output, plot type, plot axes, timer, monitor, synchronizer, - value ref enabled, value ref pattern parameters(#867) + value ref enabled, value ref pattern parameters(#867, #1415, #1416) * Experiment configuration (expconf) macros * Measurement group configuration macros: `set_meas_conf` and `get_meas_conf` (#690) * Active measurement group selection macros: `set_meas` and `get_meas` (#690) From 5c553dbd0a06f4065add0b674e39242affe47ccf Mon Sep 17 00:00:00 2001 From: firode <32323630+firode@users.noreply.github.com> Date: Thu, 17 Sep 2020 22:55:36 +0200 Subject: [PATCH 828/830] Update documentation link --- doc/source/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/index.rst b/doc/source/index.rst index 284e466994..890a58d0b9 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -50,7 +50,7 @@ Projects related to Sardana .. _ESRF: http://esrf.eu .. _LGPL: http://www.gnu.org/licenses/lgpl.html .. _PyPi: http://pypi.python.org/pypi/sardana -.. _Documentation: http://sardana.readthedocs.org +.. _Documentation: https://sardana-controls.org .. _Tango: http://www.tango-controls.org/ .. _Taurus: http://taurus-scada.org/ .. _IPython: http://ipython.org/ From 8ba5f19f430ba46020636cdc021412308903f904 Mon Sep 17 00:00:00 2001 From: aureocarneiro Date: Fri, 18 Sep 2020 10:10:30 +0200 Subject: [PATCH 829/830] Update CHANGELOG.md with version 3.0.3 and release date --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75fc7a581f..733d6bb1a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). This file follows the formats and conventions from [keepachangelog.com] -## [3.0.3] 2020-09-10 +## [3.0.3] 2020-09-18 ### Added @@ -947,8 +947,8 @@ Main improvements since sardana 1.5.0 (aka Jan15): [keepachangelog.com]: http://keepachangelog.com -[Unreleased]: https://github.com/sardana-org/sardana/compare/3.0.2...HEAD -[3.0.2]: https://github.com/sardana-org/sardana/compare/3.0.2...2.8.6 +[Unreleasead]: https://github.com/sardana-org/sardana/compare/3.0.3...HEAD +[3.0.3]: https://github.com/sardana-org/sardana/compare/3.0.3...2.8.6 [2.8.6]: https://github.com/sardana-org/sardana/compare/2.8.6...2.8.5 [2.8.5]: https://github.com/sardana-org/sardana/compare/2.8.5...2.8.4 [2.8.4]: https://github.com/sardana-org/sardana/compare/2.8.4...2.8.3 From 441030e79e50f627210bed03e50c983742c9aef2 Mon Sep 17 00:00:00 2001 From: aureocarneiro Date: Fri, 18 Sep 2020 10:12:17 +0200 Subject: [PATCH 830/830] Update CHANGELOG.md typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 733d6bb1a4..29c271e292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -947,7 +947,7 @@ Main improvements since sardana 1.5.0 (aka Jan15): [keepachangelog.com]: http://keepachangelog.com -[Unreleasead]: https://github.com/sardana-org/sardana/compare/3.0.3...HEAD +[Unreleased]: https://github.com/sardana-org/sardana/compare/3.0.3...HEAD [3.0.3]: https://github.com/sardana-org/sardana/compare/3.0.3...2.8.6 [2.8.6]: https://github.com/sardana-org/sardana/compare/2.8.6...2.8.5 [2.8.5]: https://github.com/sardana-org/sardana/compare/2.8.5...2.8.4