Пример #1
0
 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)
Пример #2
0
 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)
Пример #3
0
 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)
Пример #4
0
 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)
Пример #5
0
 def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy):
     ctrl.SetValue(value_to_str(value, u, acc))
Пример #6
0
 def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy):
     ctrl.SetValue(value_to_str(value, u, acc, pretty_time=True))
Пример #7
0
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
Пример #8
0
 def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy):
     ctrl.SetValue(value_to_str(value, u, acc))
Пример #9
0
 def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy):
     ctrl.SetValue(value_to_str(value, u, acc, pretty_time=True))
Пример #10
0
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