def __init__(self, parent=None, conf=None, dbname=None, **kwds): self.config = InstrumentConfig(name=conf) self.db, self.dbname = self.connect_db(dbname) if self.db is None: return self.epics_pvs = {} self.connected = {} self.panels = {} self.epics_server = None self.server_timer = None wx.Frame.__init__(self, parent=parent, title='Epics Instruments', size=(925, -1), **kwds) self.pvlist = EpicsPVList(self) self.colors = GUIColors() self.SetBackgroundColour(self.colors.bg) wx.EVT_CLOSE(self, self.onClose) self.create_Statusbar() self.create_Menus() self.create_Frame() self.enable_epics_server()
def __init__(self, parent=None, conf=None, dbname=None, **kwds): self.config = InstrumentConfig(name=conf) wx.Frame.__init__(self, parent=parent, title='Epics Instruments', size=(925, -1), **kwds) # splash = AS.AdvancedSplash(self, timeout=5000 self.pvlist = EpicsPVList(self) self.db, self.dbname = self.connect_db(dbname) if self.db is None: return self.connected = {} self.panels = {} self.epics_server = None self.server_timer = None self.colors = GUIColors() self.SetBackgroundColour(self.colors.bg) self.Bind(wx.EVT_CLOSE, self.onClose) self.create_Statusbar() self.create_Menus() self.create_Frame() self.enable_epics_server()
def __init__(self, parent=None, conf=None, dbname=None, **kwds): self.config = InstrumentConfig(name=conf) self.db, self.dbname = self.connect_db(dbname) if self.db is None: return self.connected = {} self.panels = {} self.epics_server = None self.server_timer = None wx.Frame.__init__(self, parent=parent, title='Epics Instruments', size=(925, -1), **kwds) self.pvlist = EpicsPVList(self) for pv in self.db.get_allpvs(): self.pvlist.connect_pv(pv.name) self.colors = GUIColors() self.SetBackgroundColour(self.colors.bg) wx.EVT_CLOSE(self, self.onClose) self.create_Statusbar() self.create_Menus() self.create_Frame() self.enable_epics_server()
class InstrumentFrame(wx.Frame): def __init__(self, parent=None, conf=None, dbname=None, **kwds): self.config = InstrumentConfig(name=conf) self.db, self.dbname = self.connect_db(dbname) if self.db is None: return self.epics_pvs = {} self.connected = {} self.panels = {} self.epics_server = None self.server_timer = None wx.Frame.__init__(self, parent=parent, title='Epics Instruments', size=(925, -1), **kwds) self.pvlist = EpicsPVList(self) self.colors = GUIColors() self.SetBackgroundColour(self.colors.bg) wx.EVT_CLOSE(self, self.onClose) self.create_Statusbar() self.create_Menus() self.create_Frame() self.enable_epics_server() def connect_db(self, dbname=None, new=False): """connects to a db, possibly creating a new one""" if dbname is None: filelist = self.config.get_dblist() if new: filelist = None dlg = ConnectDialog(filelist=filelist) dlg.Raise() if dlg.ShowModal() == wx.ID_OK: dbname = dlg.filebrowser.GetValue() if not dbname.endswith('.ein'): dbname = "%s.ein" % dbname else: return None, dbname dlg.Destroy() db = InstrumentDB() if isInstrumentDB(dbname): db.connect(dbname) set_hostpid = True if not db.check_hostpid(): hostname = db.get_info('host_name') pid = db.get_info('process_id') ret = popup(None, FILE_IN_USE_MSG % (os.path.abspath(dbname), hostname, pid), 'Database in use', style=wx.YES_NO | wx.ICON_EXCLAMATION) set_hostpid = (ret != wx.ID_YES) if set_hostpid: db.set_hostpid() else: db.create_newdb(dbname, connect=True) self.config.set_current_db(dbname) return db, dbname def create_Frame(self): self.nb = flat_nb.FlatNotebook(self, wx.ID_ANY, agwStyle=FNB_STYLE) self.server_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnServerTimer, self.server_timer) colors = self.colors self.nb.SetActiveTabColour(colors.nb_active) self.nb.SetTabAreaColour(colors.nb_area) self.nb.SetNonActiveTabTextColour(colors.nb_text) self.nb.SetActiveTabTextColour(colors.nb_activetext) self.nb.SetBackgroundColour(colors.bg) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.nb, 1, wx.EXPAND) self.create_nbpages() self.SetMinSize((850, 350)) pack(self, sizer) try: self.SetIcon(wx.Icon(ICON_FILE, wx.BITMAP_TYPE_ICO)) except: pass self.Refresh() def create_nbpages(self): if self.nb.GetPageCount() > 0: self.nb.DeleteAllPages() for inst in self.db.get_all_instruments(): if inst.show is None: inst.show = 1 if int(inst.show) == 1: self.add_instrument_page(inst) def add_instrument_page(self, inst): panel = InstrumentPanel(self, inst, db=self.db, size=(925, -1), pvlist=self.pvlist, writer=self.write_message) for pv in inst.pvs: panel.add_pv(pv.name) self.panels[inst.name] = panel # self.connect_pvs(inst, wait_time=0.10) self.nb.AddPage(panel, inst.name, True) def connect_pvs(self, inst, wait_time=0.10): """connect to PVs for an instrument..""" panel = self.panels[inst.name] return def create_Menus(self): """create menus""" mbar = wx.MenuBar() file_menu = wx.Menu() opts_menu = wx.Menu() inst_menu = wx.Menu() help_menu = wx.Menu() add_menu(self, file_menu, "&New File", "Create New Instruments File", action=self.onNew) add_menu(self, file_menu, "&Open File", "Open Instruments File", action=self.onOpen) add_menu(self, file_menu, "&Save As", "Save Instruments File", action=self.onSave) file_menu.AppendSeparator() add_menu(self, file_menu, "E&xit", "Terminate the program", action=self.onClose) add_menu(self, inst_menu, "&Create New Instrument", "Add New Instrument", action=self.onAddInstrument) add_menu(self, inst_menu, "&Edit Current Instrument", "Edit Current Instrument", action=self.onEditInstrument) add_menu(self, inst_menu, "Enter a Position for the Current Instrument", "Enter a Position for the Current Instrument", action=self.onEnterPosition) inst_menu.AppendSeparator() add_menu(self, inst_menu, "&Remove Current Instrument", "Permanently Remove Current Instrument", action=self.onRemoveInstrument) add_menu(self, opts_menu, "&Settings", "Change Settings for GUI behavior", action=self.onSettings) add_menu(self, opts_menu, "Change &Font", "Select Font", action=self.onSelectFont) add_menu(self, help_menu, 'About', "More information about this program", action=self.onAbout) mbar.Append(file_menu, "&File") mbar.Append(opts_menu, "&Options") mbar.Append(inst_menu, "&Instruments") mbar.Append(help_menu, "&Help") self.SetMenuBar(mbar) def create_Statusbar(self): "create status bar" self.statusbar = self.CreateStatusBar(2, wx.CAPTION | wx.THICK_FRAME) self.statusbar.SetStatusWidths([-4, -1]) for index, name in enumerate(("Messages", "Status")): self.statusbar.SetStatusText('', index) def write_message(self, text, status='normal'): self.SetStatusText(text) def enable_epics_server(self): """connect to an epics db to act as a server for external access.""" connect = False if (1 == int(self.db.get_info('epics_use', default=0))): epics_prefix = self.db.get_info('epics_prefix', default='') if len(epics_prefix) > 1: connect = True if not connect: return self.epics_server = EpicsInstrumentServer(epics_prefix, db=self.db) self.epics_server.Start('Initializing...') if self.epics_server is not None and self.server_timer is not None: self.epics_server.SetInfo(os.path.abspath(self.dbname)) self.server_timer.Start(100) def OnServerTimer(self, evt=None): """Epics Server Events: responds to requests from EpicsInstrument Db This allows an outside client to make move requests to pre-defined instruments/positions in this database. """ server = self.epics_server if not server._pvs['TSTAMP'].connected: return server.SetTimeStamp() req = server._request if server._inst is None and len(server.InstName) > 1: server._inst = self.db.get_instrument(server.InstName) if server._moving and self.db.restore_complete(): server.MoveDone() elif 'Move' in req: move = req.pop('Move') if move and server.PosOK == 1 and server.InstOK == 1: if 1 == int(self.db.get_info('epics_use', default=0)): server._moving = 1 self.db.restore_position(server.PosName, server._inst) server.Message = 'Moving to %s' % server.PosName else: server.MoveDone() elif 'Pos' in req: posname = req.pop('Pos') pos = None if server._inst is not None: pos = self.db.get_position(posname, instrument=server._inst) server.PosOK = {True: 1, False: 0}[pos is not None] elif 'Inst' in req: instname = req.pop('Inst') inst = self.db.get_instrument(instname) if inst is not None: server._inst = inst server.InstOK = {True: 1, False: 0}[inst is not None] pos = self.db.get_position(server.PosName, instrument=server._inst) server.PosOK = {True: 1, False: 0}[pos is not None] def onAddInstrument(self, event=None): "add a new, empty instrument and start adding PVs" newname = basename = 'New Instrument' inst = self.db.get_instrument(newname) count = 1 while inst is not None: count += 1 newname = "%s(%i)" % (basename, count) inst = self.db.get_instrument(newname) inst = self.db.add_instrument(newname) panel = InstrumentPanel(self, inst, db=self.db, size=(925, -1), writer=self.write_message) self.nb.AddPage(panel, inst.name, True) EditInstrumentFrame(parent=self, db=self.db, inst=inst, epics_pvs=self.epics_pvs) def onEditInstrument(self, event=None): "edit the current instrument" inst = self.nb.GetCurrentPage().inst EditInstrumentFrame(parent=self, db=self.db, inst=inst, epics_pvs=self.epics_pvs) def onEnterPosition(self, event=None): "enter a new position for the current instrument" page = self.nb.GetCurrentPage() inst = page.inst NewPositionFrame(parent=self, db=self.db, inst=inst, page=page) def onRemoveInstrument(self, event=None): inst = self.nb.GetCurrentPage().inst iname = inst.name MSG = "Permanently Remove Instrument '%s'?\nThis cannot be undone!" ret = popup(self, MSG % iname, 'Remove Instrument', style=wx.YES_NO | wx.ICON_QUESTION) if ret != wx.ID_YES: return self.db.remove_instrument(inst) self.db.commit() pages = {} for i in range(self.nb.GetPageCount()): pages[self.nb.GetPageText(i)] = i self.nb.DeletePage(pages[iname]) def onSettings(self, event=None): try: self.settings_frame.Raise() except: self.settting_frame = SettingsFrame(parent=self, db=self.db) def onAbout(self, event=None): # First we create and fill the info object info = wx.AboutDialogInfo() info.Name = "Epics Instruments" info.Version = "0.3" info.Copyright = "2011, Matt Newville, University of Chicago" info.Description = """ Epics Instruments is an application to manage Epics PVs. An Instrument is defined as a collection of Epics PV. For each Instrument, any number of Positions can be saved and later restored simply by selecting that name. """ wx.AboutBox(info) def onOpen(self, event=None): fname = FileOpen(self, 'Open Instrument File', wildcard=EIN_WILDCARD, default_file=self.dbname) if fname is not None: self.db.close() time.sleep(1) self.dbname = fname self.config.set_current_db(fname) self.config.write() self.create_nbpages() def onNew(self, event=None): self.connect_db(dbname=self.dbname, new=True) self.create_nbpages() def onSave(self, event=None): outfile = FileSave(self, 'Save Instrument File As', wildcard=EIN_WILDCARD, default_file=self.dbname) # save current tab/instrument mapping fulldbname = os.path.abspath(self.dbname) if outfile not in (None, self.dbname, fulldbname): self.db.close() try: shutil.copy(self.dbname, outfile) except shutil.Error: pass time.sleep(1.0) self.dbname = outfile self.config.set_current_db(outfile) self.config.write() self.db = InstrumentDB(outfile) # set current tabs to the new db insts = [(i, self.nb.GetPageText(i)) for i in range(self.nb.GetPageCount())] for nbpage, name in insts: self.nb.GetPage(nbpage).db = self.db self.nb.GetPage(nbpage).inst = self.db.get_instrument(name) self.write_message("Saved Instrument File: %s" % outfile) def onSelectFont(self, evt=None): fontdata = wx.FontData() fontdata.SetInitialFont(self.GetFont()) dlg = wx.FontDialog(self, fontdata) if dlg.ShowModal() == wx.ID_OK: font = dlg.GetFontData().GetChosenFont() set_font_with_children(self, font) self.Refresh() self.Layout() dlg.Destroy() @EpicsFunction def onClose(self, event): display_order = [ self.nb.GetPage(i).inst.name for i in range(self.nb.GetPageCount()) ] for inst in self.db.get_all_instruments(): inst.show = 0 if inst.name in display_order: inst.show = 1 inst.display_order = display_order.index(inst.name) self.db.set_hostpid(clear=True) self.db.commit() epics.poll() if self.epics_server is not None: self.epics_server.Shutdown() self.config.write() time.sleep(0.5) self.Destroy()
class InstrumentFrame(wx.Frame): def __init__(self, parent=None, conf=None, dbname=None, **kwds): self.config = InstrumentConfig(name=conf) wx.Frame.__init__(self, parent=parent, title='Epics Instruments', size=(925, -1), **kwds) # splash = AS.AdvancedSplash(self, timeout=5000 self.pvlist = EpicsPVList(self) self.db, self.dbname = self.connect_db(dbname) if self.db is None: return self.connected = {} self.panels = {} self.epics_server = None self.server_timer = None self.colors = GUIColors() self.SetBackgroundColour(self.colors.bg) self.Bind(wx.EVT_CLOSE, self.onClose) self.create_Statusbar() self.create_Menus() self.create_Frame() self.enable_epics_server() def connect_db(self, dbname=None, new=False): """connects to a db, possibly creating a new one""" dlg = None if dbname is None: filelist = self.config.get_dblist() if new: filelist = None dlg = ConnectDialog(filelist=filelist) dlg.Raise() if dlg.ShowModal() == wx.ID_OK: dbname = dlg.filebrowser.GetValue() if not dbname.endswith('.ein'): dbname = "%s.ein" % dbname else: print(" ... Dlg Destroy") dlg.Destroy() return None, dbname db = InstrumentDB() if isInstrumentDB(dbname): db.connect(dbname) set_hostpid = True if not db.check_hostpid(): hostname = db.get_info('host_name') pid = db.get_info('process_id') ret = popup(None, FILE_IN_USE_MSG % (os.path.abspath(dbname), hostname, pid), 'Database in use', style=wx.YES_NO|wx.ICON_EXCLAMATION) set_hostpid = (ret != wx.ID_YES) if set_hostpid: db.set_hostpid() else: db.create_newdb(dbname, connect=True) self.config.set_current_db(dbname) if dlg is not None: dlg.message.SetLabel("Connecting to Epics PVs in database") for pv in db.get_allpvs(): self.pvlist.init_connect(pv.name, is_motor=(4==pv.pvtype_id)) return db, dbname def create_Frame(self): self.nb = flat_nb.FlatNotebook(self, wx.ID_ANY, agwStyle=FNB_STYLE) colors = self.colors self.nb.SetActiveTabColour(colors.nb_active) self.nb.SetTabAreaColour(colors.nb_area) self.nb.SetNonActiveTabTextColour(colors.nb_text) self.nb.SetActiveTabTextColour(colors.nb_activetext) self.nb.SetBackgroundColour(colors.bg) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.nb, 1, wx.EXPAND) self.create_nbpages() self.SetSize((876, 351)) self.SetMinSize((875, 350)) pack(self, sizer) try: self.SetIcon(wx.Icon(ICON_FILE, wx.BITMAP_TYPE_ICO)) except: pass self.Refresh() def create_nbpages(self): if self.nb.GetPageCount() > 0: self.nb.DeleteAllPages() for inst in self.db.get_all_instruments(): if inst.show is None: inst.show = 1 if int(inst.show) == 1: self.add_instrument_page(inst) def add_instrument_page(self, inst): panel = InstrumentPanel(self, inst, db=self.db, size=(925, -1), pvlist = self.pvlist, writer = self.write_message) self.panels[inst.name] = panel self.nb.AddPage(panel, inst.name, True) def connect_pvs(self, inst, wait_time=0.10): """connect to PVs for an instrument..""" panel = self.panels[inst.name] return def create_Menus(self): """create menus""" mbar = wx.MenuBar() file_menu = wx.Menu() opts_menu = wx.Menu() inst_menu = wx.Menu() help_menu = wx.Menu() add_menu(self, file_menu, "&Open File", "Open or Create Instruments File", action=self.onOpen) add_menu(self, file_menu, "&Save As", "Save Instruments File", action=self.onSave) file_menu.AppendSeparator() add_menu(self, file_menu, "E&xit", "Terminate the program", action=self.onClose) add_menu(self, inst_menu, "&Create New Instrument", "Add New Instrument", action=self.onAddInstrument) add_menu(self, inst_menu, "&Edit Current Instrument", "Edit Current Instrument", action=self.onEditInstrument) add_menu(self, inst_menu, "Enter a Position for the Current Instrument", "Enter a Position for the Current Instrument", action=self.onEnterPosition) inst_menu.AppendSeparator() add_menu(self, inst_menu, "&Remove Current Instrument", "Permanently Remove Current Instrument", action=self.onRemoveInstrument) add_menu(self, opts_menu, "&Select Instruments to Show", "Change Which Instruments are Shown", action=self.onSelectInstruments) add_menu(self, opts_menu, "&General Settings", "Change Settings for GUI behavior, Epics Connection", action=self.onSettings) add_menu(self, opts_menu, "Change &Font", "Select Font", action=self.onSelectFont) add_menu(self, help_menu, 'About', "More information about this program", action=self.onAbout) mbar.Append(file_menu, "&File") mbar.Append(opts_menu, "&Options") mbar.Append(inst_menu, "&Instruments") mbar.Append(help_menu, "&Help") self.SetMenuBar(mbar) def create_Statusbar(self): "create status bar" self.statusbar = self.CreateStatusBar(2, wx.CAPTION) self.statusbar.SetStatusWidths([-4,-1]) for index, name in enumerate(("Messages", "Status")): self.statusbar.SetStatusText('', index) def write_message(self,text,status='normal'): self.SetStatusText(text) def enable_epics_server(self): """connect to an epics db to act as a server for external access.""" connect = False epics_prefix = '' if (1 == int(self.db.get_info('epics_use', default=0))): epics_prefix = self.db.get_info('epics_prefix', default='') if len(epics_prefix) > 1: connect = True if not connect: return self.epics_server = EpicsInstrumentServer(epics_prefix, db=self.db) self.epics_server.Start('Initializing...') if self.epics_server is not None: self.epics_server.SetInfo(os.path.abspath(self.dbname)) self.server_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnServerTimer, self.server_timer) self.server_timer.Start(250) def OnServerTimer(self, evt=None): """Epics Server Events: responds to requests from EpicsInstrument Db This allows an outside client to make move requests to pre-defined instruments/positions in this database. """ server = self.epics_server if not server._pvs['TSTAMP'].connected: return server.SetTimeStamp() req = server._request if server._inst is None and len(server.InstName)>1: server._inst = self.db.get_instrument(server.InstName) if server._moving and self.db.restore_complete(): server.MoveDone() elif 'Move' in req: move = req.pop('Move') if move and server.PosOK==1 and server.InstOK==1: if 1 == int(self.db.get_info('epics_use', default=0)): server._moving = 1 self.db.restore_position(server.PosName, server._inst) server.Message = 'Moving to %s' % server.PosName else: server.MoveDone() elif 'Pos' in req: posname = req.pop('Pos') pos = None if server._inst is not None: pos = self.db.get_position(posname, instrument=server._inst) server.PosOK = {True:1, False:0}[pos is not None] elif 'Inst' in req: instname = req.pop('Inst') inst = self.db.get_instrument(instname) if inst is not None: server._inst = inst server.InstOK = {True:1, False:0}[inst is not None] pos = self.db.get_position(server.PosName, instrument=server._inst) server.PosOK = {True:1, False:0}[pos is not None] def onAddInstrument(self, event=None): "add a new, empty instrument and start adding PVs" newname = basename = 'New Instrument' inst = self.db.get_instrument(newname) count = 1 while inst is not None: count += 1 newname = "%s(%i)" % (basename, count) inst = self.db.get_instrument(newname) inst = self.db.add_instrument(newname) panel = InstrumentPanel(self, inst, db=self.db, pvlist=self.pvlist, size=(925, -1), writer = self.write_message) self.nb.AddPage(panel, inst.name, True) EditInstrumentFrame(parent=self, db=self.db, inst=inst) def onEditInstrument(self, event=None): "edit the current instrument" inst = self.nb.GetCurrentPage().inst EditInstrumentFrame(parent=self, db=self.db, inst=inst) def onEnterPosition(self, event=None): "enter a new position for the current instrument" page = self.nb.GetCurrentPage() inst = page.inst NewPositionFrame(parent=self, db=self.db, inst=inst, page=page) def onRemoveInstrument(self, event=None): inst = self.nb.GetCurrentPage().inst iname = inst.name MSG = "Permanently Remove Instrument '%s'?\nThis cannot be undone!" ret = popup(self, MSG % iname, 'Remove Instrument', style=wx.YES_NO|wx.ICON_QUESTION) if ret != wx.ID_YES: return self.db.remove_instrument(inst) self.db.commit() pages = {} for i in range(self.nb.GetPageCount()): pages[self.nb.GetPageText(i)] = i self.nb.DeletePage(pages[iname]) def onSettings(self, event=None): try: self.settings_frame.Raise() except: self.settting_frame = SettingsFrame(parent=self, db=self.db) def onSelectInstruments(self, event=None): try: self.instsel_frame.Raise() except: self.instsel_frame = InstSelectionFrame(parent=self, db=self.db) def onAbout(self, event=None): # First we create and fill the info object info = wx.AboutDialogInfo() info.Name = "Epics Instruments" info.Version = "0.4" info.Copyright = "2013, Matt Newville, University of Chicago" info.Description = """ Epics Instruments is an application to manage Epics PVs. An Instrument is defined as a collection of Epics PV. For each Instrument, any number of Positions can be saved and later restored simply by selecting that name. """ wx.AboutBox(info) def onOpen(self, event=None): dbname = FileOpen(self, 'Open Instrument File', wildcard=EIN_WILDCARD, default_file=self.dbname) if dbname is not None: self.db.set_hostpid(clear=True) self.db.close() time.sleep(1) self.db, self.dbname = self.connect_db(dbname) self.config.set_current_db(dbname) self.db.set_hostpid(clear=False) self.config.write() self.create_nbpages() # def onNew(self, event=None): # fname = FileOpen(self, 'New Instrument File', # wildcard=EIN_WILDCARD, # default_file=self.dbname) # # self.db.close() # time.sleep(1) # self.dbname = fname # # self.connect_db(dbname=fname, new=True) # self.config.set_current_db(fname) # self.config.write() # self.create_nbpages() # def onSave(self, event=None): outfile = FileSave(self, 'Save Instrument File As', wildcard=EIN_WILDCARD, default_file=self.dbname) # save current tab/instrument mapping fulldbname = os.path.abspath(self.dbname) if outfile not in (None, self.dbname, fulldbname): self.db.close() try: shutil.copy(self.dbname, outfile) except shutil.Error: pass time.sleep(1.0) self.dbname = outfile self.config.set_current_db(outfile) self.config.write() self.db = InstrumentDB(outfile) # set current tabs to the new db insts = [(i, self.nb.GetPageText(i)) for i in range(self.nb.GetPageCount())] for nbpage, name in insts: self.nb.GetPage(nbpage).db = self.db self.nb.GetPage(nbpage).inst = self.db.get_instrument(name) self.write_message("Saved Instrument File: %s" % outfile) def onSelectFont(self, evt=None): fontdata = wx.FontData() fontdata.SetInitialFont(self.GetFont()) dlg = wx.FontDialog(self, fontdata) if dlg.ShowModal() == wx.ID_OK: font = dlg.GetFontData().GetChosenFont() set_font_with_children(self, font) self.Refresh() self.Layout() dlg.Destroy() # @EpicsFunction def onClose(self, event): self.config.write() display_order = [self.nb.GetPage(i).inst.name for i in range(self.nb.GetPageCount())] for inst in self.db.get_all_instruments(): inst.show = 0 if inst.name in display_order: inst.show = 1 inst.display_order = display_order.index(inst.name) self.db.set_hostpid(clear=True) self.db.commit() time.sleep(0.1) for name, pv in self.pvlist.pvs.items(): try: pv.clear_callbacks() except: pass time.sleep(0.01) if self.epics_server is not None: self.epics_server.Shutdown() time.sleep(0.1) self.Destroy()