コード例 #1
0
    def _construct_UI(self, **kwargs):
        """
        Construct widget from quantization dict, individual settings and
        the default dict below """

        # default settings
        dict_ui = {
            'wdg_name': 'ui_w',
            'label': 'WI.WF',
            'lbl_sep': '.',
            'max_led_width': 30,
            'WI': 0,
            'WI_len': 2,
            'tip_WI': 'Number of integer bits',
            'WF': 15,
            'WF_len': 2,
            'tip_WF': 'Number of fractional bits',
            'enabled': True,
            'visible': True,
            'fractional': True,
            'combo_visible': False,
            'combo_items': ['auto', 'full', 'man'],
            'tip_combo': 'Calculate Acc. width.',
            'lock_visible': False,
            'tip_lock': 'Lock input/output quantization.'
        }  #: default values

        if self.q_dict:
            dict_ui.update(self.q_dict)

        for k, v in kwargs.items():
            if k not in dict_ui:
                logger.warning("Unknown key {0}".format(k))
            else:
                dict_ui.update({k: v})

        self.wdg_name = dict_ui['wdg_name']

        if not dict_ui['fractional']:
            dict_ui['WF'] = 0
        self.WI = dict_ui['WI']
        self.WF = dict_ui['WF']
        self.W = int(self.WI + self.WF + 1)
        if self.q_dict:
            self.q_dict.update({'WI': self.WI, 'WF': self.WF, 'W': self.W})
        else:
            self.q_dict = {'WI': self.WI, 'WF': self.WF, 'W': self.W}

        lblW = QLabel(to_html(dict_ui['label'], frmt='bi'), self)

        self.cmbW = QComboBox(self)
        self.cmbW.addItems(dict_ui['combo_items'])
        self.cmbW.setVisible(dict_ui['combo_visible'])
        self.cmbW.setToolTip(dict_ui['tip_combo'])
        self.cmbW.setObjectName("cmbW")

        self.butLock = QPushButton(self)
        self.butLock.setCheckable(True)
        self.butLock.setChecked(False)
        self.butLock.setVisible(dict_ui['lock_visible'])
        self.butLock.setToolTip(dict_ui['tip_lock'])

        self.ledWI = QLineEdit(self)
        self.ledWI.setToolTip(dict_ui['tip_WI'])
        self.ledWI.setMaxLength(dict_ui['WI_len'])  # maximum of 2 digits
        self.ledWI.setFixedWidth(
            dict_ui['max_led_width'])  # width of lineedit in points
        self.ledWI.setObjectName("WI")

        lblDot = QLabel(dict_ui['lbl_sep'], self)
        lblDot.setVisible(dict_ui['fractional'])

        self.ledWF = QLineEdit(self)
        self.ledWF.setToolTip(dict_ui['tip_WF'])
        self.ledWF.setMaxLength(dict_ui['WI_len'])  # maximum of 2 digits
        self.ledWF.setFixedWidth(
            dict_ui['max_led_width'])  # width of lineedit in points
        self.ledWF.setVisible(dict_ui['fractional'])
        self.ledWF.setObjectName("WF")

        layH = QHBoxLayout()
        layH.addWidget(lblW)
        layH.addStretch()
        layH.addWidget(self.cmbW)
        layH.addWidget(self.butLock)
        layH.addWidget(self.ledWI)
        layH.addWidget(lblDot)
        layH.addWidget(self.ledWF)
        layH.setContentsMargins(0, 0, 0, 0)

        frmMain = QFrame(self)
        frmMain.setLayout(layH)

        layVMain = QVBoxLayout()  # Widget main layout
        layVMain.addWidget(frmMain)
        layVMain.setContentsMargins(0, 5, 0, 0)  # *params['wdg_margins'])

        self.setLayout(layVMain)

        # ----------------------------------------------------------------------
        # INITIAL SETTINGS
        # ----------------------------------------------------------------------
        self.ledWI.setText(qstr(dict_ui['WI']))
        self.ledWF.setText(qstr(dict_ui['WF']))

        frmMain.setEnabled(dict_ui['enabled'])
        frmMain.setVisible(dict_ui['visible'])

        # ----------------------------------------------------------------------
        # LOCAL SIGNALS & SLOTs
        # ----------------------------------------------------------------------
        self.ledWI.editingFinished.connect(self.ui2dict)
        self.ledWF.editingFinished.connect(self.ui2dict)
        self.butLock.clicked.connect(self.butLock_clicked)
        self.cmbW.currentIndexChanged.connect(self.ui2dict)

        # initialize button icon
        self.butLock_clicked(self.butLock.isChecked())
コード例 #2
0
class Input_Specs(QWidget):
    """
    Build widget for entering all filter specs
    """
    # class variables (shared between instances if more than one exists)
    sig_rx_local = pyqtSignal(
        object)  # incoming from subwidgets -> process_sig_rx_local

    sig_rx = pyqtSignal(object)  # incoming from subwidgets -> process_sig_rx
    sig_tx = pyqtSignal(object)  # from process_sig_rx: propagate local signals
    from pyfda.libs.pyfda_qt_lib import emit

    def __init__(self, parent=None):
        super(Input_Specs, self).__init__(parent)
        self.tab_label = "Specs"
        self.tool_tip = "Enter and view filter specifications."

        self._construct_UI()

    def process_sig_rx_local(self, dict_sig=None):
        """
        Flag signals coming in from local subwidgets with `propagate=True` before
        proceeding with processing in `process_sig_rx`.
        """
        self.process_sig_rx(dict_sig, propagate=True)

    def process_sig_rx(self, dict_sig=None, propagate=False):
        """
        Process signals coming in via subwidgets and sig_rx

        All signals terminate here unless the flag `propagate=True`.

        The sender name of signals coming in from local subwidgets is changed to
        its parent widget (`input_specs`) to prevent infinite loops.

        """
        # logger.debug(f"SIG_RX: {pprint_log(dict_sig)}")
        if dict_sig['id'] == id(self):
            # logger.warning(f"Stopped infinite loop:\n\tPropagate = {propagate}\
            #               \n{pprint_log(dict_sig)}")
            return
        elif 'view_changed' in dict_sig:
            self.f_specs.load_dict()
            self.t_specs.load_dict()
        elif 'specs_changed' in dict_sig:
            self.f_specs.sort_dict_freqs()
            self.t_specs.f_specs.sort_dict_freqs()
            self.color_design_button("changed")
        elif 'filt_changed' in dict_sig:
            # Changing the filter design requires updating UI because number or
            # kind of input fields changes -> call update_UI
            self.update_UI(dict_sig)
        elif 'data_changed' in dict_sig:
            if dict_sig['data_changed'] == 'filter_loaded':
                """
                Called when a new filter has been LOADED:
                Pass new filter data from the global filter dict by
                specifically calling SelectFilter.load_dict()
                """
                self.sel_fil.load_dict()  # update select_filter widget
            # Pass new filter data from the global filter dict & set button = "ok"
            self.load_dict()

        if propagate:
            # local signals are propagated with the name of this widget,
            # global signals terminate here
            dict_sig.update({'class': self.__class__.__name__})
            self.emit(dict_sig)

    def _construct_UI(self):
        """
        Construct User Interface from all input subwidgets
        """
        self.butLoadFilt = QPushButton("LOAD FILTER", self)
        self.butLoadFilt.setToolTip("Load filter from disk")
        self.butSaveFilt = QPushButton("SAVE FILTER", self)
        self.butSaveFilt.setToolTip("Save filter todisk")
        layHButtons1 = QHBoxLayout()
        layHButtons1.addWidget(self.butLoadFilt)  # <Load Filter> button
        layHButtons1.addWidget(self.butSaveFilt)  # <Save Filter> button
        layHButtons1.setContentsMargins(*params['wdg_margins_spc'])

        self.butDesignFilt = QPushButton("DESIGN FILTER", self)
        self.butDesignFilt.setToolTip("Design filter with chosen specs")
        self.butQuit = QPushButton("Quit", self)
        self.butQuit.setToolTip("Exit pyfda tool")
        layHButtons2 = QHBoxLayout()
        layHButtons2.addWidget(self.butDesignFilt)  # <Design Filter> button
        layHButtons2.addWidget(self.butQuit)  # <Quit> button
        layHButtons2.setContentsMargins(*params['wdg_margins'])

        # Subwidget for selecting filter with response type rt (LP, ...),
        #    filter type ft (IIR, ...) and filter class fc (cheby1, ...)
        self.sel_fil = select_filter.SelectFilter(self)
        self.sel_fil.setObjectName("select_filter")
        self.sel_fil.sig_tx.connect(self.sig_rx_local)

        # Subwidget for selecting the frequency unit and range
        self.f_units = freq_units.FreqUnits(self)
        self.f_units.setObjectName("freq_units")
        self.f_units.sig_tx.connect(self.sig_rx_local)

        # Changing the frequency unit requires re-display of frequency specs
        # but it does not influence the actual specs (no specsChanged )
        # Activating the "Sort" button emits 'view_changed'?specs_changed'?, requiring
        # sorting and storing the frequency entries

        # Changing filter parameters / specs requires reloading of parameters
        # in other hierarchy levels, e.g. in the plot tabs

        # Subwidget for Frequency Specs
        self.f_specs = freq_specs.FreqSpecs(self)
        self.f_specs.setObjectName("freq_specs")
        self.f_specs.sig_tx.connect(self.sig_rx_local)
        self.sig_tx.connect(self.f_specs.sig_rx)
        # Subwidget for Amplitude Specs
        self.a_specs = amplitude_specs.AmplitudeSpecs(self)
        self.a_specs.setObjectName("amplitude_specs")
        self.a_specs.sig_tx.connect(self.sig_rx_local)
        # Subwidget for Weight Specs
        self.w_specs = weight_specs.WeightSpecs(self)
        self.w_specs.setObjectName("weight_specs")
        self.w_specs.sig_tx.connect(self.sig_rx_local)
        # Subwidget for target specs (frequency and amplitude)
        self.t_specs = target_specs.TargetSpecs(self,
                                                title="Target Specifications")
        self.t_specs.setObjectName("target_specs")
        self.t_specs.sig_tx.connect(self.sig_rx_local)
        self.sig_tx.connect(self.t_specs.sig_rx)
        # Subwidget for displaying infos on the design method
        self.lblMsg = QLabel(self)
        self.lblMsg.setWordWrap(True)
        layVMsg = QVBoxLayout()
        layVMsg.addWidget(self.lblMsg)

        self.frmMsg = QFrame(self)
        self.frmMsg.setLayout(layVMsg)
        layVFrm = QVBoxLayout()
        layVFrm.addWidget(self.frmMsg)
        layVFrm.setContentsMargins(*params['wdg_margins'])

        # ----------------------------------------------------------------------
        # LAYOUT for input specifications and buttons
        # ----------------------------------------------------------------------
        layVMain = QVBoxLayout(self)
        layVMain.addLayout(layHButtons1)  # <Load> & <Save> buttons
        layVMain.addWidget(self.sel_fil)  # Design method (IIR - ellip, ...)
        layVMain.addLayout(layHButtons2)  # <Design> & <Quit> buttons
        layVMain.addWidget(self.f_units)  # Frequency units
        layVMain.addWidget(self.t_specs)  # Target specs
        layVMain.addWidget(self.f_specs)  # Freq. specifications
        layVMain.addWidget(self.a_specs)  # Amplitude specs
        layVMain.addWidget(self.w_specs)  # Weight specs
        layVMain.addLayout(layVFrm)  # Text message

        layVMain.addStretch()

        layVMain.setContentsMargins(*params['wdg_margins'])

        self.setLayout(layVMain)  # main layout of widget

        # ----------------------------------------------------------------------
        # GLOBAL SIGNALS & SLOTs
        # ----------------------------------------------------------------------
        self.sig_rx.connect(self.process_sig_rx)
        # ----------------------------------------------------------------------
        # LOCAL SIGNALS & SLOTs
        # ----------------------------------------------------------------------
        self.sig_rx_local.connect(self.process_sig_rx_local)
        self.butLoadFilt.clicked.connect(lambda: load_filter(self))
        self.butSaveFilt.clicked.connect(lambda: save_filter(self))
        self.butDesignFilt.clicked.connect(self.start_design_filt)
        self.butQuit.clicked.connect(self.quit_program)  # emit 'quit_program'
        # ----------------------------------------------------------------------

        self.update_UI()  # first time initialization
        self.start_design_filt()  # design first filter using default values

# ------------------------------------------------------------------------------

    def update_UI(self, dict_sig={}):
        """
        update_UI is called every time the filter design method or order
        (min / man) has been changed as this usually requires a different set of
        frequency and amplitude specs.

        At this time, the actual filter object instance has been created from
        the name of the design method (e.g. 'cheby1') in select_filter.py.
        Its handle has been stored in fb.fil_inst.

        fb.fil[0] (currently selected filter) is read, then general information
        for the selected filter type and order (min/man) is gathered from
        the filter tree [fb.fil_tree], i.e. which parameters are needed, which
        widgets are visible and which message shall be displayed.

        Then, the UIs of all subwidgets are updated using their "update_UI" method.
        """
        rt = fb.fil[0]['rt']  # e.g. 'LP'
        ft = fb.fil[0]['ft']  # e.g. 'FIR'
        fc = fb.fil[0]['fc']  # e.g. 'equiripple'
        fo = fb.fil[0]['fo']  # e.g. 'man'

        # the keys of the all_widgets dict are the names of the subwidgets,
        # the values are a tuple with the corresponding parameters
        all_widgets = fb.fil_tree[rt][ft][fc][fo]

        # logger.debug("rt: {0} - ft: {1} - fc: {2} - fo: {3}".format(rt, ft, fc, fo))
        # logger.debug("fb.fil_tree[rt][ft][fc][fo]:\n{0}".format(fb.fil_tree[rt][ft][fc][fo]))

        # update filter order subwidget, called by select_filter:
        # self.sel_fil.load_filter_order()

        # TARGET SPECS: is widget in the dict and is it visible (marker != 'i')?
        if ('tspecs' in all_widgets and len(all_widgets['tspecs']) > 1
                and all_widgets['tspecs'][0] != 'i'):
            self.t_specs.setVisible(True)
            # disable all subwidgets with marker 'd':
            self.t_specs.setEnabled(all_widgets['tspecs'][0] != 'd')
            self.t_specs.update_UI(new_labels=all_widgets['tspecs'][1])
        else:
            self.t_specs.hide()

        # FREQUENCY SPECS
        if ('fspecs' in all_widgets and len(all_widgets['fspecs']) > 1
                and all_widgets['fspecs'][0] != 'i'):
            self.f_specs.setVisible(True)
            self.f_specs.setEnabled(all_widgets['fspecs'][0] != 'd')
            self.f_specs.update_UI(new_labels=all_widgets['fspecs'])
        else:
            self.f_specs.hide()

        # AMPLITUDE SPECS
        if ('aspecs' in all_widgets and len(all_widgets['aspecs']) > 1
                and all_widgets['aspecs'][0] != 'i'):
            self.a_specs.setVisible(True)
            self.a_specs.setEnabled(all_widgets['aspecs'][0] != 'd')
            self.a_specs.update_UI(new_labels=all_widgets['aspecs'])
        else:
            self.a_specs.hide()

        # WEIGHT SPECS
        if ('wspecs' in all_widgets and len(all_widgets['wspecs']) > 1
                and all_widgets['wspecs'][0] != 'i'):
            self.w_specs.setVisible(True)
            self.w_specs.setEnabled(all_widgets['wspecs'][0] != 'd')
            self.w_specs.update_UI(new_labels=all_widgets['wspecs'])
        else:
            self.w_specs.hide()

        # MESSAGE PANE
        if ('msg' in all_widgets and len(all_widgets['msg']) > 1
                and all_widgets['msg'][0] != 'i'):
            self.frmMsg.setVisible(True)
            self.frmMsg.setEnabled(all_widgets['msg'][0] != 'd')
            self.lblMsg.setText(all_widgets['msg'][1:][0])
        else:
            self.frmMsg.hide()

        # Update state of "DESIGN FILTER" button
        # It is disabled for "Manual_IIR" and "Manual_FIR" filter classes
        self.color_design_button("changed")

# ------------------------------------------------------------------------------

    def load_dict(self):
        """
        Reload all specs/parameters entries from global dict fb.fil[0],
        using the "load_dict" methods of the individual classes
        """
        self.sel_fil.load_dict()  # select filter widget
        self.f_units.load_dict()  # frequency units widget
        self.f_specs.load_dict()  # frequency specification widget
        self.a_specs.load_dict()  # magnitude specs with unit
        self.w_specs.load_dict()  # weight specification
        self.t_specs.load_dict()  # target specs

        self.color_design_button("ok")

# ------------------------------------------------------------------------------

    def start_design_filt(self):
        """
        Start the actual filter design process:

        - store the entries of all input widgets in the global filter dict.
        - call the design method, passing the whole dictionary as the
          argument: let the design method pick the needed specs
        - update the input widgets in case weights, corner frequencies etc.
          have been changed by the filter design method
        - the plots are updated via signal-slot connection
        """

        try:
            logger.info(
                "Start filter design using method\n\t'{0}.{1}{2}'".format(
                    str(fb.fil[0]['fc']), str(fb.fil[0]['rt']),
                    str(fb.fil[0]['fo'])))

            # ----------------------------------------------------------------------
            # A globally accessible instance fb.fil_inst of selected filter class fc
            # has been instantiated in InputFilter.set_design_method, now
            # call the method specified in the filter dict fil[0].

            # The name of the instance method is constructed from the response
            # type (e.g. 'LP') and the filter order (e.g. 'man'), giving e.g. 'LPman'.
            # The filter is designed by passing the specs in fil[0] to the method,
            # resulting in e.g. cheby1.LPman(fb.fil[0]) and writing back coefficients,
            # P/Z etc. back to fil[0].

            err = ff.fil_factory.call_fil_method(
                fb.fil[0]['rt'] + fb.fil[0]['fo'], fb.fil[0])
            # this is the same as e.g.
            # from pyfda.filter_design import ellip
            # inst = ellip.ellip()
            # inst.LPmin(fb.fil[0])
            # -----------------------------------------------------------------------

            if err > 0:
                self.color_design_button("error")
            elif err == -1:  # filter design cancelled by user
                return
            else:
                # Update filter order. weights and freq display in case they
                # have been changed by the design algorithm
                self.sel_fil.load_filter_order()
                self.w_specs.load_dict()
                self.f_specs.load_dict()
                self.color_design_button("ok")

                self.emit({'data_changed': 'filter_designed'})
                logger.info('Designed filter with order = {0}'.format(
                    str(fb.fil[0]['N'])))
# =============================================================================
#                 logger.debug("Results:\n"
#                     "F_PB = %s, F_SB = %s "
#                     "Filter order N = %s\n"
#                     "NDim fil[0]['ba'] = %s\n\n"
#                     "b,a = %s\n\n"
#                     "zpk = %s\n",
#                     str(fb.fil[0]['F_PB']), str(fb.fil[0]['F_SB']), str(fb.fil[0]['N']),
#                     str(np.ndim(fb.fil[0]['ba'])), pformat(fb.fil[0]['ba']),
#                     pformat(fb.fil[0]['zpk']))
#
# =============================================================================
        except Exception as e:
            if ('__doc__' in str(e)):
                logger.warning("Filter design:\n %s\n %s\n", e.__doc__, e)
            else:
                logger.warning("{0}".format(e))
            self.color_design_button("error")

    def color_design_button(self, state):
        man = "manual" in fb.fil[0]['fc'].lower()
        self.butDesignFilt.setDisabled(man)
        if man:
            state = 'ok'
        fb.design_filt_state = state
        qstyle_widget(self.butDesignFilt, state)

# ------------------------------------------------------------------------------

    def quit_program(self):
        """
        When <QUIT> button is pressed, send 'quit_program'
        """
        self.emit({'quit_program': ''})
コード例 #3
0
    def _construct_UI(self, **kwargs):
        """ Construct widget """

        dict_ui = {
            'wdg_name': 'ui_q',
            'label': '',
            'label_q': 'Quant.',
            'tip_q': 'Select the kind of quantization.',
            'cmb_q': ['round', 'fix', 'floor'],
            'cur_q': 'round',
            'label_ov': 'Ovfl.',
            'tip_ov': 'Select overflow behaviour.',
            'cmb_ov': ['wrap', 'sat'],
            'cur_ov': 'wrap',
            'enabled': True,
            'visible': True
        }  #: default widget settings

        if 'quant' in self.q_dict and self.q_dict['quant'] in dict_ui['cmb_q']:
            dict_ui['cur_q'] = self.q_dict['quant']
        if 'ovfl' in self.q_dict and self.q_dict['ovfl'] in dict_ui['cmb_ov']:
            dict_ui['cur_ov'] = self.q_dict['ovfl']

        for key, val in kwargs.items():
            dict_ui.update({key: val})
        # dict_ui.update(map(kwargs)) # same as above?

        self.wdg_name = dict_ui['wdg_name']

        lblQuant = QLabel(dict_ui['label_q'], self)
        self.cmbQuant = QComboBox(self)
        self.cmbQuant.addItems(dict_ui['cmb_q'])
        qset_cmb_box(self.cmbQuant, dict_ui['cur_q'])
        self.cmbQuant.setToolTip(dict_ui['tip_q'])
        self.cmbQuant.setObjectName('quant')

        lblOvfl = QLabel(dict_ui['label_ov'], self)
        self.cmbOvfl = QComboBox(self)
        self.cmbOvfl.addItems(dict_ui['cmb_ov'])
        qset_cmb_box(self.cmbOvfl, dict_ui['cur_ov'])
        self.cmbOvfl.setToolTip(dict_ui['tip_ov'])
        self.cmbOvfl.setObjectName('ovfl')

        # ComboBox size is adjusted automatically to fit the longest element
        self.cmbQuant.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.cmbOvfl.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        layH = QHBoxLayout()
        if dict_ui['label'] != "":
            lblW = QLabel(to_html(dict_ui['label'], frmt='bi'), self)
            layH.addWidget(lblW)
        layH.addStretch()
        layH.addWidget(lblOvfl)
        layH.addWidget(self.cmbOvfl)
        # layH.addStretch(1)
        layH.addWidget(lblQuant)
        layH.addWidget(self.cmbQuant)
        layH.setContentsMargins(0, 0, 0, 0)

        frmMain = QFrame(self)
        frmMain.setLayout(layH)

        layVMain = QVBoxLayout()  # Widget main layout
        layVMain.addWidget(frmMain)
        layVMain.setContentsMargins(0, 0, 0, 0)  # *params['wdg_margins'])

        self.setLayout(layVMain)

        # ----------------------------------------------------------------------
        # INITIAL SETTINGS
        # ----------------------------------------------------------------------
        self.ovfl = qget_cmb_box(self.cmbOvfl, data=False)
        self.quant = qget_cmb_box(self.cmbQuant, data=False)
        frmMain.setEnabled(dict_ui['enabled'])
        frmMain.setVisible(dict_ui['visible'])

        # ----------------------------------------------------------------------
        # LOCAL SIGNALS & SLOTs
        # ----------------------------------------------------------------------
        self.cmbOvfl.currentIndexChanged.connect(self.ui2dict)
        self.cmbQuant.currentIndexChanged.connect(self.ui2dict)