def init_scandb(self): dbconn = self.config if dbconn is not None: self.instname = dbconn.get('instrument', 'microscope_stages') self.scandb = ScanDB() self.instdb = InstrumentDB(self.scandb) print("Connect ScanDB ", self.scandb.engine, self.instname) if self.instdb.get_instrument(self.instname) is None: pvs = self.viewer.config['stages'].keys() self.instdb.add_instrument(self.instname, pvs=pvs)
def make_uscope_rotation(scandb, offline_inst='IDE_Microscope', offline_xyz=('13IDE:m1.VAL', '13IDE:m2.VAL', '13IDE:m3.VAL'), online_inst='IDE_SampleStage', online_xyz=('13XRM:m4.VAL', '13XRM:m6.VAL', '13XRM:m5.VAL')): """ Calculate and store the rotation maxtrix needed to convert positions from the GSECARS offline microscope (OSCAR) to the SampleStage in the microprobe station. This calculates the rotation matrix based on all position names that occur in the Position List for both instruments. Note: The result is saved as a json dictionary to the config table """ instdb = InstrumentDB(scandb) pos_us = read_xyz(instdb, offline_inst, offline_xyz) pos_ss = read_xyz(instdb, online_inst, online_xyz) # calculate the rotation matrix mat_us2ss, v1, v2 = calc_rotmatrix(pos_us, pos_ss) if mat_us2ss is None: return #endif uscope = instdb.get_instrument(offline_inst) sample = instdb.get_instrument(online_inst) uname = uscope.name.replace(' ', '_') sname = sample.name.replace(' ', '_') conf_us2ss = "CoordTrans:%s:%s" % (uname, sname) us2ss = dict(source=offline_xyz, dest=online_xyz, rotmat=mat_us2ss.tolist()) scandb.set_config(conf_us2ss, json.dumps(us2ss)) # calculate the rotation matrix going the other way mat_ss2us, v1, v2 = calc_rotmatrix(pos_ss, pos_us) conf_ss2us = "CoordTrans:%s:%s" % (sname, uname) ss2us = dict(source=online_xyz, dest=offline_xyz, rotmat=mat_ss2us.tolist()) print("Saving Calibration %s" % conf_ss2us) scandb.set_config(conf_ss2us, json.dumps(ss2us)) scandb.commit()
def init_scandb(self): dbconn = self.config if dbconn is not None: self.instname = dbconn.get('instrument', 'microscope_stages') dbconn_dir = dbconn.get('dbconn_dir', '') dbconn_file = dbconn.get('dbconn_file', '') if os.path.exists(dbconn_dir) and len(dbconn_file) > 0: sys.path.insert(0, dbconn_dir) mod = __import__(dbconn_file) conn = mod.conn scandb = ScanDB(**mod.conn) self.instdb = InstrumentDB(scandb) if self.instdb.get_instrument(self.instname) is None: pvs = self.viewer.config['stages'].keys() self.instdb.add_instrument(self.instname, pvs=pvs)
def init_scandb(self): dbconn = self.config if dbconn is not None: self.instname = dbconn.get('instrument', 'microscope_stages') if dbconn['port'] in ('', 'None', None): dbconn.pop('port') scandb = ScanDB(**dbconn) self.instdb = InstrumentDB(scandb) if self.instdb.get_instrument(self.instname) is None: pvs = self.viewer.config['stages'].keys() self.instdb.add_instrument(self.instname, pvs=pvs)
def init_scandb(self): dbconn = self.config if dbconn is not None: self.instname = dbconn.get("instrument", "microscope_stages") if dbconn["port"] in ("", "None", None): dbconn.pop("port") scandb = ScanDB(**dbconn) self.instdb = InstrumentDB(scandb) if self.instdb.get_instrument(self.instname) is None: pvs = self.parent.config["stages"].keys() self.instdb.add_instrument(self.instname, pvs=pvs)
def connect_scandb(dbname=None, server='postgresql', _larch=None, **kwargs): if (_larch.symtable.has_symbol(SCANDB_NAME) and _larch.symtable.get_symbol(SCANDB_NAME) is not None): scandb = _larch.symtable.get_symbol(SCANDB_NAME) else: scandb = ScanDB(dbname=dbname, server=server, **kwargs) _larch.symtable.set_symbol(SCANDB_NAME, scandb) if (_larch.symtable.has_symbol(INSTDB_NAME) and _larch.symtable.get_symbol(INSTDB_NAME) is not None): instdb = _larch.symtable.get_symbol(INSTDB_NAME) else: instdb = InstrumentDB(scandb) _larch.symtable.set_symbol(INSTDB_NAME, instdb) return scandb
def connect_scandb(scandb=None, dbname=None, _larch=None, **kwargs): global _scandb, _instdb if _scandb is not None: return _scandb if scandb is None: scandb = ScanDB(dbname=dbname, **kwargs) if scandb is not None: _scandb = scandb if _larch is not None: _larch.symtable.set_symbol(SCANDB_NAME, _scandb) if _instdb is None: _instdb = InstrumentDB(_scandb) if _larch is not None: _larch.symtable.set_symbol(INSTDB_NAME, _instdb) return _scandb
class PositionPanel(wx.Panel): """panel of position lists, with buttons""" def __init__(self, parent, viewer, config=None, **kws): wx.Panel.__init__(self, parent, -1, size=(300, 500)) self.size = (300, 600) self.parent = parent self.viewer = viewer self.config = config self.poslist_query = None self.image_display = None self.pos_name = wx.TextCtrl(self, value="", size=(300, 25), style= wx.TE_PROCESS_ENTER) self.pos_name.Bind(wx.EVT_TEXT_ENTER, self.onSave1) tlabel = wx.StaticText(self, label="Save Position: ") bkws = dict(size=(55, -1)) btn_goto = add_button(self, "Go To", action=self.onGo, **bkws) btn_erase = add_button(self, "Erase", action=self.onErase, **bkws) btn_show = add_button(self, "Show", action=self.onShow, **bkws) # btn_many = add_button(self, "Erase Many", action=self.onEraseMany, **bkws) brow = wx.BoxSizer(wx.HORIZONTAL) brow.Add(btn_goto, 0, ALL_EXP|wx.ALIGN_LEFT, 1) brow.Add(btn_erase, 0, ALL_EXP|wx.ALIGN_LEFT, 1) brow.Add(btn_show, 0, ALL_EXP|wx.ALIGN_LEFT, 1) # brow.Add(btn_many, 0, ALL_EXP|wx.ALIGN_LEFT, 1) self.pos_list = wx.ListBox(self) self.pos_list.SetBackgroundColour(wx.Colour(253, 253, 250)) self.pos_list.Bind(wx.EVT_LISTBOX, self.onSelect) self.pos_list.Bind(wx.EVT_RIGHT_DOWN, self.onRightClick) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(tlabel, 0, wx.ALIGN_LEFT|wx.ALL) sizer.Add(self.pos_name, 0, wx.ALIGN_LEFT|wx.ALL) sizer.Add(brow, 0, wx.ALIGN_LEFT|wx.ALL) sizer.Add(self.pos_list, 1, ALL_EXP|wx.ALIGN_CENTER, 3) pack(self, sizer) self.init_scandb() self.last_refresh = 0 self.get_positions_from_db() self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onTimer, self.timer) self.timer.Start(5000) def init_scandb(self): dbconn = self.config if dbconn is not None: self.instname = dbconn.get('instrument', 'microscope_stages') if dbconn['port'] in ('', 'None', None): dbconn.pop('port') scandb = ScanDB(**dbconn) self.instdb = InstrumentDB(scandb) if self.instdb.get_instrument(self.instname) is None: pvs = self.viewer.config['stages'].keys() self.instdb.add_instrument(self.instname, pvs=pvs) def onSave1(self, event): "save from text enter" self.onSave(event.GetString().strip()) def onSave2(self, event): "save from button push" self.onSave(self.pos_name.GetValue().strip()) def onSave(self, name): if len(name) < 1: return if name in self.positions and self.viewer.v_replace: ret = popup(self, "Overwrite Position %s?" % name, "Veriry Overwrite Position", style=wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION) if ret != wx.ID_YES: return imgfile = '%s.jpg' % time.strftime('%b%d_%H%M%S') imgfile = os.path.join(self.viewer.imgdir, imgfile) tmp_pos = self.viewer.ctrlpanel.read_position() imgdata, count = None, 0 if not os.path.exists(self.viewer.imgdir): os.makedirs(self.viewer.imgdir) while imgdata is None and count <100: imgdata = self.viewer.save_image(imgfile) if imgdata is None: time.sleep(0.5) count = count + 1 imgdata['source'] = 'SampleStage' imgdata['data_format'] = 'file' imgdata.pop('data') notes = json.dumps(imgdata) fullpath = os.path.join(os.getcwd(), imgfile) self.positions[name] = {'image': fullpath, 'timestamp': time.strftime('%b %d %H:%M:%S'), 'position': tmp_pos, 'notes': notes} if name not in self.pos_list.GetItems(): self.pos_list.Append(name) self.instdb.save_position(self.instname, name, tmp_pos, notes=notes, image=fullpath) self.pos_list.SetStringSelection(name) # auto-save file self.viewer.autosave(positions=self.positions) self.viewer.write_htmllog(name, self.positions[name]) imgfile_exists = False t0 = time.time() if not imgfile_exists and time.time()-t0 < 10: imgfile_exists = os.path.exists(fullpath) time.sleep(0.5) if imgfile_exists: self.viewer.write_message("Saved Position '%s', image in %s" % (name, imgfile)) else: self.viewer.write_message("COULD NOT SAVE IMAGE FILE!!") wx.CallAfter(Closure(self.onSelect, event=None, name=name)) def onShow(self, event): posname = self.pos_list.GetStringSelection() ipos = self.pos_list.GetSelection() if posname is None or len(posname) < 1: return try: self.image_display.Show() self.image_display.Raise() except: del self.image_display self.image_display = None if self.image_display is None: self.image_display = ImageDisplayFrame() self.image_display.Raise() thispos = self.positions[posname] try: notes = json.loads(thispos['notes']) except: notes = {'data_format': ''} if isinstance(notes, basestring): notes = json.loads(notes) label = [] stages = self.viewer.config['stages'] posvals = self.positions[posname]['position'] for pvname, value in posvals.items(): value = thispos['position'][pvname] desc = stages.get(pvname, {}).get('desc', None) if desc is None: desc = pvname label.append("%s=%.4f" % (desc, float(value))) label = ', '.join(label) label = '%s: %s' % (posname, label) data = thispos['image'] if str(notes['data_format']) == 'file': self.image_display.showfile(data, title=posname, label=label) elif str(notes['data_format']) == 'base64': size = notes.get('image_size', (800, 600)) self.image_display.showb64img(data, size=size, title=posname, label=label) else: print 'Cannot show image for %s' % posname def onGo(self, event): posname = self.pos_list.GetStringSelection() if posname is None or len(posname) < 1: return stages = self.viewer.config['stages'] posvals = self.positions[posname]['position'] postext = [] for pvname, value in posvals.items(): label = pvname desc = stages.get(pvname, {}).get('desc', None) if desc is not None: label = '%s (%s)' % (desc, pvname) postext.append(' %s\t= %.4f' % (label, float(value))) postext = '\n'.join(postext) if self.viewer.v_move: ret = popup(self, "Move to %s?: \n%s" % (posname, postext), 'Verify Move', style=wx.YES_NO|wx.ICON_QUESTION) if ret != wx.ID_YES: return for pvname, value in posvals.items(): caput(pvname, value) self.viewer.write_message('moved to %s' % posname) def onErase(self, event=None, posname=None, query=True): if posname is None: posname = self.pos_list.GetStringSelection() if posname is None or len(posname) < 1: return if self.viewer.v_erase and query: if wx.ID_YES != popup(self, "Erase %s?" % (posname), 'Verify Erase', style=wx.YES_NO|wx.ICON_QUESTION): return pos_names = self.pos_list.GetItems() ipos = pos_names.index(posname) self.instdb.remove_position(self.instname, posname) self.positions.pop(posname) self.pos_list.Delete(ipos) self.pos_name.Clear() self.viewer.write_message('Erased Position %s' % posname) def onEraseMany(self, event=None): if self.instdb is not None: ErasePositionsDialog(self.positions.keys(), instname=self.instname, instdb=self.instdb) def onMicroscopeTransfer(self, event=None): offline = self.config.get('offline', '') if self.instdb is not None: TransferPositionsDialog(offline, instname=self.instname, instdb=self.instdb, parent=self) def onMicroscopeCalibrate(self, event=None, **kws): offline = self.config.get('offline', '') print 'Calibrate to Offline : ', offline def onSelect(self, event=None, name=None): "Event handler for selecting a named position" if name is None: name = str(event.GetString().strip()) if name is None or name not in self.positions: return self.pos_name.SetValue(name) def onRightClick(self, event=None): menu = wx.Menu() # make basic widgets for popup menu for item, name in (('popup_up1', 'Move up'), ('popup_dn1', 'Move down'), ('popup_upall', 'Move to top'), ('popup_dnall', 'Move to bottom')): setattr(self, item, wx.NewId()) wid = getattr(self, item) self.Bind(wx.EVT_MENU, self.onRightEvent, wid) menu.Append(wid, name) self.PopupMenu(menu) menu.Destroy() def onRightEvent(self, event=None): "popup box event handler" idx = self.pos_list.GetSelection() if idx < 0: # no item selected return wid = event.GetId() namelist = list(self.positions.keys())[:] stmp = {} for name in namelist: stmp[name] = self.positions[name] if wid == self.popup_up1 and idx > 0: namelist.insert(idx-1, namelist.pop(idx)) elif wid == self.popup_dn1 and idx < len(namelist): namelist.insert(idx+1, namelist.pop(idx)) elif wid == self.popup_upall: namelist.insert(0, namelist.pop(idx)) elif wid == self.popup_dnall: namelist.append( namelist.pop(idx)) newpos = {} for name in namelist: newpos[name] = stmp[name] self.init_positions(newpos) self.viewer.autosave(positions=self.positions) def set_positions(self, positions): "set the list of position on the left-side panel" cur_sel = self.pos_list.GetStringSelection() self.pos_list.Clear() self.positions = positions for name, val in self.positions.items(): self.pos_list.Append(name) if cur_sel in self.positions: self.pos_list.SetStringSelection(cur_sel) self.last_refresh = 0 def onTimer(self, evt=None): if self.poslist_query is None: inst = self.instdb.get_instrument(self.instname) cls, tab = self.instdb.scandb.get_table('positions') self.poslist_select = tab.select().where(tab.c.instruments_id==inst.id) npos = len(self.poslist_select.execute().fetchall()) now = time.time() if (npos != len(self.posnames) or (now - self.last_refresh) > 600.0): # print("Timer ", npos, len(self.posnames), now-self.last_refresh) self.get_positions_from_db() self.last_refresh = now def get_positions_from_db(self): if self.instdb is None: return positions = OrderedDict() iname = self.instname posnames = self.instdb.get_positionlist(iname) self.posnames = posnames for pname in posnames: thispos = self.instdb.get_position(iname, pname) image = '' notes = {} if thispos.image is not None: image = thispos.image if thispos.notes is not None: notes = thispos.notes pdat = OrderedDict() for pvpos in thispos.pvs: pdat[pvpos.pv.name] = pvpos.value positions[pname] = dict(position=pdat, image=image, notes=notes) self.set_positions(positions) def SavePositions(self, fname): """ save positions to external file """ out = [POS_HEADER] for name, val in self.positions.items(): pos = [] notes = val['notes'] img = val['image'] ts = val.get('timestamp', '') for pvname, val in val['position'].items(): pos.append((pvname, val)) out.append(json.dumps((name, pos, notes, img, ts))) out.append('') out = '\n'.join(out) fout = open(fname, 'w') fout.write(out) fout.close() def LoadPositions(self, fname): """ save positions to external file """ try: fh = open(fname, 'r') text = fh.readlines() fh.close() except IOError: print 'IO Error' return -1 header = text[0].replace('\n', '').replace('\r', '') if header != POS_HEADER: print 'Bad Header', header return -2 for line in text[1:]: name, pos, notes, img, ts = json.loads(line) tmp_pos = OrderedDict(pos) try: self.positions[name] = {'image': img, 'timestamp': ts, 'position': tmp_pos, 'notes': notes} except: print 'Cannot set', name, tmp_pos, notes, img try: self.instdb.save_position(self.instname, name, tmp_pos, notes=json.dumps(notes), image=img) except: print 'Could save ', name, tmp_pos, notes, img #print(" Import Pos ", name, img, notes) self.set_positions(self.positions) return 0
class PositionPanel(wx.Panel): """panel of position lists, with buttons""" def __init__(self, parent, viewer, config=None, **kws): wx.Panel.__init__(self, parent, -1, size=(300, 500)) self.size = (300, 600) self.parent = parent self.viewer = viewer self.config = config self.poslist_select = None self.image_display = None self.pos_name = wx.TextCtrl(self, value="", size=(300, 25), style=wx.TE_PROCESS_ENTER) self.pos_name.Bind(wx.EVT_TEXT_ENTER, self.onSave1) tlabel = wx.StaticText(self, label="Save Position: ") bkws = dict(size=(55, -1)) btn_goto = add_button(self, "Go To", action=self.onGo, **bkws) btn_erase = add_button(self, "Erase", action=self.onErase, **bkws) btn_show = add_button(self, "Show", action=self.onShow, **bkws) # btn_many = add_button(self, "Erase Many", action=self.onEraseMany, **bkws) brow = wx.BoxSizer(wx.HORIZONTAL) brow.Add(btn_goto, 0, ALL_EXP | wx.ALIGN_LEFT, 1) brow.Add(btn_erase, 0, ALL_EXP | wx.ALIGN_LEFT, 1) brow.Add(btn_show, 0, ALL_EXP | wx.ALIGN_LEFT, 1) # brow.Add(btn_many, 0, ALL_EXP|wx.ALIGN_LEFT, 1) self.pos_list = wx.ListBox(self) self.pos_list.SetBackgroundColour(wx.Colour(253, 253, 250)) self.pos_list.Bind(wx.EVT_LISTBOX, self.onSelect) self.pos_list.Bind(wx.EVT_RIGHT_DOWN, self.onRightClick) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(tlabel, 0, wx.ALIGN_LEFT | wx.ALL) sizer.Add(self.pos_name, 0, wx.ALIGN_LEFT | wx.ALL) sizer.Add(brow, 0, wx.ALIGN_LEFT | wx.ALL) sizer.Add(self.pos_list, 1, ALL_EXP | wx.ALIGN_CENTER, 3) pack(self, sizer) self.init_scandb() self.last_refresh = 0 self.get_positions_from_db() self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onTimer, self.timer) self.timer.Start(2500) def init_scandb(self): dbconn = self.config if dbconn is not None: self.instname = dbconn.get('instrument', 'microscope_stages') dbconn_dir = dbconn.get('dbconn_dir', '') dbconn_file = dbconn.get('dbconn_file', '') if os.path.exists(dbconn_dir) and len(dbconn_file) > 0: sys.path.insert(0, dbconn_dir) mod = __import__(dbconn_file) conn = mod.conn scandb = ScanDB(**mod.conn) self.instdb = InstrumentDB(scandb) if self.instdb.get_instrument(self.instname) is None: pvs = self.viewer.config['stages'].keys() self.instdb.add_instrument(self.instname, pvs=pvs) def onSave1(self, event): "save from text enter" self.onSave(event.GetString().strip()) def onSave2(self, event): "save from button push" self.onSave(self.pos_name.GetValue().strip()) def onSave(self, name): if len(name) < 1: return if name in self.positions and self.viewer.v_replace: ret = popup(self, "Overwrite Position %s?" % name, "Veriry Overwrite Position", style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) if ret != wx.ID_YES: return imgfile = '%s.jpg' % time.strftime('%b%d_%H%M%S') imgfile = os.path.join(self.viewer.imgdir, imgfile) tmp_pos = self.viewer.ctrlpanel.read_position() imgdata, count = None, 0 if not os.path.exists(self.viewer.imgdir): os.makedirs(self.viewer.imgdir) while imgdata is None and count < 100: imgdata = self.viewer.save_image(imgfile) if imgdata is None: time.sleep(0.5) count = count + 1 imgdata['source'] = 'SampleStage' imgdata['data_format'] = 'file' imgdata.pop('data') notes = json.dumps(imgdata) fullpath = os.path.join(os.getcwd(), imgfile) self.positions[name] = { 'image': fullpath, 'timestamp': time.strftime('%b %d %H:%M:%S'), 'position': tmp_pos, 'notes': notes } if name not in self.pos_list.GetItems(): self.pos_list.Append(name) self.instdb.save_position(self.instname, name, tmp_pos, notes=notes, image=fullpath) self.pos_list.SetStringSelection(name) # auto-save file self.viewer.autosave(positions=self.positions) self.viewer.write_htmllog(name, self.positions[name]) imgfile_exists = False t0 = time.time() if not imgfile_exists and time.time() - t0 < 10: imgfile_exists = os.path.exists(fullpath) time.sleep(0.5) if imgfile_exists: self.viewer.write_message("Saved Position '%s', image in %s" % (name, imgfile)) else: self.viewer.write_message("COULD NOT SAVE IMAGE FILE!!") wx.CallAfter(Closure(self.onSelect, event=None, name=name)) def onShow(self, event): posname = self.pos_list.GetStringSelection() ipos = self.pos_list.GetSelection() if posname is None or len(posname) < 1: return try: self.image_display.Show() self.image_display.Raise() except: del self.image_display self.image_display = None if self.image_display is None: self.image_display = ImageDisplayFrame() self.image_display.Raise() thispos = self.positions[posname] try: notes = json.loads(thispos['notes']) except: notes = {'data_format': ''} if isinstance(notes, basestring): notes = json.loads(notes) label = [] stages = self.viewer.config['stages'] posvals = self.positions[posname]['position'] for pvname, value in posvals.items(): value = thispos['position'][pvname] desc = stages.get(pvname, {}).get('desc', None) if desc is None: desc = pvname label.append("%s=%.4f" % (desc, float(value))) label = ', '.join(label) label = '%s: %s' % (posname, label) data = thispos['image'] if str(notes['data_format']) == 'file': self.image_display.showfile(data, title=posname, label=label) elif str(notes['data_format']) == 'base64': size = notes.get('image_size', (800, 600)) self.image_display.showb64img(data, size=size, title=posname, label=label) else: print('Cannot show image for %s' % posname) def onGo(self, event): posname = self.pos_list.GetStringSelection() if posname is None or len(posname) < 1: return stages = self.viewer.config['stages'] posvals = self.positions[posname]['position'] postext = [] for pvname, value in posvals.items(): label = pvname desc = stages.get(pvname, {}).get('desc', None) if desc is not None: label = '%s (%s)' % (desc, pvname) postext.append(' %s\t= %.4f' % (label, float(value))) postext = '\n'.join(postext) if self.viewer.v_move: ret = popup(self, "Move to %s?: \n%s" % (posname, postext), 'Verify Move', style=wx.YES_NO | wx.ICON_QUESTION) if ret != wx.ID_YES: return for pvname, value in posvals.items(): caput(pvname, value) self.viewer.write_message('moved to %s' % posname) def onErase(self, event=None, posname=None, query=True): if posname is None: posname = self.pos_list.GetStringSelection() if posname is None or len(posname) < 1: return if self.viewer.v_erase and query: if wx.ID_YES != popup(self, "Erase %s?" % (posname), 'Verify Erase', style=wx.YES_NO | wx.ICON_QUESTION): return pos_names = self.pos_list.GetItems() ipos = pos_names.index(posname) self.instdb.remove_position(self.instname, posname) self.positions.pop(posname) self.pos_list.Delete(ipos) self.pos_name.Clear() self.viewer.write_message('Erased Position %s' % posname) def onEraseMany(self, event=None): if self.instdb is not None: ErasePositionsDialog(self.positions.keys(), instname=self.instname, instdb=self.instdb) def onMicroscopeTransfer(self, event=None): offline = self.config.get('offline', '') if self.instdb is not None: TransferPositionsDialog(offline, instname=self.instname, instdb=self.instdb, parent=self) def onMicroscopeCalibrate(self, event=None, **kws): offline = self.config.get('offline', '') print('Calibrate to Offline : ', offline) def onSelect(self, event=None, name=None): "Event handler for selecting a named position" if name is None: name = str(event.GetString().strip()) if name is None or name not in self.positions: return self.pos_name.SetValue(name) def onRightClick(self, event=None): menu = wx.Menu() # make basic widgets for popup menu for item, name in (('popup_up1', 'Move up'), ('popup_dn1', 'Move down'), ('popup_upall', 'Move to top'), ('popup_dnall', 'Move to bottom')): setattr(self, item, wx.NewId()) wid = getattr(self, item) self.Bind(wx.EVT_MENU, self.onRightEvent, wid) menu.Append(wid, name) self.PopupMenu(menu) menu.Destroy() def onRightEvent(self, event=None): "popup box event handler" idx = self.pos_list.GetSelection() if idx < 0: # no item selected return wid = event.GetId() namelist = list(self.positions.keys())[:] stmp = {} for name in namelist: stmp[name] = self.positions[name] if wid == self.popup_up1 and idx > 0: namelist.insert(idx - 1, namelist.pop(idx)) elif wid == self.popup_dn1 and idx < len(namelist): namelist.insert(idx + 1, namelist.pop(idx)) elif wid == self.popup_upall: namelist.insert(0, namelist.pop(idx)) elif wid == self.popup_dnall: namelist.append(namelist.pop(idx)) newpos = {} for name in namelist: newpos[name] = stmp[name] self.init_positions(newpos) self.viewer.autosave(positions=self.positions) def set_positions(self, positions): "set the list of position on the left-side panel" cur_sel = self.pos_list.GetStringSelection() self.pos_list.Clear() self.positions = positions for name, val in self.positions.items(): self.pos_list.Append(name) if cur_sel in self.positions: self.pos_list.SetStringSelection(cur_sel) self.last_refresh = 0 def onTimer(self, evt=None): if self.poslist_select is None: inst = self.instdb.get_instrument(self.instname) cls, tab = self.instdb.scandb.get_table('position') self.poslist_select = tab.select().where( tab.c.instrument_id == inst.id) npos = len(self.poslist_select.execute().fetchall()) now = time.time() if (npos != len(self.posnames) or (now - self.last_refresh) > 900.0): # print("Timer ", npos, len(self.posnames), now-self.last_refresh) self.get_positions_from_db() self.last_refresh = now def get_positions_from_db(self): if self.instdb is None: return positions = OrderedDict() iname = self.instname posnames = self.instdb.get_positionlist(iname) self.posnames = posnames for pname in posnames: thispos = self.instdb.get_position(iname, pname) # print(pname, thispos) image = '' notes = {} if thispos.image is not None: image = thispos.image if thispos.notes is not None: notes = thispos.notes pdat = OrderedDict() for pvpos in thispos.pv: pdat[pvpos.pv.name] = pvpos.value positions[pname] = dict(position=pdat, image=image, notes=notes) self.set_positions(positions) def SavePositions(self, fname): """ save positions to external file """ out = [POS_HEADER] for name, val in self.positions.items(): pos = [] notes = val['notes'] img = val['image'] ts = val.get('timestamp', '') for pvname, val in val['position'].items(): pos.append((pvname, val)) out.append(json.dumps((name, pos, notes, img, ts))) out.append('') out = '\n'.join(out) fout = open(fname, 'w') fout.write(out) fout.close() def LoadPositions(self, fname): """ save positions to external file """ try: fh = open(fname, 'r') text = fh.readlines() fh.close() except IOError: print('IO Error') return -1 header = text[0].replace('\n', '').replace('\r', '') if header != POS_HEADER: print('Bad Header', header) return -2 for line in text[1:]: name, pos, notes, img, ts = json.loads(line) tmp_pos = OrderedDict(pos) try: self.positions[name] = { 'image': img, 'timestamp': ts, 'position': tmp_pos, 'notes': notes } except: print('Cannot set', name, tmp_pos, notes, img) try: self.instdb.save_position(self.instname, name, tmp_pos, notes=json.dumps(notes), image=img) except: print('Could save ', name, tmp_pos, notes, img) #print(" Import Pos ", name, img, notes) self.set_positions(self.positions) return 0
class PositionPanel(wx.Panel): """panel of position lists, with buttons""" def __init__(self, parent, config=None): wx.Panel.__init__(self, parent, -1, size=(300, 500)) self.size = (300, 500) self.parent = parent self.config = config self.image_display = None self.pos_name = wx.TextCtrl(self, value="", size=(285, 25), style=wx.TE_PROCESS_ENTER) self.pos_name.Bind(wx.EVT_TEXT_ENTER, self.onSave1) tlabel = wx.StaticText(self, label="Save Position: ") bkws = dict(size=(55, -1)) btn_save = add_button(self, "Save", action=self.onSave2, **bkws) btn_goto = add_button(self, "Go To", action=self.onGo, **bkws) btn_erase = add_button(self, "Erase", action=self.onErase, **bkws) btn_show = add_button(self, "Show", action=self.onShow, **bkws) # btn_many = add_button(self, "Erase Many", action=self.onEraseMany, **bkws) brow = wx.BoxSizer(wx.HORIZONTAL) brow.Add(btn_save, 0, ALL_EXP | wx.ALIGN_LEFT, 1) brow.Add(btn_goto, 0, ALL_EXP | wx.ALIGN_LEFT, 1) brow.Add(btn_erase, 0, ALL_EXP | wx.ALIGN_LEFT, 1) brow.Add(btn_show, 0, ALL_EXP | wx.ALIGN_LEFT, 1) # brow.Add(btn_many, 0, ALL_EXP|wx.ALIGN_LEFT, 1) self.pos_list = wx.ListBox(self) self.pos_list.SetBackgroundColour(wx.Colour(253, 253, 250)) self.pos_list.Bind(wx.EVT_LISTBOX, self.onSelect) self.pos_list.Bind(wx.EVT_RIGHT_DOWN, self.onRightClick) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(tlabel, 0, wx.ALIGN_LEFT | wx.ALL) sizer.Add(self.pos_name, 0, wx.ALIGN_LEFT | wx.ALL) sizer.Add(brow, 0, wx.ALIGN_LEFT | wx.ALL) sizer.Add(self.pos_list, 1, ALL_EXP | wx.ALIGN_CENTER, 3) # print(" Position Panel ", self.GetSize(), self.size) pack(self, sizer) self.init_scandb() self.last_refresh = 0 self.get_positions_from_db() self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onTimer, self.timer) self.timer.Start(3000) def init_scandb(self): dbconn = self.config if dbconn is not None: self.instname = dbconn.get("instrument", "microscope_stages") if dbconn["port"] in ("", "None", None): dbconn.pop("port") scandb = ScanDB(**dbconn) self.instdb = InstrumentDB(scandb) if self.instdb.get_instrument(self.instname) is None: pvs = self.parent.config["stages"].keys() self.instdb.add_instrument(self.instname, pvs=pvs) def onSave1(self, event): "save from text enter" self.onSave(event.GetString().strip()) def onSave2(self, event): "save from button push" self.onSave(self.pos_name.GetValue().strip()) def onSave(self, name): if len(name) < 1: return if name in self.positions and self.parent.v_replace: ret = popup( self, "Overwrite Position %s?" % name, "Veriry Overwrite Position", style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION, ) if ret != wx.ID_YES: return imgfile = "%s.jpg" % time.strftime("%b%d_%H%M%S") imgfile = os.path.join(self.parent.imgdir, imgfile) tmp_pos = self.parent.ctrlpanel.read_position() imgdata, count = None, 0 while imgdata is None and count < 100: imgdata = self.parent.save_image(imgfile) if imgdata is None: time.sleep(0.5) count = count + 1 imgdata["source"] = "SampleStage" imgdata["data_format"] = "file" imgdata.pop("data") notes = json.dumps(imgdata) fullpath = os.path.join(os.getcwd(), imgfile) self.positions[name] = { "image": fullpath, "timestamp": time.strftime("%b %d %H:%M:%S"), "position": tmp_pos, "notes": notes, } if name not in self.pos_list.GetItems(): self.pos_list.Append(name) self.instdb.save_position(self.instname, name, tmp_pos, notes=notes, image=fullpath) self.pos_list.SetStringSelection(name) # auto-save file self.parent.autosave(positions=self.positions) self.parent.write_htmllog(name, self.positions[name]) self.parent.write_message("Saved Position '%s', image in %s" % (name, imgfile)) wx.CallAfter(Closure(self.onSelect, event=None, name=name)) def onShow(self, event): posname = self.pos_list.GetStringSelection() ipos = self.pos_list.GetSelection() if posname is None or len(posname) < 1: return try: self.image_display.Show() self.image_display.Raise() except: del self.image_display self.image_display = None if self.image_display is None: self.image_display = ImageDisplayFrame() self.image_display.Raise() thispos = self.positions[posname] try: notes = json.loads(thispos["notes"]) except: notes = {"data_format": ""} label = [] stages = self.parent.config["stages"] posvals = self.positions[posname]["position"] for pvname, value in posvals.items(): value = thispos["position"][pvname] desc = stages.get(pvname, {}).get("desc", None) if desc is None: desc = pvname label.append("%s=%.4f" % (desc, float(value))) label = ", ".join(label) label = "%s: %s" % (posname, label) data = thispos["image"] if str(notes["data_format"]) == "file": self.image_display.showfile(data, title=posname, label=label) elif str(notes["data_format"]) == "base64": size = notes.get("image_size", (800, 600)) self.image_display.showb64img(data, size=size, title=posname, label=label) else: print "Cannot show image for %s" % posname def onGo(self, event): posname = self.pos_list.GetStringSelection() if posname is None or len(posname) < 1: return stages = self.parent.config["stages"] posvals = self.positions[posname]["position"] postext = [] for pvname, value in posvals.items(): label = pvname desc = stages.get(pvname, {}).get("desc", None) if desc is not None: label = "%s (%s)" % (desc, pvname) postext.append(" %s\t= %.4f" % (label, float(value))) postext = "\n".join(postext) if self.parent.v_move: ret = popup( self, "Move to %s?: \n%s" % (posname, postext), "Verify Move", style=wx.YES_NO | wx.ICON_QUESTION ) if ret != wx.ID_YES: return for pvname, value in posvals.items(): caput(pvname, value) self.parent.write_message("moved to %s" % posname) def onErase(self, event): posname = self.pos_list.GetStringSelection() ipos = self.pos_list.GetSelection() if posname is None or len(posname) < 1: return if self.parent.v_erase: if wx.ID_YES != popup(self, "Erase %s?" % (posname), "Verify Erase", style=wx.YES_NO | wx.ICON_QUESTION): return self.instdb.remove_position(self.instname, posname) self.positions.pop(posname) self.pos_list.Delete(ipos) self.pos_name.Clear() self.parent.write_message("Erased Position %s" % posname) def onEraseMany(self, event=None): if self.instdb is not None: ErasePositionsDialog(self.positions.keys(), instname=self.instname, instdb=self.instdb) def onSelect(self, event=None, name=None): "Event handler for selecting a named position" if name is None: name = str(event.GetString().strip()) if name is None or name not in self.positions: return self.pos_name.SetValue(name) def onRightClick(self, event=None): menu = wx.Menu() # make basic widgets for popup menu for item, name in ( ("popup_up1", "Move up"), ("popup_dn1", "Move down"), ("popup_upall", "Move to top"), ("popup_dnall", "Move to bottom"), ): setattr(self, item, wx.NewId()) wid = getattr(self, item) self.Bind(wx.EVT_MENU, self.onRightEvent, wid) menu.Append(wid, name) self.PopupMenu(menu) menu.Destroy() def onRightEvent(self, event=None): "popup box event handler" idx = self.pos_list.GetSelection() if idx < 0: # no item selected return wid = event.GetId() namelist = list(self.positions.keys())[:] stmp = {} for name in namelist: stmp[name] = self.positions[name] if wid == self.popup_up1 and idx > 0: namelist.insert(idx - 1, namelist.pop(idx)) elif wid == self.popup_dn1 and idx < len(namelist): namelist.insert(idx + 1, namelist.pop(idx)) elif wid == self.popup_upall: namelist.insert(0, namelist.pop(idx)) elif wid == self.popup_dnall: namelist.append(namelist.pop(idx)) newpos = {} for name in namelist: newpos[name] = stmp[name] self.init_positions(newpos) self.parent.autosave(positions=self.positions) def set_positions(self, positions): "set the list of position on the left-side panel" cur_sel = self.pos_list.GetStringSelection() self.pos_list.Clear() self.positions = positions for name, val in self.positions.items(): self.pos_list.Append(name) if cur_sel in self.positions: self.pos_list.SetStringSelection(cur_sel) self.last_refresh = time.time() def onTimer(self, evt=None): posnames = self.instdb.get_positionlist(self.instname) if len(posnames) != len(self.posnames) or (time.time() - self.last_refresh) > 15.0: self.get_positions_from_db() def get_positions_from_db(self): if self.instdb is None: return positions = OrderedDict() iname = self.instname posnames = self.instdb.get_positionlist(iname) self.posnames = posnames for pname in posnames: thispos = self.instdb.get_position(iname, pname) image = "" notes = {} if thispos.image is not None: image = thispos.image if thispos.notes is not None: notes = thispos.notes pdat = OrderedDict() for pvpos in thispos.pvs: pdat[pvpos.pv.name] = pvpos.value positions[pname] = dict(position=pdat, image=image, notes=notes) self.set_positions(positions)