Coverage for larch/wxmap/mapimageframe.py: 14%

218 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-10-16 21:04 +0000

1#!/usr/bin/python 

2""" 

3subclass of wxmplot.ImageFrame specific for Map Viewer -- adds custom menus 

4""" 

5 

6import os 

7import time 

8from threading import Thread 

9import socket 

10 

11from functools import partial 

12import wx 

13 

14import numpy as np 

15from matplotlib.figure import Figure 

16from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg 

17 

18import wxmplot 

19from wxmplot import ImageFrame, PlotFrame, PlotPanel 

20from wxmplot.imagepanel import ImagePanel 

21 

22from wxmplot.imageconf import ColorMap_List, Interp_List 

23from wxmplot.colors import rgb2hex, register_custom_colormaps 

24 

25from wxutils import (SimpleText, TextCtrl, Button, Popup, Choice, pack) 

26 

27def get_wxmplot_version(): 

28 return "this function is deprecated" 

29 

30class MapImageFrame(ImageFrame): 

31 """ 

32 MatPlotlib Image Display on a wx.Frame, using ImagePanel 

33 """ 

34 

35 def __init__(self, parent=None, size=(900, 750), mode='intensity', 

36 lasso_callback=None, move_callback=None, save_callback=None, 

37 show_xsections=False, cursor_labels=None, 

38 with_savepos=True,output_title='Image', **kws): 

39 self.det = None 

40 self.xrmfile = None 

41 self.map = None 

42 self.move_callback = move_callback 

43 self.save_callback = save_callback 

44 self.with_savepos = with_savepos 

45 

46 ImageFrame.__init__(self, parent=parent, size=size, 

47 lasso_callback=lasso_callback, 

48 cursor_labels=cursor_labels, mode=mode, 

49 output_title=output_title, **kws) 

50 

51 w0, h0 = self.GetSize() 

52 w1, h1 = self.GetBestSize() 

53 self.SetSize((max(w0, w1)+25, max(h0, h1)+50)) 

54 self.SetMinSize((500, 500)) 

55 self.prof_plotter = None 

56 self.zoom_ini = None 

57 self.lastpoint = [None, None] 

58 

59 self.rbbox = None 

60 

61 def display(self, map, det=None, xrmfile=None, xoff=0, yoff=0, 

62 with_savepos=True, **kws): 

63 self.xoff = xoff 

64 self.yoff = yoff 

65 self.det = det 

66 self.xrmfile = xrmfile 

67 self.map = map 

68 self.title = '' 

69 if 'title' in kws: 

70 self.title = kws['title'] 

71 if 'contrast_level' not in kws: 

72 kws['contrast_level'] = 0.5 

73 ImageFrame.display(self, map, **kws) 

74 # self.set_contrast_levels() 

75 

76 if self.save_callback is not None and hasattr(self, 'pos_name'): 

77 self.pos_name.Enable(with_savepos) 

78 

79 sd = kws.get('subtitles', None) 

80 if sd is not None: 

81 for i, name in enumerate(('red', 'green', 'blue')): 

82 sub = sd.get(name, None) 

83 if sub is not None: 

84 self.cmap_panels[i].title.SetLabel(sub) 

85 

86 

87 def onCursorMode(self, event=None, mode='zoom'): 

88 self.panel.cursor_mode = mode 

89 choice = self.zoom_mode.GetString(self.zoom_mode.GetSelection()) 

90 if event is not None: 

91 if choice.startswith('Pick Area'): 

92 self.panel.cursor_mode = 'lasso' 

93 

94 def onLasso(self, data=None, selected=None, mask=None, **kws): 

95 if hasattr(self.lasso_callback , '__call__'): 

96 

97 self.lasso_callback(data=data, selected=selected, mask=mask, 

98 xoff=self.xoff, yoff=self.yoff, det=self.det, 

99 xrmfile=self.xrmfile, **kws) 

100 

101 self.zoom_mode.SetSelection(0) 

102 self.panel.cursor_mode = 'zoom' 

103 

104 def CustomConfig(self, panel, sizer=None, irow=0): 

105 """config panel for left-hand-side of frame""" 

106 

107 labstyle = wx.ALIGN_LEFT|wx.LEFT|wx.TOP|wx.EXPAND 

108 

109 if self.lasso_callback is None: 

110 zoom_opts = ('Zoom to Rectangle',) 

111 else: 

112 zoom_opts = ('Zoom to Rectangle', 

113 'Pick Area for XRF Spectrum') 

114 

115 cpanel = wx.Panel(panel) 

116 if sizer is None: 

117 sizer = wx.BoxSizer(wx.VERTICAL) 

118 sizer.Add(SimpleText(cpanel, label='Cursor Modes', style=labstyle), 

119 0, labstyle, 3) 

120 self.zoom_mode = wx.RadioBox(cpanel, -1, "", 

121 wx.DefaultPosition, wx.DefaultSize, 

122 zoom_opts, 1, wx.RA_SPECIFY_COLS) 

123 self.zoom_mode.Bind(wx.EVT_RADIOBOX, self.onCursorMode) 

124 

125 sizer.Add(self.zoom_mode, 1, labstyle, 4) 

126 

127 if self.save_callback is not None: 

128 sizer.Add(SimpleText(cpanel, label='Save Position:', style=labstyle), 

129 0, labstyle, 3) 

130 self.pos_name = wx.TextCtrl(cpanel, -1, '', size=(155, -1), 

131 style=wx.TE_PROCESS_ENTER) 

132 self.pos_name.Bind(wx.EVT_TEXT_ENTER, self.onSavePixel) 

133 sizer.Add(self.pos_name, 0, labstyle, 3) 

134 

135 pack(cpanel, sizer) 

136 return cpanel 

137 

138 def onSavePixel(self, event=None): 

139 ix, iy = self.panel.conf.slice_xy 

140 if ix > 0 and iy > 0 and self.save_callback is not None: 

141 name = str(event.GetString().strip()) 

142 x = float(self.panel.xdata[int(ix)]) 

143 y = float(self.panel.ydata[int(iy)]) 

144 self.save_callback(name, ix, iy, x=x, y=y, 

145 title=self.title, xrmfile=self.xrmfile) 

146 

147 

148class CorrelatedMapFrame(wxmplot.ImageMatrixFrame): 

149 """ 

150 wx.Frame, with 3 ImagePanels and correlation plot for 2 map arrays 

151 """ 

152 def __init__(self, parent=None, xrmfile=None, 

153 title='CorrelatedMaps', **kws): 

154 

155 self.xrmfile = xrmfile 

156 wxmplot.ImageMatrixFrame.__init__(self, parent, size=(900, 625), 

157 title=title, **kws) 

158 

159 def CustomConfig(self, parent): 

160 pass 

161 

162 

163class with_profile_mode: 

164 def prof_motion(self, event=None): 

165 if not event.inaxes or self.zoom_ini is None: 

166 return 

167 try: 

168 xmax, ymax = event.x, event.y 

169 except: 

170 return 

171 xmin, ymin, xd, yd = self.zoom_ini 

172 if event.xdata is not None: 

173 self.lastpoint[0] = event.xdata 

174 if event.ydata is not None: 

175 self.lastpoint[1] = event.ydata 

176 

177 yoff = self.panel.canvas.figure.bbox.height 

178 ymin, ymax = yoff - ymin, yoff - ymax 

179 

180 zdc = wx.ClientDC(self.panel.canvas) 

181 zdc.SetLogicalFunction(wx.XOR) 

182 zdc.SetBrush(wx.TRANSPARENT_BRUSH) 

183 zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) 

184 zdc.ResetBoundingBox() 

185 

186 # erase previous box 

187 if self.rbbox is not None: 

188 zdc.DrawLine(*self.rbbox) 

189 self.rbbox = (xmin, ymin, xmax, ymax) 

190 zdc.DrawLine(*self.rbbox) 

191 

192 def prof_leftdown(self, event=None): 

193 self.report_leftdown(event=event) 

194 if event.inaxes: # and len(self.map.shape) == 2: 

195 self.lastpoint = [None, None] 

196 self.zoom_ini = [event.x, event.y, event.xdata, event.ydata] 

197 

198 def prof_leftup(self, event=None): 

199 # print("Profile Left up ", self.map.shape, self.rbbox) 

200 if self.rbbox is not None: 

201 zdc = wx.ClientDC(self.panel.canvas) 

202 zdc.SetLogicalFunction(wx.XOR) 

203 zdc.SetBrush(wx.TRANSPARENT_BRUSH) 

204 zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) 

205 zdc.ResetBoundingBox() 

206 zdc.DrawLine(*self.rbbox) 

207 self.rbbox = None 

208 

209 if self.zoom_ini is None or self.lastpoint[0] is None: 

210 return 

211 

212 x0 = int(self.zoom_ini[2]) 

213 x1 = int(self.lastpoint[0]) 

214 y0 = int(self.zoom_ini[3]) 

215 y1 = int(self.lastpoint[1]) 

216 dx, dy = abs(x1-x0), abs(y1-y0) 

217 

218 self.lastpoint, self.zoom_ini = [None, None], None 

219 if dx < 2 and dy < 2: 

220 self.zoom_ini = None 

221 return 

222 

223 outdat = [] 

224 if dy > dx: 

225 _y0 = min(int(y0), int(y1+0.5)) 

226 _y1 = max(int(y0), int(y1+0.5)) 

227 

228 for iy in range(_y0, _y1): 

229 ix = int(x0 + (iy-int(y0))*(x1-x0)/(y1-y0)) 

230 outdat.append((ix, iy)) 

231 else: 

232 _x0 = min(int(x0), int(x1+0.5)) 

233 _x1 = max(int(x0), int(x1+0.5)) 

234 for ix in range(_x0, _x1): 

235 iy = int(y0 + (ix-int(x0))*(y1-y0)/(x1-x0)) 

236 outdat.append((ix, iy)) 

237 x, y, z = [], [], [] 

238 for ix, iy in outdat: 

239 x.append(ix) 

240 y.append(iy) 

241 z.append(self.panel.conf.data[iy, ix]) 

242 self.prof_dat = dy>dx, outdat 

243 

244 if self.prof_plotter is not None: 

245 try: 

246 self.prof_plotter.Raise() 

247 self.prof_plotter.clear() 

248 

249 except (AttributeError, Exception): 

250 self.prof_plotter = None 

251 

252 if self.prof_plotter is None: 

253 self.prof_plotter = PlotFrame(self, title='Profile') 

254 self.prof_plotter.panel.report_leftdown = self.prof_report_coords 

255 

256 xlabel, y2label = 'Pixel (x)', 'Pixel (y)' 

257 

258 x = np.array(x) 

259 y = np.array(y) 

260 z = np.array(z) 

261 if dy > dx: 

262 x, y = y, x 

263 xlabel, y2label = y2label, xlabel 

264 self.prof_plotter.panel.clear() 

265 

266 if len(self.title) < 1: 

267 self.title = os.path.split(self.xrmfile.filename)[1] 

268 

269 opts = dict(linewidth=2, marker='+', markersize=3, 

270 show_legend=True, xlabel=xlabel) 

271 

272 if isinstance(z[0], np.ndarray) and len(z[0]) == 3: # color plot 

273 rlab = self.subtitles['red'] 

274 glab = self.subtitles['green'] 

275 blab = self.subtitles['blue'] 

276 self.prof_plotter.plot(x, z[:, 0], title=self.title, color='red', 

277 zorder=20, xmin=min(x)-3, xmax=max(x)+3, 

278 ylabel='counts', label=rlab, **opts) 

279 self.prof_plotter.oplot(x, z[:, 1], title=self.title, color='darkgreen', 

280 zorder=20, xmin=min(x)-3, xmax=max(x)+3, 

281 ylabel='counts', label=glab, **opts) 

282 self.prof_plotter.oplot(x, z[:, 2], title=self.title, color='blue', 

283 zorder=20, xmin=min(x)-3, xmax=max(x)+3, 

284 ylabel='counts', label=blab, **opts) 

285 

286 else: 

287 

288 self.prof_plotter.plot(x, z, title=self.title, color='blue', 

289 zorder=20, xmin=min(x)-3, xmax=max(x)+3, 

290 ylabel='counts', label='counts', **opts) 

291 

292 self.prof_plotter.oplot(x, y, y2label=y2label, label=y2label, 

293 zorder=3, side='right', color='black', **opts) 

294 

295 self.prof_plotter.panel.unzoom_all() 

296 self.prof_plotter.Show() 

297 self.zoom_ini = None 

298 

299 self.zoom_mode.SetSelection(0) 

300 self.panel.cursor_mode = 'zoom' 

301 

302 def prof_report_coords(self, event=None): 

303 """override report leftdown for profile plotter""" 

304 if event is None: 

305 return 

306 ex, ey = event.x, event.y 

307 msg = '' 

308 plotpanel = self.prof_plotter.panel 

309 axes = plotpanel.fig.properties()['axes'][0] 

310 write = plotpanel.write_message 

311 try: 

312 x, y = axes.transData.inverted().transform((ex, ey)) 

313 except: 

314 x, y = event.xdata, event.ydata 

315 

316 if x is None or y is None: 

317 return 

318 

319 _point = 0, 0, 0, 0, 0 

320 for ix, iy in self.prof_dat[1]: 

321 if (int(x) == ix and not self.prof_dat[0] or 

322 int(x) == iy and self.prof_dat[0]): 

323 _point = (ix, iy, 

324 self.panel.xdata[ix], 

325 self.panel.ydata[iy], 

326 self.panel.conf.data[iy, ix]) 

327 

328 msg = "Pixel [%i, %i], X, OME = [%.4f mm, %.4f deg], Intensity= %g" % _point 

329 write(msg, panel=0)