Coverage for /Users/Newville/Codes/xraylarch/larch/qtlib/items.py: 0%
239 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3"""
4This module provides classes to implement model items for spectroscopy data.
5"""
6from __future__ import absolute_import, division
7import weakref
9from silx.gui import qt
10from silx.utils.weakref import WeakList
12from larch.utils.logging import getLogger
13_logger = getLogger('larch.qtlib.items')
15__authors__ = ['Marius Retegan', 'Mauro Rovezzi']
18class Signal(object):
19 """Base class for signal objects."""
21 def __init__(self):
22 self._slots = WeakList()
24 def connect(self, slot):
25 """Register a slot.
27 Adding an already registered slot has no effect.
29 :param callable slot: The function or method to register.
30 """
31 if slot not in self._slots:
32 self._slots.append(slot)
33 else:
34 _logger.warning('Ignoring addition of an already registered slot')
36 def disconnect(self, slot):
37 """Remove a previously registered slot.
39 :param callable slot: The function or method to unregister.
40 """
41 try:
42 self._slots.remove(slot)
43 except ValueError:
44 _logger.warning('Trying to remove a slot that is not registered')
46 def emit(self, *args, **kwargs):
47 """Notify all registered slots with the given parameters.
49 Slots are called directly in this method.
50 Slots are called in the order they were registered.
51 """
52 for slot in self._slots:
53 slot(*args, **kwargs)
56class TreeItem(object):
58 def __init__(self, name=None, parentItem=None, isChecked=False):
59 """Base class for items of the tree model.
61 Use a weak reference for the parent item to avoid circular
62 references, i.e. if the parent is destroyed, the child will only
63 have a week reference to it, so the garbage collector will remove
64 the parent object from memory.
66 Using a weakref.proxy object for the parent makes the parentItem
67 unhashable, which causes problem if they are used as dictionary keys.
69 :param name: Name of the tree item, defaults to None
70 :param parentItem: Parent of the tree item, defaults to None
71 """
72 self.name = name
73 self._parentItem = None if parentItem is None else weakref.ref(parentItem) # noqa
74 self.isChecked = isChecked
76 self.childItems = list()
77 self.itemChanged = Signal()
79 @property
80 def parentItem(self):
81 return self._parentItem() if self._parentItem is not None else None
83 def child(self, row):
84 try:
85 return self.childItems[row]
86 except KeyError:
87 return None
89 def data(self, column, name, role):
90 if name is None:
91 return None
93 try:
94 return getattr(self, name)
95 except AttributeError:
96 return None
98 def setData(self, column, name, value, role):
99 if name is None:
100 return False
102 try:
103 setattr(self, name, value)
104 return True
105 except AttributeError:
106 return False
108 def parent(self):
109 return self.parentItem
111 def childPosition(self):
112 if self.parentItem is not None:
113 return self.parentItem.childItems.index(self)
114 else:
115 # The root item has no parent; for this item, we return zero
116 # to be consistent with the other items.
117 return 0
119 def childCount(self):
120 return len(self.childItems)
122 def columnCount(self):
123 return len(self.name)
125 def lastChild(self):
126 return self.childItems[-1]
128 def insertRows(self, row, count):
129 for i in range(count):
130 name = 'TreeItem{}'.format(self.childCount())
131 item = TreeItem(name=name, parentItem=self)
132 self.childItems.insert(row, item)
133 _logger.debug('Inserted {}'.format(name))
134 return True
136 def removeRows(self, row, count):
137 try:
138 childItem = self.childItems[row]
139 except IndexError:
140 return False
142 for i in range(count):
143 self.childItems.pop(row)
145 _logger.debug('Removed {}'.format(childItem.name))
146 return True
148 def flags(self, column):
149 return qt.Qt.ItemIsEnabled | qt.Qt.ItemIsSelectable
151 @property
152 def legend(self):
153 tokens = list()
154 tokens.append(self.name)
156 parentItem = self.parentItem
157 while parentItem is not None:
158 if parentItem.name is not None:
159 tokens.append(parentItem.name)
160 parentItem = parentItem.parentItem
162 tokens.reverse()
163 return '/'.join(tokens)
166class RootItem(TreeItem):
167 """Root item of the TreeModel (not visible in the view)"""
169 def __init__(self, name=None, parentItem=None):
170 super(RootItem, self).__init__(name, parentItem)
173class ExperimentItem(TreeItem):
174 """Experiment as generic data container
175 (appearing at root level in the view)
177 Root
178 +---> Experiment
180 """
182 def __init__(self, name=None, parentItem=None):
183 super(ExperimentItem, self).__init__(name, parentItem)
186class GroupItem(TreeItem):
187 """Group item is equivalent to data group in HDF5"""
189 def __init__(self, name=None, parentItem=None):
190 super(GroupItem, self).__init__(name, parentItem)
193class DatasetItem(TreeItem):
194 """Dataset item is equivalent of dataset in HDF5"""
196 def __init__(self, name=None, parentItem=None):
197 super(DatasetItem, self).__init__(name, parentItem)
200class FileItem(GroupItem):
201 """Equivalent to h5py.File -> datagroup"""
203 def __init__(self, name=None, parentItem=None):
204 super(FileItem, self).__init__(name, parentItem)
207class ScanItem(DatasetItem):
208 """Dataset representing a 1D scan
210 TODO: needs refactoring
211 """
213 def __init__(self, name=None, parentItem=None, isChecked=False, data=None):
214 super(ScanItem, self).__init__(name, parentItem, isChecked)
215 self._xLabel = None
216 self._signalLabel = None
217 self._monitorLabel = None
218 self._plotWindows = None
219 self._currentPlotWindow = None
221 self.scanData = data
223 def data(self, column, name, role):
224 if role == qt.Qt.CheckStateRole:
225 if column == 0:
226 if self.isChecked:
227 return qt.Qt.Checked
228 else:
229 return qt.Qt.Unchecked
230 return super(ScanItem, self).data(column, name, role)
232 def setData(self, column, name, value, role):
233 if role == qt.Qt.CheckStateRole:
234 if value == qt.Qt.Checked:
235 self.isChecked = True
236 else:
237 self.isChecked = False
238 return True
239 return super(ScanItem, self).setData(column, name, value, role)
241 def flags(self, column):
242 flags = super(ScanItem, self).flags(column)
243 if column == 0:
244 return flags | qt.Qt.ItemIsUserCheckable
245 else:
246 return flags
248 @property
249 def xLabel(self):
250 if self._xLabel is None:
251 self._xLabel = list(self.counters)[0]
252 return self._xLabel
254 @xLabel.setter
255 def xLabel(self, value):
256 self._xLabel = value
258 @property
259 def signalLabel(self):
260 if self._signalLabel is None:
261 self._signalLabel = list(self.counters)[1]
262 return self._signalLabel
264 @signalLabel.setter
265 def signalLabel(self, value):
266 self._signalLabel = value
268 @property
269 def monitorLabel(self):
270 if self._monitorLabel is None:
271 self._monitorLabel = list(self.counters)[2]
272 return self._monitorLabel
274 @monitorLabel.setter
275 def monitorLabel(self, value):
276 self._monitorLabel = value
278 @property
279 def currentPlotWindowIndex(self):
280 if self.currentPlotWindow is not None:
281 return str(self.currentPlotWindow.index())
282 else:
283 return None
285 @currentPlotWindowIndex.setter
286 def currentPlotWindowIndex(self, value):
287 try:
288 self._currentPlotWindowIndex = int(value)
289 except ValueError:
290 self.currentPlotWindow = None
291 else:
292 self.currentPlotWindow = self.plotWindows[self._currentPlotWindowIndex] # noqa
294 @property
295 def currentPlotWindow(self):
296 if self._currentPlotWindow is None:
297 if self.plotWindows:
298 self._currentPlotWindow = self.plotWindows[0]
299 else:
300 if self._currentPlotWindow not in self.plotWindows:
301 if self.plotWindows:
302 self._currentPlotWindow = self.plotWindows[0]
303 else:
304 self._currentPlotWindow = None
305 return self._currentPlotWindow
307 @currentPlotWindow.setter
308 def currentPlotWindow(self, value):
309 self._currentPlotWindow = value
311 @property
312 def plotWindowsIndexes(self):
313 indexes = list()
314 if self.plotWindows is not None:
315 for index, _ in enumerate(self.plotWindows):
316 indexes.append(str(index))
317 return indexes
319 @property
320 def plotWindows(self):
321 return self._plotWindows
323 @plotWindows.setter
324 def plotWindows(self, value):
325 self._plotWindows = value
327 @property
328 def counters(self):
329 return self.scanData['measurement']
331 @property
332 def command(self):
333 return str(self.scanData['title'].value)
335 @property
336 def x(self):
337 try:
338 return self.counters[self.xLabel].value
339 except KeyError:
340 return None
342 @property
343 def signal(self):
344 try:
345 return self.counters[self.signalLabel].value
346 except KeyError:
347 return None
349 @property
350 def monitor(self):
351 try:
352 return self.counters[self.monitorLabel].value
353 except KeyError:
354 return None
356 def plot(self):
357 if self.x is None or self.signal is None:
358 return
360 x = self.x
361 signal = self.signal
362 legend = self.legend
364 if 0 not in self.monitor:
365 try:
366 signal = signal / self.monitor
367 except (TypeError, ValueError):
368 pass
370 if not self.currentPlotWindow.getItems():
371 resetzoom = True
372 else:
373 resetzoom = False
374 self.currentPlotWindow.addCurve(x, signal, legend=legend,
375 resetzoom=resetzoom)
376 self.currentPlotWindow.setGraphYLabel('Signal / Monitor')