Beispiel #1
0
 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)
Beispiel #2
0
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()
Beispiel #3
0
 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)
Beispiel #4
0
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()
Beispiel #5
0
 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)
Beispiel #6
0
 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)
Beispiel #7
0
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
Beispiel #8
0
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
Beispiel #9
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
Beispiel #10
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
Beispiel #11
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)