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