#!/usr/bin/env python3 import argparse parser = argparse.ArgumentParser() parser.add_argument("-a", "--all", dest="show_all_settings", action="store_true", help="show all settings") clargs = parser.parse_args() SHOWN_SETTINGS = { "image_background": "Background", "threshold": "Threshold", "project_axis": "Projection axis", "roi_background": "ROI Background", "roi_signal": "ROI Signal", "roi_radial": "ROI Radial", "radial_x0": "Radial x0", "radial_y0": "Radial y0" } ENFORCED_SETTINGS = [ "image_background_enable", "image_threshold", "function" ] KNOWN_TYPES = { "threshold": 0., "project_axis": 0, "roi_background": [0]*4, "roi_signal": [0]*4, "roi_radial": [0]*4, "radial_x0": 0, "radial_y0": 0 } SCRIPT_NAME = "spectrometer.py" TITLE = "SCam" if not clargs.show_all_settings else "SCam Expert Mode" GOOD = "✅" BAD = "❌" import itertools import subprocess import wx try: from cam_server_client import PipelineClient except ImportError: from fake import PipelineClient from tools import EXPANDING, STRETCH, make_filled_vbox, make_filled_hbox from entrybutton import EntryButton from settings import SettingsList from pipelines import Pipelines pc = PipelineClient("http://sf-daqsync-01:8889") class MainFrame(wx.Frame): def __init__(self, parent=None, title=TITLE): super().__init__(parent, title=title) panel_main = MainPanel(self) self.sizer = sizer = make_filled_vbox([panel_main]) self.SetSizerAndFit(sizer) class MainPanel(wx.Panel): def __init__(self, parent): super().__init__(parent) self.instance = None self.camera = None self.pls = pls = Pipelines() pl_names = [GOOD + n for n in pls.active] pl_names += [BAD + n for n in pls.offline] pl_names.sort(key=lambda x: x[1:]) # pl_names = pls.all if not clargs.show_all_settings: pl_names = sorted(i for i in pl_names if "spec_db" in i) self.cb_pls = cb_pls = wx.ComboBox(self, choices=pl_names) self.st_status = st_status = wx.StaticText(self) self.eb_bkg = eb_bkg = EntryButton(self, label="Background Images", value=100, button="Record Background") self.btn_screenpanel = btn_screenpanel = wx.Button(self, label="Open ScreenPanel") self.btn_get_roi_bkg = btn_get_roi_bkg = wx.Button(self, label="Get ROI Background from ScreenPanel") self.btn_get_roi_sig = btn_get_roi_sig = wx.Button(self, label="Get ROI Signal from ScreenPanel") self.btn_get_roi_rad = btn_get_roi_rad = wx.Button(self, label="Get ROI Radial from ScreenPanel") self.entries = entries = SettingsList(self) self.btn_print = btn_print = wx.Button(self, label="Print") self.btn_save = btn_save = wx.Button(self, label="Save") eb_bkg.Disable() btn_screenpanel.Disable() btn_get_roi_bkg.Disable() btn_get_roi_sig.Disable() btn_get_roi_rad.Disable() btn_print.Disable() btn_save.Disable() cb_pls.Bind(wx.EVT_COMBOBOX, self.on_select) eb_bkg.button.Bind(wx.EVT_BUTTON, self.on_save_bkg) btn_screenpanel.Bind(wx.EVT_BUTTON, self.on_screenpanel) btn_get_roi_bkg.Bind(wx.EVT_BUTTON, self.on_get_roi_bkg) btn_get_roi_sig.Bind(wx.EVT_BUTTON, self.on_get_roi_sig) btn_get_roi_rad.Bind(wx.EVT_BUTTON, self.on_get_roi_rad) btn_print.Bind(wx.EVT_BUTTON, self.on_print) btn_save.Bind(wx.EVT_BUTTON, self.on_save_cfg) widgets = [btn_screenpanel, btn_get_roi_bkg, btn_get_roi_sig, btn_get_roi_rad] btns_get_roi = make_filled_vbox(widgets) widgets = [btn_print, btn_save] btns_bottom = make_filled_hbox(widgets) widgets = [cb_pls, st_status, STRETCH, eb_bkg, btns_get_roi, STRETCH, entries, btns_bottom] sizer = make_filled_vbox(widgets, border=10) self.SetSizer(sizer) def on_save_bkg(self, event): num = self.eb_bkg.GetValue() bkgname = pc.collect_background(self.camera, num) self.entries.set("image_background", bkgname) print(bkgname) def on_select(self, event): self.entries.clear() self.instance = instance = self.cb_pls.GetValue().strip(GOOD + BAD) pipeline = self.pls[instance] state = pipeline.state print(f"{instance} is {state}") symbol = GOOD if state == "active" else BAD self.st_status.SetLabel(f"status: {state} {symbol}") cfg = pipeline.get() print("current config:", cfg) # use contents of KNOWN_TYPES to pre-fill cfg #TODO use setdefault instead? cfg_o = cfg cfg_x = KNOWN_TYPES.copy() cfg_x.update(cfg) cfg = cfg_x self.camera = camera = cfg["camera_name"] self.orig_cfg = cfg name_mapping = None if not clargs.show_all_settings: self.orig_cfg = filter_dict(cfg, SHOWN_SETTINGS, ENFORCED_SETTINGS) name_mapping = SHOWN_SETTINGS check_incoming_cfg(cfg) cfg = filter_dict(cfg, SHOWN_SETTINGS) self.entries.update(cfg, name_mapping) # disable and clear lines added only from KNOWN_TYPES for i in self.entries.children.values(): if i.name not in cfg_o: i.disable() i.clear() # ensure background name is always set unless there is no background at all bkg_setting = self.entries["image_background"] if not bkg_setting.get_state(): try: latest_bkg = pc.get_latest_background(camera) except ValueError: pass else: bkg_setting.set_value(latest_bkg) self.eb_bkg.Enable() self.btn_screenpanel.Enable() self.btn_get_roi_sig.Enable() self.btn_get_roi_bkg.Enable() self.btn_get_roi_rad.Enable() self.btn_print.Enable() self.btn_save.Enable() self._adjust_size() def _adjust_size(self): parent = self.GetParent() parent.sizer.Layout() parent.Fit() def make_cfg(self): cfg = self.entries.get() if not clargs.show_all_settings: cfg = check_outgoing_cfg(cfg) return cfg def on_screenpanel(self, event): cam = self.camera cmd = f"screen_panel -persist -cam={cam}" print(cmd) subprocess.Popen(cmd.split(), start_new_session=True) def on_get_roi_bkg(self, event): roi = self._get_roi() if roi is None: return self.entries.set("roi_background", roi) def on_get_roi_sig(self, event): roi = self._get_roi() if roi is None: return self.entries.set("roi_signal", roi) def on_get_roi_rad(self, event): roi = self._get_roi() if roi is None: return self.entries.set("roi_radial", roi) def _get_roi(self): cam = self.camera si = pc.get_server_info() all_pls = si["active_instances"] cam_pls = [i for i in all_pls if i.startswith(cam)] print(cam_pls) suffices = ("sp", "sp1") sp_pls = [i for i in cam_pls if i.split("_")[1] in suffices] if len(sp_pls) == 0: print("no screenpanel found for camera:", cam) return None if len(sp_pls) > 1: print("several screenpanels found for camera:", cam) for sp in sp_pls: print("-", sp) return None sp = sp_pls[0] cfg = pc.get_instance_config(sp) roi = cfg["image_region_of_interest"] if roi is None: print("no roi set in screenpanel:", sp) return None xmin, xdelta, ymin, ydelta = roi xmax = xmin + xdelta ymax = ymin + ydelta return ymin, ymax, xmin, xmax def on_print(self, event): old_cfg = self.orig_cfg new_cfg = self.make_cfg() print("\nold config:\n", old_cfg) print("\nnew config:\n", new_cfg) print("\nunchanged\n" if old_cfg == new_cfg else "\nchanged\n") def on_save_cfg(self, event): new_cfg = self.make_cfg() pipeline = self.pls[self.instance] res = pipeline.set(new_cfg) print("result config:", res) def check_incoming_cfg(cfg): bkg_enable = cfg.get("image_background_enable") if bkg_enable not in (None, "passive"): printable_bkg_enable = printable(bkg_enable) print(f"Warning: found: image_background_enable = {printable_bkg_enable}, this will be set to None or \"passive\" upon saving") img_thresh = cfg.get("image_threshold") if img_thresh is not None: printable_img_thresh = printable(img_thresh) print(f"Warning: found: image_threshold = {printable_img_thresh}, this will be set to None upon saving") thresh = cfg.get("threshold") if thresh is None: print(f"Warning: found: threshold = None, will overwrite with value of image_threshold ({printable_img_thresh})") script = cfg.get("function") if script != SCRIPT_NAME: printable_script = printable(script) print(f"Warning: found: function = {printable_script}, this will be set to \"{SCRIPT_NAME}\" upon saving") def check_outgoing_cfg(cfg): bkg = cfg["image_background"] cfg["image_background_enable"] = "passive" if bkg else None cfg["image_threshold"] = None cfg["function"] = SCRIPT_NAME return cfg def printable(val): return repr(val).replace("'", '"') def filter_dict(d, *args): allkeys = itertools.chain.from_iterable(args) return {k: d.get(k) for k in allkeys} app = wx.App() frame = MainFrame() frame.Show() app.MainLoop()