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

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 

8 

9from silx.gui import qt 

10from silx.utils.weakref import WeakList 

11 

12from larch.utils.logging import getLogger 

13_logger = getLogger('larch.qtlib.items') 

14 

15__authors__ = ['Marius Retegan', 'Mauro Rovezzi'] 

16 

17 

18class Signal(object): 

19 """Base class for signal objects.""" 

20 

21 def __init__(self): 

22 self._slots = WeakList() 

23 

24 def connect(self, slot): 

25 """Register a slot. 

26 

27 Adding an already registered slot has no effect. 

28 

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') 

35 

36 def disconnect(self, slot): 

37 """Remove a previously registered slot. 

38 

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') 

45 

46 def emit(self, *args, **kwargs): 

47 """Notify all registered slots with the given parameters. 

48 

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) 

54 

55 

56class TreeItem(object): 

57 

58 def __init__(self, name=None, parentItem=None, isChecked=False): 

59 """Base class for items of the tree model. 

60 

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. 

65 

66 Using a weakref.proxy object for the parent makes the parentItem 

67 unhashable, which causes problem if they are used as dictionary keys. 

68 

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 

75 

76 self.childItems = list() 

77 self.itemChanged = Signal() 

78 

79 @property 

80 def parentItem(self): 

81 return self._parentItem() if self._parentItem is not None else None 

82 

83 def child(self, row): 

84 try: 

85 return self.childItems[row] 

86 except KeyError: 

87 return None 

88 

89 def data(self, column, name, role): 

90 if name is None: 

91 return None 

92 

93 try: 

94 return getattr(self, name) 

95 except AttributeError: 

96 return None 

97 

98 def setData(self, column, name, value, role): 

99 if name is None: 

100 return False 

101 

102 try: 

103 setattr(self, name, value) 

104 return True 

105 except AttributeError: 

106 return False 

107 

108 def parent(self): 

109 return self.parentItem 

110 

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 

118 

119 def childCount(self): 

120 return len(self.childItems) 

121 

122 def columnCount(self): 

123 return len(self.name) 

124 

125 def lastChild(self): 

126 return self.childItems[-1] 

127 

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 

135 

136 def removeRows(self, row, count): 

137 try: 

138 childItem = self.childItems[row] 

139 except IndexError: 

140 return False 

141 

142 for i in range(count): 

143 self.childItems.pop(row) 

144 

145 _logger.debug('Removed {}'.format(childItem.name)) 

146 return True 

147 

148 def flags(self, column): 

149 return qt.Qt.ItemIsEnabled | qt.Qt.ItemIsSelectable 

150 

151 @property 

152 def legend(self): 

153 tokens = list() 

154 tokens.append(self.name) 

155 

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 

161 

162 tokens.reverse() 

163 return '/'.join(tokens) 

164 

165 

166class RootItem(TreeItem): 

167 """Root item of the TreeModel (not visible in the view)""" 

168 

169 def __init__(self, name=None, parentItem=None): 

170 super(RootItem, self).__init__(name, parentItem) 

171 

172 

173class ExperimentItem(TreeItem): 

174 """Experiment as generic data container 

175 (appearing at root level in the view) 

176 

177 Root 

178 +---> Experiment 

179 

180 """ 

181 

182 def __init__(self, name=None, parentItem=None): 

183 super(ExperimentItem, self).__init__(name, parentItem) 

184 

185 

186class GroupItem(TreeItem): 

187 """Group item is equivalent to data group in HDF5""" 

188 

189 def __init__(self, name=None, parentItem=None): 

190 super(GroupItem, self).__init__(name, parentItem) 

191 

192 

193class DatasetItem(TreeItem): 

194 """Dataset item is equivalent of dataset in HDF5""" 

195 

196 def __init__(self, name=None, parentItem=None): 

197 super(DatasetItem, self).__init__(name, parentItem) 

198 

199 

200class FileItem(GroupItem): 

201 """Equivalent to h5py.File -> datagroup""" 

202 

203 def __init__(self, name=None, parentItem=None): 

204 super(FileItem, self).__init__(name, parentItem) 

205 

206 

207class ScanItem(DatasetItem): 

208 """Dataset representing a 1D scan 

209 

210 TODO: needs refactoring 

211 """ 

212 

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 

220 

221 self.scanData = data 

222 

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) 

231 

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) 

240 

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 

247 

248 @property 

249 def xLabel(self): 

250 if self._xLabel is None: 

251 self._xLabel = list(self.counters)[0] 

252 return self._xLabel 

253 

254 @xLabel.setter 

255 def xLabel(self, value): 

256 self._xLabel = value 

257 

258 @property 

259 def signalLabel(self): 

260 if self._signalLabel is None: 

261 self._signalLabel = list(self.counters)[1] 

262 return self._signalLabel 

263 

264 @signalLabel.setter 

265 def signalLabel(self, value): 

266 self._signalLabel = value 

267 

268 @property 

269 def monitorLabel(self): 

270 if self._monitorLabel is None: 

271 self._monitorLabel = list(self.counters)[2] 

272 return self._monitorLabel 

273 

274 @monitorLabel.setter 

275 def monitorLabel(self, value): 

276 self._monitorLabel = value 

277 

278 @property 

279 def currentPlotWindowIndex(self): 

280 if self.currentPlotWindow is not None: 

281 return str(self.currentPlotWindow.index()) 

282 else: 

283 return None 

284 

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 

293 

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 

306 

307 @currentPlotWindow.setter 

308 def currentPlotWindow(self, value): 

309 self._currentPlotWindow = value 

310 

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 

318 

319 @property 

320 def plotWindows(self): 

321 return self._plotWindows 

322 

323 @plotWindows.setter 

324 def plotWindows(self, value): 

325 self._plotWindows = value 

326 

327 @property 

328 def counters(self): 

329 return self.scanData['measurement'] 

330 

331 @property 

332 def command(self): 

333 return str(self.scanData['title'].value) 

334 

335 @property 

336 def x(self): 

337 try: 

338 return self.counters[self.xLabel].value 

339 except KeyError: 

340 return None 

341 

342 @property 

343 def signal(self): 

344 try: 

345 return self.counters[self.signalLabel].value 

346 except KeyError: 

347 return None 

348 

349 @property 

350 def monitor(self): 

351 try: 

352 return self.counters[self.monitorLabel].value 

353 except KeyError: 

354 return None 

355 

356 def plot(self): 

357 if self.x is None or self.signal is None: 

358 return 

359 

360 x = self.x 

361 signal = self.signal 

362 legend = self.legend 

363 

364 if 0 not in self.monitor: 

365 try: 

366 signal = signal / self.monitor 

367 except (TypeError, ValueError): 

368 pass 

369 

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')