コード例 #1
0
ファイル: expressions.py プロジェクト: datamail321/kunquat
class DefaultNoteExpr(QWidget, AudioUnitUpdater):
    def __init__(self):
        super().__init__()
        self._expr_names = KqtComboBox()

        h = QHBoxLayout()
        h.setContentsMargins(0, 0, 0, 0)
        h.setSpacing(4)
        h.addWidget(QLabel('Default note expression:'))
        h.addWidget(self._expr_names, 1)
        self.setLayout(h)

    def _on_setup(self):
        self.register_action(self._get_list_update_signal_type(),
                             self._update_contents)
        self.register_action(self._get_default_update_signal_type(),
                             self._update_contents)

        self._expr_names.currentIndexChanged.connect(self._change_expression)

        self._update_contents()

    def _get_list_update_signal_type(self):
        return 'signal_expr_list_{}'.format(self._au_id)

    def _get_default_update_signal_type(self):
        return 'signal_expr_default_{}'.format(self._au_id)

    def _get_audio_unit(self):
        module = self._ui_model.get_module()
        au = module.get_audio_unit(self._au_id)
        return au

    def _update_contents(self):
        au = self._get_audio_unit()
        names = sorted(au.get_expression_names())
        selection = au.get_default_note_expression()

        old_block = self._expr_names.blockSignals(True)
        self._expr_names.setEnabled(len(names) > 0)
        self._expr_names.set_items(chain(['(none)'], (name for name in names)))
        try:
            self._expr_names.setCurrentIndex(names.index(selection) + 1)
        except ValueError:
            self._expr_names.setCurrentIndex(0)
        self._expr_names.blockSignals(old_block)

    def _change_expression(self, item_index):
        au = self._get_audio_unit()
        if item_index == 0:
            au.set_default_note_expression('')
        else:
            expr_name = self._expr_names.itemText(item_index)
            au.set_default_note_expression(expr_name)
コード例 #2
0
class WaveformEditor(QWidget):

    def __init__(self):
        super().__init__()
        self._au_id = None
        self._proc_id = None
        self._ui_model = None
        self._updater = None

        self._prewarp_list = WarpList(
                'pre', self._get_base_wave, self._get_update_signal_type)
        self._base_func_selector = KqtComboBox()
        self._postwarp_list = WarpList(
                'post', self._get_base_wave, self._get_update_signal_type)
        self._waveform = Waveform()

        pw_layout = QVBoxLayout()
        pw_layout.setSpacing(0)
        pw_layout.addWidget(self._prewarp_list)
        pw_layout.addWidget(self._base_func_selector)
        pw_layout.addWidget(self._postwarp_list)

        ed_layout = QHBoxLayout()
        ed_layout.setSpacing(0)
        ed_layout.addLayout(pw_layout)
        ed_layout.addWidget(self._waveform)

        v = QVBoxLayout()
        v.setContentsMargins(0, 0, 0, 0)
        v.setSpacing(2)
        v.addWidget(HeaderLine('Waveshaping'))
        v.addLayout(ed_layout)
        self.setLayout(v)

    def set_au_id(self, au_id):
        self._au_id = au_id
        self._prewarp_list.set_au_id(au_id)
        self._postwarp_list.set_au_id(au_id)

    def set_proc_id(self, proc_id):
        self._proc_id = proc_id
        self._prewarp_list.set_proc_id(proc_id)
        self._postwarp_list.set_proc_id(proc_id)

    def set_ui_model(self, ui_model):
        self._ui_model = ui_model
        self._prewarp_list.set_ui_model(ui_model)
        self._postwarp_list.set_ui_model(ui_model)

        self._updater = ui_model.get_updater()
        self._updater.register_updater(self._perform_updates)
        self._update_style()
        self._update_all()

        QObject.connect(
                self._base_func_selector,
                SIGNAL('currentIndexChanged(int)'),
                self._base_func_selected)

    def unregister_updaters(self):
        self._updater.unregister_updater(self._perform_updates)
        self._prewarp_list.unregister_updaters()
        self._postwarp_list.unregister_updaters()

    def _perform_updates(self, signals):
        update_signals = set(['signal_au', self._get_update_signal_type()])
        if not signals.isdisjoint(update_signals):
            self._update_all()
        if 'signal_style_changed' in signals:
            self._update_style()

    def _update_style(self):
        style_manager = self._ui_model.get_style_manager()
        if not style_manager.is_custom_style_enabled():
            self._waveform.set_config({})
            return

        def get_colour(name):
            return QColor(style_manager.get_style_param(name))

        disabled_colour = QColor(get_colour('bg_colour'))
        disabled_colour.setAlpha(0x7f)

        config = {
            'bg_colour': get_colour('waveform_bg_colour'),
            'centre_line_colour': get_colour('waveform_centre_line_colour'),
            'waveform_colour': get_colour('waveform_zoomed_out_colour'),
            'disabled_colour': disabled_colour,
        }

        self._waveform.set_config(config)

    def _update_all(self):
        base_wave = self._get_base_wave()

        selected_base_func = base_wave.get_waveform_func()
        enable_warps = (selected_base_func != None)

        self._prewarp_list.setEnabled(enable_warps)

        old_block = self._base_func_selector.blockSignals(True)
        func_names = list(base_wave.get_waveform_func_names())
        if not selected_base_func:
            func_names.append('Custom')
        self._base_func_selector.set_items(func_names)
        self._base_func_selector.setCurrentIndex(
                self._base_func_selector.findText(selected_base_func or 'Custom'))
        self._base_func_selector.blockSignals(old_block)

        self._postwarp_list.setEnabled(enable_warps)

        self._waveform.set_waveform(base_wave.get_waveform())

    def _base_func_selected(self, index):
        base_wave = self._get_base_wave()
        func_names = base_wave.get_waveform_func_names()
        base_wave.set_waveform_func(func_names[index])
        self._updater.signal_update(set([self._get_update_signal_type()]))

    # Protected interface

    def _get_base_wave(self):
        raise NotImplementedError

    def _get_update_signal_type(self):
        raise NotImplementedError
コード例 #3
0
class WarpEditor(QWidget):

    _ARG_SCALE = 1000

    def __init__(self, warp_type, index, get_base_wave, get_update_signal_type):
        super().__init__()
        self._au_id = None
        self._proc_id = None
        self._ui_model = None
        self._updater = None

        self._warp_type = warp_type
        self._index = index
        self._get_base_wave = get_base_wave
        self._get_update_signal_type = get_update_signal_type

        self._down_button = SmallButton()
        self._up_button = SmallButton()
        self._func_selector = KqtComboBox()
        self._slider = QSlider(Qt.Horizontal)
        self._slider.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
        self._value_display = QLabel()
        self._remove_button = SmallButton()

        self._slider.setRange(-self._ARG_SCALE, self._ARG_SCALE)

        self._up_button.setEnabled(self._index != 0)

        fm = QFontMetrics(QFont())
        value_width = fm.boundingRect('{}'.format(-1.0 / self._ARG_SCALE)).width()
        value_width += 10
        self._value_display.setFixedWidth(value_width)

        h = QHBoxLayout()
        h.setContentsMargins(0, 0, 0, 0)
        h.setSpacing(2)
        h.addWidget(self._down_button)
        h.addWidget(self._up_button)
        h.addWidget(self._func_selector)
        h.addWidget(self._slider)
        h.addWidget(self._value_display)
        h.addWidget(self._remove_button)
        self.setLayout(h)

    def set_au_id(self, au_id):
        self._au_id = au_id

    def set_proc_id(self, proc_id):
        self._proc_id = proc_id

    def set_ui_model(self, ui_model):
        self._ui_model = ui_model
        self._updater = ui_model.get_updater()
        self._updater.register_updater(self._perform_updates)

        icon_bank = self._ui_model.get_icon_bank()
        self._down_button.setIcon(QIcon(icon_bank.get_icon_path('arrow_down_small')))
        self._up_button.setIcon(QIcon(icon_bank.get_icon_path('arrow_up_small')))
        self._remove_button.setIcon(QIcon(icon_bank.get_icon_path('delete_small')))

        base_wave = self._get_base_wave()

        func_names = base_wave.get_warp_func_names(self._warp_type)
        self._func_selector.set_items(name for name in func_names)

        self._update_all()

        QObject.connect(self._down_button, SIGNAL('clicked()'), self._moved_down)
        QObject.connect(self._up_button, SIGNAL('clicked()'), self._moved_up)
        QObject.connect(
                self._func_selector,
                SIGNAL('currentIndexChanged(int)'),
                self._func_selected)
        QObject.connect(self._slider, SIGNAL('valueChanged(int)'), self._slider_adjusted)
        QObject.connect(self._remove_button, SIGNAL('clicked()'), self._removed)

    def unregister_updaters(self):
        self._updater.unregister_updater(self._perform_updates)

    def _perform_updates(self, signals):
        update_signals = set(['signal_au', self._get_update_signal_type()])
        if not signals.isdisjoint(update_signals):
            self._update_all()

    def _update_all(self):
        base_wave = self._get_base_wave()

        warp_func_count = base_wave.get_warp_func_count(self._warp_type)

        if self._index >= warp_func_count:
            # We have been removed
            return

        self._down_button.setEnabled(self._index < warp_func_count - 1)

        name, arg = base_wave.get_warp_func(self._warp_type, self._index)

        old_block = self._func_selector.blockSignals(True)
        for i in range(self._func_selector.count()):
            if self._func_selector.itemText(i) == name:
                self._func_selector.setCurrentIndex(i)
                break
        self._func_selector.blockSignals(old_block)

        old_block = self._slider.blockSignals(True)
        int_val = int(round(arg * self._ARG_SCALE))
        self._slider.setValue(int_val)
        self._slider.blockSignals(old_block)

        self._value_display.setText(str(float(arg)))

    def _moved_down(self):
        base_wave = self._get_base_wave()
        base_wave.move_warp_func(self._warp_type, self._index, self._index + 1)
        self._updater.signal_update(set([self._get_update_signal_type()]))

    def _moved_up(self):
        base_wave = self._get_base_wave()
        base_wave.move_warp_func(self._warp_type, self._index, self._index - 1)
        self._updater.signal_update(set([self._get_update_signal_type()]))

    def _set_warp(self):
        base_wave = self._get_base_wave()
        name = str(self._func_selector.currentText())
        arg = self._slider.value() / float(self._ARG_SCALE)
        base_wave.set_warp_func(self._warp_type, self._index, name, arg)
        self._updater.signal_update(set([self._get_update_signal_type()]))

    def _func_selected(self, index):
        self._set_warp()

    def _slider_adjusted(self, int_val):
        self._set_warp()

    def _removed(self):
        base_wave = self._get_base_wave()
        base_wave.remove_warp_func(self._warp_type, self._index)
        self._updater.signal_update(set([self._get_update_signal_type()]))
コード例 #4
0
class DefaultNoteExpr(QWidget):

    def __init__(self):
        super().__init__()
        self._au_id = None
        self._ui_model = None
        self._updater = None

        self._expr_names = KqtComboBox()

        h = QHBoxLayout()
        h.setContentsMargins(0, 0, 0, 0)
        h.setSpacing(4)
        h.addWidget(QLabel('Default note expression:'))
        h.addWidget(self._expr_names, 1)
        self.setLayout(h)

    def set_au_id(self, au_id):
        self._au_id = au_id

    def set_ui_model(self, ui_model):
        self._ui_model = ui_model
        self._updater = ui_model.get_updater()
        self._updater.register_updater(self._perform_updates)

        QObject.connect(
                self._expr_names,
                SIGNAL('currentIndexChanged(int)'),
                self._change_expression)

        self._update_contents()

    def unregister_updaters(self):
        self._updater.unregister_updater(self._perform_updates)

    def _get_list_update_signal_type(self):
        return 'signal_expr_list_{}'.format(self._au_id)

    def _get_default_update_signal_type(self):
        return 'signal_expr_default_{}'.format(self._au_id)

    def _perform_updates(self, signals):
        update_signals = set([
            self._get_list_update_signal_type(), self._get_default_update_signal_type()])
        if not signals.isdisjoint(update_signals):
            self._update_contents()

    def _get_audio_unit(self):
        module = self._ui_model.get_module()
        au = module.get_audio_unit(self._au_id)
        return au

    def _update_contents(self):
        au = self._get_audio_unit()
        names = sorted(au.get_expression_names())
        selection = au.get_default_note_expression()

        old_block = self._expr_names.blockSignals(True)
        self._expr_names.setEnabled(len(names) > 0)
        self._expr_names.set_items(chain(['(none)'], (name for name in names)))
        try:
            self._expr_names.setCurrentIndex(names.index(selection) + 1)
        except ValueError:
            self._expr_names.setCurrentIndex(0)
        self._expr_names.blockSignals(old_block)

    def _change_expression(self, item_index):
        au = self._get_audio_unit()
        if item_index == 0:
            au.set_default_note_expression('')
        else:
            expr_name = self._expr_names.itemText(item_index)
            au.set_default_note_expression(expr_name)
コード例 #5
0
ファイル: waveformeditor.py プロジェクト: datamail321/kunquat
class WaveformEditor(QWidget, ProcessorUpdater):
    def __init__(self):
        super().__init__()
        self._prewarp_list = WarpList('pre', self._get_base_wave,
                                      self._get_update_signal_type)
        self._base_func_selector = KqtComboBox()
        self._postwarp_list = WarpList('post', self._get_base_wave,
                                       self._get_update_signal_type)
        self._waveform = Waveform()

        pw_layout = QVBoxLayout()
        pw_layout.setSpacing(0)
        pw_layout.addWidget(self._prewarp_list)
        pw_layout.addWidget(self._base_func_selector)
        pw_layout.addWidget(self._postwarp_list)

        ed_layout = QHBoxLayout()
        ed_layout.setSpacing(0)
        ed_layout.addLayout(pw_layout)
        ed_layout.addWidget(self._waveform)

        v = QVBoxLayout()
        v.setContentsMargins(0, 0, 0, 0)
        v.setSpacing(2)
        v.addWidget(HeaderLine('Waveshaping'))
        v.addLayout(ed_layout)
        self.setLayout(v)

    def _on_setup(self):
        self.add_to_updaters(self._prewarp_list, self._postwarp_list)
        self.register_action('signal_au', self._update_all)
        self.register_action(self._get_update_signal_type(), self._update_all)
        self.register_action('signal_style_changed', self._update_style)

        self._update_style()
        self._update_all()

        self._base_func_selector.currentIndexChanged.connect(
            self._base_func_selected)

    def _update_style(self):
        style_mgr = self._ui_model.get_style_manager()
        if not style_mgr.is_custom_style_enabled():
            self._waveform.set_config({})
            return

        def get_colour(name):
            return QColor(style_mgr.get_style_param(name))

        disabled_colour = QColor(get_colour('bg_colour'))
        disabled_colour.setAlpha(0x7f)

        config = {
            'bg_colour': get_colour('waveform_bg_colour'),
            'centre_line_colour': get_colour('waveform_centre_line_colour'),
            'waveform_colour': get_colour('waveform_zoomed_out_colour'),
            'disabled_colour': disabled_colour,
        }

        self._waveform.set_config(config)

    def _update_all(self):
        base_wave = self._get_base_wave()

        selected_base_func = base_wave.get_waveform_func()
        enable_warps = (selected_base_func != None)

        self._prewarp_list.setEnabled(enable_warps)

        old_block = self._base_func_selector.blockSignals(True)
        func_names = list(base_wave.get_waveform_func_names())
        if not selected_base_func:
            func_names.append('Custom')
        self._base_func_selector.set_items(func_names)
        self._base_func_selector.setCurrentIndex(
            self._base_func_selector.findText(selected_base_func or 'Custom'))
        self._base_func_selector.blockSignals(old_block)

        self._postwarp_list.setEnabled(enable_warps)

        self._waveform.set_waveform(base_wave.get_waveform())

    def _base_func_selected(self, index):
        base_wave = self._get_base_wave()
        func_names = base_wave.get_waveform_func_names()
        base_wave.set_waveform_func(func_names[index])
        self._updater.signal_update(self._get_update_signal_type())

    # Protected interface

    def _get_base_wave(self):
        raise NotImplementedError

    def _get_update_signal_type(self):
        raise NotImplementedError
コード例 #6
0
ファイル: waveformeditor.py プロジェクト: datamail321/kunquat
class WarpEditor(QWidget, ProcessorUpdater):

    _ARG_SCALE = 1000

    def __init__(self, warp_type, index, get_base_wave,
                 get_update_signal_type):
        super().__init__()
        self._warp_type = warp_type
        self._index = index
        self._get_base_wave = get_base_wave
        self._get_update_signal_type = get_update_signal_type

        self._down_button = SmallButton()
        self._up_button = SmallButton()
        self._func_selector = KqtComboBox()
        self._slider = QSlider(Qt.Horizontal)
        self._slider.setSizePolicy(QSizePolicy.MinimumExpanding,
                                   QSizePolicy.Preferred)
        self._value_display = QLabel()
        self._remove_button = SmallButton()

        self._slider.setRange(-self._ARG_SCALE, self._ARG_SCALE)

        self._up_button.setEnabled(self._index != 0)

        fm = QFontMetrics(QFont())
        value_width = fm.boundingRect('{}'.format(-1.0 /
                                                  self._ARG_SCALE)).width()
        value_width += 10
        self._value_display.setFixedWidth(value_width)

        h = QHBoxLayout()
        h.setContentsMargins(0, 0, 0, 0)
        h.setSpacing(2)
        h.addWidget(self._down_button)
        h.addWidget(self._up_button)
        h.addWidget(self._func_selector)
        h.addWidget(self._slider)
        h.addWidget(self._value_display)
        h.addWidget(self._remove_button)
        self.setLayout(h)

    def _on_setup(self):
        self.register_action('signal_au', self._update_all)
        self.register_action(self._get_update_signal_type(), self._update_all)

        icon_bank = self._ui_model.get_icon_bank()
        self._down_button.setIcon(
            QIcon(icon_bank.get_icon_path('arrow_down_small')))
        self._up_button.setIcon(
            QIcon(icon_bank.get_icon_path('arrow_up_small')))
        self._remove_button.setIcon(
            QIcon(icon_bank.get_icon_path('delete_small')))

        base_wave = self._get_base_wave()

        func_names = base_wave.get_warp_func_names(self._warp_type)
        self._func_selector.set_items(name for name in func_names)

        self._update_all()

        self._down_button.clicked.connect(self._moved_down)
        self._up_button.clicked.connect(self._moved_up)
        self._func_selector.currentIndexChanged.connect(self._func_selected)
        self._slider.valueChanged.connect(self._slider_adjusted)
        self._remove_button.clicked.connect(self._removed)

    def _update_all(self):
        base_wave = self._get_base_wave()

        warp_func_count = base_wave.get_warp_func_count(self._warp_type)

        if self._index >= warp_func_count:
            # We have been removed
            return

        self._down_button.setEnabled(self._index < warp_func_count - 1)

        name, arg = base_wave.get_warp_func(self._warp_type, self._index)

        old_block = self._func_selector.blockSignals(True)
        for i in range(self._func_selector.count()):
            if self._func_selector.itemText(i) == name:
                self._func_selector.setCurrentIndex(i)
                break
        self._func_selector.blockSignals(old_block)

        old_block = self._slider.blockSignals(True)
        int_val = int(round(arg * self._ARG_SCALE))
        self._slider.setValue(int_val)
        self._slider.blockSignals(old_block)

        self._value_display.setText(str(float(arg)))

    def _moved_down(self):
        base_wave = self._get_base_wave()
        base_wave.move_warp_func(self._warp_type, self._index, self._index + 1)
        self._updater.signal_update(self._get_update_signal_type())

    def _moved_up(self):
        base_wave = self._get_base_wave()
        base_wave.move_warp_func(self._warp_type, self._index, self._index - 1)
        self._updater.signal_update(self._get_update_signal_type())

    def _set_warp(self):
        base_wave = self._get_base_wave()
        name = str(self._func_selector.currentText())
        arg = self._slider.value() / float(self._ARG_SCALE)
        base_wave.set_warp_func(self._warp_type, self._index, name, arg)
        self._updater.signal_update(self._get_update_signal_type())

    def _func_selected(self, index):
        self._set_warp()

    def _slider_adjusted(self, int_val):
        self._set_warp()

    def _removed(self):
        base_wave = self._get_base_wave()
        base_wave.remove_warp_func(self._warp_type, self._index)
        self._updater.signal_update(self._get_update_signal_type())
コード例 #7
0
ファイル: waveformeditor.py プロジェクト: kagu/kunquat
class WaveformEditor(QWidget, ProcessorUpdater):

    def __init__(self):
        super().__init__()
        self._prewarp_list = WarpList(
                'pre', self._get_base_wave, self._get_update_signal_type)
        self._base_func_selector = KqtComboBox()
        self._postwarp_list = WarpList(
                'post', self._get_base_wave, self._get_update_signal_type)
        self._waveform = Waveform()

        pw_layout = QVBoxLayout()
        pw_layout.setSpacing(0)
        pw_layout.addWidget(self._prewarp_list)
        pw_layout.addWidget(self._base_func_selector)
        pw_layout.addWidget(self._postwarp_list)

        ed_layout = QHBoxLayout()
        ed_layout.setSpacing(0)
        ed_layout.addLayout(pw_layout)
        ed_layout.addWidget(self._waveform)

        self._header = HeaderLine('Waveshaping')

        v = QVBoxLayout()
        v.setContentsMargins(0, 0, 0, 0)
        v.setSpacing(2)
        v.addWidget(self._header)
        v.addLayout(ed_layout)
        self.setLayout(v)

    def _on_setup(self):
        self.add_to_updaters(self._prewarp_list, self._postwarp_list)
        self.register_action('signal_au', self._update_all)
        self.register_action(self._get_update_signal_type(), self._update_all)
        self.register_action('signal_style_changed', self._update_style)

        self._update_style()
        self._update_all()

        self._base_func_selector.currentIndexChanged.connect(self._base_func_selected)

    def _update_style(self):
        style_mgr = self._ui_model.get_style_manager()

        self._header.update_style(style_mgr)
        self.layout().setSpacing(style_mgr.get_scaled_size_param('small_padding'))

        def get_colour(name):
            return QColor(style_mgr.get_style_param(name))

        disabled_colour = QColor(get_colour('bg_colour'))
        disabled_colour.setAlpha(0x7f)

        config = {
            'bg_colour': get_colour('waveform_bg_colour'),
            'centre_line_colour': get_colour('waveform_centre_line_colour'),
            'waveform_colour': get_colour('waveform_zoomed_out_colour'),
            'disabled_colour': disabled_colour,
            'line_thickness' : style_mgr.get_scaled_size(0.1),
        }

        self._waveform.set_config(config)

    def _update_all(self):
        base_wave = self._get_base_wave()

        selected_base_func = base_wave.get_waveform_func()
        enable_warps = (selected_base_func != None)

        self._prewarp_list.setEnabled(enable_warps)

        old_block = self._base_func_selector.blockSignals(True)
        func_names = list(base_wave.get_waveform_func_names())
        if not selected_base_func:
            func_names.append('Custom')
        self._base_func_selector.set_items(func_names)
        self._base_func_selector.setCurrentIndex(
                self._base_func_selector.findText(selected_base_func or 'Custom'))
        self._base_func_selector.blockSignals(old_block)

        self._postwarp_list.setEnabled(enable_warps)

        self._waveform.set_waveform(base_wave.get_waveform())

    def _base_func_selected(self, index):
        base_wave = self._get_base_wave()
        func_names = base_wave.get_waveform_func_names()
        base_wave.set_waveform_func(func_names[index])
        self._updater.signal_update(self._get_update_signal_type())

    # Protected interface

    def _get_base_wave(self):
        raise NotImplementedError

    def _get_update_signal_type(self):
        raise NotImplementedError
コード例 #8
0
ファイル: waveformeditor.py プロジェクト: kagu/kunquat
class WarpEditor(QWidget, ProcessorUpdater):

    _ARG_SCALE = 1000

    def __init__(self, warp_type, index, get_base_wave, get_update_signal_type):
        super().__init__()
        self._warp_type = warp_type
        self._index = index
        self._get_base_wave = get_base_wave
        self._get_update_signal_type = get_update_signal_type

        self._down_button = ProcessorIconButton()
        self._up_button = ProcessorIconButton()
        self._func_selector = KqtComboBox()
        self._slider = QSlider(Qt.Horizontal)
        self._slider.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
        self._value_display = QLabel()
        self._remove_button = ProcessorIconButton()

        self.add_to_updaters(self._down_button, self._up_button, self._remove_button)

        self._slider.setRange(-self._ARG_SCALE, self._ARG_SCALE)

        self._up_button.setEnabled(self._index != 0)

        fm = QFontMetrics(QFont())
        value_width = fm.boundingRect('{}'.format(-1.0 / self._ARG_SCALE)).width()
        value_width += 10
        self._value_display.setFixedWidth(value_width)

        h = QHBoxLayout()
        h.setContentsMargins(0, 0, 0, 0)
        h.setSpacing(2)
        h.addWidget(self._down_button)
        h.addWidget(self._up_button)
        h.addWidget(self._func_selector)
        h.addWidget(self._slider)
        h.addWidget(self._value_display)
        h.addWidget(self._remove_button)
        self.setLayout(h)

    def _on_setup(self):
        self.register_action('signal_au', self._update_all)
        self.register_action(self._get_update_signal_type(), self._update_all)
        self.register_action('signal_style_changed', self._update_style)

        self._down_button.set_icon('arrow_down_small')
        self._up_button.set_icon('arrow_up_small')
        self._remove_button.set_icon('delete_small')

        style_mgr = self._ui_model.get_style_manager()
        for button in (self._down_button, self._up_button, self._remove_button):
            button.set_sizes(
                    style_mgr.get_style_param('list_button_size'),
                    style_mgr.get_style_param('list_button_padding'))

        base_wave = self._get_base_wave()

        func_names = base_wave.get_warp_func_names(self._warp_type)
        self._func_selector.set_items(name for name in func_names)

        self._update_style()
        self._update_all()

        self._down_button.clicked.connect(self._moved_down)
        self._up_button.clicked.connect(self._moved_up)
        self._func_selector.currentIndexChanged.connect(self._func_selected)
        self._slider.valueChanged.connect(self._slider_adjusted)
        self._remove_button.clicked.connect(self._removed)

    def _update_style(self):
        style_mgr = self._ui_model.get_style_manager()
        self.layout().setSpacing(style_mgr.get_scaled_size_param('small_padding'))

    def _update_all(self):
        base_wave = self._get_base_wave()

        warp_func_count = base_wave.get_warp_func_count(self._warp_type)

        if self._index >= warp_func_count:
            # We have been removed
            return

        self._down_button.setEnabled(self._index < warp_func_count - 1)

        name, arg = base_wave.get_warp_func(self._warp_type, self._index)

        old_block = self._func_selector.blockSignals(True)
        for i in range(self._func_selector.count()):
            if self._func_selector.itemText(i) == name:
                self._func_selector.setCurrentIndex(i)
                break
        self._func_selector.blockSignals(old_block)

        old_block = self._slider.blockSignals(True)
        int_val = int(round(arg * self._ARG_SCALE))
        self._slider.setValue(int_val)
        self._slider.blockSignals(old_block)

        self._value_display.setText(str(float(arg)))

    def _moved_down(self):
        base_wave = self._get_base_wave()
        base_wave.move_warp_func(self._warp_type, self._index, self._index + 1)
        self._updater.signal_update(self._get_update_signal_type())

    def _moved_up(self):
        base_wave = self._get_base_wave()
        base_wave.move_warp_func(self._warp_type, self._index, self._index - 1)
        self._updater.signal_update(self._get_update_signal_type())

    def _set_warp(self):
        base_wave = self._get_base_wave()
        name = str(self._func_selector.currentText())
        arg = self._slider.value() / float(self._ARG_SCALE)
        base_wave.set_warp_func(self._warp_type, self._index, name, arg)
        self._updater.signal_update(self._get_update_signal_type())

    def _func_selected(self, index):
        self._set_warp()

    def _slider_adjusted(self, int_val):
        self._set_warp()

    def _removed(self):
        base_wave = self._get_base_wave()
        base_wave.remove_warp_func(self._warp_type, self._index)
        self._updater.signal_update(self._get_update_signal_type())