def format_choices(choices, uniformat=True, si=None): """ Transform the given choices into an ordered list of (value, formatted value) tuples Args: choices (Iterable): The choices to be formatted or None uniformat (bool): If True, all the values will be formatted using the same SI prefix si (bool): si unit to format the choice values to. This argument takes precedence over the `uniformat` argument. Returns: ([(value, formatted value)], si prefix) or (None, None) """ if choices: choices_si_prefix = None # choice_fmt is an iterable of tuples: (choice, formatted choice) if isinstance(choices, dict): # In this case we assume that the values are already formatted choices_formatted = choices.items() elif si is not None: fmt, choices_si_prefix = utun.si_scale_list(choices, si) choices_formatted = zip(choices, [u"%g" % c for c in fmt]) elif len(choices) > 1 and all([isinstance(c, numbers.Real) for c in choices]): try: # TODO: does it matter? Can't we always do non-uniform? # Or just guess based on the magnitude between the lowest and biggest value? if uniformat: fmt, choices_si_prefix = utun.si_scale_list(choices) choices_formatted = zip(choices, [u"%g" % c for c in fmt]) else: fmt = [] for choice in choices: fmt.append(to_string_si_prefix(choice)) return zip(choices, fmt), choices_si_prefix except Exception: logging.exception("Formatting error!") choices_formatted = [(c, choice_to_str(c)) for c in choices] else: choices_formatted = [(c, choice_to_str(c)) for c in choices] if not isinstance(choices, OrderedDict): choices_formatted = sorted(choices_formatted) return choices_formatted, choices_si_prefix else: return None, None
def format_axis_choices(name, axis_def): """ Transform the given choices for an axis into an user friendly display name (str): the name of the axis axis_def (Axis): the axis definition returns: choices_formatted (None or list of (value, str): axis value/user-friendly display name (including the unit). None if axis doesn't support choices. """ try: choices = axis_def.choices except AttributeError: return None if not choices: return None unit = axis_def.unit if isinstance(choices, dict): choices_formatted = list(choices.items()) # In this case, normally the values are already formatted, but for # wavelength band, the "formatted" value is still a band info (ie, two # values in m) if name == "band": def to_readable_band(v): if (isinstance(v, (tuple, list)) and len(v) > 1 and all(isinstance(c, numbers.Real) for c in v)): return fluo.to_readable_band(v) else: return v choices_formatted = [(k, to_readable_band(v)) for k, v in choices_formatted] elif len(choices) > 1 and all(isinstance(c, numbers.Real) for c in choices): choices_formatted = None try: choices = sorted(choices) # Can we fit them (more or less) all with the same unit prefix? mn_non0 = min(c for c in choices if c != 0) if abs(choices[-1] / mn_non0) < 1000: fmt, choices_si_prefix = utun.si_scale_list(choices) fmt = [utun.to_string_pretty(c, 3, unit) for c in fmt] choices_formatted = list(zip(choices, fmt)) except Exception: logging.exception("Formatting error for %s", choices) if choices_formatted is None: choices_formatted = [(c, readable_str(c, unit=unit, sig=3)) for c in choices] else: choices_formatted = [(c, u"%s %s" % (choice_to_str(c), unit)) for c in choices] if not isinstance(choices, OrderedDict): # sort 2-tuples = according to first value in tuple choices_formatted = sorted(choices_formatted) return choices_formatted
def format_axis_choices(name, axis_def): """ Transform the given choices for an axis into an user friendly display name (str): the name of the axis axis_def (Axis): the axis definition returns: choices_formatted (None or list of (value, str): axis value/user-friendly display name (including the unit). None if axis doesn't support choices. """ try: choices = axis_def.choices except AttributeError: return None if not choices: return None unit = axis_def.unit if isinstance(choices, dict): choices_formatted = choices.items() # In this case, normally the values are already formatted, but for # wavelength band, the "formatted" value is still a band info (ie, two # values in m) if name == "band": def to_readable_band(v): if (isinstance(v, (tuple, list)) and len(v) > 1 and all(isinstance(c, numbers.Real) for c in v)): return fluo.to_readable_band(v) else: return v choices_formatted = [(k, to_readable_band(v)) for k, v in choices_formatted] elif len(choices) > 1 and all(isinstance(c, numbers.Real) for c in choices): choices_formatted = None try: choices = sorted(choices) # Can we fit them (more or less) all with the same unit prefix? mn_non0 = min(c for c in choices if c != 0) if abs(choices[-1] / mn_non0) < 1000: fmt, choices_si_prefix = utun.si_scale_list(choices) fmt = [utun.to_string_pretty(c, 3, unit) for c in fmt] choices_formatted = zip(choices, fmt) except Exception: logging.exception("Formatting error for %s", choices) if choices_formatted is None: choices_formatted = [(c, readable_str(c, unit=unit, sig=3)) for c in choices] else: choices_formatted = [(c, u"%s %s" % (choice_to_str(c), unit)) for c in choices] if not isinstance(choices, OrderedDict): # sort 2-tuples = according to first value in tuple choices_formatted = sorted(choices_formatted) return choices_formatted
def format_choices(choices): """ Transform the given choices into an ordered list of (value, formatted value) tuples Args: choices (Iterable): The choices to be formatted or None Returns: ([(value, formatted value)], si prefix) or (None, None) """ if choices: choices_si_prefix = None # choice_fmt is an iterable of tuples: (choice, formatted choice) if isinstance(choices, dict): # In this case we assume that the values are already formatted choices_formatted = choices.items() elif len(choices) > 1 and all(isinstance(c, numbers.Real) for c in choices): try: choices = sorted(choices) # Can we fit them (more or less) all with the same unit prefix? mn_non0 = min(c for c in choices if c != 0) if abs(choices[-1] / mn_non0) < 1000: fmt, choices_si_prefix = utun.si_scale_list(choices) fmt = [utun.to_string_pretty(c, 3) for c in fmt] choices_formatted = zip(choices, fmt) else: fmt = [to_string_si_prefix(c, sig=3) for c in choices] return zip(choices, fmt), None except Exception: logging.exception("Formatting error for %s", choices) choices_formatted = [(c, choice_to_str(c)) for c in choices] else: choices_formatted = [(c, choice_to_str(c)) for c in choices] if not isinstance(choices, OrderedDict): choices_formatted = sorted(choices_formatted) return choices_formatted, choices_si_prefix else: return None, None
def create_axis_entry(container, name, comp, conf=None): # If no conf provided, set it to an empty dictionary conf = conf or {} # Format label label_text = conf.get('label', label_to_human(name)) tooltip = conf.get('tooltip', "") logging.debug("Adding Axis control %s", label_text) ad = comp.axes[name] pos = comp.position.value[name] unit = ad.unit # If axis has .range (continuous) => slider # If axis has .choices (enumerated) => combo box if hasattr(ad, "range"): if "range" in conf: minv, maxv = conf["range"] else: minv, maxv = ad.range ctrl_conf = { 'min_val': minv, 'max_val': maxv, 'unit': unit, 'accuracy': conf.get('accuracy', 3), } lbl_ctrl, value_ctrl = container.add_float_slider(label_text, pos, ctrl_conf) # don't bind to wx.EVT_SLIDER, which happens as soon as the slider moves, # but to EVT_SCROLL_CHANGED, which happens when the user has made his # mind. This avoid too many unnecessary actuator moves and disabling the # widget too early. axis_entry = AxisSettingEntry(name, comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_SCROLL_CHANGED) else: # FIXME: should be readonly, but it fails with GetInsertionPoint (wx.CB_READONLY) lbl_ctrl, value_ctrl = container.add_combobox_control(label_text) # format the choices choices = ad.choices if isinstance(choices, dict): # it's then already value -> string (user-friendly display) choices_fmt = choices.items() unit = None elif (unit and len(choices) > 1 and all([isinstance(c, numbers.Real) for c in choices])): # TODO: need same update as add_value fmt, prefix = utun.si_scale_list(choices) choices_fmt = zip(choices, [u"%g" % c for c in fmt]) unit = prefix + unit else: choices_fmt = [(c, choice_to_str(c)) for c in choices] choices_fmt = sorted(choices_fmt) # sort 2-tuples = according to first value in tuple # FIXME: Is this still needed? def _eat_event(evt): """ Quick and dirty empty function used to 'eat' mouse wheel events """ pass value_ctrl.Bind(wx.EVT_MOUSEWHEEL, _eat_event) # Set choices if unit is None: unit = "" for choice, formatted in choices_fmt: 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, ctrl=value_ctrl, unit=unit): for i in range(ctrl.Count): if ctrl.GetClientData(i) == value: logging.debug("Setting ComboBox value to %s", ctrl.Items[i]) return ctrl.SetValue(ctrl.Items[i]) else: logging.warning("No existing label found for value %s", value) return ctrl.GetValue() # equivalent wrapper function to retrieve the actual value def cb_get(ctrl=value_ctrl, name=name): value = ctrl.GetValue() # Try to use the predefined value if it's available for i in range(ctrl.Count): if ctrl.Items[i] == value: logging.debug("Getting CB value %s", ctrl.GetClientData(i)) return ctrl.GetClientData(i) else: logging.error("Failed to find value %s for axis %s", value, name) axis_entry = AxisSettingEntry(name, comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, pos_2_ctrl=cb_set, ctrl_2_pos=cb_get, events=(wx.EVT_COMBOBOX, wx.EVT_TEXT_ENTER)) value_ctrl.SetToolTipString(tooltip) lbl_ctrl.SetToolTipString(tooltip) return axis_entry
def create_axis_entry(container, name, comp, conf=None): # If no conf provided, set it to an empty dictionary conf = conf or {} # Format label label_text = conf.get('label', label_to_human(name)) tooltip = conf.get('tooltip', "") logging.debug("Adding Axis control %s", label_text) ad = comp.axes[name] pos = comp.position.value[name] unit = ad.unit # Determine control type try: control_type = conf['control_type'] except KeyError: # If axis has .range (continuous) => slider # If axis has .choices (enumerated) => combo box if hasattr(ad, "range"): control_type = odemis.gui.CONTROL_SLIDER else: control_type = odemis.gui.CONTROL_COMBO if callable(control_type): control_type = control_type(comp, name, conf) if control_type == odemis.gui.CONTROL_SLIDER: if "range" in conf: minv, maxv = conf["range"] else: minv, maxv = ad.range ctrl_conf = { 'min_val': minv, 'max_val': maxv, 'unit': unit, 'accuracy': conf.get('accuracy', 3), } lbl_ctrl, value_ctrl = container.add_float_slider(label_text, pos, ctrl_conf) # don't bind to wx.EVT_SLIDER, which happens as soon as the slider moves, # but to EVT_SCROLL_CHANGED, which happens when the user has made his # mind. This avoid too many unnecessary actuator moves and disabling the # widget too early. axis_entry = AxisSettingEntry(name, comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_SCROLL_CHANGED) elif control_type == odemis.gui.CONTROL_FLT: if "range" in conf: minv, maxv = conf["range"] else: minv, maxv = ad.range ctrl_conf = { 'min_val': minv, 'max_val': maxv, 'unit': unit, 'accuracy': conf.get('accuracy', 3), } lbl_ctrl, value_ctrl = container.add_float_field(label_text, conf=ctrl_conf) axis_entry = AxisSettingEntry(name, comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, events=wx.EVT_COMMAND_ENTER) elif control_type == odemis.gui.CONTROL_COMBO: # FIXME: should be readonly, but it fails with GetInsertionPoint (wx.CB_READONLY) lbl_ctrl, value_ctrl = container.add_combobox_control(label_text) # format the choices choices = ad.choices if isinstance(choices, dict): # it's then already value -> string (user-friendly display) choices_fmt = choices.items() unit = None elif (unit and len(choices) > 1 and all([isinstance(c, numbers.Real) for c in choices])): # TODO: need same update as add_value fmt, prefix = utun.si_scale_list(choices) choices_fmt = zip(choices, [u"%g" % c for c in fmt]) unit = prefix + unit else: choices_fmt = [(c, choice_to_str(c)) for c in choices] choices_fmt = sorted(choices_fmt) # sort 2-tuples = according to first value in tuple # Set choices if unit is None: unit = "" for choice, formatted in choices_fmt: 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, ctrl=value_ctrl, unit=unit): for i in range(ctrl.Count): 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 = readable_str(value, unit) ctrl.SetValue(txt) # equivalent wrapper function to retrieve the actual value def cb_get(ctrl=value_ctrl, name=name): 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] == value: logging.debug("Getting item value %s from combobox control", ctrl.GetClientData(i)) return ctrl.GetClientData(i) else: logging.error("Failed to find value %s for axis %s", value, name) axis_entry = AxisSettingEntry(name, comp, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl, pos_2_ctrl=cb_set, ctrl_2_pos=cb_get, events=(wx.EVT_COMBOBOX, wx.EVT_TEXT_ENTER)) else: logging.error("Unknown control type %s", control_type) value_ctrl.SetToolTipString(tooltip) lbl_ctrl.SetToolTipString(tooltip) return axis_entry