source: sasview/src/sas/qtgui/MainWindow/DataManager.py @ a54bbf2b

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since a54bbf2b was a54bbf2b, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

Added plot roles to Data1D/Data2D structures to allow for smoother plot logic.

  • Property mode set to 100755
File size: 15.8 KB
Line 
1################################################################################
2#This software was developed by the University of Tennessee as part of the
3#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4#project funded by the US National Science Foundation.
5#
6#See the license text in license.txt
7#
8#copyright 2010, University of Tennessee
9################################################################################
10"""
11This module manages all data loaded into the application. Data_manager makes
12available all data loaded  for the current perspective.
13
14All modules "creating Data" posts their data to data_manager .
15Data_manager  make these new data available for all other perspectives.
16"""
17#
18# REDUNDANT CLASS!!!
19# ALL THE FUNCTIONALITY IS BEING MOVED TO GuiManager.py
20#
21import os
22import copy
23import logging
24import json
25import time
26from io import BytesIO
27import numpy as np
28
29from sas.qtgui.Plotting.PlotterData import Data1D
30from sas.qtgui.Plotting.PlotterData import Data2D
31from sas.qtgui.Plotting.Plottables import Plottable
32from sas.qtgui.Plotting.Plottables import PlottableTheory1D
33from sas.qtgui.Plotting.Plottables import PlottableFit1D
34from sas.qtgui.Plotting.Plottables import Text
35from sas.qtgui.Plotting.Plottables import Chisq
36from sas.qtgui.Plotting.Plottables import View
37
38from sas.qtgui.Utilities import GuiUtils
39from sas.qtgui.MainWindow.DataState import DataState
40import sas.sascalc.dataloader.data_info as DataInfo
41
42# used for import/export
43from sas.sascalc.dataloader.data_info import Sample, Source, Vector
44
45class DataManager(object):
46    """
47    Manage a list of data
48    """
49    def __init__(self):
50        """
51        Store opened path and data object created at the loading time
52        :param auto_plot: if True the datamanager sends data to plotting
53                            plugin.
54        :param auto_set_data: if True the datamanager sends to the current
55        perspective
56        """
57        self.stored_data = {}
58        self.message = ""
59        self.data_name_dict = {}
60        self.count = 0
61        self.list_of_id = []
62        self.time_stamp = time.time()
63
64    def __str__(self):
65        _str  = ""
66        _str += "No of states  is %s \n" % str(len(self.stored_data))
67        n_count = 0
68        for  value in list(self.stored_data.values()):
69            n_count += 1
70            _str += "State No %s \n"  % str(n_count)
71            _str += str(value) + "\n"
72        return _str
73
74    def create_gui_data(self, data, path=None):
75        """
76        Receive data from loader and create a data to use for guiframe
77        """
78
79        if issubclass(Data2D, data.__class__):
80            new_plot = Data2D(image=None, err_image=None)
81        else:
82            new_plot = Data1D(x=[], y=[], dx=None, dy=None)
83
84        new_plot.copy_from_datainfo(data)
85        data.clone_without_data(clone=new_plot)
86        #creating a name for data
87        title = ""
88        file_name = os.path.basename(path) if path is not None else os.path.basename(data.filename)
89        if file_name:
90            name = file_name
91        elif data.run:
92            name = data.run[0]
93        else:
94            name = "data"
95        name = self.rename(name)
96        #find title
97        if data.title.strip():
98            title = data.title
99        if title.strip() == "":
100            title = file_name
101
102        stripped_filename = new_plot.filename.strip()
103        if not stripped_filename:
104            new_plot.filename = file_name
105        if stripped_filename != os.path.basename(stripped_filename):
106            new_plot.filename = file_name
107
108        new_plot.name = name
109        new_plot.title = title
110        ## allow to highlight data when plotted
111        new_plot.interactive = True
112        ## when 2 data have the same id override the 1 st plotted
113        self.time_stamp += 1
114        new_plot.id = str(name) + str(self.time_stamp)
115        ##group_id specify on which panel to plot this data
116        new_plot.group_id = str(name) + str(self.time_stamp)
117        new_plot.is_data = True
118        new_plot.path = path
119        new_plot.list_group_id = []
120        # Assign the plot role to data
121        new_plot.plot_role = Data1D.ROLE_DATA
122        ##post data to plot
123        # plot data
124        return new_plot
125
126    def rename(self, name):
127        """
128        rename data
129        """
130        # name of the data allow to differentiate data when plotted
131        try:
132            name = GuiUtils.parseName(name=name, expression="_")
133        except TypeError:
134            # bad name sent to rename
135            return None
136
137        max_char = name.find("[")
138        if max_char < 0:
139            max_char = len(name)
140        name = name[0:max_char]
141
142        if name not in self.data_name_dict:
143            self.data_name_dict[name] = 0
144        else:
145            self.data_name_dict[name] += 1
146            name = name + " [" + str(self.data_name_dict[name]) + "]"
147        return name
148
149
150    def add_data(self, data_list):
151        """
152        receive a list of
153        """
154        for id, data in data_list.items():
155            if id  in self.stored_data:
156                msg = "Data manager already stores %s" % str(data.name)
157                msg += ""
158                logging.info(msg)
159                data_state = self.stored_data[id]
160                data_state.data = data
161            else:
162                data_state = DataState(data)
163                data_state.id = id
164                data_state.path = data.path
165                self.stored_data[id] = data_state
166
167    def update_data(self, prev_data, new_data):
168        """
169        """
170        if prev_data.id not in list(self.stored_data.keys()):
171            return None, {}
172        data_state = self.stored_data[prev_data.id]
173        self.stored_data[new_data.id]  = data_state.clone()
174        self.stored_data[new_data.id].data = new_data
175        if prev_data.id in list(self.stored_data.keys()):
176            del self.stored_data[prev_data.id]
177        return prev_data.id, {new_data.id: self.stored_data[new_data.id]}
178
179    def update_theory(self, theory, data_id=None, state=None):
180        """
181        """
182        uid = data_id
183        if data_id is None and theory is not None:
184            uid = theory.id
185        if uid in list(self.stored_data.keys()):
186             data_state = self.stored_data[uid]
187        else:
188            data_state = DataState()
189        data_state.uid = uid
190        data_state.set_theory(theory_data=theory, theory_state=state)
191        self.stored_data[uid] = data_state
192        return {uid: self.stored_data[uid]}
193
194
195    def get_message(self):
196        """
197        return message
198        """
199        return self.message
200
201    def get_by_id(self, id_list=None):
202        """
203        """
204        _selected_data = {}
205        _selected_theory_list = {}
206        if id_list is None:
207            return
208        for d_id in self.stored_data:
209            for search_id in id_list:
210                data_state = self.stored_data[d_id]
211                data = data_state.data
212                theory_list = data_state.get_theory()
213                if search_id == d_id:
214                    _selected_data[search_id] = data
215                if search_id in list(theory_list.keys()):
216                     _selected_theory_list[search_id] = theory_list[search_id]
217
218        return _selected_data, _selected_theory_list
219
220
221    def freeze(self, theory_id):
222        """
223        """
224        return self.freeze_theory(list(self.stored_data.keys()), theory_id)
225
226    def freeze_theory(self, data_id, theory_id):
227        """
228        """
229        selected_theory = {}
230        for d_id in data_id:
231            if d_id in self.stored_data:
232                data_state = self.stored_data[d_id]
233                theory_list = data_state.get_theory()
234                for t_id in theory_id:
235                    if t_id in list(theory_list.keys()):
236                        theory_data, theory_state = theory_list[t_id]
237                        new_theory = copy.deepcopy(theory_data)
238                        new_theory.id  = time.time()
239                        new_theory.is_data = True
240                        new_theory.name += '_@' + \
241                                    str(new_theory.id)[7:-1].replace('.', '')
242                        new_theory.title = new_theory.name
243                        new_theory.label = new_theory.name
244                        selected_theory[new_theory.id] = DataState(new_theory)
245                        self.stored_data[new_theory.id] = \
246                                    selected_theory[new_theory.id]
247
248        return selected_theory
249
250
251    def delete_data(self, data_id, theory_id=None, delete_all=False):
252        """
253        """
254        for d_id in data_id:
255            if d_id in list(self.stored_data.keys()):
256                data_state = self.stored_data[d_id]
257                if data_state.data.name in self.data_name_dict:
258                    del self.data_name_dict[data_state.data.name]
259                del self.stored_data[d_id]
260
261        self.delete_theory(data_id, theory_id)
262        if delete_all:
263            self.stored_data = {}
264            self.data_name_dict = {}
265
266    def delete_theory(self, data_id, theory_id):
267        """
268        """
269        for d_id in data_id:
270            if d_id in self.stored_data:
271                data_state = self.stored_data[d_id]
272                theory_list = data_state.get_theory()
273                if theory_id in list(theory_list.keys()):
274                    del theory_list[theory_id]
275        #del pure theory
276        self.delete_by_id(theory_id)
277
278    def delete_by_id(self, id_list=None):
279        """
280        save data and path
281        """
282        for id in id_list:
283            if id in self.stored_data:
284                del self.stored_data[id]
285
286    def get_by_name(self, name_list=None):
287        """
288        return a list of data given a list of data names
289        """
290        _selected_data = {}
291        for selected_name in name_list:
292            for id, data_state in self.stored_data.items():
293                if data_state.data.name == selected_name:
294                    _selected_data[id] = data_state.data
295        return _selected_data
296
297    def delete_by_name(self, name_list=None):
298        """
299        save data and path
300        """
301        for selected_name in name_list:
302            for id, data_state in self.stored_data.items():
303                if data_state.data.name == selected_name:
304                    del self.stored_data[id]
305
306    def update_stored_data(self, name_list=None):
307        """ update stored data after deleting files in Data Explorer """
308        for selected_name in name_list:
309            # Take the copy of current, possibly shorter stored_data dict
310            stored_data = copy.deepcopy(self.stored_data)
311            for idx in list(stored_data.keys()):
312                if str(selected_name) in str(idx):
313                    del self.stored_data[idx]
314
315    def get_data_state(self, data_id):
316        """
317        Send list of selected data
318        """
319        _selected_data_state = {}
320        for id in data_id:
321            if id in list(self.stored_data.keys()):
322                _selected_data_state[id] = self.stored_data[id]
323        return _selected_data_state
324
325    def get_all_data(self):
326        """
327        return list of all available data
328        """
329        return self.stored_data
330
331    def assign(self, other):
332        self.stored_data = other.stored_data
333        self.message = other.message
334        self.data_name_dict = other.data_name_dict
335        self.count = other.count
336        self.list_of_id = other.list_of_id
337        self.time_stamp = other.time_stamp
338
339    def save_to_writable(self, fp):
340        """
341        save content of stored_data to fp (a .write()-supporting file-like object)
342        """
343
344        def add_type(dict, type):
345            dict['__type__'] = type.__name__
346            return dict
347
348        def jdefault(o):
349            """
350            objects that can't otherwise be serialized need to be converted
351            """
352            # tuples and sets (TODO: default JSONEncoder converts tuples to lists, create custom Encoder that preserves tuples)
353            if isinstance(o, (tuple, set)):
354                content = { 'data': list(o) }
355                return add_type(content, type(o))
356
357            # "simple" types
358            if isinstance(o, (Sample, Source, Vector)):
359                return add_type(o.__dict__, type(o))
360            if isinstance(o, (Plottable, View)):
361                return add_type(o.__dict__, type(o))
362
363            # DataState
364            if isinstance(o, DataState):
365                # don't store parent
366                content = o.__dict__.copy()
367                content.pop('parent')
368                return add_type(content, type(o))
369
370            # ndarray
371            if isinstance(o, np.ndarray):
372                buffer = BytesIO()
373                np.save(buffer, o)
374                buffer.seek(0)
375                content = { 'data': buffer.read().decode('latin-1') }
376                return add_type(content, type(o))
377
378            # not supported
379            logging.info("data cannot be serialized to json: %s" % type(o))
380            return None
381
382        json.dump(self.stored_data, fp, indent=2, sort_keys=True, default=jdefault)
383
384
385    def load_from_readable(self, fp):
386        """
387        load content from tp to stored_data (a .read()-supporting file-like object)
388        """
389
390        supported = [
391            tuple, set,
392            Sample, Source, Vector,
393            Plottable, Data1D, Data2D, PlottableTheory1D, PlottableFit1D, Text, Chisq, View,
394            DataState, np.ndarray]
395
396        lookup = dict((cls.__name__, cls) for cls in supported)
397
398        class TooComplexException(Exception):
399            pass
400
401        def simple_type(cls, data, level):
402            class Empty(object):
403                def __init__(self):
404                    for key, value in data.items():
405                        setattr(self, key, generate(value, level))
406
407            # create target object
408            o = Empty()
409            o.__class__ = cls
410
411            return o
412
413        def construct(type, data, level):
414            try:
415                cls = lookup[type]
416            except KeyError:
417                logging.info('unknown type: %s' % type)
418                return None
419
420            # tuples and sets
421            if cls in (tuple, set):
422                # convert list to tuple/set
423                return cls(generate(data['data'], level))
424
425            # "simple" types
426            if cls in (Sample, Source, Vector):
427                return simple_type(cls, data, level)
428            if issubclass(cls, Plottable) or (cls == View):
429                return simple_type(cls, data, level)
430
431            # DataState
432            if cls == DataState:
433                o = simple_type(cls, data, level)
434                o.parent = None # TODO: set to ???
435                return o
436
437            # ndarray
438            if cls == np.ndarray:
439                buffer = BytesIO()
440                buffer.write(data['data'].encode('latin-1'))
441                buffer.seek(0)
442                return np.load(buffer)
443
444            logging.info('not implemented: %s, %s' % (type, cls))
445            return None
446
447        def generate(data, level):
448            if level > 16: # recursion limit (arbitrary number)
449                raise TooComplexException()
450            else:
451                level += 1
452
453            if isinstance(data, dict):
454                try:
455                    type = data['__type__']
456                except KeyError:
457                    # if dictionary doesn't have __type__ then it is assumed to be just an ordinary dictionary
458                    o = {}
459                    for key, value in data.items():
460                        o[key] = generate(value, level)
461                    return o
462
463                return construct(type, data, level)
464
465            if isinstance(data, list):
466                return [generate(item, level) for item in data]
467
468            return data
469
470        new_stored_data = {}
471        for id, data in json.load(fp).items():
472            try:
473                new_stored_data[id] = generate(data, 0)
474            except TooComplexException:
475                logging.info('unable to load %s' % id)
476
477        self.stored_data = new_stored_data
478
Note: See TracBrowser for help on using the repository browser.