Coverage for /Users/Newville/Codes/xraylarch/larch/qtrixs/plotrixs.py: 0%

181 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 

4""" 

5Plot RIXS data 

6============== 

7""" 

8from silx.gui import qt 

9from silx.gui.plot.actions import PlotAction 

10from silx.gui.plot.tools.roi import RegionOfInterestManager 

11from silx.gui.plot.tools.roi import RegionOfInterestTableWidget 

12# from silx.gui.plot.items.roi import RectangleROI 

13from silx.gui.plot.items import LineMixIn, SymbolMixIn 

14from larch.utils.logging import getLogger 

15from larch.qtlib.plotarea import PlotArea, MdiSubWindow 

16from larch.qtlib.plot1D import Plot1D 

17from larch.qtlib.plot2D import Plot2D 

18from .profiletoolbar import (_DEFAULT_OVERLAY_COLORS, RixsProfileToolBar) 

19from .view import RixsListView 

20from .model import RixsListModel 

21 

22_logger = getLogger("larch.qtrixs.plotrixs") 

23 

24 

25class RixsROIManager(RegionOfInterestManager): 

26 

27 def __init__(self, plot, color='pink'): 

28 super(RixsROIManager, self).__init__(plot) 

29 self.setColor(color) 

30 self.sigRoiAdded.connect(self.updateAddedRegionOfInterest) 

31 

32 def updateAddedRegionOfInterest(self, roi): 

33 """Called for each added region of interest: set the name""" 

34 if roi.getLabel() == '': 

35 roi.setLabel('%d' % len(self.getRois())) 

36 if isinstance(roi, LineMixIn): 

37 roi.setLineWidth(2) 

38 roi.setLineStyle('--') 

39 if isinstance(roi, SymbolMixIn): 

40 roi.setSymbol('+') 

41 roi.setSymbolSize(3) 

42 

43 

44class RixsROIDockWidget(qt.QDockWidget): 

45 

46 def __init__(self, plot, parent=None): 

47 

48 assert isinstance(plot, RixsPlot2D), "'plot' should be an instance of RixsPlot2D" 

49 _title = f"Plot {plot._index} : cursors infos" 

50 super(RixsROIDockWidget, self).__init__(_title, parent=parent) 

51 

52 self._roiManager = RixsROIManager(plot) 

53 

54 #: Create the table widget displaying infos 

55 self._roiTable = RegionOfInterestTableWidget() 

56 self._roiTable.setRegionOfInterestManager(self._roiManager) 

57 

58 #: Create a toolbar containing buttons for all ROI 'drawing' modes 

59 self._roiToolbar = qt.QToolBar() 

60 self._roiToolbar.setIconSize(qt.QSize(16, 16)) 

61 

62 for roiClass in self._roiManager.getSupportedRoiClasses(): 

63 # Create a tool button and associate it with the QAction of each 

64 # mode 

65 action = self._roiManager.getInteractionModeAction(roiClass) 

66 self._roiToolbar.addAction(action) 

67 

68 # Add the region of interest table and the buttons to a dock widget 

69 self._widget = qt.QWidget() 

70 self._layout = qt.QVBoxLayout() 

71 self._widget.setLayout(self._layout) 

72 self._layout.addWidget(self._roiToolbar) 

73 self._layout.addWidget(self._roiTable) 

74 

75 self.setWidget(self._widget) 

76 self.visibilityChanged.connect(self.roiDockVisibilityChanged) 

77 

78 def roiDockVisibilityChanged(self, visible): 

79 """Handle change of visibility of the roi dock widget 

80 

81 If dock becomes hidden, ROI interaction is stopped. 

82 """ 

83 if not visible: 

84 self._roiManager.stop() 

85 

86 

87class RixsRotateAction(PlotAction): 

88 """QAction rotating a Rixs plane 

89 

90 :param plot: :class:`.PlotWidget` instance on which to operate 

91 :param parent: See :class:`QAction` 

92 """ 

93 

94 def __init__(self, plot, parent=None): 

95 PlotAction.__init__(self, 

96 plot, 

97 icon='compare-align-auto', 

98 text='Rixs_et', 

99 tooltip='Rotate RIXS plane to energy transfer', 

100 triggered=self.rotateImage, 

101 parent=parent) 

102 raise NotImplementedError 

103 

104 def rotateImage(self): 

105 """""" 

106 return 

107 

108 

109class RixsPlot2D(Plot2D): 

110 """RIXS equivalent of Plot2D""" 

111 

112 def __init__(self, parent=None, backend=None, logger=None, 

113 profileWindow=None, overlayColors=None, title="RixsPlot2D"): 

114 """Constructor""" 

115 super(RixsPlot2D, self).__init__(parent=parent, backend=backend, title=title) 

116 

117 self._title = title 

118 self._logger = logger or _logger 

119 self._profileWindow = profileWindow or Plot1D(title="Profiles") 

120 self._overlayColors = overlayColors or _DEFAULT_OVERLAY_COLORS 

121 

122 #: cleaning toolbar 

123 self.getMaskAction().setVisible(False) 

124 self.getYAxisInvertedAction().setVisible(False) 

125 self.getKeepDataAspectRatioAction().setVisible(False) 

126 self.getColorBarAction().setVisible(False) 

127 

128 # Change default profile toolbar 

129 self.removeToolBar(self.profile) 

130 self.profile = RixsProfileToolBar(plot=self, 

131 profileWindow=self._profileWindow, 

132 overlayColors=self._overlayColors) 

133 self.addToolBar(self.profile) 

134 self.setKeepDataAspectRatio(True) 

135 self.getDefaultColormap().setName('YlOrBr') 

136 

137 

138class RixsPlotArea(PlotArea): 

139 """RIXS equivalent of PlotArea""" 

140 

141 def __init__(self, parent=None, profileWindow=None, overlayColors=None, 

142 logger=None): 

143 super(RixsPlotArea, self).__init__(parent=parent) 

144 

145 self._logger = logger or _logger 

146 self._overlayColors = overlayColors or _DEFAULT_OVERLAY_COLORS 

147 self._profileWindow = profileWindow or self._addProfileWindow() 

148 self.addRixsPlot2D() 

149 self.setMinimumSize(300, 300) 

150 self.setWindowTitle('RixsPlotArea') 

151 

152 def showContextMenu(self, position): 

153 menu = qt.QMenu('RixsPlotArea Menu', self) 

154 

155 action = qt.QAction('Add RixsPlot2D Window', self, 

156 triggered=self.addRixsPlot2D) 

157 menu.addAction(action) 

158 

159 menu.addSeparator() 

160 

161 action = qt.QAction('Cascade Windows', self, 

162 triggered=self.cascadeSubWindows) 

163 menu.addAction(action) 

164 

165 action = qt.QAction('Tile Windows', self, 

166 triggered=self.tileSubWindows) 

167 menu.addAction(action) 

168 

169 menu.exec_(self.mapToGlobal(position)) 

170 

171 def _addProfileWindow(self): 

172 """Add a ProfileWindow in the mdi Area""" 

173 subWindow = MdiSubWindow(parent=self) 

174 plotWindow = Plot1D(parent=subWindow, title='Profiles') 

175 plotWindow.setIndex(len(self.plotWindows())) 

176 subWindow.setWidget(plotWindow) 

177 subWindow.show() 

178 self.changed.emit() 

179 return plotWindow 

180 

181 def addRixsPlot2D(self, profileWindow=None): 

182 """Add a RixPlot2D window in the mdi Area""" 

183 subWindow = MdiSubWindow(parent=self) 

184 profileWindow = profileWindow or self._profileWindow 

185 plotWindow = RixsPlot2D(parent=subWindow, 

186 profileWindow=profileWindow, 

187 overlayColors=self._overlayColors) 

188 plotWindow.setIndex(len(self.plotWindows())) 

189 subWindow.setWidget(plotWindow) 

190 subWindow.show() 

191 self.changed.emit() 

192 return plotWindow 

193 

194 def getProfileWindow(self): 

195 return self._profileWindow 

196 

197 

198class RixsMainWindow(qt.QMainWindow): 

199 

200 def __init__(self, parent=None, logger=None): 

201 

202 super(RixsMainWindow, self).__init__(parent=parent) 

203 

204 self._logger = logger or _logger 

205 

206 if parent is not None: 

207 #: behave as a widget 

208 self.setWindowFlags(qt.Qt.Widget) 

209 else: 

210 #: main window 

211 self.setWindowTitle('RIXS_VIEW') 

212 

213 self.setGeometry(0, 0, 1280, 960) 

214 

215 #: Model (= simple RixsData container) 

216 self._model = RixsListModel() 

217 

218 #: View (= simply show list of loaded data) 

219 self._view = RixsListView(parent=self) 

220 self._view.setModel(self._model) 

221 

222 #: View dock widget 

223 self._dockDataWidget = qt.QDockWidget(parent=self) 

224 self._dockDataWidget.setObjectName('Data View') 

225 self._dockDataWidget.setWidget(self._view) 

226 self.addDockWidget(qt.Qt.LeftDockWidgetArea, self._dockDataWidget) 

227 

228 #: Plot Area 

229 self._plotArea = RixsPlotArea(self) 

230 self.setCentralWidget(self._plotArea) 

231 self.setMinimumSize(600, 600) 

232 

233 #: TODO Tab widget containing Cursors Infos dock widgets 

234 # self._tabCurInfos = qt.QTabWidget(parent=self) 

235 # self._tabCurInfos.setLayoutDirection(qt.Qt.LeftToRight) 

236 # self._tabCurInfos.setDocumentMode(False) 

237 # self._tabCurInfos.setTabsClosable(False) 

238 # self._tabCurInfos.setMovable(False) 

239 # self._dockCurInfos = qt.QDockWidget('Plot Cursors Infos', self) 

240 # self.addDockWidget(qt.Qt.BottomDockWidgetArea, self._dockCurInfos) 

241 # self._dockCurInfos.setWidget(self._tabCurInfos) 

242 

243 def _plot_rixs(self, rd, pw): 

244 """Plot rixs full plane""" 

245 pw.addImage(rd.rixs_map, x=rd.ene_in, y=rd.ene_out, 

246 title=rd.sample_name, 

247 xlabel=rd.ene_in_label, 

248 ylabel=rd.ene_out_label) 

249 

250 def _plot_rixs_et(self, rd, pw): 

251 """Plot rixs_et full plane""" 

252 pw.addImage(rd.rixs_et_map, x=rd.ene_in, y=rd.ene_et, 

253 title=rd.sample_name, 

254 xlabel=rd.ene_in_label, 

255 ylabel=rd.ene_et_label) 

256 

257 def _plot_rixs_crop(self, rd, pw): 

258 """Plot rixs_et crop_area plane""" 

259 _title = f"{rd.sample_name} [CROP: {rd._crop_area}]" 

260 pw.addImage(rd.rixs_map_crop, x=rd.ene_in_crop, y=rd.ene_out_crop, 

261 title=_title, 

262 xlabel=rd.ene_in_label, 

263 ylabel=rd.ene_out_label) 

264 

265 def _plot_rixs_et_crop(self, rd, pw): 

266 """Plot rixs_et crop_area plane""" 

267 _title = f"{rd.sample_name} [CROP: {rd._crop_area}]" 

268 pw.addImage(rd.rixs_et_map_crop, 

269 x=rd.ene_in_crop, 

270 y=rd.ene_et_crop, 

271 title=_title, 

272 xlabel=rd.ene_in_label, 

273 ylabel=rd.ene_et_label) 

274 

275 def plot(self, dataIndex, plotIndex, 

276 crop=False, rixs_et=False, 

277 nlevels=50): 

278 """Plot given data index to given plot""" 

279 rd = self.getData(dataIndex) 

280 pw = self.getPlotWindow(plotIndex) 

281 if rd is None or pw is None: 

282 return 

283 pw.reset() 

284 if type(crop) is tuple: 

285 rd.crop(crop) 

286 if crop: 

287 if rixs_et: 

288 self._plot_rixs_et_crop(rd, pw) 

289 else: 

290 self._plot_rixs_crop(rd, pw) 

291 else: 

292 if rixs_et: 

293 self._plot_rixs_et(rd, pw) 

294 else: 

295 self._plot_rixs(rd, pw) 

296 pw.addContours(nlevels) 

297 

298 def getData(self, index): 

299 try: 

300 return self._model._data[index] 

301 except IndexError: 

302 self._logger.error("data index is wrong") 

303 self._logger.info("use 'addData' to add new data to the list") 

304 return None 

305 

306 def getPlotWindow(self, index): 

307 try: 

308 return self._plotArea.getPlotWindow(index) 

309 except IndexError: 

310 self._plotArea.addRixsPlot2D() 

311 self._logger.warning("plot index wrong -> created a new RixsPlot2D") 

312 return self._plotArea.getPlotWindow(-1) 

313 

314 def addData(self, data): 

315 """Append RixsData object to the model""" 

316 self._model.appendRow(data) 

317 

318 def addRixsDOIDockWidget(self, plotIndex): 

319 plot = self.getPlotWindow(plotIndex) 

320 _roiDock = RixsROIDockWidget(plot, parent=self) 

321 self.addDockWidget(qt.Qt.BottomDockWidgetArea, _roiDock) 

322 

323 def getPlotArea(self): 

324 return self._plotArea 

325 

326 def getProfileWindow(self): 

327 return self.getPlotArea().getProfileWindow() 

328 

329 

330if __name__ == '__main__': 

331 pass