Coverage for larch/wxmap/mapviewer.py: 10%
1931 statements
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
1#!/usr/bin/env python
2"""
3GUI for displaying maps from HDF5 files
5"""
7import os
8import platform
9import sys
10import time
11import json
12import socket
13import datetime
14from functools import partial
15from threading import Thread
16from collections import namedtuple
17from pathlib import Path
19import wx
20from wx.adv import AboutBox, AboutDialogInfo
21import wx.lib.scrolledpanel as scrolled
22import wx.lib.mixins.inspection
24import wx.dataview as dv
25DVSTY = dv.DV_SINGLE|dv.DV_VERT_RULES|dv.DV_ROW_LINES
27HAS_EPICS = False
28try:
29 from epics import caput
30 HAS_EPICS = True
31except:
32 pass
34import numpy as np
35import scipy.stats as stats
37#from matplotlib.widgets import Slider, Button, RadioButtons
39from wxmplot import PlotFrame
41import larch
42from larch.larchlib import read_workdir, save_workdir
43from larch.wxlib import (LarchPanel, LarchFrame, EditableListBox, SimpleText,
44 FloatCtrl, Font, pack, Popup, Button, MenuItem,
45 Choice, Check, GridPanel, FileSave, HLine, flatnotebook,
46 HLine, OkCancel, LEFT, LarchUpdaterDialog, LarchWxApp)
47from larch.wxxas.xas_dialogs import fit_dialog_window
48from larch.utils.strutils import bytes2str, version_ge
49from larch.utils import get_cwd
50from larch.site_config import icondir
51from larch.version import check_larchversion
52from larch.utils.physical_constants import PLANCK_HC
54from ..xrd import lambda_from_E, xrd1d, save1D, calculate_xvalues, read_poni
55from ..xrmmap import GSEXRM_MapFile, GSEXRM_FileStatus, h5str, ensure_subgroup, DEFAULT_XRAY_ENERGY
56from ..apps import check_larchversion, update_larch
57from ..epics import pv_fullname
58from ..wxlib.xrfdisplay import XRFDisplayFrame
60from .mapimageframe import MapImageFrame, CorrelatedMapFrame
61from .mapmathpanel import MapMathPanel
62from .maptomopanel import TomographyPanel
63from .mapxrfpanel import XRFAnalysisPanel
65from ..wxxrd.xrd1d_display import XRD1DFrame
67def timestring():
68 return datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')
70FONTSIZE = 8
71if platform.system() in ('Windows', 'Darwin'):
72 FONTSIZE = 10
74CEN = wx.ALIGN_CENTER
75LEFT = wx.ALIGN_LEFT
76RIGHT = wx.ALIGN_RIGHT
77ALL_CEN = wx.ALL|CEN
78ALL_LEFT = wx.ALL|LEFT
79ALL_RIGHT = wx.ALL|RIGHT
82FILE_WILDCARDS = 'X-ray Maps (*.h5)|*.h5|All files (*.*)|*.*'
84XRF_ICON_FILE = 'gse_xrfmap.ico'
86NOT_OWNER_MSG = """The File
87 '%s'
88appears to be open by another process. Having two
89processes writing to the file can cause corruption.
91Do you want to take ownership of the file?
92"""
94NOT_GSEXRM_FILE = """The File
95 '%s'
96doesn't seem to be a Map File
97"""
99NOT_GSEXRM_FOLDER = """The Folder
100 '%s'
101doesn't seem to be a Map Folder
102"""
103FILE_ALREADY_READ = """The File
104 '%s'
105has already been read.
106"""
108FRAMESTYLE = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL
109BEAMLINE = '13-ID-E'
110FACILITY = 'APS'
112PLOT_TYPES = ('Single ROI Map', 'Three ROI Map', 'Correlation Plot')
113PROCROWS_CHOICES = ('All', '500', '200', '100', '50', '20', '10')
114PLOT_OPERS = ('/', '*', '-', '+')
116ESCAN_CRED = os.environ.get('ESCAN_CREDENTIALS', None)
117if ESCAN_CRED is not None:
118 try:
119 from ..epics.larchscan import connect_scandb
120 except ImportError:
121 ESCAN_CRED = None
123CWID = 150
124WWID = 100 + CWID*4
126class MapPanel(GridPanel):
127 '''Panel of Controls for viewing maps'''
128 label = 'ROI Map'
129 def __init__(self, parent, owner=None, **kws):
131 self.owner = owner
132 self.cfile, self.xrmmap = None,None
133 self.last_process_time = 0
134 self.detectors_set = False
135 GridPanel.__init__(self, parent, nrows=8, ncols=6, **kws)
137 self.plot_choice = Choice(self, choices=PLOT_TYPES, size=(CWID, -1))
138 self.plot_choice.Bind(wx.EVT_CHOICE, self.plotSELECT)
140 self.det_choice = [Choice(self, size=(CWID, -1)),
141 Choice(self, size=(CWID, -1)),
142 Choice(self, size=(CWID, -1)),
143 Choice(self, size=(CWID, -1))]
145 self.roi_choice = [Choice(self, size=(CWID, -1)),
146 Choice(self, size=(CWID, -1)),
147 Choice(self, size=(CWID, -1)),
148 Choice(self, size=(CWID, -1))]
149 for i,det_chc in enumerate(self.det_choice):
150 det_chc.Bind(wx.EVT_CHOICE, partial(self.detSELECT,i))
152 for i,roi_chc in enumerate(self.roi_choice):
153 roi_chc.Bind(wx.EVT_CHOICE, partial(self.roiSELECT,i))
155 self.det_label = [SimpleText(self,'Intensity'),
156 SimpleText(self,''),
157 SimpleText(self,''),
158 SimpleText(self, 'Normalization')]
159 self.roi_label = [SimpleText(self,''),
160 SimpleText(self,''),
161 SimpleText(self,''),
162 SimpleText(self,'')]
164 fopts = dict(minval=-50000, precision=0, size=(70, -1))
165 self.lims = [FloatCtrl(self, value= 0, **fopts),
166 FloatCtrl(self, value=-1, **fopts),
167 FloatCtrl(self, value= 0, **fopts),
168 FloatCtrl(self, value=-1, **fopts)]
170 self.zigoff = FloatCtrl(self, value=0, minval=-15, maxval=15,
171 precision=0, size=(70, -1))
172 for wid in self.lims:
173 wid.Disable()
175 self.use_dtcorr = Check(self, default=True,
176 label='Correct for Detector Deadtime',
177 action=self.onDTCorrect)
178 self.use_hotcols = Check(self, default=False,
179 label='Remove First and Last columns',
180 action=self.onHotCols)
182 self.use_zigzag = Check(self, default=False, label='Fix ZigZag',
183 action=self.onZigZag)
185 self.limrange = Check(self, default=False,
186 label=' Limit Map Range to Pixel Range:',
187 action=self.onLimitRange)
189 map_shownew = Button(self, 'Show New Map', size=(CWID, -1),
190 action=partial(self.onROIMap, new=True))
191 map_update = Button(self, 'Replace Last Map', size=(CWID, -1),
192 action=partial(self.onROIMap, new=False))
193 self.mapproc_btn = Button(self, 'Add More Rows', size=(CWID, -1),
194 action=self.onProcessMap)
196 map_showxrf = Button(self, 'Show Full XRF', size=(CWID, -1),
197 action=self.onShowXRF)
199 self.mapproc_nrows = Choice(self, choices=PROCROWS_CHOICES, size=(CWID, -1))
200 self.mapproc_nrows.SetStringSelection('100')
202 self.Add(SimpleText(self, 'Build Map From Raw Data Folder:'),
203 dcol=2, style=LEFT, newrow=True)
204 self.Add(self.mapproc_btn, dcol=1, style=LEFT)
205 self.Add(SimpleText(self, 'Max # Rows to Add:'), dcol=1,
206 style=LEFT, newrow=False)
207 self.Add(self.mapproc_nrows, dcol=1, style=LEFT)
209 self.Add(HLine(self, size=(WWID, 5)), dcol=8, style=LEFT, newrow=True)
210 self.Add((5, 5), newrow=True)
212 self.Add(SimpleText(self, 'Display ROI Maps: Plot Type:'), dcol=2,
213 style=LEFT, newrow=True)
214 self.Add(self.plot_choice, dcol=1, style=LEFT)
215 self.AddMany((SimpleText(self,''), self.det_label[0],
216 self.det_label[1], self.det_label[2], self.det_label[3]),
217 style=LEFT, newrow=True)
219 self.AddMany((SimpleText(self,'Detector:'), self.det_choice[0],
220 self.det_choice[1], self.det_choice[2], self.det_choice[3]),
221 style=LEFT, newrow=True)
223 self.AddMany((SimpleText(self,'ROI:'),self.roi_choice[0],
224 self.roi_choice[1],self.roi_choice[2], self.roi_choice[3]),
225 style=LEFT, newrow=True)
227 self.AddMany((SimpleText(self,''),self.roi_label[0],
228 self.roi_label[1],self.roi_label[2], self.roi_label[3]),
229 style=LEFT, newrow=True)
230 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
231 self.Add(map_shownew, dcol=1, style=LEFT)
232 self.Add(map_update, dcol=1, style=LEFT)
234 self.Add(HLine(self, size=(WWID, 5)), dcol=8, style=LEFT, newrow=True)
235 self.Add(SimpleText(self,'Options:'), dcol=1, style=LEFT, newrow=True)
236 self.Add(self.use_dtcorr, dcol=2, style=LEFT)
237 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
238 self.Add(self.use_hotcols, dcol=2, style=LEFT)
239 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
240 self.Add(self.use_zigzag, dcol=1, style=LEFT)
241 self.Add(self.zigoff, dcol=1, style=LEFT)
242 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
243 self.Add(self.limrange, dcol=2, style=LEFT)
244 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
245 self.Add(SimpleText(self, 'X Range:'), dcol=1, style=LEFT)
246 self.Add(self.lims[0], dcol=1, style=LEFT)
247 self.Add(self.lims[1], dcol=1, style=LEFT)
248 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
249 self.Add(SimpleText(self, 'Y Range:'), dcol=1, style=LEFT)
250 self.Add(self.lims[2], dcol=1, style=LEFT)
251 self.Add(self.lims[3], dcol=1, style=LEFT)
252 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
253 self.Add(map_showxrf, dcol=1, style=LEFT)
254 self.Add(HLine(self, size=(WWID, 5)), dcol=8, style=LEFT, newrow=True)
255 self.pack()
257 def onDTCorrect(self, event=None):
258 xrmfile = self.owner.current_file
259 if xrmfile is not None:
260 xrmfile.dtcorrect = self.use_dtcorr.IsChecked()
262 def onHotCols(self, event=None):
263 xrmfile = self.owner.current_file
264 if xrmfile is not None:
265 xrmfile.hotcols = self.use_hotcols.IsChecked()
267 def onZigZag(self, event=None):
268 xrmfile = self.owner.current_file
269 if xrmfile is not None:
270 zigzag = 0
271 if self.use_zigzag.IsChecked():
272 zigzag = int(self.zigoff.GetValue())
273 xrmfile.zigzag = zigzag
275 def update_xrmmap(self, xrmfile=None, set_detectors=False):
276 if xrmfile is None:
277 xrmfile = self.owner.current_file
278 self.cfile = xrmfile
279 self.xrmmap = self.cfile.xrmmap
280 #if set_detectors or not self.detectors_set:
281 self.set_det_choices()
282 self.plotSELECT()
284 def onLimitRange(self, event=None):
285 if self.limrange.IsChecked():
286 for wid in self.lims:
287 wid.Enable()
288 else:
289 for wid in self.lims:
290 wid.Disable()
292 def detSELECT(self, idet, event=None):
293 self.set_roi_choices(idet=idet)
295 def roiSELECT(self,iroi,event=None):
297 detname = self.det_choice[iroi].GetStringSelection()
298 roiname = self.roi_choice[iroi].GetStringSelection()
300 if version_ge(self.cfile.version, '2.0.0'):
301 try:
302 roi = self.cfile.xrmmap['roimap'][detname][roiname]
303 limits = roi['limits'][:]
304 units = bytes2str(roi['limits'].attrs.get('units',''))
305 roistr = '[%0.1f to %0.1f %s]' % (limits[0],limits[1],units)
306 except:
307 roistr = ''
308 else:
309 try:
310 roi = self.cfile.xrmmap[detname]
311 en = list(roi['energy'][:])
312 index = list(roi['roi_name'][:]).index(roiname)
313 limits = list(roi['roi_limits'][:][index])
314 roistr = '[%0.1f to %0.1f keV]' % (en[limits[0]],en[limits[1]])
315 except:
316 roistr = ''
318 self.roi_label[iroi].SetLabel(roistr)
320 def plotSELECT(self,event=None):
321 if len(self.owner.filemap) > 0:
322 plot_type = self.plot_choice.GetStringSelection().lower()
323 if 'single' in plot_type:
324 for i in (1, 2):
325 self.det_choice[i].Disable()
326 self.roi_choice[i].Disable()
327 self.roi_label[i].SetLabel('')
328 for i, label in enumerate(['Intensity', ' ', ' ']):
329 self.det_label[i].SetLabel(label)
330 elif 'three' in plot_type:
331 for i in (1, 2):
332 self.det_choice[i].Enable()
333 self.roi_choice[i].Enable()
334 for i, label in enumerate(['Red', 'Green', 'Blue']):
335 self.det_label[i].SetLabel(label)
336 self.set_roi_choices()
337 elif 'correl' in plot_type:
338 self.det_choice[1].Enable()
339 self.roi_choice[1].Enable()
340 self.det_choice[2].Disable()
341 self.roi_choice[2].Disable()
342 for i, label in enumerate([' X ',' Y ', '']):
343 self.det_label[i].SetLabel(label)
344 self.set_roi_choices()
346 def onClose(self):
347 for p in self.plotframes:
348 try:
349 p.Destroy()
350 except:
351 pass
353 def ShowMap(self, xrmfile=None, new=True):
354 subtitles = None
355 plt3 = 'three' in self.plot_choice.GetStringSelection().lower()
357 if xrmfile is None:
358 xrmfile = self.owner.current_file
360 self.onZigZag()
362 args={'hotcols' : xrmfile.hotcols,
363 'dtcorrect' : xrmfile.dtcorrect}
365 det_name, roi_name, plt_name = [], [], []
366 for det, roi in zip(self.det_choice, self.roi_choice):
367 det_name += [det.GetStringSelection()]
368 roi_name += [roi.GetStringSelection()]
369 if det_name[-1] == 'scalars':
370 plt_name += ['%s' % roi_name[-1]]
371 else:
372 plt_name += ['%s(%s)' % (roi_name[-1],det_name[-1])]
374 mapx = 1.0
375 if roi_name[-1] != '1':
376 mapx = xrmfile.get_roimap(roi_name[-1], det=det_name[-1], **args)
377 mapx[np.where(mapx==0)] = 1.
379 r_map = xrmfile.get_roimap(roi_name[0], det=det_name[0], **args)
380 if plt3:
381 g_map = xrmfile.get_roimap(roi_name[1], det=det_name[1], **args)
382 b_map = xrmfile.get_roimap(roi_name[2], det=det_name[2], **args)
384 x = xrmfile.get_pos(0, mean=True)
385 y = xrmfile.get_pos(1, mean=True)
387 fname = Path(xrmfile.filename).name
389 if plt3:
390 map = np.array([r_map/mapx, g_map/mapx, b_map/mapx])
391 map = np.einsum('kij->ijk', map)
393 title = fname
394 info = ''
395 if roi_name[-1] == '1':
396 subtitles = {'red': 'Red: %s' % plt_name[0],
397 'green': 'Green: %s' % plt_name[1],
398 'blue': 'Blue: %s' % plt_name[2]}
399 else:
400 subtitles = {'red': 'Red: %s / %s' % (plt_name[0], plt_name[-1]),
401 'green': 'Green: %s / %s' % (plt_name[1], plt_name[-1]),
402 'blue': 'Blue: %s / %s' % (plt_name[2], plt_name[-1])}
404 else:
405 map = r_map/mapx
406 if roi_name[-1] == '1':
407 title = plt_name[0]
408 else:
409 title = '%s / %s' % (plt_name[0], plt_name[-1])
410 title = '%s: %s' % (fname, title)
411 info = 'Intensity: [%g, %g]' %(map.min(), map.max())
412 subtitle = None
414 det = None
415 if (plt3 and det_name[0]==det_name[1] and det_name[0]==det_name[2]) or not plt3:
416 for s in det_name[0]:
417 if s.isdigit(): det = int(s)
419 if len(self.owner.im_displays) == 0 or new:
420 iframe = self.owner.add_imdisplay(title, det=det)
422 xoff, yoff = 0, 0
423 if self.limrange.IsChecked():
424 lims = [wid.GetValue() for wid in self.lims]
425 map = map[lims[2]:lims[3], lims[0]:lims[1]]
426 xoff, yoff = lims[0], lims[2]
427 self.owner.display_map(map, title=title, info=info, x=x, y=y, det=det,
428 xoff=xoff, yoff=yoff, subtitles=subtitles,
429 xrmfile=self.cfile)
431 def onLasso(self, selected=None, mask=None, data=None, xrmfile=None, **kws):
432 if xrmfile is None:
433 xrmfile = self.owner.current_file
434 ny, nx = xrmfile.get_shape()
435 indices = []
436 for idx in selected:
437 iy, ix = divmod(idx, ny)
438 indices.append((ix, iy))
441 def ShowCorrel(self, xrmfile=None, new=True):
443 if xrmfile is None:
444 xrmfile = self.owner.current_file
445 self.onZigZag()
446 args={'hotcols' : xrmfile.hotcols,
447 'dtcorrect' : xrmfile.dtcorrect}
448 det_name,roi_name = [],[]
449 plt_name = []
451 xdet = self.det_choice[0].GetStringSelection()
452 xroi = self.roi_choice[0].GetStringSelection()
453 xlab = f"{xroi}({xdet})"
454 if 'scalar' in xdet.lower():
455 xlab = xroi
456 ydet = self.det_choice[1].GetStringSelection()
457 yroi = self.roi_choice[1].GetStringSelection()
459 ylab = f"{yroi}({ydet})"
460 if 'scalar' in ydet.lower():
461 ylab = yroi
463 map1 = xrmfile.get_roimap(xroi, det=xdet, **args)
464 map2 = xrmfile.get_roimap(yroi, det=ydet, **args)
466 x = xrmfile.get_pos(0, mean=True)
467 y = xrmfile.get_pos(1, mean=True)
469 fname = Path(xrmfile.filename).name
470 title = f'{fname}: {ylabl} vs. {xlab}'
472 correl_plot = CorrelatedMapFrame(parent=self.owner, xrmfile=xrmfile)
473 correl_plot.display(map1, map2, name1=xlab, name2=ylab,
474 x=x, y=y, title=title)
475 correl_plot.Show()
476 correl_plot.Raise()
477 self.owner.plot_displays.append(correl_plot)
479 def onProcessMap(self, event=None, max_new_rows=None):
480 xrmfile = self.owner.current_file
481 if xrmfile is None:
482 return
483 fname = Path(xrmfile.filename).name
484 if max_new_rows is None:
485 max_new_rows = self.mapproc_nrows.GetStringSelection().lower()
486 if max_new_rows.lower() == 'all':
487 max_new_rows = None
488 else:
489 max_new_rows = int(max_new_rows)
490 self.owner.process_file(fname, max_new_rows=max_new_rows)
491 self.update_xrmmap(xrmfile=self.owner.current_file, set_detectors=True)
493 def onROIMap(self, event=None, new=True):
494 plotcmd = partial(self.ShowMap, new=new)
495 if 'correlation' in self.plot_choice.GetStringSelection().lower():
496 plotcmd = partial(self.ShowCorrel, new=new)
497 plotcmd()
499 def onShowXRF(self, event=None):
500 owner = self.owner
501 det_list = owner.current_file.get_detector_list()
502 detname = self.det_choice[0].GetStringSelection()
503 ny, nx = owner.current_file.get_shape()
505 xmin, ymin = 0, 0
506 xmax, ymax = nx, ny
507 if self.limrange.IsChecked():
508 xmin = int(self.lims[0].GetValue())
509 xmax = int(self.lims[1].GetValue())
510 ymin = int(self.lims[2].GetValue())
511 ymax = int(self.lims[3].GetValue())
512 if xmax < 0:
513 xmax += nx
514 if ymax < 0:
515 ymax += ny
516 my, mx= (ymax - ymin), (xmax - xmin)
518 owner.show_XRFDisplay()
519 self._mca = owner.current_file.get_mca_rect(ymin, ymax, xmin, xmax, det=detname,
520 dtcorrect=owner.dtcor)
521 fname = Path(self.owner.current_file.filename).name
522 self._mca.filename = fname
523 self._mca.title = f"({mx} x {my} pixels)"
524 self._mca.npixels = my*mx
525 self.owner.message("Plotting Full XRF Spectra (%d x %d) for '%s'" % (mx, my, fname))
527 self.owner.subframes['xrfdisplay'].add_mca(self._mca, label=fname, plot=True)
530 def set_det_choices(self):
531 det_list = self.cfile.get_detector_list()
532 for det_ch in self.det_choice:
533 det_ch.SetChoices(det_list)
534 if 'scalars' in det_list: ## should set 'denominator' to scalars as default
535 self.det_choice[-1].SetStringSelection('scalars')
536 self.set_roi_choices()
538 def set_roi_choices(self, idet=None):
539 force_rois = True # not self.detectors_set
540 if idet is None:
541 for idet, det_ch in enumerate(self.det_choice):
542 detname = self.det_choice[idet].GetStringSelection()
543 rois = self.cfile.get_roi_list(detname, force=force_rois)
544 cur = self.roi_choice[idet].GetStringSelection()
545 self.roi_choice[idet].SetChoices(rois)
546 if cur in rois:
547 self.roi_choice[idet].SetStringSelection(cur)
548 self.roiSELECT(idet)
549 else:
550 detname = self.det_choice[idet].GetStringSelection()
551 rois = self.cfile.get_roi_list(detname, force=force_rois)
552 cur = self.roi_choice[idet].GetStringSelection()
553 self.roi_choice[idet].SetChoices(rois)
554 if cur in rois:
555 self.roi_choice[idet].SetStringSelection(cur)
556 self.roiSELECT(idet)
558 def update_roi(self, detname):
559 force = True # not self.detectors_set
560 return self.cfile.get_roi_list(detname, force=force)
562class MapInfoPanel(scrolled.ScrolledPanel):
563 """Info Panel """
564 label = 'Map Info'
565 def __init__(self, parent, owner=None, **kws):
566 scrolled.ScrolledPanel.__init__(self, parent, -1,
567 style=wx.GROW|wx.TAB_TRAVERSAL, **kws)
568 self.owner = owner
570 sizer = wx.GridBagSizer(3, 3)
571 self.wids = {}
573 ir = 0
574 for label in ('Facility','Run Cycle','Proposal Number','User group',
575 'H5 Map Created',
576 'Scan Time','File Compression','Map Data',
577 'Ring Current', 'X-ray Energy', 'X-ray Intensity (I0)',
578 'Original data path', 'User Comments 1', 'User Comments 2',
579 'Scan Fast Motor', 'Scan Slow Motor', 'Dwell Time',
580 'Sample Fine Stages',
581 'Sample Stage X', 'Sample Stage Y',
582 'Sample Stage Z', 'Sample Stage Theta',
583 'XRD Calibration'):
585 ir += 1
586 thislabel = SimpleText(self, '%s:' % label, style=wx.LEFT, size=(125, -1))
587 self.wids[label] = SimpleText(self, ' ' , style=wx.LEFT, size=(350, -1))
589 sizer.Add(thislabel, (ir, 0), (1, 1), 1)
590 sizer.Add(self.wids[label], (ir, 1), (1, 1), 1)
592 pack(self, sizer)
593 self.SetupScrolling()
595 def update_xrmmap(self, xrmfile=None, set_detectors=None):
596 if xrmfile is None:
597 xrmfile = self.owner.current_file
598 xrmmap = xrmfile.xrmmap
599 def time_between(d1, d2):
600 d1 = datetime.datetime.strptime(d1, "%Y-%m-%d %H:%M:%S")
601 d2 = datetime.datetime.strptime(d2, "%Y-%m-%d %H:%M:%S")
602 diff = d2 - d1 if d2 > d1 else d1 - d2
603 return diff.days,diff.seconds
605 config_grp = ensure_subgroup('config',xrmmap)
606 notes_grp = ensure_subgroup('notes',config_grp)
607 time_str = bytes2str(notes_grp.attrs.get('h5_create_time',''))
609 self.wids['H5 Map Created'].SetLabel(time_str)
611 try:
612 d,s = time_between(bytes2str(notes_grp.attrs.get('scan_start_time','')),
613 bytes2str(notes_grp.attrs.get('scan_end_time','')))
614 time_str = str(datetime.timedelta(days=d,seconds=s))
615 except:
616 time_str = bytes2str(xrmmap.attrs.get('Start_Time',''))
618 self.wids['Scan Time'].SetLabel( time_str )
619 self.wids['File Compression'].SetLabel(bytes2str(xrmmap.attrs.get('Compression','')))
621 comments = h5str(xrmmap['config/scan/comments'][()]).split('\n', 2)
622 for i, comm in enumerate(comments):
623 self.wids['User Comments %i' %(i+1)].SetLabel(comm)
625 pos_addrs = [str(x) for x in xrmmap['config/positioners'].keys()]
626 pos_label = [h5str(x[()]) for x in xrmmap['config/positioners'].values()]
628 scan_pos1 = h5str(xrmmap['config/scan/pos1'][()])
629 scan_pos2 = h5str(xrmmap['config/scan/pos2'][()])
630 i1 = pos_addrs.index(scan_pos1)
631 i2 = pos_addrs.index(scan_pos2)
633 start1 = float(xrmmap['config/scan/start1'][()])
634 start2 = float(xrmmap['config/scan/start2'][()])
635 stop1 = float(xrmmap['config/scan/stop1'][()])
636 stop2 = float(xrmmap['config/scan/stop2'][()])
638 step1 = float(xrmmap['config/scan/step1'][()])
639 step2 = float(xrmmap['config/scan/step2'][()])
641 npts1 = int((abs(stop1 - start1) + 1.1*step1)/step1)
642 npts2 = int((abs(stop2 - start2) + 1.1*step2)/step2)
644 sfmt = '%s: [%.4f:%.4f], step=%.4f, %i pixels'
645 scan1 = sfmt % (pos_label[i1], start1, stop1, step1, npts1)
646 scan2 = sfmt % (pos_label[i2], start2, stop2, step2, npts2)
648 rowtime = float(xrmmap['config/scan/time1'][()])
650 self.wids['Scan Fast Motor'].SetLabel(scan1)
651 self.wids['Scan Slow Motor'].SetLabel(scan2)
652 pixtime = xrmfile.pixeltime
653 if pixtime is None:
654 pixtime = xrmfile.calc_pixeltime()
655 pixtime =int(round(1000.0*pixtime))
656 self.wids['Dwell Time'].SetLabel('%.1f ms per pixel' % pixtime)
658 env_names = list(xrmmap['config/environ/name'])
659 env_vals = list(xrmmap['config/environ/value'])
660 env_addrs = list(xrmmap['config/environ/address'])
662 fines = {'X': '?', 'Y': '?'}
663 i0vals = {'flux':'?', 'current':'?'}
665 en = xrmfile.get_incident_energy()
666 enmsg = '%0.1f eV (%0.3f \u00c5)' % (en, lambda_from_E(en, E_units='eV'))
667 if abs(en - DEFAULT_XRAY_ENERGY) < 1.0:
668 enmsg = "%s : PROBABLY NOT CORRECT" % enmsg
669 self.wids['X-ray Energy'].SetLabel(enmsg)
672 for name, addr, val in zip(env_names, env_addrs, env_vals):
673 name = bytes2str(name).lower()
674 val = h5str(val)
675 if 'ring_current' in name or 'ring current' in name:
676 self.wids['Ring Current'].SetLabel('%s mA' % val)
677 elif 'beamline.fluxestimate' in name or 'transmitted flux' in name:
678 i0vals['flux'] = val
679 elif 'i0 current' in name:
680 i0vals['current'] = val
682 elif name.startswith('sample'):
683 name = name.replace('samplestage.', '')
684 if 'coarsex' in name or 'coarse x' in name:
685 self.wids['Sample Stage X'].SetLabel('%s mm' % val)
686 elif 'coarsey' in name or 'coarse y' in name:
687 self.wids['Sample Stage Y'].SetLabel('%s mm' % val)
688 elif 'coarsez' in name or 'coarse z' in name:
689 self.wids['Sample Stage Z'].SetLabel('%s mm' % val)
690 elif 'theta' in name:
691 self.wids['Sample Stage Theta'].SetLabel('%s deg' % val)
692 elif 'finex' in name or 'fine x' in name:
693 fines['X'] = val
694 elif 'finey' in name or 'fine y' in name:
695 fines['Y'] = val
697 if i0vals['current'] == '?':
698 i0val = 'Flux=%(flux)s Hz' % i0vals
699 else:
700 i0val = u'Flux=%(flux)s Hz, I0 Current=%(current)s \u03BCA' % i0vals
701 self.wids['X-ray Intensity (I0)'].SetLabel(i0val)
702 self.wids['Sample Fine Stages'].SetLabel('X, Y = %(X)s, %(Y)s mm' % (fines))
704 folderpath = bytes2str(xrmmap.attrs.get('Map_Folder',''))
705 if len(folderpath) > 35:
706 folderpath = '...'+folderpath[-35:]
707 self.wids['Original data path'].SetLabel(folderpath)
709 self.wids['XRD Calibration'].SetLabel('')
710 xrd_calibration = ''
711 if 'xrd1d' in xrmmap:
712 xrd_calibration = bytes2str(xrmmap['xrd1d'].attrs.get('calfile',''))
713 if not Path(xrd_calibration).exists():
714 xrd_calibration = ''
715 self.wids['XRD Calibration'].SetLabel(Path(xrd_calibration).name)
717 notes = {}
718 config_grp = ensure_subgroup('config',xrmmap)
719 notes_grp = ensure_subgroup('notes',config_grp)
720 for key in notes_grp.attrs.keys():
721 try:
722 notes[key] = bytes2str(notes_grp.attrs[key])
723 except:
724 pass
725 note_title = ['Facility','Run Cycle','Proposal Number','User group']
726 note_str = ['','','','']
727 if 'beamline' in notes and 'facility' in notes:
728 note_str[0] = '%s @ %s' % (notes['beamline'],notes['facility'])
729 if 'run' in notes:
730 note_str[1] = notes['run']
731 if 'proposal' in notes:
732 note_str[2] = notes['proposal']
733 if 'user' in notes:
734 note_str[3] = notes['user']
736 for title,note in zip(note_title,note_str):
737 self.wids[title].SetLabel(note)
739 xrmfile.reset_flags()
740 if xrmfile.has_xrf:
741 if xrmfile.has_xrd2d and xrmfile.has_xrd1d:
742 datastr = 'XRF, 2D- and 1D-XRD data'
743 elif xrmfile.has_xrd2d:
744 datastr = 'XRF, 2D-XRD data'
745 elif xrmfile.has_xrd1d:
746 datastr = 'XRF, 1D-XRD data'
747 else:
748 datastr = 'XRF data'
749 else:
750 if xrmfile.has_xrd2d and xrmfile.has_xrd1d:
751 datastr = '2D- and 1D-XRD data'
752 elif xrmfile.has_xrd2d:
753 datastr = '2D-XRD data'
754 elif xrmfile.has_xrd1d:
755 datastr = '1D-XRD data'
756 else:
757 datastr = ''
759 self.wids['Map Data'].SetLabel(datastr)
761 def onClose(self):
762 pass
765class MapAreaPanel(scrolled.ScrolledPanel):
767 label = 'Map Areas'
768 delstr = """ Delete Area '%s'?
770 WARNING: This cannot be undone!
772 """
774 def __init__(self, parent, owner=None, **kws):
775 scrolled.ScrolledPanel.__init__(self, parent, -1,
776 style=wx.GROW|wx.TAB_TRAVERSAL, **kws)
778 ######################################
779 ## GENERAL MAP AREAS
780 self.owner = owner
781 pane = wx.Panel(self)
782 sizer = wx.GridBagSizer(3, 3)
783 self.choices = {}
784 bsize = (CWID, -1)
785 self.choice = Choice(pane, size=(225, -1), action=self.onSelect)
786 self.desc = wx.TextCtrl(pane, -1, '', size=(225, -1))
787 self.info1 = wx.StaticText(pane, -1, '', size=(275, -1))
788 self.info2 = wx.StaticText(pane, -1, '', size=(275, -1))
789 self.onmap = Button(pane, 'Show on Map', size=bsize, action=self.onShow)
790 self.clear = Button(pane, 'Clear Map', size=bsize, action=self.onClear)
791 self.bdelete = Button(pane, 'Delete', size=bsize, action=self.onDelete)
792 self.update = Button(pane, 'Apply', size=bsize, action=self.onLabel)
793 self.bexport = Button(pane, 'Export Areas', size=bsize, action=self.onExport)
794 self.bimport = Button(pane, 'Import Areas', size=bsize, action=self.onImport)
795 self.bcopy = Button(pane, 'Copy to Other Maps', size=bsize, action=self.onCopy)
796 self.xrf = Button(pane, 'Show XRF (Fore)', size=bsize, action=self.onXRF)
797 self.xrf2 = Button(pane, 'Show XRF (Back)', size=bsize,
798 action=partial(self.onXRF, as_mca2=True))
800 self.onstats = Button(pane, 'Calculate XRF Stats', size=bsize,
801 action=self.onShowStats)
802 self.onreport = Button(pane, 'Save XRF Stats', size=bsize,
803 action=self.onReport)
805 self.xrd1d_plot = Button(pane, 'Show 1D XRD', size=bsize,
806 action=partial(self.onXRD, show=True, xrd1d=True))
808 self.xrd2d_plot = Button(pane, 'Show 2D XRD', size=bsize,
809 action=partial(self.onXRD, show=True, xrd2d=True))
811 legend = wx.StaticText(pane, -1, 'Values in Counts per second', size=(200, -1))
813 def txt(s):
814 return SimpleText(pane, s)
815 irow = 1
816 sizer.Add(txt('Map Areas and Saved Points'), ( 0, 0), (1, 5), ALL_CEN, 2)
817 sizer.Add(txt('Area: '), (irow, 0), (1, 1), ALL_LEFT, 2)
818 sizer.Add(self.choice, (irow, 1), (1, 2), ALL_LEFT, 2)
819 sizer.Add(self.bdelete, (irow, 3), (1, 1), ALL_LEFT, 2)
822 irow += 1
823 sizer.Add(txt('Info: '), (irow, 0), (1, 1), ALL_LEFT, 2)
824 sizer.Add(self.info1, (irow, 1), (1, 2), ALL_LEFT, 2)
825 sizer.Add(self.info2, (irow, 3), (1, 2), ALL_LEFT, 2)
827 irow += 1
828 sizer.Add(txt('Rename: '), (irow, 0), (1, 1), ALL_LEFT, 2)
829 sizer.Add(self.desc, (irow, 1), (1, 2), ALL_LEFT, 2)
830 sizer.Add(self.update, (irow, 3), (1, 1), ALL_LEFT, 2)
832 irow += 1
833 sizer.Add(txt('Show: '), (irow, 0), (1, 1), ALL_LEFT, 2)
834 sizer.Add(self.onmap, (irow, 1), (1, 1), ALL_LEFT, 2)
835 sizer.Add(self.clear, (irow, 2), (1, 1), ALL_LEFT, 2)
837 irow += 1
838 sizer.Add(txt('Save: '), (irow, 0), (1, 1), ALL_LEFT, 2)
839 sizer.Add(self.bexport, (irow, 1), (1, 1), ALL_LEFT, 2)
840 sizer.Add(self.bimport, (irow, 2), (1, 1), ALL_LEFT, 2)
841 sizer.Add(self.bcopy, (irow, 3), (1, 1), ALL_LEFT, 2)
843 irow += 1
844 sizer.Add(txt('XRF: '), (irow, 0), (1, 1), ALL_LEFT, 2)
845 sizer.Add(self.xrf, (irow, 1), (1, 1), ALL_LEFT, 2)
846 sizer.Add(self.xrf2, (irow, 2), (1, 1), ALL_LEFT, 2)
847 sizer.Add(self.onstats, (irow, 3), (1, 1), ALL_LEFT, 2)
848 sizer.Add(self.onreport, (irow, 4), (1, 1), ALL_LEFT, 2)
851 irow += 1
852 sizer.Add(txt('XRD: '), (irow, 0), (1, 1), ALL_LEFT, 2)
853 sizer.Add(self.xrd1d_plot, (irow, 1), (1, 1), ALL_LEFT, 2)
854 sizer.Add(self.xrd2d_plot, (irow, 2), (1, 1), ALL_LEFT, 2)
856 # sizer.Add(self.xrd1d_save, (irow, 0), (1, 2), ALL_LEFT, 2)
857 # sizer.Add(self.xrd2d_save, (irow, 2), (1, 2), ALL_LEFT, 2)
858 irow += 1
859 sizer.Add(legend, (irow, 1), (1, 2), ALL_LEFT, 2)
860 pack(pane, sizer)
862 for btn in (self.xrd1d_plot, self.xrd2d_plot):
863 btn.Disable()
865 # main sizer
866 msizer = wx.BoxSizer(wx.VERTICAL)
867 msizer.Add(pane, 0, wx.ALIGN_LEFT|wx.ALL, 1)
869 msizer.Add(wx.StaticLine(self, size=(375, 2), style=wx.LI_HORIZONTAL),
870 0, wx.EXPAND|wx.ALL, 1)
872 self.report = None
873 rep = self.report = dv.DataViewListCtrl(self, style=DVSTY)
874 rep.AppendTextColumn('ROI ', width=150)
875 rep.AppendTextColumn('Min', width=90)
876 rep.AppendTextColumn('Max', width=90)
877 rep.AppendTextColumn('Mean ', width=90)
878 rep.AppendTextColumn('Sigma', width=90)
879 rep.AppendTextColumn('Median', width=90)
880 rep.AppendTextColumn('Mode', width=90)
881 for col in range(7):
882 align = wx.ALIGN_RIGHT
883 if col == 0: align = wx.ALIGN_LEFT
884 rep.Columns[col].Sortable = False
885 rep.Columns[col].Renderer.Alignment = align
886 rep.Columns[col].Alignment = align
888 rep.SetMinSize((800, 300))
889 msizer.Add(rep, 1, wx.ALIGN_LEFT|wx.ALL, 1)
891 pack(self, msizer)
892 self.SetupScrolling()
894 def onCopy(self, event=None):
895 xrmfile = self.owner.current_file
896 xrmmap = xrmfile.xrmmap
897 print("Copy Area : shape", xrmfile, xrmmap.shape)
899 def show_stats(self):
900 # self.stats = self.xrmfile.get_area_stats(self.areaname)
901 if self.report is None:
902 return
904 self.report.DeleteAllItems()
905 self.report_data = []
907 def report_info(dname,d):
908 try:
909 hmean, gmean = stats.gmean(d), stats.hmean(d)
910 skew, kurtosis = stats.skew(d), stats.kurtosis(d)
911 except ValueError:
912 hmean, gmean, skew, kurtosis = 0, 0, 0, 0
914 smode = '--'
915 fmt = '{:,.1f}'.format # use thousands commas, 1 decimal place
916 mode = stats.mode(d)
917 if len(mode) > 0:
918 mode = mode[0]
919 if len(mode) > 0:
920 smode = fmt(mode[0])
921 dat = (dname, fmt(d.min()), fmt(d.max()), fmt(d.mean()),
922 fmt(d.std()), fmt(np.median(d)), smode)
923 self.report_data.append(dat)
924 self.report.AppendItem(dat)
926 areaname = self._getarea()
927 xrmfile = self.owner.current_file
928 xrmmap = xrmfile.xrmmap
929 ctime = xrmfile.pixeltime
931 area = xrmfile.get_area(name=areaname)
932 amask = area[()]
934 def match_mask_shape(det, mask):
935 if mask.shape[1] == det.shape[1] - 2: # hotcols
936 det = det[:,1:-1]
937 if mask.shape[0] < det.shape[0]:
938 det = det[:mask.shape[0]]
939 return det[mask]
941 if 'roistats' in area.attrs:
942 for dat in json.loads(area.attrs.get('roistats','')):
943 dat = tuple(dat)
944 self.report_data.append(dat)
945 self.report.AppendItem(dat)
946 self.choice.Enable()
947 return
949 version = xrmmap.attrs.get('Version','1.0.0')
951 if version_ge(version, '2.0.0'):
952 d_pref = 'mca'
953 d_scas = [d for d in xrmmap['scalars']]
954 det_list = xrmfile.get_detector_list()
955 detnames = [x for x in det_list if d_pref in x]
956 d_rois = xrmfile.get_roi_list(detnames[0])
958 else:
959 d_addrs = [d.lower() for d in xrmmap['roimap/det_address']]
960 d_names = [d for d in xrmmap['roimap/det_name']]
961 d_pref = 'det'
963 # MNREAL
964 #for i in range(1, xrmfile.nmca+1):
965 # tname = '%s%i/realtime' % (d_pref, i)
966 # rtime = xrmmap[tname][()]
967 # if amask.shape[1] == rtime.shape[1] - 2: # hotcols
968 # rtime = rtime[:,1:-1]
970 if version_ge(version, '2.0.0'):
971 for scalar in d_scas:
972 d = xrmmap['scalars'][scalar][()]
973 d = match_mask_shape(d, amask)
974 report_info(scalar, d/ctime)
976 for roi in d_rois:
977 for det in detnames:
978 d = xrmfile.get_roimap(roi, det=det, dtcorrect=False)
979 d = match_mask_shape(d, amask)
980 report_info('%s (%s)' % (roi, det), d/ctime)
982 else:
983 for idet, dname in enumerate(d_names):
984 try:
985 daddr = h5str(d_addrs[idet])
986 except IndexError:
987 break
988 if 'mca' in daddr:
989 det = 1
990 words = daddr.split('mca')
991 if len(words) > 1:
992 det = int(words[1].split('.')[0])
994 d = xrmmap['roimap/det_raw'][:,:,idet]
995 d = match_mask_shape(d, amask)
996 report_info(dname, d/ctime)
998 if 'roistats' not in area.attrs:
999 area.attrs['roistats'] = json.dumps(self.report_data)
1000 xrmfile.h5root.flush()
1002 def update_xrmmap(self, xrmfile=None, set_detectors=None):
1003 if xrmfile is None: xrmfile = self.owner.current_file
1004 xrmmap = xrmfile.xrmmap
1005 self.set_area_choices(xrmmap, show_last=True)
1006 self.set_enabled_btns(xrmfile=xrmfile)
1007 self.report.DeleteAllItems()
1008 self.report_data = []
1009 try:
1010 self.onSelect()
1011 except:
1012 pass
1014 def set_enabled_btns(self, xrmfile=None):
1015 if xrmfile is None:
1016 xrmfile = self.owner.current_file
1018 xrmfile.reset_flags()
1019 self.xrd2d_plot.Enable(xrmfile.has_xrd1d)
1020 self.xrd1d_plot.Enable(xrmfile.has_xrd1d)
1022 def clear_area_choices(self):
1024 self.info1.SetLabel('')
1025 self.info2.SetLabel('')
1026 self.desc.SetValue('')
1027 self.choice.Clear()
1029 def set_area_choices(self, xrmmap, show_last=False):
1031 self.clear_area_choices()
1033 areas = xrmmap['areas']
1035 c = self.choice
1036 c.Clear()
1037 self.choices = {}
1038 choice_labels = []
1039 for a in areas:
1040 desc = bytes2str(areas[a].attrs.get('description', a))
1041 self.choices[desc] = a
1042 choice_labels.append(desc)
1044 c.AppendItems(choice_labels)
1045 this_label = ''
1046 if len(self.choices) > 0:
1047 idx = 0
1048 if show_last:
1049 idx = len(self.choices)-1
1050 try:
1051 this_label = choice_labels[idx]
1052 except:
1053 return
1054 c.SetStringSelection(this_label)
1055 self.desc.SetValue(this_label)
1058 def onReport(self, event=None):
1059 aname = self._getarea()
1060 fname = Path(self.owner.current_file.filename).fname
1061 deffile = f'{fname}_{aname}'
1062 deffile = deffile.replace('.', '_') + '.dat'
1063 outfile = FileSave(self, 'Save Area XRF Statistics File',
1064 default_file=deffile,
1065 wildcard=FILE_WILDCARDS)
1067 if outfile is None:
1068 return
1070 area = self.owner.current_file.xrmmap['areas/%s' % aname]
1071 npix = area[()].sum()
1072 pixtime = self.owner.current_file.pixeltime
1074 mca = self.owner.current_file.get_mca_area(aname)
1075 dtime = mca.real_time
1076 info_fmt = '%i Pixels, %i ms/pixel, %.3f total seconds'
1077 buff = ['# Map %s, Area %s' % (self.owner.current_file.filename, aname),
1078 '# %i Pixels' % npix,
1079 '# %i ms per pixel' % int(round(1000.0*pixtime)),
1080 '# %.3f total seconds' % dtime,
1081 '# Time (TSCALER) in ms',
1082 '# All other values in counts per second',
1083 '#----------------------------------',
1084 '# ROI Min Max Mean Sigma Median Mode']
1085 for dat in self.report_data:
1086 buff.append(' '.join(dat))
1087 buff.append('')
1088 try:
1089 fout = open(outfile, 'w', encoding=sys.getdefaultencoding())
1090 fout.write('\n'.join(buff))
1091 fout.close()
1092 except IOError:
1093 print('could not write %s' % outfile)
1095 def _getarea(self):
1096 return self.choices[self.choice.GetStringSelection()]
1098 def onExport(self, event=None):
1099 ofile = self.owner.current_file.export_areas()
1100 self.owner.message('Exported Areas to %s' % ofile)
1102 def onImport(self, event=None):
1103 wildcards = 'Area Files (*_Areas.npz)|*_Areas.npz|All files (*.*)|*.*'
1104 dlg = wx.FileDialog(self, message='Read Areas File',
1105 defaultDir=get_cwd(),
1106 wildcard=wildcards, style=wx.FD_OPEN)
1108 if dlg.ShowModal() == wx.ID_OK:
1109 fname = dlg.GetPath().replace('\\', '/')
1110 self.owner.current_file.import_areas(fname)
1111 self.owner.message('Imported Areas from %s' % fname)
1112 self.set_area_choices(self.owner.current_file.xrmmap)
1113 self.onSelect()
1115 def onSelect(self, event=None):
1116 try:
1117 aname = self._getarea()
1118 except:
1119 return
1120 area = self.owner.current_file.xrmmap['areas/%s' % aname]
1121 npix = area[()].sum()
1122 yvals, xvals = np.where(area[()])
1123 pixtime = self.owner.current_file.pixeltime
1124 dtime = npix*pixtime
1125 info1_fmt = '%i Pixels, %.3f seconds'
1126 info2_fmt = ' Range (pixels) X: [%i:%i], Y: [%i:%i] '
1127 self.info1.SetLabel(info1_fmt % (npix, dtime))
1128 self.info2.SetLabel(info2_fmt % (xvals.min(), xvals.max(),
1129 yvals.min(), yvals.max()))
1131 self.desc.SetValue(area.attrs.get('description', aname))
1132 self.report.DeleteAllItems()
1133 self.report_data = []
1134 if 'roistats' in area.attrs:
1135 self.show_stats()
1137 def onShowStats(self, event=None):
1138 if self.report is None:
1139 return
1140 self.show_stats()
1142 def onLabel(self, event=None):
1143 aname = self._getarea()
1144 area = self.owner.current_file.xrmmap['areas/%s' % aname]
1145 new_label = str(self.desc.GetValue())
1146 area.attrs['description'] = new_label
1147 self.owner.current_file.h5root.flush()
1148 self.set_area_choices(self.owner.current_file.xrmmap)
1149 self.choice.SetStringSelection(new_label)
1150 self.desc.SetValue(new_label)
1152 def onShow(self, event=None):
1153 aname = self._getarea()
1154 area = self.owner.current_file.xrmmap['areas'][aname]
1155 label = bytes2str(area.attrs.get('description', aname))
1157 if len(self.owner.tomo_displays) > 0:
1158 imd = self.owner.tomo_displays[-1]
1159 try:
1160 imd.add_highlight_area(area[()], label=label)
1161 except:
1162 pass
1164 if len(self.owner.im_displays) > 0:
1165 imd = self.owner.im_displays[-1]
1166 h, w = self.owner.current_file.get_shape()
1167 highlight = np.zeros((h, w))
1169 highlight[np.where(area[()])] = 1
1170 imd.panel.add_highlight_area(highlight, label=label)
1172 def onDone(self, event=None):
1173 self.Destroy()
1175 def onDelete(self, event=None):
1176 aname = self._getarea()
1177 erase = (wx.ID_YES == Popup(self.owner, self.delstr % aname,
1178 'Delete Area?', style=wx.YES_NO))
1180 if erase:
1181 xrmmap = self.owner.current_file.xrmmap
1182 del xrmmap['areas/%s' % aname]
1184 self.set_area_choices(xrmmap)
1186 self.onSelect()
1188 def onClear(self, event=None):
1189 if len(self.owner.im_displays) > 0:
1190 imd = self.owner.im_displays[-1]
1191 try:
1192 for area in imd.panel.conf.highlight_areas:
1193 for w in area.collections + area.labelTexts:
1194 w.remove()
1195 imd.panel.conf.highlight_areas = []
1196 imd.panel.redraw()
1197 except:
1198 pass
1200 if len(self.owner.tomo_displays) > 0:
1201 imd = self.owner.tomo_displays[-1]
1202 try:
1203 imd.clear_highlight_area()
1204 except:
1205 pass
1207 def onXRF(self, event=None, as_mca2=False):
1208 aname = self._getarea()
1209 xrmfile = self.owner.current_file
1210 area = xrmfile.xrmmap['areas/%s' % aname]
1212 label = bytes2str(area.attrs.get('description', aname))
1213 self._mca = None
1214 self.owner.message("Getting XRF Spectra for area '%s'..." % aname)
1215 def _getmca_area(aname):
1216 o = self.owner
1217 self._mca = o.current_file.get_mca_area(aname,
1218 dtcorrect=o.dtcor)
1219 mca_thread = Thread(target=_getmca_area, args=(aname,))
1220 mca_thread.start()
1221 self.owner.show_XRFDisplay()
1222 mca_thread.join()
1224 fname = Path(self.owner.current_file.filename).name
1226 npix = area[()].sum()
1227 self._mca.filename = fname
1228 self._mca.title = label
1229 self._mca.npixels = npix
1230 self.owner.message(f"Plotting XRF Spectra for area '{aname}'...")
1231 self.owner.subframes['xrfdisplay'].add_mca(self._mca, label=f"{fname}:{label}",
1232 plot=not as_mca2)
1233 if as_mca2:
1234 self.owner.subframes['xrfdisplay'].swap_mcas()
1236 def onXRD(self, event=None, save=False, show=False,
1237 xrd1d=False, xrd2d=False, verbose=True):
1238 try:
1239 aname = self._getarea()
1240 xrmfile = self.owner.current_file
1241 area = xrmfile.xrmmap['areas/%s' % aname]
1243 title = area.attrs.get('description', aname)
1245 env_names = list(xrmfile.xrmmap['config/environ/name'])
1246 env_vals = list(xrmfile.xrmmap['config/environ/value'])
1247 for name, val in zip(env_names, env_vals):
1248 if 'mono.energy' in str(name).lower():
1249 energy = float(val)/1000.
1250 except:
1251 if verbose:
1252 print('No map file and/or areas specified.')
1253 return
1255 xrmfile.reset_flags()
1256 if not xrmfile.has_xrd1d and not xrmfile.has_xrd2d:
1257 if verbose:
1258 print('No XRD data in map file: %s' % self.owner.current_file.filename)
1259 return
1261 ponifile = bytes2str(xrmfile.xrmmap['xrd1d'].attrs.get('calfile',''))
1262 ponifile = ponifile if Path(ponifile).exists() else None
1264 if show:
1265 self.owner.message(f"Plotting XRD pattern for '{title}'")
1266 if save:
1267 self.owner.message(f"Saving XRD pattern for '{title}'")
1268 stem = Path(self.owner.current_file.filename).name
1269 stem = f"{stem}_{title}"
1271 energy = 0.001*xrmfile.get_incident_energy()
1272 kwargs = dict(filename=self.owner.current_file.filename,
1273 npixels=area[()].sum(), energy=energy,
1274 calfile=ponifile, title=title, xrd2d=False)
1276 if xrd1d and xrmfile.has_xrd1d:
1277 self._xrd = xrmfile.get_xrd1d_area(aname, **kwargs)
1279 if show:
1280 label = f'{Path(self._xrd.filename).name}: {title}'
1281 self.owner.display_xrd1d(self._xrd.data1D, self._xrd.q,
1282 self._xrd.energy, label=label)
1283 if save:
1284 wildcards = '1D XRD file (*.xy)|*.xy|All files (*.*)|*.*'
1285 dlg = wx.FileDialog(self, 'Save file as...',
1286 defaultDir=get_cwd(),
1287 defaultFile='%s.xy' % stem,
1288 wildcard=wildcards,
1289 style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1290 if dlg.ShowModal() == wx.ID_OK:
1291 filename = dlg.GetPath().replace('\\', '/')
1293 dlg.Destroy()
1295 print('\nSaving 1D XRD in file: %s' % (filename))
1296 save1D(filename, self._xrd.data1D[0], self._xrd.data1D[1], calfile=ponifile)
1298 ## turns off flag since it has already been displayed/saved
1299 xrd1d = False
1302 if xrd2d:
1303 print("Looking for 2D XRD Data")
1304 try:
1305 _xrd = xrmfile.get_xrd2d_area(aname, **kwargs)
1306 except:
1307 _xrd = None
1308 if _xrd is None:
1309 print("no 2D XRD Data")
1310 return
1312 label = f'{Path(_xrd.filename).name}: {title}'
1313 self.owner.display_xrd2d(_xrd.data2D, label=label, xrmfile=xrmfile)
1315 wildcards = '2D XRD file (*.tiff)|*.tif;*.tiff;*.edf|All files (*.*)|*.*'
1316 fname = xrmfile.filename + '_' + aname
1317 #dlg = wx.FileDialog(self, 'Save file as...',
1318 # defaultDir=get_cwd(),
1319 # defaultFile='%s.tiff' % fname,
1320 # wildcard=wildcards,
1321 # style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1322 #if dlg.ShowModal() == wx.ID_OK:
1323 # filename = Path(dlg.GetPath()).absolute().as_posix()
1324 # _xrd.save_2D(file=filename, verbose=True)
1325 # dlg.Destroy()
1328class MapViewerFrame(wx.Frame):
1329 cursor_menulabels = {'lasso': ('Select Points for XRF Spectra\tCtrl+X',
1330 'Left-Drag to select points for XRF Spectra')}
1332 def __init__(self, parent=None, filename=None, _larch=None, title=None,
1333 use_scandb=False, check_version=True, size=(925, 650),
1334 **kwds):
1336 if check_version:
1337 def check_version():
1338 self.vinfo = check_larchversion()
1339 version_thread = Thread(target=check_version)
1340 version_thread.start()
1342 kwds['style'] = wx.DEFAULT_FRAME_STYLE
1343 wx.Frame.__init__(self, parent, -1, size=size, **kwds)
1345 self.data = None
1346 self.use_scandb = use_scandb
1347 self.filemap = {}
1348 self.im_displays = []
1349 self.tomo_displays = []
1350 self.plot_displays = []
1351 self.current_file = None
1353 self.larch_buffer = parent
1354 if not isinstance(parent, LarchFrame):
1355 self.larch_buffer = LarchFrame(_larch=_larch, is_standalone=False, with_raise=False)
1357 self.larch = self.larch_buffer.larchshell
1359 self.subframes = {'xrfdisplay': None,
1360 'xrd1d': None}
1361 self.watch_files = False
1363 self.files_in_progress = []
1365 # self.hotcols = False
1366 self.dtcor = True
1367 self.showxrd = False
1369 if title is None:
1370 title = "XRF Map Viewing and Analysis"
1371 self.SetTitle(title)
1373 self.createMainPanel()
1374 self.SetFont(Font(FONTSIZE))
1376 self.createMenus()
1377 self.statusbar = self.CreateStatusBar(2, 0)
1378 self.statusbar.SetStatusWidths([-3, -1])
1379 statusbar_fields = ['Initializing....', ' ']
1380 for i in range(len(statusbar_fields)):
1381 self.statusbar.SetStatusText(statusbar_fields[i], i)
1383 self.htimer = wx.Timer(self)
1384 self.Bind(wx.EVT_TIMER, self.onTimer, self.htimer)
1385 self.h5convert_done = True
1386 self.h5convert_irow = 0
1387 self.h5convert_nrow = 0
1389 read_workdir('gsemap.dat')
1390 self.onFolderSelect()
1391 self.statusbar.SetStatusText('Set Working Folder', 0)
1393 w0, h0 = self.GetSize()
1394 w1, h1 = self.GetBestSize()
1395 self.SetSize((max(w0, w1)+5, max(h0, h1)+5))
1396 self.SetMinSize((500, 300))
1397 self.Show()
1399 self.scandb = None
1400 self.instdb = None
1401 self.inst_name = None
1402 self.move_callback = None
1405 self.init_larch()
1406 self.statusbar.SetStatusText('ready', 0)
1407 self.Raise()
1410 if filename is not None:
1411 self.onRead(filename)
1413 if check_version:
1414 version_thread.join()
1415 if self.vinfo is not None:
1416 if self.vinfo.update_available:
1417 self.onCheckforUpdates()
1418 self.statusbar.SetStatusText(f'Larch Version {self.vinfo.remote_version} is available!', 0)
1419 self.statusbar.SetStatusText(f'Larch Version {self.vinfo.local_version}', 1)
1420 else:
1421 self.statusbar.SetStatusText(f'Larch Version {self.vinfo.local_version} (latest)', 1)
1424 def CloseFile(self, filename, event=None):
1425 if filename in self.filemap:
1426 self.filemap[filename].close()
1427 self.filemap.pop(filename)
1429 def createMainPanel(self):
1430 splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
1431 splitter.SetMinimumPaneSize(250)
1433 self.filelist = EditableListBox(splitter, self.ShowFile,
1434 remove_action=self.CloseFile,
1435 size=(250, -1))
1437 dpanel = self.detailspanel = wx.Panel(splitter)
1438 self.createNBPanels(dpanel)
1439 splitter.SplitVertically(self.filelist, self.detailspanel, 1)
1440 sizer = wx.BoxSizer(wx.VERTICAL)
1441 sizer.Add(splitter, 1, wx.GROW|wx.ALL, 5)
1442 pack(self, sizer)
1443 fico = Path(icondir, XRF_ICON_FILE).absolute().as_posix()
1444 try:
1445 self.SetIcon(wx.Icon(fico, wx.BITMAP_TYPE_ICO))
1446 except:
1447 pass
1449 def createNBPanels(self, parent):
1450 self.title = SimpleText(parent, ' ', size=(680, -1))
1452 self.SetBackgroundColour('#F0F0E8')
1454 nbpanels = {}
1455 for panel in (MapPanel, MapInfoPanel, MapAreaPanel, MapMathPanel,
1456 TomographyPanel, XRFAnalysisPanel):
1457 nbpanels[panel.label] = panel
1458 self.nb = flatnotebook(parent, nbpanels, panelkws={'owner':self},
1459 on_change=self.onNBChanged)
1460 self.roimap_panel = self.nb.GetPage(0)
1461 sizer = wx.BoxSizer(wx.VERTICAL)
1462 sizer.Add(self.title, 0, ALL_CEN)
1463 sizer.Add(self.nb, 1, wx.ALL|wx.EXPAND)
1464 parent.SetSize((700, 400))
1465 pack(parent, sizer)
1467 def onNBChanged(self, event=None):
1468 cb = getattr(self.nb.GetCurrentPage(), 'update_xrmmap', None)
1469 if callable(cb):
1470 cb()
1472 def get_mca_area(self, mask, xoff=0, yoff=0, det=None, xrmfile=None):
1473 if xrmfile is None:
1474 xrmfile = self.current_file
1476 if xrmfile.write_access:
1477 aname = xrmfile.add_area(mask)
1478 self.sel_mca = xrmfile.get_mca_area(aname, det=det)
1479 else:
1480 dgroup = xrmfile.get_detname(det)
1481 _ay, _ax = np.where(mask)
1482 ymin, ymax, xmin, xmax = _ay.min(), _ay.max()+1, _ax.min(), _ax.max()+1
1483 opts = {'dtcorrect': None, 'det': det}
1484 counts = xrmfile.get_counts_rect(ymin, ymax, xmin, xmax, det=det)
1485 ltime, rtime = xrmfile.get_livereal_rect(ymin, ymax, xmin,
1486 xmax, det=det)
1487 ltime = ltime[mask[ymin:ymax, xmin:xmax]].sum()
1488 rtime = rtime[mask[ymin:ymax, xmin:xmax]].sum()
1489 counts = counts[mask[ymin:ymax, xmin:xmax]]
1490 while(len(counts.shape) > 1):
1491 counts = counts.sum(axis=0)
1492 self.sel_mca = xrmfile._getmca(dgroup, counts, 'selected area',
1493 npixels=mask.sum(),
1494 real_time=rtime, live_time=ltime)
1497 def lassoHandler(self, mask=None, xrmfile=None, xoff=0, yoff=0,
1498 det=None, **kws):
1499 if xrmfile is None:
1500 xrmfile = self.current_file
1502 ny, nx = xrmfile.get_shape()
1503 if mask.sum() < 1:
1504 return
1506 if (xoff>0 or yoff>0) or mask.shape != (ny, nx):
1507 if mask.shape == (nx, ny): ## sinogram
1508 mask = np.swapaxes(mask,0,1)
1509 # elif mask.shape == (ny, ny) or mask.shape == (nx, nx): ## tomograph
1510 # tomo = True
1511 else:
1512 ym, xm = mask.shape
1513 tmask = np.zeros((ny, nx)).astype(bool)
1514 xmax = min(nx, xm+xoff)
1515 for iy in range(ym):
1516 if iy+yoff < ny:
1517 tmask[iy+yoff, xoff:xmax] = mask[iy]
1518 mask = tmask
1520 kwargs = dict(xrmfile=xrmfile, xoff=xoff, yoff=yoff, det=det)
1521 mca_thread = Thread(target=self.get_mca_area,
1522 args=(mask,), kwargs=kwargs)
1523 mca_thread.start()
1524 self.show_XRFDisplay()
1525 mca_thread.join()
1526 if hasattr(self, 'sel_mca'):
1527 fname = Path(xrmfile.filename).name
1528 aname = self.sel_mca.areaname
1529 if self.sel_mca.npixels is None:
1530 try:
1531 area = xrmfile.xrmmap['areas/%s' % aname]
1532 npix = area[()].sum()
1533 self.sel_mca.npixels = npix
1534 except:
1535 pass
1537 if self.sel_mca.npixels is None:
1538 self.sel_mca.npixels = 0
1539 self.sel_mca.filename = fname
1540 self.sel_mca.title = aname
1541 self.subframes['xrfdisplay'].add_mca(self.sel_mca, label='%s:%s'% (fname, aname),
1542 plot=True)
1543 self.subframes['xrfdisplay'].roi_callback = self.UpdateROI
1544 update_xrmmap = getattr(self.nb.GetCurrentPage(), 'update_xrmmap', None)
1545 if callable(update_xrmmap):
1546 update_xrmmap(xrmfile=self.current_file)
1548 if self.showxrd:
1549 for page in self.nb.pagelist:
1550 if hasattr(page, 'onXRD'):
1551 page.onXRD(show=True, xrd1d=True,verbose=False)
1553 def show_subframe(self, name, frameclass, **opts):
1554 shown = False
1555 if name in self.subframes:
1556 try:
1557 self.subframes[name].Raise()
1558 shown = True
1559 except:
1560 del self.subframes[name]
1561 if not shown:
1562 self.subframes[name] = frameclass(self, **opts)
1564 def show_XRD1D(self, event=None):
1565 self.show_subframe('xrd1d', XRD1DFrame, _larch=self.larch)
1567 def show_XRFDisplay(self, do_raise=True, clear=True, xrmfile=None):
1568 'make sure XRF plot frame is enabled and visible'
1569 if xrmfile is None:
1570 xrmfile = self.current_file
1571 self.show_subframe('xrfdisplay', XRFDisplayFrame,
1572 parent=self.larch_buffer,
1573 roi_callback=self.UpdateROI)
1575 self.subframes['xrfdisplay'].Show()
1576 if do_raise:
1577 self.subframes['xrfdisplay'].Raise()
1578 if clear:
1579 self.subframes['xrfdisplay'].panel.clear()
1580 self.subframes['xrfdisplay'].panel.reset_config()
1582 def onMoveToPixel(self, xval, yval):
1583 if not HAS_EPICS:
1584 return
1586 xrmmap = self.current_file.xrmmap
1587 pos_addrs = [str(x) for x in xrmmap['config/positioners'].keys()]
1588 pos_label = [str(x[()]) for x in xrmmap['config/positioners'].values()]
1590 pos1 = h5str(xrmmap['config/scan/pos1'][()])
1591 pos2 = h5str(xrmmap['config/scan/pos2'][()])
1592 i1 = pos_addrs.index(pos1)
1593 i2 = pos_addrs.index(pos2)
1594 msg = '%s(%s) = %.4f, %s(%s) = %.4f?' % (pos_label[i1], pos_addrs[i1], xval,
1595 pos_label[i2], pos_addrs[i2], yval)
1597 if (wx.ID_YES == Popup(self, 'Really move stages to\n %s?' % msg,
1598 'move stages to pixel?', style=wx.YES_NO)):
1599 caput(pos_addrs[i1], xval)
1600 caput(pos_addrs[i2], yval)
1602 def onSavePixel(self, name, ix, iy, x=None, y=None, title=None, xrmfile=None):
1603 'save pixel as area, and perhaps to scandb'
1604 if x is None:
1605 x = float(xrmfile.get_pos(0, mean=True)[ix])
1606 if y is None:
1607 y = float(xrmfile.get_pos(1, mean=True)[iy])
1609 if len(name) < 1:
1610 return
1611 if xrmfile is None:
1612 xrmfile = self.current_file
1614 # first, create 1-pixel mask for area, and save that
1615 ny, nx = xrmfile.get_shape()
1616 tmask = np.zeros((ny, nx)).astype(bool)
1617 tmask[int(iy), int(ix)] = True
1618 xrmfile.add_area(tmask, name=name)
1619 # for page in self.nb.pagelist:
1620 # if hasattr(page, 'update_xrmmap'):
1621 # page.update_xrmmap(xrmfile=xrmfile)
1622 update_xrmmap = getattr(self.nb.GetCurrentPage(), 'update_xrmmap', None)
1623 if callable(update_xrmmap):
1624 update_xrmmap(xrmfile=xrmfile)
1626 # show position on map
1627 self.im_displays[-1].panel.add_highlight_area(tmask, label=name)
1629 # make sure we can save position into database
1630 if self.scandb is None or self.instdb is None:
1631 return
1632 samplestage = self.instdb.get_instrument(self.inst_name)
1633 if samplestage is None:
1634 return
1635 pvmap = dict([(r.id, r.name) for r in self.scandb.get_rows('pv')])
1637 pv_rows = self.scandb.get_rows('instrument_pv',
1638 where={'instrument_id': samplestage.id})
1640 allpvs = []
1641 for row in pv_rows:
1642 for pvid, pvname in pvmap.items():
1643 if pvid == row.pv_id:
1644 allpvs.append(pvname)
1646 pvn = pv_fullname
1647 conf = xrmfile.xrmmap['config']
1648 pos_addrs = [pvn(h5str(tval)) for tval in conf['positioners']]
1649 env_addrs = [pvn(h5str(tval)) for tval in conf['environ/address']]
1650 env_vals = [h5str(tval) for tval in conf['environ/value']]
1652 position = {}
1653 for pv in allpvs:
1654 position[pv] = None
1656 for addr, val in zip(env_addrs, env_vals):
1657 if addr in allpvs:
1658 position[addr] = float(val)
1660 position[pvn(h5str(conf['scan/pos1'][()]))] = x
1661 position[pvn(h5str(conf['scan/pos2'][()]))] = y
1663 notes = {'source': '%s: %s' % (xrmfile.filename, name)}
1664 self.instdb.save_position(self.inst_name, name, position,
1665 notes=json.dumps(notes))
1668 def add_tomodisplay(self, title, det=None, _lassocallback=True):
1670 if _lassocallback:
1671 lasso_cb = partial(self.lassoHandler, det=det)
1672 else:
1673 lasso_cb = None
1675 imframe = MapImageFrame(output_title=title,
1676 lasso_callback=lasso_cb)
1678 self.tomo_displays.append(imframe)
1680 def display_tomo(self, tomo, title='', info='', x=None, y=None, xoff=0,
1681 yoff=0, det=None, subtitles=None, xrmfile=None,
1682 _lassocallback=True):
1684 displayed = False
1685 if _lassocallback:
1686 lasso_cb = partial(self.lassoHandler, det=det, xrmfile=xrmfile)
1687 else:
1688 lasso_cb = None
1690 while not displayed:
1691 try:
1692 tmd = self.tomo_displays.pop()
1693 clevel = tmd.panel.conf.contrast_level
1694 if clevel in (0, None):
1695 clevel = 0.5
1696 tmd.display(tomo, title=title, subtitles=subtitles,
1697 contrast_level=clevel)
1698 tmd.lasso_callback = lasso_cb
1699 displayed = True
1700 except IndexError:
1701 tmd = MapImageFrame(output_title=title,
1702 lasso_callback=lasso_cb)
1703 tmd.display(tomo, title=title, subtitles=subtitles,
1704 contrast_level=0.5)
1705 displayed = True
1706 except:
1707 displayed = False
1708 self.tomo_displays.append(tmd)
1709 tmd.SetStatusText(info, 1)
1710 tmd.Show()
1711 tmd.Raise()
1713 def add_imdisplay(self, title, det=None):
1714 imd = MapImageFrame(output_title=title,
1715 lasso_callback=partial(self.lassoHandler, det=det),
1716 cursor_labels=self.cursor_menulabels,
1717 save_callback=self.onSavePixel)
1718 self.im_displays.append(imd)
1719 return imd
1721 def display_map(self, map, title='', info='', x=None, y=None, xoff=0, yoff=0,
1722 det=None, subtitles=None, xrmfile=None, with_savepos=True):
1723 """display a map in an available image display"""
1724 if xrmfile is None:
1725 hotcols = False
1726 else:
1727 hotcols = xrmfile.hotcols
1729 if x is not None:
1730 zigzag = abs(xrmfile.zigzag)
1731 if zigzag != 0:
1732 x = x[zigzag:-zigzag]
1733 elif hotcols and map.shape[1] != x.shape[0]:
1734 x = x[1:-1]
1736 dopts = dict(title=title, x=x, y=y, xoff=xoff, yoff=yoff,
1737 det=det, subtitles=subtitles,
1738 xrmfile=xrmfile, with_savepos=with_savepos)
1739 displayed = False
1740 while not displayed:
1741 if 'contrast_level' not in dopts:
1742 dopts['contrast_level'] = 0.5
1743 if len(self.im_displays) == 0:
1744 imd = self.add_imdisplay(title=title, det=det)
1745 imd.display(map, **dopts)
1746 else:
1747 try:
1748 imd = self.im_displays[-1]
1749 if imd.panel.conf.contrast_level not in (0, None):
1750 dopts['contrast_level'] = imd.panel.conf.contrast_level
1751 imd.display(map, **dopts)
1752 displayed = True
1753 except IndexError:
1754 pass
1755 except:
1756 self.im_displays.pop()
1757 imd.SetStatusText(info, 1)
1758 imd.Show()
1759 imd.Raise()
1761 def display_xrd2d(self, map, label='image 0', xrmfile=None, flip=True):
1762 '''
1763 displays 2D XRD pattern in diFFit viewer
1764 '''
1765 if xrmfile is None:
1766 xrmfile = self.current_file
1767 calfile = bytes2str(xrmfile.xrmmap['xrd1d'].attrs.get('calfile',''))
1768 energy = xrmfile.get_incident_energy()
1770 if len(calfile) < 2 or not Path(calfile).exists():
1771 tfile = Path(xrmfile.folder, 'XRD.poni')
1772 if tfile.exists():
1773 calfile = tfile.as_posix()
1774 if Path(calfile).exists():
1775 self.current_file.xrmmap['xrd1d'].attrs['calfile'] = calfile
1777 self.show_XRD1D()
1778 self.subframes['xrd1d'].flip = 'vertical' if flip is True else False
1779 self.subframes['xrd1d'].set_wavelength(PLANCK_HC/energy)
1780 self.subframes['xrd1d'].calfile = calfile
1781 self.subframes['xrd1d'].set_ponifile(calfile)
1782 self.subframes['xrd1d'].display_xrd_image(map, label=label)
1783 self.subframes['xrd1d'].Show()
1785 def display_xrd1d(self, counts, q, energy, label='dataset 0', xrmfile=None):
1786 '''
1787 displays 1D XRD pattern in diFFit viewer
1788 '''
1789 wavelength = lambda_from_E(energy, E_units='keV')
1790 xdat = xrd1d(label=label, energy=energy, wavelength=wavelength)
1791 xdat.set_xy_data(np.array([q, counts]), 'q')
1793 xrmfile = self.current_file
1794 ponidata = json.loads(bytes2str(xrmfile.xrmmap['xrd1d'].attrs.get('caldata','{}')))
1795 if 'rot1' not in ponidata: # invalid poni data
1796 ponifile = bytes2str(xrmfile.xrmmap['xrd1d'].attrs.get('calfile',''))
1797 if len(ponifile) < 2 or not Path(ponifile).exists():
1798 t_ponifile = Path(xrmfile.folder, 'XRD.poni').absolute()
1799 if t_ponifile.exists():
1800 ponifile = t_ponifile.as_posix()
1801 if len(ponifile) > 1:
1802 ponidata = read_poni(ponifile)
1803 if 'rot1' in ponidata:
1804 xrmfile.xrmmap['xrd1d'].attrs['caldata'] = json.dumps(ponidata)
1805 self.show_XRD1D()
1806 self.subframes['xrd1d'].set_wavelength(wavelength)
1807 if 'rot1' in ponidata:
1808 self.subframes['xrd1d'].set_poni(ponidata)
1810 self.subframes['xrd1d'].add_data(xdat, label=label)
1811 self.subframes['xrd1d'].Show()
1813 def init_larch(self):
1814 self.datagroups = self.larch.symtable
1815 if ESCAN_CRED is not None:
1816 self.move_callback = self.onMoveToPixel
1817 try:
1818 self.scandb = connect_scandb(_larch=self.larch)
1819 self.instdb = self.larch.symtable._scan._instdb
1820 self.inst_name = self.scandb.get_info('samplestage_instrument',
1821 default='SampleStage')
1822 print(" ScanDB: %s, Instrument=%s" % (self.scandb.engine, self.inst_name))
1823 except:
1824 etype, emsg, tb = sys.exc_info()
1825 print('Could not connect to ScanDB: %s' % (emsg))
1826 self.scandb = self.instdb = None
1828 def ShowFile(self, evt=None, filename=None, process_file=True, **kws):
1829 if filename is None and evt is not None:
1830 filename = evt.GetString()
1831 if not self.h5convert_done or filename not in self.filemap:
1832 return
1833 self.current_file = self.filemap[filename]
1834 if (self.check_ownership(filename) and
1835 self.current_file.folder_has_newdata()):
1836 if process_file:
1837 mnew = self.roimap_panel.mapproc_nrows.GetStringSelection()
1838 try:
1839 mnew = int(mnew)
1840 except:
1841 mnew = None
1842 self.process_file(filename, max_new_rows=mnew)
1844 ny, nx = self.current_file.get_shape()
1845 self.title.SetLabel('%s: (%i x %i)' % (filename, nx, ny))
1847 fnames = self.filelist.GetItems()
1849 cb = getattr(self.nb.GetCurrentPage(), 'update_xrmmap', None)
1850 if callable(cb):
1851 cb(xrmfile=self.current_file)
1852 cb = getattr(self.nb.GetCurrentPage(), 'set_file_choices', None)
1853 if callable(cb):
1854 cb(fnames)
1856 def createMenus(self):
1857 self.menubar = wx.MenuBar()
1858 fmenu = wx.Menu()
1860 MenuItem(self, fmenu, '&Open XRM Map File\tCtrl+O', 'Read XRM Map File', self.onReadFile)
1861 MenuItem(self, fmenu, '&Open XRM Map Folder\tCtrl+F', 'Read XRM Map Folder', self.onReadFolder)
1862 fmenu.AppendSeparator()
1863 MenuItem(self, fmenu, 'Change &Working Folder', 'Choose working directory',
1864 self.onFolderSelect)
1865 MenuItem(self, fmenu, 'Show Larch Buffer\tCtrl+L', 'Show Larch Programming Buffer',
1866 self.onShowLarchBuffer)
1868 # cmenu = fmenu.Append(-1, '&Watch HDF5 Files\tCtrl+W', 'Watch HDF5 Files', kind=wx.ITEM_CHECK)
1869 # fmenu.Check(cmenu.Id, self.watch_files) ## False
1870 # self.Bind(wx.EVT_MENU, self.onWatchFiles, id=cmenu.Id)
1872 fmenu.AppendSeparator()
1873 MenuItem(self, fmenu, '&Quit\tCtrl+Q',
1874 'Quit program', self.onClose)
1876 rmenu = wx.Menu()
1877 MenuItem(self, rmenu, 'Add / Delete ROIs',
1878 'Define new ROIs, Remove ROIs', self.manageROIs)
1879 MenuItem(self, rmenu, 'Load ROI File for 1DXRD',
1880 'Load ROI File for 1DXRD', self.add1DXRDFile)
1881 rmenu.AppendSeparator()
1882 MenuItem(self, rmenu, 'Load XRD calibration file',
1883 'Load XRD calibration file', self.openPONI)
1884 MenuItem(self, rmenu, 'Add 1DXRD for HDF5 file',
1885 'Calculate 1DXRD for HDF5 file', self.add1DXRD)
1888 # cmenu = fmenu.Append(-1, 'Display 1DXRD for areas',
1889 # 'Display 1DXRD for areas',
1890 # kind=wx.ITEM_CHECK)
1891 #fmenu.Check(cmenu.Id, self.showxrd) ## False
1892 #self.Bind(wx.EVT_MENU, self.onShow1DXRD, id=cmenu.Id)
1894 hmenu = wx.Menu()
1895 MenuItem(self, hmenu, 'About GSE XRM MapViewer', 'About GSE XRM MapViewer',
1896 self.onAbout)
1897 MenuItem(self, hmenu, 'Check for Updates', 'Check for Updates',
1898 self.onCheckforUpdates)
1900 self.menubar.Append(fmenu, '&File')
1901 self.menubar.Append(rmenu, '&ROIs')
1902 self.menubar.Append(hmenu, '&Help')
1903 self.SetMenuBar(self.menubar)
1904 self.Bind(wx.EVT_CLOSE, self.onClose)
1906 def onShowLarchBuffer(self, evt=None):
1907 if self.larch_buffer is None:
1908 self.larch_buffer = LarchFrame(_larch=self.larch, is_standalone=False)
1910 self.larch_buffer.Show()
1911 self.larch_buffer.Raise()
1913 def onFolderSelect(self, evt=None):
1914 dlg = wx.DirDialog(self, 'Select Working Directory:',
1915 get_cwd(),
1916 style=wx.DD_DIR_MUST_EXIST|wx.DD_DEFAULT_STYLE)
1918 if dlg.ShowModal() == wx.ID_OK:
1919 basedir = Path(dlg.GetPath()).absolute().as_posix()
1920 try:
1921 if len(basedir) > 0:
1922 os.chdir(basedir)
1923 save_workdir(basedir)
1924 except OSError:
1925 print( 'Changed folder failed')
1926 pass
1927 save_workdir('gsemap.dat')
1928 dlg.Destroy()
1930 def onAbout(self, event=None):
1931 info = AboutDialogInfo()
1932 info.SetName('GSE XRM MapViewer')
1933 info.SetDescription('X-ray Microprobe Mapping Data Visualization and Analysis')
1934 info.SetVersion(larch.version.__version__)
1935 info.AddDeveloper('Matthew Newville: newville at cars.uchicago.edu')
1936 dlg = AboutBox(info)
1938 def onCheckforUpdates(self, event=None):
1939 dlg = LarchUpdaterDialog(self, caller='GSE MapViewer')
1940 dlg.Raise()
1941 dlg.SetWindowStyle(wx.STAY_ON_TOP)
1942 res = dlg.GetResponse()
1943 dlg.Destroy()
1944 if res.ok and res.run_updates:
1945 from larch.apps import update_larch
1946 update_larch()
1947 self.onClose(evt=event, prompt=False)
1949 def onClose(self, evt=None, prompt=True):
1950 if prompt:
1951 dlg = wx.MessageDialog(None, 'Really Quit?', 'Question',
1952 wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
1954 ret = dlg.ShowModal()
1955 if ret != wx.ID_YES:
1956 return
1958 save_workdir('gsemap.dat')
1959 try:
1960 self.htimer.Stop()
1961 except:
1962 pass
1963 try:
1964 self.file_timer.Stop()
1965 except:
1966 pass
1969 for xrmfile in self.filemap.values():
1970 try:
1971 xrmfile.close()
1972 except KeyError:
1973 pass
1975 try:
1976 self.larch.symtable._plotter.close_all_displays()
1977 except:
1978 pass
1980 ## Closes maps, 2D XRD image
1981 for disp in self.im_displays + self.plot_displays + self.tomo_displays:
1982 try:
1983 disp.Destroy()
1984 except:
1985 pass
1987 for key, wid in self.subframes.items():
1988 if wid is not None:
1989 try:
1990 wid.onClose()
1991 except:
1992 pass
1993 if self.larch_buffer is not None:
1994 self.larch_buffer.exit_on_close = True
1995 self.larch_buffer.onExit(force=True, with_sysexit=False)
1996 self.Destroy()
1998 def onReadFile(self, evt=None):
1999 if not self.h5convert_done:
2000 print('cannot open file while processing a map folder')
2001 return
2003 dlg = wx.FileDialog(self, message='Read XRM Map File',
2004 defaultDir=get_cwd(),
2005 wildcard=FILE_WILDCARDS,
2006 style=wx.FD_OPEN|wx.FD_MULTIPLE)
2007 path, read = None, False
2008 if dlg.ShowModal() == wx.ID_OK:
2009 read = True
2010 paths = [p.replace('\\', '/') for p in dlg.GetPaths()]
2011 dlg.Destroy()
2013 if not read:
2014 return
2016 for path in paths:
2017 fname = Path(path).name
2018 read = True
2019 if fname in self.filemap:
2020 read = (wx.ID_YES == Popup(self, f"Re-read file '{path}'?",
2021 'Re-read file?', style=wx.YES_NO))
2022 if read:
2023 xrmfile = GSEXRM_MapFile(filename=str(path), scandb=self.scandb)
2024 self.add_xrmfile(xrmfile)
2026 def onRead(self, path):
2027 "simple Read and install XRM Map File"
2028 xrmfile = GSEXRM_MapFile(filename=str(path), scandb=self.scandb)
2029 self.add_xrmfile(xrmfile)
2031 def onReadFolder(self, evt=None):
2032 if not self.h5convert_done:
2033 print( 'cannot open file while processing a map folder')
2034 return
2036 dlg = wx.DirDialog(self, message='Read XRM Map Folder',
2037 defaultPath=get_cwd(),
2038 style=wx.DD_DIR_MUST_EXIST|wx.DD_DEFAULT_STYLE)
2040 if dlg.ShowModal() == wx.ID_OK:
2041 folder = Path(dlg.GetPath()).absolute().as_posix()
2042 dlg.Destroy()
2044 xrmfile = GSEXRM_MapFile(folder=folder, scandb=self.scandb)
2045 self.add_xrmfile(xrmfile)
2048 def add_xrmfile(self, xrmfile):
2049 fpath = Path(xrmfile.filename)
2050 fname = fpath.name
2051 parent = fpath.parent.as_posix()
2052 # print("Add XRM File ", fname)
2053 # look for group with this name or for next available group
2054 for i in range(1000):
2055 gname = 'map%3.3i' % (i+1)
2056 xgroup = getattr(self.datagroups, gname, None)
2057 if xgroup is None:
2058 break
2059 gfname = Path(xgroup.filename).name
2060 if gfname == fname:
2061 break
2063 setattr(self.datagroups, gname, xrmfile)
2064 xrmfile.groupname = gname
2066 if fname not in self.filemap:
2067 self.filemap[fname] = xrmfile
2068 if fname not in self.filelist.GetItems():
2069 self.filelist.Append(fname)
2070 self.filelist.SetStringSelection(fname)
2072 if self.check_ownership(fname):
2073 mnew = self.roimap_panel.mapproc_nrows.GetStringSelection()
2074 try:
2075 mnew = int(mnew)
2076 except:
2077 mnew = None
2078 self.process_file(fname, max_new_rows=mnew)
2080 self.ShowFile(filename=fname)
2081 if parent is not None and len(parent) > 0:
2082 try:
2083 os.chdir(unixpath(parent))
2084 save_workdir(unixpath(parent))
2085 except:
2086 pass
2088 def openPONI(self, evt=None):
2089 """
2090 Read specified poni file.
2091 mkak 2016.07.21
2092 """
2094 if len(self.filemap) > 0:
2095 myDlg = OpenPoniFile()
2096 read = False
2097 if myDlg.ShowModal() == wx.ID_OK:
2098 read = True
2099 path = myDlg.XRDInfo[1].GetValue()
2100 flip = False if myDlg.XRDInfo[0].GetSelection() == 1 else True
2101 myDlg.Destroy()
2103 if read:
2104 self.current_file.add_XRDfiles(xrdcalfile=path,flip=flip)
2105 update_xrmmap = getattr(self.nb.GetCurrentPage(),
2106 'update_xrmmap', None)
2107 if callable(update_xrmmap):
2108 update_xrmmap(xrmfile=self.current_file)
2110 def UpdateROI(self, name, xrange=None, action='add', units='keV', roitype='XRF'):
2111 "add or remove an ROI with name, range"
2112 cfile = self.current_file
2113 if xrange is None: xrange = [1, 2]
2114 if roitype == 'XRF':
2115 if action.startswith('del'):
2116 cfile.del_xrfroi(name)
2117 else:
2118 cfile.add_xrfroi(name, xrange, unit=units)
2120 if roitype == '1DXRD':
2121 if action.startswith('del'):
2122 cfile.del_xrd1droi(name)
2123 else:
2124 cfile.add_xrd1droi(name, xrange, unit=units)
2126 self.current_file.get_roi_list('mcasum', force=True)
2127 for page in self.nb.pagelist:
2128 if hasattr(page, 'update_xrmmap'):
2129 page.update_xrmmap(xrmfile=self.current_file)
2130 if hasattr(page, 'set_roi_choices'):
2131 page.set_roi_choices()
2133 def manageROIs(self, event=None):
2134 if not self.h5convert_done:
2135 print( 'cannot open file while processing a map folder')
2136 elif len(self.filemap) > 0:
2137 ROIDialog(self, roi_callback=self.UpdateROI).Show()
2139 def add1DXRDFile(self, event=None):
2140 if len(self.filemap) > 0:
2141 read = False
2142 wildcards = '1D-XRD ROI file (*.dat)|*.dat|All files (*.*)|*.*'
2143 dlg = wx.FileDialog(self, message='Select 1D-XRD ROI file',
2144 defaultDir=get_cwd(),
2145 wildcard=wildcards,
2146 style=wx.FD_OPEN)
2148 if dlg.ShowModal() == wx.ID_OK:
2149 read = True
2150 path = dlg.GetPath().replace('\\', '/')
2151 dlg.Destroy()
2153 if read and Path(path).exists():
2154 time.sleep(1)
2155 self.current_file.read_xrd1D_ROIFile(path)
2157 def add1DXRD(self, event=None):
2159 if len(self.filemap) > 0:
2160 xrd1Dgrp = ensure_subgroup('xrd1d',self.current_file.xrmmap)
2161 poni_path = bytes2str(xrd1Dgrp.attrs.get('calfile',''))
2163 if not Path(poni_path).exists():
2164 self.openPONI()
2165 poni_path = bytes2str(xrd1Dgrp.attrs.get('calfile',''))
2167 if Path(poni_path).exists():
2168 self.current_file.add_xrd1d()
2170 def onShow1DXRD(self, event=None):
2171 self.showxrd = event.IsChecked()
2172 if self.showxrd:
2173 msg = 'Show 1DXRD data for area'
2174 else:
2175 msg = 'Not displaying 1DXRD for area'
2176 self.message(msg)
2177 ##print(msg)
2179# def onCorrectDeadtime(self, event=None):
2180# self.dtcor = event.IsChecked()
2181# if self.dtcor:
2182# msg = 'Using deadtime corrected data...'
2183# else:
2184# msg = 'Using raw data...'
2185# self.message(msg)
2186# ##print(msg)
2187#
2188# def onHotColumns(self, event=None):
2189# self.hotcols = event.IsChecked()
2190# if self.hotcols:
2191# msg = 'Ignoring first/last data columns.'
2192# else:
2193# msg = 'Using all data columns'
2194# self.message(msg)
2195# ##print(msg)
2197 def onWatchFiles(self, event=None):
2198 self.watch_files = event.IsChecked()
2199 if not self.watch_files:
2200 self.file_timer.Stop()
2201 msg = 'Watching Files/Folders for Changes: Off'
2202 else:
2203 self.file_timer.Start(10000)
2204 msg = 'Watching Files/Folders for Changes: On'
2205 self.message(msg)
2207 def onFileWatchTimer(self, event=None):
2208 if self.current_file is not None and len(self.files_in_progress) == 0:
2209 if self.current_file.folder_has_newdata():
2210 fname = Path(self.current_file.filename).name
2211 self.process_file(fname, max_new_rows=1e6)
2213 def process_file(self, filename, max_new_rows=None, on_complete=None):
2214 """Request processing of map file.
2215 This can take awhile, so is done in a separate thread,
2216 with updates displayed in message bar
2217 """
2218 xrmfile = self.filemap[filename]
2219 if xrmfile.status == GSEXRM_FileStatus.created:
2220 xrmfile.initialize_xrmmap(callback=self.updateTimer)
2222 if xrmfile.dimension is None and isGSEXRM_MapFolder(self.folder):
2223 xrmfile.read_master()
2225 # print("PROCESS_FILE!!", xrmfile.folder_has_newdata(), self.h5convert_done,
2226 # filename in self.files_in_progress)
2228 if (xrmfile.folder_has_newdata() and self.h5convert_done
2229 and filename not in self.files_in_progress):
2231 self.files_in_progress.append(filename)
2232 self.h5convert_fname = filename
2233 self.h5convert_done = False
2234 self.h5convert_oncomplete = on_complete
2235 self.htimer.Start(500)
2236 maxrow = None
2237 if max_new_rows is not None:
2238 maxrow = max_new_rows + xrmfile.last_row + 1
2240 ## this calls process function of xrm_mapfile class
2241 self.h5convert_thread = Thread(target=xrmfile.process,
2242 kwargs={'callback':self.updateTimer,
2243 'maxrow': maxrow})
2244 self.h5convert_thread.start()
2245 elif callable(on_complete):
2246 on_complete()
2248 def updateTimer(self, row=None, maxrow=None, filename=None, status=None):
2249 # print("== UPDATE TIMER ", row, maxrow, filename, status)
2250 if row is not None: self.h5convert_irow = row
2251 if maxrow is not None: self.h5convert_nrow = maxrow
2252 if filename is not None: self.h5convert_fname = filename
2253 self.h5convert_done = True if status == 'complete' else False
2254 msg = 'processing %s: row %i of %i' % (self.h5convert_fname,
2255 self.h5convert_irow,
2256 self.h5convert_nrow)
2257 wx.CallAfter(self.message, msg)
2259 def onTimer(self, event=None):
2260 if self.h5convert_done:
2261 # print("h5convert done, stopping timer")
2262 fname = self.h5convert_fname
2263 irow, nrow = self.h5convert_irow, self.h5convert_nrow
2264 self.htimer.Stop()
2265 self.h5convert_thread.join()
2266 self.files_in_progress = []
2267 self.message(f'MapViewer processing {fname}: complete!')
2268 _fname = Path(fname).name
2269 if _fname in self.filemap:
2270 cfile = self.current_file = self.filemap[_fname]
2271 ny, nx = cfile.get_shape()
2272 self.title.SetLabel('%s: (%i x %i)' % (_fname, nx, ny))
2273 update_xrmmap = getattr(self.nb.GetCurrentPage(),
2274 'update_xrmmap', None)
2275 if callable(update_xrmmap) and _fname in self.filemap:
2276 update_xrmmap(xrmfile=cfile)
2277 if self.h5convert_oncomplete is not None:
2278 self.h5convert_oncomplete()
2281 def message(self, msg, win=0):
2282 self.statusbar.SetStatusText(msg, win)
2284 def check_ownership(self, fname):
2285 """
2286 check whether we're currently owner of the file.
2287 this is important!! HDF5 files can be corrupted.
2288 """
2289 if not self.filemap[fname].check_hostid():
2290 if (wx.ID_YES == Popup(self, NOT_OWNER_MSG % fname,
2291 'Not Owner of HDF5 File',
2292 style=wx.YES_NO)):
2293 self.filemap[fname].take_ownership()
2294 return self.filemap[fname].check_hostid()
2296class OpenPoniFile(wx.Dialog):
2297 """"""
2299 #----------------------------------------------------------------------
2300 def __init__(self):
2302 """Constructor"""
2303 dialog = wx.Dialog.__init__(self, None, title='XRD Calibration File', size=(350, 280))
2305 panel = wx.Panel(self)
2307 ################################################################################
2308 cal_chc = ['Dioptas calibration file:','pyFAI calibration file:']
2309 cal_spn = wx.SP_VERTICAL|wx.SP_ARROW_KEYS|wx.SP_WRAP
2310 self.PoniInfo = [ Choice(panel, choices=cal_chc ),
2311 wx.TextCtrl(panel, size=(320, 25)),
2312 Button(panel, label='Browse...')]
2314 self.PoniInfo[2].Bind(wx.EVT_BUTTON, self.onBROWSEponi)
2316 ponisizer = wx.BoxSizer(wx.VERTICAL)
2317 ponisizer.Add(self.PoniInfo[0], flag=wx.TOP, border=15)
2318 ponisizer.Add(self.PoniInfo[1], flag=wx.TOP, border=5)
2319 ponisizer.Add(self.PoniInfo[2], flag=wx.TOP|wx.BOTTOM, border=5)
2321 ################################################################################
2322 hlpBtn = wx.Button(panel, wx.ID_HELP )
2323 okBtn = wx.Button(panel, wx.ID_OK )
2324 canBtn = wx.Button(panel, wx.ID_CANCEL )
2326 minisizer = wx.BoxSizer(wx.HORIZONTAL)
2327 minisizer.Add(hlpBtn, flag=wx.RIGHT, border=5)
2328 minisizer.Add(canBtn, flag=wx.RIGHT, border=5)
2329 minisizer.Add(okBtn, flag=wx.RIGHT, border=5)
2330 ################################################################################
2331 sizer = wx.BoxSizer(wx.VERTICAL)
2332 sizer.Add((-1, 10))
2333 sizer.Add(ponisizer, flag=wx.TOP|wx.LEFT, border=5)
2334 sizer.Add((-1, 15))
2335 sizer.Add(minisizer, flag=wx.ALIGN_RIGHT, border=5)
2337 panel.SetSizer(sizer)
2338 ################################################################################
2340 ## Set defaults
2341 self.PoniInfo[0].SetSelection(0)
2343 self.FindWindowById(wx.ID_OK).Disable()
2345 def checkOK(self,event=None):
2347 if Path(self.PoniInfo[1].GetValue()).exists():
2348 self.FindWindowById(wx.ID_OK).Enable()
2349 else:
2350 self.FindWindowById(wx.ID_OK).Disable()
2352 def onBROWSEponi(self,event=None):
2353 wildcards = 'XRD calibration file (*.poni)|*.poni|All files (*.*)|*.*'
2354 if Path(self.PoniInfo[1].GetValue()).exists():
2355 dfltDIR = self.PoniInfo[1].GetValue()
2356 else:
2357 dfltDIR = get_cwd()
2359 dlg = wx.FileDialog(self, message='Select XRD calibration file',
2360 defaultDir=dfltDIR,
2361 wildcard=wildcards, style=wx.FD_OPEN)
2362 path, read = None, False
2363 if dlg.ShowModal() == wx.ID_OK:
2364 read = True
2365 path = dlg.GetPath().replace('\\', '/')
2366 dlg.Destroy()
2368 if read:
2369 self.PoniInfo[1].Clear()
2370 self.PoniInfo[1].SetValue(str(path))
2371 self.checkOK()
2373######
2374class ROIDialog(wx.Dialog):
2375 """"""
2376 #----------------------------------------------------------------------
2377 def __init__(self, owner, roi_callback=None, **kws):
2378 """Constructor"""
2379 print("ROI Dialog owner ", owner)
2380 wx.Dialog.__init__(self, owner, wx.ID_ANY, title='Add and Delete ROIs',
2381 size=(450, 350))
2383 self.owner = owner
2384 self.roi_callback = roi_callback
2385 self.Bind(wx.EVT_CLOSE, self.onClose)
2387 self.gp = gp = GridPanel(self, nrows=8, ncols=4, itemstyle=LEFT, gap=3, **kws)
2389 self.roi_name = wx.TextCtrl(gp, -1, 'ROI_001', size=(120, -1))
2390 fopts = dict(minval=-1, precision=3, size=(120, -1))
2391 self.roi_type = Choice(gp, size=(120, -1))
2392 self.roi_lims = [FloatCtrl(gp, value=0, **fopts),
2393 FloatCtrl(gp, value=-1, **fopts)]
2394 self.roi_units = Choice(gp, size=(120, -1))
2396 gp.Add(SimpleText(gp, ' Add new ROI: '), dcol=2, style=LEFT)
2397 gp.Add(SimpleText(gp, ' Name:'), newrow=True)
2398 gp.Add(self.roi_name, dcol=2)
2399 gp.Add(SimpleText(gp, ' Type:'), newrow=True)
2400 gp.Add(self.roi_type, dcol=2)
2402 gp.Add(SimpleText(gp, ' Limits:'), newrow=True)
2403 gp.AddMany((self.roi_lims[0], self.roi_lims[1], self.roi_units),
2404 dcol=1, style=LEFT)
2405 gp.Add(SimpleText(gp, ' '), newrow=True)
2406 gp.Add(Button(gp, 'Add ROI', size=(120, -1), action=self.onCreateROI),
2407 dcol=2)
2409 ###############################################################################
2411 self.rm_roi_name = Choice(gp, size=(120, -1))
2412 self.rm_roi_det = Choice(gp, size=(120, -1))
2413 fopts = dict(minval=-1, precision=3, size=(100, -1))
2414 gp.Add(SimpleText(gp, ''),newrow=True)
2415 gp.Add(HLine(gp, size=(350, 4)), dcol=4, newrow=True)
2416 gp.Add(SimpleText(gp, ''),newrow=True)
2417 gp.Add(SimpleText(gp, 'Delete ROI: '), dcol=2, newrow=True)
2419 gp.AddMany((SimpleText(gp, 'Detector:'),self.rm_roi_det), newrow=True)
2420 gp.AddMany((SimpleText(gp, 'ROI:'),self.rm_roi_name), newrow=True)
2422 gp.Add(SimpleText(gp, ''), newrow=True)
2423 gp.Add(Button(gp, 'Remove This ROI', size=(120, -1), action=self.onRemoveROI),
2424 dcol=2)
2426 self.roi_type.Bind(wx.EVT_CHOICE, self.roiUNITS)
2427 self.rm_roi_name.Bind(wx.EVT_CHOICE, self.roiSELECT)
2429 gp.pack()
2430 fit_dialog_window(self, gp)
2431 self.owner.current_file.reset_flags()
2432 self.roiTYPE()
2434 def roiTYPE(self, event=None):
2435 roitype = []
2436 cfile = self.owner.current_file
2437 det_list = cfile.get_detector_list()
2438 if cfile.has_xrf:
2439 roitype += ['XRF']
2440 if cfile.has_xrd1d:
2441 roitype += ['1DXRD']
2442 if len(roitype) < 1:
2443 roitype = ['']
2444 self.roi_type.SetChoices(roitype)
2445 self.roiUNITS()
2446 self.rm_roi_det.SetChoices(det_list)
2447 self.setROI()
2449 def onRemoveROI(self,event=None):
2450 detname = self.rm_roi_det.GetStringSelection()
2451 roiname = self.rm_roi_name.GetStringSelection()
2453 if self.roi_callback is not None:
2454 self.roi_callback(roiname, action='del')
2455 self.setROI()
2456 self.roiTYPE()
2458 def setROI(self):
2459 detname = self.rm_roi_det.GetStringSelection()
2460 cfile = self.owner.current_file
2461 try:
2462 detgrp = cfile.xrmmap['roimap'][detname]
2463 except:
2464 return
2466 limits = []
2467 names = detgrp.keys()
2468 for name in names:
2469 limits += [list(detgrp[name]['limits'][:])]
2470 if len(limits) > 0:
2471 self.rm_roi_name.SetChoices([x for (y,x) in sorted(zip(limits,names))])
2472 self.roiSELECT()
2474 def roiSELECT(self, event=None):
2475 detname = self.rm_roi_det.GetStringSelection()
2476 cfile = self.owner.current_file
2477 roinames = cfile.get_roi_list(detname)
2478 self.rm_roi_name.SetChoices(roinames)
2480 def roiUNITS(self,event=None):
2481 choice = self.roi_type.GetStringSelection()
2482 roiunit = ['']
2483 if choice == 'XRF':
2484 roiunit = ['keV', 'eV', 'channels']
2485 elif choice == '1DXRD':
2486 roiunit = [u'\u212B\u207B\u00B9 (q)',u'\u00B0 (2\u03B8)',u'\u212B (d)']
2488 self.roi_units.SetChoices(roiunit)
2490 def onCreateROI(self,event=None):
2491 rtype = self.roi_type.GetStringSelection()
2492 name = self.roi_name.GetValue()
2493 xrange = [float(lims.GetValue()) for lims in self.roi_lims]
2495 units = self.roi_units.GetStringSelection()
2496 if rtype == '1DXRD':
2497 units = ['q', '2th', 'd'][self.roi_units.GetSelection()]
2500 self.owner.message(f'Building ROI data for: {name:s}')
2501 if self.roi_callback is not None:
2502 self.roi_callback(name, xrange=xrange, action='add', units=units, roitype=rtype)
2504 self.owner.message(f'Added ROI: {name:s}')
2505 self.roiTYPE()
2507 def onClose(self, event=None):
2508 self.Destroy()
2510##################a
2514class OpenMapFolder(wx.Dialog):
2515 """"""
2517 #----------------------------------------------------------------------
2518 def __init__(self, folder):
2519 """Constructor"""
2520 self.folder = folder
2521 f = Path(folder).name
2522 title = f"Read XRM Map Folder: {f}"
2523 wx.Dialog.__init__(self, None,
2524 title=title, size=(475, 750))
2527 panel = wx.Panel(self)
2529 ChkTtl = SimpleText(panel, label='Build map including data:' )
2530 self.ChkBx = [ Check(panel, label='XRF' ),
2531 Check(panel, label='2DXRD' ),
2532 Check(panel, label='1DXRD (requires calibration file)' )]
2534 for chkbx in self.ChkBx:
2535 chkbx.Bind(wx.EVT_CHECKBOX, self.checkOK)
2537 cbsizer = wx.BoxSizer(wx.HORIZONTAL)
2538 cbsizer.Add(self.ChkBx[0])
2539 cbsizer.Add(self.ChkBx[1])
2540 cbsizer.Add(self.ChkBx[2])
2542 ckbxsizer = wx.BoxSizer(wx.VERTICAL)
2543 ckbxsizer.Add(ChkTtl, flag=wx.BOTTOM|wx.LEFT)
2544 ckbxsizer.Add(cbsizer)
2545 ################################################################################
2546 infoTtl = [ SimpleText(panel, label='Facility'),
2547 SimpleText(panel, label='Beamline'),
2548 SimpleText(panel, label='Run cycle'),
2549 SimpleText(panel, label='Proposal'),
2550 SimpleText(panel, label='User group')]
2551 self.info = [ wx.TextCtrl(panel, size=(100, 25) ),
2552 wx.TextCtrl(panel, size=(100, 25) ),
2553 wx.TextCtrl(panel, size=(100, 25) ),
2554 wx.TextCtrl(panel, size=(100, 25) ),
2555 wx.TextCtrl(panel, size=(320, 25) )]
2557 infosizer0 = wx.BoxSizer(wx.HORIZONTAL)
2558 infosizer0.Add(infoTtl[0], flag=wx.RIGHT, border=5)
2559 infosizer0.Add(self.info[0], flag=wx.RIGHT, border=15)
2560 infosizer0.Add(infoTtl[1], flag=wx.RIGHT, border=5)
2561 infosizer0.Add(self.info[1], flag=wx.RIGHT, border=15)
2563 infosizer1 = wx.BoxSizer(wx.HORIZONTAL)
2564 infosizer1.Add(infoTtl[2], flag=wx.RIGHT, border=5)
2565 infosizer1.Add(self.info[2], flag=wx.RIGHT, border=15)
2566 infosizer1.Add(infoTtl[3], flag=wx.RIGHT, border=5)
2567 infosizer1.Add(self.info[3], flag=wx.RIGHT, border=15)
2569 infosizer2 = wx.BoxSizer(wx.HORIZONTAL)
2570 infosizer2.Add(infoTtl[4], flag=wx.RIGHT, border=5)
2571 infosizer2.Add(self.info[4], flag=wx.RIGHT, border=15)
2573 infosizer = wx.BoxSizer(wx.VERTICAL)
2574 infosizer.Add(infosizer0, flag=wx.TOP, border=5)
2575 infosizer.Add(infosizer1, flag=wx.TOP|wx.BOTTOM, border=5)
2576 infosizer.Add(infosizer2, flag=wx.BOTTOM, border=15)
2577 ################################################################################
2578 cal_chc = ['Dioptas calibration file:','pyFAI calibration file:']
2579 bkgd_chc = ['2DXRD background (optional):','1DXRD background (optional):']
2580 cal_spn = wx.SP_VERTICAL|wx.SP_ARROW_KEYS|wx.SP_WRAP
2581 self.XRDInfo = [ Choice(panel, choices=cal_chc ),
2582 wx.TextCtrl(panel, size=(320, 25)),
2583 Button(panel, label='Browse...'),
2584 SimpleText(panel, label='Steps:'),
2585 wx.TextCtrl(panel, size=(80, 25)),
2586 SimpleText(panel, label='Wedges:'),
2587 wx.SpinCtrl(panel, style=cal_spn, size=(100, -1)),
2588 Choice(panel, choices=bkgd_chc ),
2589 wx.TextCtrl(panel, size=(320, 25)),
2590 Button(panel, label='Browse...'),
2591 SimpleText(panel, label='Background scale:'),
2592 wx.TextCtrl(panel, size=(80, 25)),
2593 SimpleText(panel, label='2DXRD mask file (optional):'),
2594 wx.TextCtrl(panel, size=(320, 25)),
2595 Button(panel, label='Browse...'),]
2597 for i in [1,8,13]:
2598 self.XRDInfo[i+1].Bind(wx.EVT_BUTTON, partial(self.onBROWSEfile,i=i))
2600 xrdsizer1 = wx.BoxSizer(wx.HORIZONTAL)
2602 xrdsizer1.Add(self.XRDInfo[3], flag=wx.RIGHT, border=5)
2603 xrdsizer1.Add(self.XRDInfo[4], flag=wx.RIGHT, border=5)
2604 xrdsizer1.Add(self.XRDInfo[5], flag=wx.RIGHT, border=5)
2605 xrdsizer1.Add(self.XRDInfo[6], flag=wx.RIGHT, border=5)
2607 xrdsizer2 = wx.BoxSizer(wx.HORIZONTAL)
2609 xrdsizer2.Add(self.XRDInfo[9], flag=wx.RIGHT, border=30)
2610 xrdsizer2.Add(self.XRDInfo[10], flag=wx.RIGHT, border=5)
2611 xrdsizer2.Add(self.XRDInfo[11], flag=wx.RIGHT, border=5)
2613 xrdsizer = wx.BoxSizer(wx.VERTICAL)
2614 xrdsizer.Add(self.XRDInfo[0], flag=wx.TOP, border=5)
2615 xrdsizer.Add(self.XRDInfo[1], flag=wx.TOP, border=5)
2616 xrdsizer.Add(self.XRDInfo[2], flag=wx.TOP|wx.BOTTOM, border=5)
2617 xrdsizer.Add(xrdsizer1, flag=wx.BOTTOM, border=5)
2618 xrdsizer.Add(self.XRDInfo[7], flag=wx.TOP, border=8)
2619 xrdsizer.Add(self.XRDInfo[8], flag=wx.TOP, border=5)
2620# xrdsizer.Add(self.XRDInfo[9], flag=wx.TOP|wx.BOTTOM, border=5)
2621 xrdsizer.Add(xrdsizer2, flag=wx.TOP|wx.BOTTOM, border=5)
2622 xrdsizer.Add(self.XRDInfo[12], flag=wx.TOP, border=8)
2623 xrdsizer.Add(self.XRDInfo[13], flag=wx.TOP, border=5)
2624 xrdsizer.Add(self.XRDInfo[14], flag=wx.TOP|wx.BOTTOM, border=5)
2627 ################################################################################
2628 h5cmpr_chc = ['gzip','lzf']
2629 h5cmpr_opt = ['%i' % i for i in np.arange(10)]
2631 self.H5cmprInfo = [Choice(panel, choices=h5cmpr_chc),
2632 Choice(panel, choices=h5cmpr_opt)]
2633 h5txt = SimpleText(panel, label='H5 File Comppression:')
2635 self.H5cmprInfo[0].SetSelection(0)
2636 self.H5cmprInfo[1].SetSelection(2)
2638 self.H5cmprInfo[0].Bind(wx.EVT_CHOICE, self.onH5cmpr)
2640 h5cmprsizer = wx.BoxSizer(wx.HORIZONTAL)
2641 h5cmprsizer.Add(h5txt, flag=wx.RIGHT, border=5)
2642 h5cmprsizer.Add(self.H5cmprInfo[0], flag=wx.RIGHT, border=5)
2643 h5cmprsizer.Add(self.H5cmprInfo[1], flag=wx.RIGHT, border=5)
2644 ################################################################################
2645 self.ok_btn = wx.Button(panel, wx.ID_OK)
2646 self.cancel_btn = wx.Button(panel, wx.ID_CANCEL)
2648 minisizer = wx.BoxSizer(wx.HORIZONTAL)
2649 minisizer.Add(self.cancel_btn, flag=wx.RIGHT, border=5)
2650 minisizer.Add(self.ok_btn, flag=wx.RIGHT, border=5)
2651 ################################################################################
2652 sizer = wx.BoxSizer(wx.VERTICAL)
2654 sizer.Add(ckbxsizer, flag=wx.TOP|wx.LEFT, border=5)
2656 sizer.Add(HLine(panel, size=(320, 2)),flag=wx.TOP|wx.LEFT, border=5)
2657 sizer.Add(infosizer, flag=wx.TOP|wx.LEFT, border=5)
2658 sizer.Add(HLine(panel, size=(320, 2)),flag=wx.TOP|wx.LEFT, border=5)
2659 sizer.Add(xrdsizer, flag=wx.TOP|wx.LEFT, border=5)
2660 sizer.Add(HLine(panel, size=(320, 2)),flag=wx.TOP|wx.LEFT, border=5)
2661 sizer.Add(h5cmprsizer, flag=wx.TOP|wx.LEFT, border=5)
2662 sizer.Add(minisizer, flag=wx.ALIGN_RIGHT, border=5)
2665 pack(panel, sizer)
2666 w, h = panel.GetBestSize()
2667 w = 25*(2 + int(w*0.04))
2668 h = 25*(2 + int(h*0.04))
2669 panel.SetSize((w, h))
2671 # HX
2672 ################################################################################
2674 ## Set defaults
2675 self.ChkBx[0].SetValue(True)
2676 self.ChkBx[1].SetValue(False)
2677 self.ChkBx[2].SetValue(False)
2679 self.XRDInfo[0].SetSelection(0)
2680 self.XRDInfo[7].SetSelection(0)
2682 self.XRDInfo[4].SetValue('5001')
2683 self.XRDInfo[6].SetValue(1)
2684 self.XRDInfo[6].SetRange(0,36)
2686 self.XRDInfo[11].SetValue('1.0')
2688 for poniinfo in self.XRDInfo:
2689 poniinfo.Disable()
2691 self.info[0].SetValue(FACILITY)
2692 self.info[1].SetValue(BEAMLINE)
2693 for line in open(Path(self.folder, 'Scan.ini'), 'r'):
2694 if line.split()[0] == 'basedir':
2695 npath = line.split()[-1].replace('\\', '/').split('/')
2696 cycle, usr = npath[-2], npath[-1]
2697 self.info[2].SetValue(cycle)
2698 self.info[4].SetValue(usr)
2699 self.checkOK()
2701 def checkOK(self, evt=None):
2703 if self.ChkBx[2].GetValue():
2704 for poniinfo in self.XRDInfo:
2705 poniinfo.Enable()
2706 elif self.ChkBx[1].GetValue():
2707 for poniinfo in self.XRDInfo[8:]:
2708 poniinfo.Enable()
2709 for poniinfo in self.XRDInfo[:8]:
2710 poniinfo.Disable()
2711 self.XRDInfo[7].SetSelection(0)
2712 else:
2713 for poniinfo in self.XRDInfo:
2714 poniinfo.Disable()
2716 def onH5cmpr(self,event=None):
2718 if self.H5cmprInfo[0].GetSelection() == 0:
2719 self.H5cmprInfo[1].Enable()
2720 self.H5cmprInfo[1].SetChoices(['%i' % i for i in np.arange(10)])
2721 self.H5cmprInfo[1].SetSelection(2)
2722 else:
2723 self.H5cmprInfo[1].Disable()
2724 self.H5cmprInfo[1].SetChoices([''])
2726 def onBROWSEfile(self,event=None,i=1):
2728 if i == 8:
2729 wldcd = '2D XRD background file (*.tiff)|*.tif;*.tiff;*.edf|All files (*.*)|*.*'
2730 if i == 13:
2731 wldcd = '1D XRD background file (*.xy)|*.xy|All files (*.*)|*.*'
2732 else: ## elif i == 1:
2733 wldcd = 'XRD calibration file (*.poni)|*.poni|All files (*.*)|*.*'
2735 if Path(self.XRDInfo[i].GetValue()).exists():
2736 dfltDIR = self.XRDInfo[i].GetValue()
2737 else:
2738 dfltDIR = get_cwd()
2740 dlg = wx.FileDialog(self, message='Select %s' % wldcd.split(' (')[0],
2741 defaultDir=dfltDIR,
2742 wildcard=wldcd, style=wx.FD_OPEN)
2743 path, read = None, False
2744 if dlg.ShowModal() == wx.ID_OK:
2745 read = True
2746 path = dlg.GetPath().replace('\\', '/')
2747 dlg.Destroy()
2749 if read:
2750 self.XRDInfo[i].Clear()
2751 self.XRDInfo[i].SetValue(str(path))
2754class MapViewer(LarchWxApp):
2755 def __init__(self, use_scandb=False, _larch=None, filename=None,
2756 title=None, check_version=True, with_inspect=False, **kws):
2758 self.filename = filename
2759 self.title = title
2760 self.use_scandb = use_scandb
2761 self.check_version = check_version
2762 LarchWxApp.__init__(self, _larch=_larch,
2763 with_inspect=with_inspect, **kws)
2765 def createApp(self):
2766 frame = MapViewerFrame(use_scandb=self.use_scandb,
2767 title=self.title,
2768 filename=self.filename,
2769 check_version=self.check_version,
2770 _larch=self._larch)
2771 self.SetTopWindow(frame)
2772 return True
2775def mapviewer(use_scandb=False, filename=None, _larch=None,
2776 with_inspect=False, **kws):
2777 MapViewer(use_scandb=use_scandb, filename=filename, _larch=_larch,
2778 with_inspect=with_inspect, **kws)