def cb_set(value, ctrl=value_ctrl, unit=unit): for i in range(ctrl.GetCount()): if ((isinstance(value, float) and util.almost_equal(ctrl.GetClientData(i), value)) or ctrl.GetClientData(i) == value): logging.debug("Setting ComboBox value to %s", ctrl.Items[i]) ctrl.SetSelection(i) break else: logging.warning("No existing label found for value %s", value) # entering value as free text txt = value_to_str(value, unit) ctrl.SetValue(txt)
def cb_set(value, va=va, ctrl=value_ctrl, u=unit, acc=accuracy): # Re-read the value from the VA because it'll be called via # CallAfter(), and if the value is changed multiple times, it might # not be in chronological order. value = va.value for i in range(ctrl.GetCount()): d = ctrl.GetClientData(i) if (d == value or (all(isinstance(v, float) for v in (value, d)) and util.almost_equal(d, value)) ): logging.debug("Setting combobox value to %s", ctrl.Items[i]) ctrl.SetSelection(i) break else: logging.debug("No existing label found for value %s in combobox ctrl %d", value, id(ctrl)) # entering value as free text txt = value_to_str(value, u, acc) ctrl.SetValue(txt)
def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy): ctrl.SetValue(value_to_str(value, u, acc))
def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy): ctrl.SetValue(value_to_str(value, u, acc, pretty_time=True))
def create_setting_entry(container, name, va, hw_comp, conf=None, change_callback=None): """ Determine what type on control to use for a setting and have the container create it Args: container (SettingsController or StreamController): Controller in charge of the settings name (str): Name of the setting va (VigilantAttribute): The va containing the value of the setting hw_comp (Component): The hardware component to which the setting belongs conf ({} or None): The optional configuration options for the control change_callback (callable): Callable to bind to the control's change event Returns: SettingEntry or None (if CONTROL_NONE) """ value_ctrl = lbl_ctrl = setting_entry = None # If no conf provided, set it to an empty dictionary conf = conf or {} # Get the range and choices min_val, max_val, choices, unit = process_setting_metadata(hw_comp, va, conf) # Format the provided choices choices_formatted, choices_si_prefix = format_choices(choices) # Determine the control type to use, either from config or some 'smart' default control_type = determine_control_type(hw_comp, va, choices_formatted, conf) # Special case, early stop if control_type == odemis.gui.CONTROL_NONE: return None # Format label label_text = conf.get('label', label_to_human(name)) tooltip = conf.get('tooltip', "") logging.debug("Adding VA %s", label_text) # Create the needed wxPython controls if control_type == odemis.gui.CONTROL_READONLY: val = va.value accuracy = conf.get('accuracy', 3) val_str = value_to_str(val, unit, accuracy, pretty_time=True) lbl_ctrl, value_ctrl = container.add_readonly_field(label_text, val_str) def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy): ctrl.SetValue(value_to_str(value, u, acc, pretty_time=True)) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, va_2_ctrl=set_ctrl) elif control_type == odemis.gui.CONTROL_TEXT: val = va.value accuracy = conf.get('accuracy', 3) val_str = value_to_str(val, unit, accuracy) # No pretty_time, as we don't support reading it back lbl_ctrl, value_ctrl = container.add_text_field(label_text, val_str) # To set the value on the control (when the VA is updated) def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy): ctrl.SetValue(value_to_str(value, u, acc)) # To retrieve the actual value, which may contain prefix and unit def text_get(ctrl=value_ctrl, va=va): ctrl_value = ctrl.GetValue() va_val = va.value try: new_val = str_to_value(ctrl_value, va) except (ValueError, TypeError): logging.warning("Value %s couldn't be understood", ctrl_value, exc_info=True) new_val = va_val # To force going back to last value # if it ends up being the same value as before the VA will # not update, so force reformatting it if va_val == new_val: set_ctrl(va_val) return new_val setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, va_2_ctrl=set_ctrl, ctrl_2_va=text_get, events=wx.EVT_TEXT_ENTER) if change_callback: value_ctrl.Bind(wx.EVT_TEXT_ENTER, change_callback) elif control_type in (odemis.gui.CONTROL_SAVE_FILE, odemis.gui.CONTROL_OPEN_FILE): val = va.value if not val: config = guiconf.get_acqui_conf() val = config.last_path if control_type == odemis.gui.CONTROL_SAVE_FILE: dialog_style = wx.FD_SAVE else: # odemis.gui.CONTROL_OPEN_FILE dialog_style = wx.FD_OPEN clearlabel = conf.get('clearlabel') # Text to show when no filename (+ allow to clear the filename) wildcard = conf.get('wildcard', "*.*") # File extension wildcard string lbl_ctrl, value_ctrl = container.add_file_button(label_text, val, clearlabel, wildcard=wildcard, dialog_style=dialog_style) # Add the corresponding setting entry setting_entry = SettingEntry(name=label_text, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=EVT_FILE_SELECT) elif control_type == odemis.gui.CONTROL_SLIDER: # The slider is accompanied by an extra number text field if "type" in conf: if conf["type"] == "integer": # add_integer_slider factory = container.add_integer_slider elif conf["type"] == "slider": factory = container.add_slider else: factory = container.add_float_slider else: # guess from value(s) known_values = [va.value, min_val, max_val] if choices is not None: known_values.extend(list(choices)) if any(isinstance(v, float) for v in known_values): factory = container.add_float_slider else: factory = container.add_integer_slider # The event configuration determines what event will signal that the # setting entry has changed value. update_event = conf.get("event", wx.EVT_SLIDER) if update_event.typeId not in (wx.EVT_SCROLL_CHANGED.typeId, wx.EVT_SLIDER.typeId): raise ValueError("Illegal event type %d for Slider setting entry!" % (update_event.typeId,)) ctrl_conf = { 'min_val': min_val, 'max_val': max_val, 'scale': conf.get('scale', None), 'unit': unit, 'accuracy': conf.get('accuracy', 4), } lbl_ctrl, value_ctrl = factory(label_text, va.value, ctrl_conf) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=update_event) if change_callback: if not isinstance(update_event, collections.Iterable): update_event = (update_event,) for event in update_event: value_ctrl.Bind(event, change_callback) elif control_type == odemis.gui.CONTROL_INT: if unit == "": # don't display unit prefix if no unit unit = None ctrl_conf = { 'min_val': min_val, 'max_val': max_val, 'unit': unit, 'choices': choices, } if 'key_step' in conf: ctrl_conf['key_step'] = conf['key_step'] if 'key_step_min' in conf: ctrl_conf['key_step_min'] = conf['key_step_min'] lbl_ctrl, value_ctrl = container.add_int_field(label_text, conf=ctrl_conf) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_COMMAND_ENTER) if change_callback: value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback) elif control_type == odemis.gui.CONTROL_FLT: if unit == "": # don't display unit prefix if no unit unit = None ctrl_conf = { 'min_val': min_val, 'max_val': max_val, 'unit': unit, 'choices': choices, 'accuracy': conf.get('accuracy', 5), } if 'key_step' in conf: ctrl_conf['key_step'] = conf['key_step'] if 'key_step_min' in conf: ctrl_conf['key_step_min'] = conf['key_step_min'] lbl_ctrl, value_ctrl = container.add_float_field(label_text, conf=ctrl_conf) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_COMMAND_ENTER) if change_callback: value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback) elif control_type == odemis.gui.CONTROL_CHECK: # Only supports boolean VAs lbl_ctrl, value_ctrl = container.add_checkbox_control(label_text, value=va.value) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_CHECKBOX) if change_callback: value_ctrl.Bind(wx.EVT_CHECKBOX, change_callback) elif control_type == odemis.gui.CONTROL_RADIO: unit_fmt = (choices_si_prefix or "") + (unit or "") ctrl_conf = { 'size': (-1, 16), 'units': unit_fmt, 'choices': [v for v, _ in choices_formatted], 'labels': [l for _, l in choices_formatted], } lbl_ctrl, value_ctrl = container.add_radio_control(label_text, conf=ctrl_conf) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_BUTTON) if change_callback: value_ctrl.Bind(wx.EVT_BUTTON, change_callback) elif control_type == odemis.gui.CONTROL_COMBO: accuracy = conf.get('accuracy', 3) # TODO: Might need size=(100, 16)!! cbconf = {} if hasattr(va, "choices") and isinstance(va.choices, collections.Iterable): # Enumerated VA => don't allow entering other values cbconf["style"] = wx.CB_READONLY lbl_ctrl, value_ctrl = container.add_combobox_control(label_text, conf=cbconf) # Set choices if choices_si_prefix: for choice, formatted in choices_formatted: value_ctrl.Append(u"%s %s" % (formatted, choices_si_prefix + unit), choice) else: for choice, formatted in choices_formatted: value_ctrl.Append(u"%s%s" % (formatted, unit), choice) # A small wrapper function makes sure that the value can # be set by passing the actual value (As opposed to the text label) def cb_set(value, va=va, ctrl=value_ctrl, u=unit, acc=accuracy): # Re-read the value from the VA because it'll be called via # CallAfter(), and if the value is changed multiple times, it might # not be in chronological order. value = va.value for i in range(ctrl.GetCount()): d = ctrl.GetClientData(i) if (d == value or (all(isinstance(v, float) for v in (value, d)) and util.almost_equal(d, value)) ): logging.debug("Setting combobox value to %s", ctrl.Items[i]) ctrl.SetSelection(i) break else: logging.debug("No existing label found for value %s in combobox ctrl %d", value, id(ctrl)) # entering value as free text txt = value_to_str(value, u, acc) ctrl.SetValue(txt) # equivalent wrapper function to retrieve the actual value def cb_get(ctrl=value_ctrl, va=va, u=unit): ctrl_value = ctrl.GetValue() # Try to use the predefined value if it's available i = ctrl.GetSelection() # Warning: if the text contains an unknown value, GetSelection will # not return wx.NOT_FOUND (as expected), but the last selection value if i != wx.NOT_FOUND and ctrl.Items[i] == ctrl_value: logging.debug("Getting item value %s from combobox control", ctrl.GetClientData(i)) return ctrl.GetClientData(i) else: va_val = va.value try: new_val = str_to_value(ctrl_value, va) except (ValueError, TypeError): logging.warning("Value %s couldn't be understood", ctrl_value, exc_info=True) new_val = va_val # To force going back to last value # if it ends up being the same value as before the combobox will # not update, so force it now if va_val == new_val: cb_set(va_val) return new_val setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, va_2_ctrl=cb_set, ctrl_2_va=cb_get, events=(wx.EVT_COMBOBOX, wx.EVT_TEXT_ENTER)) if change_callback: value_ctrl.Bind(wx.EVT_COMBOBOX, change_callback) value_ctrl.Bind(wx.EVT_TEXT_ENTER, change_callback) else: logging.error("Unknown control type %s", control_type) value_ctrl.SetToolTip(tooltip) lbl_ctrl.SetToolTip(tooltip) return setting_entry
def create_setting_entry(container, name, va, hw_comp, conf=None, change_callback=None): """ Determine what type on control to use for a setting and have the container create it Args: container (SettingsController or StreamController): Controller in charge of the settings name (str): Name of the setting va (VigilantAttribute): The va containing the value of the setting hw_comp (Component): The hardware component to which the setting belongs conf ({} or None): The optional configuration options for the control change_callback (callable): Callable to bind to the control's change event Returns: SettingEntry """ value_ctrl = lbl_ctrl = setting_entry = None # If no conf provided, set it to an empty dictionary conf = conf or {} # Get the range and choices min_val, max_val, choices, unit = process_setting_metadata(hw_comp, va, conf) # Format the provided choices choices_formatted, choices_si_prefix = format_choices(choices) # Determine the control type to use, either from config or some 'smart' default control_type = determine_control_type(hw_comp, va, choices_formatted, conf) # Special case, early stop if control_type == odemis.gui.CONTROL_NONE: # No value, not even a label, just an empty entry, so that the settings are saved # during acquisition return SettingEntry(name=name, va=va, hw_comp=hw_comp) # Format label label_text = conf.get('label', label_to_human(name)) tooltip = conf.get('tooltip', "") logging.debug("Adding VA %s", label_text) # Create the needed wxPython controls if control_type == odemis.gui.CONTROL_READONLY: val = va.value accuracy = conf.get('accuracy', 3) val_str = value_to_str(val, unit, accuracy, pretty_time=True) lbl_ctrl, value_ctrl = container.add_readonly_field(label_text, val_str) def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy): ctrl.SetValue(value_to_str(value, u, acc, pretty_time=True)) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, va_2_ctrl=set_ctrl) elif control_type == odemis.gui.CONTROL_TEXT: val = va.value accuracy = conf.get('accuracy', 3) val_str = value_to_str(val, unit, accuracy) # No pretty_time, as we don't support reading it back lbl_ctrl, value_ctrl = container.add_text_field(label_text, val_str) # To set the value on the control (when the VA is updated) def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy): ctrl.SetValue(value_to_str(value, u, acc)) # To retrieve the actual value, which may contain prefix and unit def text_get(ctrl=value_ctrl, va=va): ctrl_value = ctrl.GetValue() va_val = va.value try: new_val = str_to_value(ctrl_value, va) except (ValueError, TypeError): logging.warning("Value %s couldn't be understood", ctrl_value, exc_info=True) new_val = va_val # To force going back to last value # if it ends up being the same value as before the VA will # not update, so force reformatting it if va_val == new_val: set_ctrl(va_val) return new_val setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, va_2_ctrl=set_ctrl, ctrl_2_va=text_get, events=wx.EVT_TEXT_ENTER) if change_callback: value_ctrl.Bind(wx.EVT_TEXT_ENTER, change_callback) elif control_type in (odemis.gui.CONTROL_SAVE_FILE, odemis.gui.CONTROL_OPEN_FILE): val = va.value if not val: config = guiconf.get_acqui_conf() val = config.last_path if control_type == odemis.gui.CONTROL_SAVE_FILE: dialog_style = wx.FD_SAVE else: # odemis.gui.CONTROL_OPEN_FILE dialog_style = wx.FD_OPEN clearlabel = conf.get('clearlabel') # Text to show when no filename (+ allow to clear the filename) wildcard = conf.get('wildcard', "*.*") # File extension wildcard string lbl_ctrl, value_ctrl = container.add_file_button(label_text, val, clearlabel, wildcard=wildcard, dialog_style=dialog_style) # Add the corresponding setting entry setting_entry = SettingEntry(name=label_text, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=EVT_FILE_SELECT) elif control_type == odemis.gui.CONTROL_SLIDER: # The slider is accompanied by an extra number text field if "type" in conf: if conf["type"] == "integer": # add_integer_slider factory = container.add_integer_slider elif conf["type"] == "slider": factory = container.add_slider else: factory = container.add_float_slider else: # guess from value(s) known_values = [va.value, min_val, max_val] if choices is not None: known_values.extend(list(choices)) if any(isinstance(v, float) for v in known_values): factory = container.add_float_slider else: factory = container.add_integer_slider # The event configuration determines what event will signal that the # setting entry has changed value. update_event = conf.get("event", wx.EVT_SLIDER) if update_event.typeId not in (wx.EVT_SCROLL_CHANGED.typeId, wx.EVT_SLIDER.typeId): raise ValueError("Illegal event type %d for Slider setting entry!" % (update_event.typeId,)) ctrl_conf = { 'min_val': min_val, 'max_val': max_val, 'scale': conf.get('scale', None), 'unit': unit, 'accuracy': conf.get('accuracy', 4), } lbl_ctrl, value_ctrl = factory(label_text, va.value, ctrl_conf) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=update_event) if change_callback: if not isinstance(update_event, collections.Iterable): update_event = (update_event,) for event in update_event: value_ctrl.Bind(event, change_callback) elif control_type == odemis.gui.CONTROL_INT: if unit == "": # don't display unit prefix if no unit unit = None ctrl_conf = { 'min_val': min_val, 'max_val': max_val, 'unit': unit, 'choices': choices, } if 'key_step' in conf: ctrl_conf['key_step'] = conf['key_step'] if 'key_step_min' in conf: ctrl_conf['key_step_min'] = conf['key_step_min'] lbl_ctrl, value_ctrl = container.add_int_field(label_text, conf=ctrl_conf) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_COMMAND_ENTER) if change_callback: value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback) elif control_type == odemis.gui.CONTROL_FLT: if unit == "": # don't display unit prefix if no unit unit = None ctrl_conf = { 'min_val': min_val, 'max_val': max_val, 'unit': unit, 'choices': choices, 'accuracy': conf.get('accuracy', 5), } if 'key_step' in conf: ctrl_conf['key_step'] = conf['key_step'] if 'key_step_min' in conf: ctrl_conf['key_step_min'] = conf['key_step_min'] lbl_ctrl, value_ctrl = container.add_float_field(label_text, conf=ctrl_conf) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_COMMAND_ENTER) if change_callback: value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback) elif control_type == odemis.gui.CONTROL_CHECK: # Only supports boolean VAs lbl_ctrl, value_ctrl = container.add_checkbox_control(label_text, value=va.value) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_CHECKBOX) if change_callback: value_ctrl.Bind(wx.EVT_CHECKBOX, change_callback) elif control_type == odemis.gui.CONTROL_RADIO: unit_fmt = (choices_si_prefix or "") + (unit or "") ctrl_conf = { 'size': (-1, 16), 'units': unit_fmt, 'choices': [v for v, _ in choices_formatted], 'labels': [l for _, l in choices_formatted], } lbl_ctrl, value_ctrl = container.add_radio_control(label_text, conf=ctrl_conf) setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_BUTTON) if change_callback: value_ctrl.Bind(wx.EVT_BUTTON, change_callback) elif control_type == odemis.gui.CONTROL_COMBO: accuracy = conf.get('accuracy', 3) # TODO: Might need size=(100, 16)!! cbconf = {} if hasattr(va, "choices") and isinstance(va.choices, collections.Iterable): # Enumerated VA => don't allow entering other values cbconf["style"] = wx.CB_READONLY lbl_ctrl, value_ctrl = container.add_combobox_control(label_text, conf=cbconf) # Set choices if choices_si_prefix: for choice, formatted in choices_formatted: value_ctrl.Append(u"%s %s" % (formatted, choices_si_prefix + unit), choice) else: for choice, formatted in choices_formatted: value_ctrl.Append(u"%s%s" % (formatted, unit), choice) # A small wrapper function makes sure that the value can # be set by passing the actual value (As opposed to the text label) def cb_set(value, va=va, ctrl=value_ctrl, u=unit, acc=accuracy): # Re-read the value from the VA because it'll be called via # CallAfter(), and if the value is changed multiple times, it might # not be in chronological order. value = va.value for i in range(ctrl.GetCount()): d = ctrl.GetClientData(i) if (d == value or (all(isinstance(v, float) for v in (value, d)) and util.almost_equal(d, value)) ): logging.debug("Setting combobox value to %s", ctrl.Items[i]) ctrl.SetSelection(i) break else: logging.debug("No existing label found for value %s in combobox ctrl %d", value, id(ctrl)) # entering value as free text txt = value_to_str(value, u, acc) ctrl.SetValue(txt) # equivalent wrapper function to retrieve the actual value def cb_get(ctrl=value_ctrl, va=va, u=unit): ctrl_value = ctrl.GetValue() # Try to use the predefined value if it's available i = ctrl.GetSelection() # Warning: if the text contains an unknown value, GetSelection will # not return wx.NOT_FOUND (as expected), but the last selection value if i != wx.NOT_FOUND and ctrl.Items[i] == ctrl_value: logging.debug("Getting item value %s from combobox control", ctrl.GetClientData(i)) return ctrl.GetClientData(i) else: va_val = va.value try: new_val = str_to_value(ctrl_value, va) except (ValueError, TypeError): logging.warning("Value %s couldn't be understood", ctrl_value, exc_info=True) new_val = va_val # To force going back to last value # if it ends up being the same value as before the combobox will # not update, so force it now if va_val == new_val: cb_set(va_val) return new_val setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, va_2_ctrl=cb_set, ctrl_2_va=cb_get, events=(wx.EVT_COMBOBOX, wx.EVT_TEXT_ENTER)) if change_callback: value_ctrl.Bind(wx.EVT_COMBOBOX, change_callback) value_ctrl.Bind(wx.EVT_TEXT_ENTER, change_callback) else: logging.error("Unknown control type %s", control_type) value_ctrl.SetToolTip(tooltip) lbl_ctrl.SetToolTip(tooltip) return setting_entry