def _copy_options(self): """ Set options for copying to/from clipboard or file. """ self.opt_widget = CSV_option_box(self) # important: Handle must be class attribute #self.opt_widget.show() # modeless dialog, i.e. non-blocking self.opt_widget.exec_() # modal dialog (blocking)
def _open_csv_win(self): """ Pop-up window for CSV options """ if self.but_csv_options.isChecked(): qstyle_widget(self.but_csv_options, "changed") else: qstyle_widget(self.but_csv_options, "normal") if dirs.csv_options_handle is None: # no handle to the window? Create a new instance if self.but_csv_options.isChecked(): # Important: Handle to window must be class attribute, otherwise it # (and the attached window) is deleted immediately when it goes # out of scope dirs.csv_options_handle = CSV_option_box(self) dirs.csv_options_handle.sig_tx.connect(self.process_sig_rx) dirs.csv_options_handle.show() # modeless i.e. non-blocking popup window else: if not self.but_csv_options.isChecked(): # this should not happen if dirs.csv_options_handle is None: logger.warning("CSV options window is already closed!") else: dirs.csv_options_handle.close() self.emit({'ui_changed': 'csv'})
class Input_Coeffs(QWidget): """ Create widget with a (sort of) model-view architecture for viewing / editing / entering data contained in `self.ba` which is a list of two numpy arrays: - `self.ba[0]` contains the numerator coefficients ("b") - `self.ba[1]` contains the denominator coefficients ("a") The list don't neccessarily have the same length but they are always defined. For FIR filters, `self.ba[1][0] = 1`, all other elements are zero. The length of both lists can be egalized with `self._equalize_ba_length()`. Views / formats are handled by the ItemDelegate() class. """ sig_tx = pyqtSignal(object) # emitted when filter has been saved sig_rx = pyqtSignal(object) # incoming from input_tab_widgets def __init__(self, parent): super(Input_Coeffs, self).__init__(parent) self.opt_widget = None # handle for pop-up options widget self.tool_tip = "Display and edit filter coefficients." self.tab_label = "b,a" self.data_changed = True # initialize flag: filter data has been changed self.fx_specs_changed = True # fixpoint specs have been changed outside self.ui = Input_Coeffs_UI(self) # create the UI part with buttons etc. self._construct_UI() #------------------------------------------------------------------------------ def process_sig_rx(self, dict_sig=None): """ Process signals coming from sig_rx """ logger.debug("process_sig_rx(): vis={0}\n{1}"\ .format(self.isVisible(), pprint_log(dict_sig))) if dict_sig['sender'] == __name__: logger.debug("Stopped infinite loop\n{0}".format(pprint_log(dict_sig))) return if 'ui_changed' in dict_sig and dict_sig['ui_changed'] == 'csv': self.ui._set_load_save_icons() elif self.isVisible(): if self.data_changed or 'data_changed' in dict_sig: self.load_dict() self.data_changed = False if self.fx_specs_changed or ('fx_sim' in dict_sig and dict_sig['fx_sim'] == 'specs_changed'): self.qdict2ui() self.fx_specs_changed = False else: # TODO: draw wouldn't be necessary for 'view_changed', only update view if 'data_changed' in dict_sig: self.data_changed = True elif 'fx_sim' in dict_sig and dict_sig['fx_sim'] == 'specs_changed': self.fx_specs_changed = True #------------------------------------------------------------------------------ def _construct_UI(self): """ Intitialize the widget, consisting of: - top chkbox row - coefficient table - two bottom rows with action buttons """ # --------------------------------------------------------------------- # Coefficient table widget # --------------------------------------------------------------------- self.tblCoeff = QTableWidget(self) self.tblCoeff.setAlternatingRowColors(True) self.tblCoeff.horizontalHeader().setHighlightSections(True) # highlight when selected self.tblCoeff.horizontalHeader().setFont(self.ui.bfont) # self.tblCoeff.QItemSelectionModel.Clear self.tblCoeff.setDragEnabled(True) # self.tblCoeff.setDragDropMode(QAbstractItemView.InternalMove) # doesn't work like intended self.tblCoeff.setItemDelegate(ItemDelegate(self)) # ============== Main UI Layout ===================================== layVMain = QVBoxLayout() layVMain.setAlignment(Qt.AlignTop) # this affects only the first widget (intended here) layVMain.addWidget(self.ui) layVMain.addWidget(self.tblCoeff) layVMain.setContentsMargins(*params['wdg_margins']) self.setLayout(layVMain) self.myQ = fx.Fixed(fb.fil[0]['fxqc']['QCB']) # initialize fixpoint object self.load_dict() # initialize + refresh table with default values from filter dict # TODO: this needs to be optimized - self._refresh is being called in both routines self._set_number_format() #---------------------------------------------------------------------- # GLOBAL SIGNALS & SLOTs #---------------------------------------------------------------------- self.sig_rx.connect(self.process_sig_rx) #---------------------------------------------------------------------- # LOCAL (UI) SIGNALS & SLOTs #---------------------------------------------------------------------- # wdg.textChanged() is emitted when contents of widget changes # wdg.textEdited() is only emitted for user changes # wdg.editingFinished() is only emitted for user changes self.ui.butEnable.clicked.connect(self._refresh_table) self.ui.spnDigits.editingFinished.connect(self._refresh_table) self.ui.cmbQFrmt.currentIndexChanged.connect(self._set_number_format) self.ui.butFromTable.clicked.connect(self._copy_from_table) self.ui.butToTable.clicked.connect(self._copy_to_table) self.ui.cmbFilterType.currentIndexChanged.connect(self._filter_type) self.ui.butDelCells.clicked.connect(self._delete_cells) self.ui.butAddCells.clicked.connect(self._add_cells) self.ui.butLoad.clicked.connect(self.load_dict) self.ui.butSave.clicked.connect(self._save_dict) self.ui.butClear.clicked.connect(self._clear_table) self.ui.ledEps.editingFinished.connect(self._set_eps) self.ui.butSetZero.clicked.connect(self._set_coeffs_zero) # store new settings and refresh table self.ui.cmbFormat.currentIndexChanged.connect(self.ui2qdict) self.ui.cmbQOvfl.currentIndexChanged.connect(self.ui2qdict) self.ui.cmbQuant.currentIndexChanged.connect(self.ui2qdict) self.ui.ledWF.editingFinished.connect(self.ui2qdict) self.ui.ledWI.editingFinished.connect(self.ui2qdict) self.ui.ledW.editingFinished.connect(self._W_changed) self.ui.ledScale.editingFinished.connect(self._set_scale) self.ui.butQuant.clicked.connect(self.quant_coeffs) self.ui.sig_tx.connect(self.sig_tx) # ===================================================================== #------------------------------------------------------------------------------ def _filter_type(self, ftype=None): """ Get / set 'FIR' and 'IIR' filter from cmbFilterType combobox and set filter dict and table properties accordingly. When argument fil_type is not None, set the combobox accordingly. Reload from filter dict unless ftype is specified [does this make sense?!] """ if ftype in {'FIR', 'IIR'}: ret=qset_cmb_box(self.ui.cmbFilterType, ftype) if ret == -1: logger.warning("Unknown filter type {0}".format(ftype)) if self.ui.cmbFilterType.currentText() == 'IIR': fb.fil[0]['ft'] = 'IIR' self.col = 2 self.tblCoeff.setColumnCount(2) self.tblCoeff.setHorizontalHeaderLabels(["b", "a"]) else: fb.fil[0]['ft'] = 'FIR' self.col = 1 self.tblCoeff.setColumnCount(1) self.tblCoeff.setHorizontalHeaderLabels(["b"]) self.ba[1] = np.zeros_like(self.ba[1]) # enforce FIR filter self.ba[1][0] = 1. self._equalize_ba_length() qstyle_widget(self.ui.butSave, 'changed') self._refresh_table() #------------------------------------------------------------------------------ def _W_changed(self): """ Set fractional and integer length `WF` and `WI` when wordlength `W` has been changed. Try to preserve `WI` or `WF` settings depending on the number format (integer or fractional). """ W = safe_eval(self.ui.ledW.text(), self.myQ.W, return_type='int', sign='pos') if W < 2: logger.warn("W must be > 1, restoring previous value.") W = self.myQ.W # fall back to previous value self.ui.ledW.setText(str(W)) if qget_cmb_box(self.ui.cmbQFrmt) == 'qint': # integer format, preserve WI bits WI = W - self.myQ.WF - 1 self.ui.ledWI.setText(str(WI)) self.ui.ledScale.setText(str(1 << (W-1))) else: # fractional format, preserve WF bit setting WF = W - self.myQ.WI - 1 if WF < 0: self.ui.ledWI.setText(str(W - 1)) WF = 0 self.ui.ledWF.setText(str(WF)) self.ui2qdict() #------------------------------------------------------------------------------ def _set_scale(self): """ Triggered by `ui.ledScale` Set scale for calculating floating point value from fixpoint representation and vice versa """ # if self.ui.ledScale.isModified() ... self.ui.ledScale.setModified(False) scale = safe_eval(self.ui.ledScale.text(), self.myQ.scale, return_type='float', sign='pos') self.ui.ledScale.setText(str(scale)) self.ui2qdict() #------------------------------------------------------------------------------ def _refresh_table_item(self, row, col): """ Refresh the table item with the index `row, col` from self.ba """ item = self.tblCoeff.item(row, col) if item: # does item exist? item.setText(str(self.ba[col][row]).strip('()')) else: # no, construct it: self.tblCoeff.setItem(row,col,QTableWidgetItem( str(self.ba[col][row]).strip('()'))) self.tblCoeff.item(row, col).setTextAlignment(Qt.AlignRight|Qt.AlignCenter) #------------------------------------------------------------------------------ def _refresh_table(self): """ (Re-)Create the displayed table from `self.ba` (list with 2 one-dimensional numpy arrays). Data is displayed via `ItemDelegate.displayText()` in the number format set by `self.frmt`. - self.ba[0] -> b coefficients - self.ba[1] -> a coefficients The table dimensions are set according to the filter type set in `fb.fil[0]['ft']` which is either 'FIR' or 'IIR' and by the number of rows in `self.ba`. Called at the end of nearly every method. """ if np.ndim(self.ba) == 1 or fb.fil[0]['ft'] == 'FIR': self.num_rows = len(self.ba[0]) else: self.num_rows = max(len(self.ba[1]), len(self.ba[0])) # logger.warning("np.shape(ba) = {0}".format(np.shape(self.ba))) params['FMT_ba'] = int(self.ui.spnDigits.text()) # When format is 'float', disable all fixpoint options is_float = (qget_cmb_box(self.ui.cmbFormat, data=False).lower() == 'float') self.ui.spnDigits.setVisible(is_float) # number of digits can only be selected self.ui.lblDigits.setVisible(is_float) # for format = 'float' self.ui.cmbQFrmt.setVisible(not is_float) # hide unneeded widgets for format = 'float' self.ui.lbl_W.setVisible(not is_float) self.ui.ledW.setVisible(not is_float) self.ui.frmQSettings.setVisible(not is_float) # hide all q-settings for float if self.ui.butEnable.isChecked(): self.ui.butEnable.setIcon(QIcon(':/circle-x.svg')) self.ui.frmButtonsCoeffs.setVisible(True) self.tblCoeff.setVisible(True) # check whether filter is FIR and only needs one column if fb.fil[0]['ft'] == 'FIR': self.num_cols = 1 self.tblCoeff.setColumnCount(1) self.tblCoeff.setHorizontalHeaderLabels(["b"]) qset_cmb_box(self.ui.cmbFilterType, 'FIR') else: self.num_cols = 2 self.tblCoeff.setColumnCount(2) self.tblCoeff.setHorizontalHeaderLabels(["b", "a"]) qset_cmb_box(self.ui.cmbFilterType, 'IIR') self.ba[1][0] = 1.0 # restore fa[0] = 1 of denonimator polynome self.tblCoeff.setRowCount(self.num_rows) self.tblCoeff.setColumnCount(self.num_cols) # Create strings for index column (vertical header), starting with "0" idx_str = [str(n) for n in range(self.num_rows)] self.tblCoeff.setVerticalHeaderLabels(idx_str) self.tblCoeff.blockSignals(True) for col in range(self.num_cols): for row in range(self.num_rows): self._refresh_table_item(row, col) # make a[0] selectable but not editable if fb.fil[0]['ft'] == 'IIR': item = self.tblCoeff.item(0,1) item.setFlags(Qt.ItemIsSelectable| Qt.ItemIsEnabled) item.setFont(self.ui.bfont) self.tblCoeff.blockSignals(False) self.tblCoeff.resizeColumnsToContents() self.tblCoeff.resizeRowsToContents() self.tblCoeff.clearSelection() else: self.ui.frmButtonsCoeffs.setVisible(False) self.ui.butEnable.setIcon(QIcon(':/circle-check.svg')) self.tblCoeff.setVisible(False) #------------------------------------------------------------------------------ def load_dict(self): """ Load all entries from filter dict `fb.fil[0]['ba']` into the coefficient list `self.ba` and update the display via `self._refresh_table()`. The filter dict is a "normal" 2D-numpy float array for the b and a coefficients while the coefficient register `self.ba` is a list of two float ndarrays to allow for different lengths of b and a subarrays while adding / deleting items. """ self.ba = [0., 0.] # initial list with two elements self.ba[0] = np.array(fb.fil[0]['ba'][0]) # deep copy from filter dict to self.ba[1] = np.array(fb.fil[0]['ba'][1]) # coefficient register # set quantization comboBoxes from dictionary self.qdict2ui() self._refresh_table() qstyle_widget(self.ui.butSave, 'normal') #------------------------------------------------------------------------------ def _copy_options(self): """ Set options for copying to/from clipboard or file. """ self.opt_widget = CSV_option_box(self) # important: Handle must be class attribute #self.opt_widget.show() # modeless dialog, i.e. non-blocking self.opt_widget.exec_() # modal dialog (blocking) #------------------------------------------------------------------------------ def _copy_from_table(self): """ Copy data from coefficient table `self.tblCoeff` to clipboard / file in CSV format. """ qtable2text(self.tblCoeff, self.ba, self, 'ba', self.myQ.frmt, title="Export Filter Coefficients") #------------------------------------------------------------------------------ def _copy_to_table(self): """ Read data from clipboard / file and copy it to `self.ba` as float / cmplx # TODO: More checks for swapped row <-> col, single values, wrong data type ... """ data_str = qtext2table(self, 'ba', title="Import Filter Coefficients") # returns ndarray of str if data_str is None: # file operation has been aborted or some other error return logger.debug("importing data: dim - shape = {0} - {1} - {2}\n{3}"\ .format(type(data_str), np.ndim(data_str), np.shape(data_str), data_str)) conv = self.myQ.frmt2float # frmt2float_vec? frmt = self.myQ.frmt if np.ndim(data_str) > 1: num_cols, num_rows = np.shape(data_str) orientation_horiz = num_cols > num_rows # need to transpose data elif np.ndim(data_str) == 1: num_rows = len(data_str) num_cols = 1 orientation_horiz = False else: logger.error("Imported data is a single value or None.") return None logger.info("_copy_to_table: c x r = {0} x {1}".format(num_cols, num_rows)) if orientation_horiz: self.ba = [[],[]] for c in range(num_cols): self.ba[0].append(conv(data_str[c][0], frmt)) if num_rows > 1: self.ba[1].append(conv(data_str[c][1], frmt)) if num_rows > 1: self._filter_type(ftype='IIR') else: self._filter_type(ftype='FIR') else: self.ba[0] = [conv(s, frmt) for s in data_str[0]] if num_cols > 1: self.ba[1] = [conv(s, frmt) for s in data_str[1]] self._filter_type(ftype='IIR') else: self.ba[1] = [1] self._filter_type(ftype='FIR') self.ba[0] = np.asarray(self.ba[0]) self.ba[1] = np.asarray(self.ba[1]) self._equalize_ba_length() qstyle_widget(self.ui.butSave, 'changed') self._refresh_table() #------------------------------------------------------------------------------ def _set_number_format(self): """ Triggered by `contruct_UI()`, `qdict2ui()`and by `ui.cmbQFrmt.currentIndexChanged()` Set one of three number formats: Integer, fractional, normalized fractional (triggered by self.ui.cmbQFrmt combobox) """ qfrmt = qget_cmb_box(self.ui.cmbQFrmt) is_qfrac = False W = safe_eval(self.ui.ledW.text(), self.myQ.W, return_type='int', sign='pos') if qfrmt == 'qint': self.ui.ledWI.setText(str(W - 1)) self.ui.ledWF.setText("0") elif qfrmt == 'qnfrac': # normalized fractional format self.ui.ledWI.setText("0") self.ui.ledWF.setText(str(W - 1)) else: # qfrmt == 'qfrac': is_qfrac = True WI = safe_eval(self.ui.ledWI.text(), self.myQ.WI, return_type='int') self.ui.ledScale.setText(str(1 << WI)) self.ui.ledWI.setEnabled(is_qfrac) self.ui.lblDot.setEnabled(is_qfrac) self.ui.ledWF.setEnabled(is_qfrac) self.ui.ledW.setEnabled(not is_qfrac) self.ui.ledScale.setEnabled(False) self.ui2qdict() # save UI to dict and to class attributes #------------------------------------------------------------------------------ def _update_MSB_LSB(self): """ Update the infos (LSB, MSB, Max) """ self.ui.lblLSB.setText("{0:.{1}g}".format(self.myQ.LSB, params['FMT_ba'])) self.ui.lblMSB.setText("{0:.{1}g}".format(self.myQ.MSB, params['FMT_ba'])) self.ui.lblMAX.setText("{0:.6g}".format(self.myQ.MAX)) #------------------------------------------------------------------------------ def qdict2ui(self): """ Triggered by: - process_sig_rx() if self.fx_specs_changed or dict_sig['fx_sim'] == 'specs_changed' - Set the UI from the quantization dict and update the fixpoint object. When neither WI == 0 nor WF == 0, set the quantization format to general fractional format qfrac. """ self.ui.ledWI.setText(qstr(fb.fil[0]['fxqc']['QCB']['WI'])) self.ui.ledWF.setText(qstr(fb.fil[0]['fxqc']['QCB']['WF'])) self.ui.ledW.setText(qstr(fb.fil[0]['fxqc']['QCB']['W'])) if fb.fil[0]['fxqc']['QCB']['WI'] != 0 and fb.fil[0]['fxqc']['QCB']['WF'] != 0: qset_cmb_box(self.ui.cmbQFrmt, 'qfrac', data=True) self.ui.ledScale.setText(qstr(fb.fil[0]['fxqc']['QCB']['scale'])) qset_cmb_box(self.ui.cmbQuant, fb.fil[0]['fxqc']['QCB']['quant']) qset_cmb_box(self.ui.cmbQOvfl, fb.fil[0]['fxqc']['QCB']['ovfl']) self.myQ.setQobj(fb.fil[0]['fxqc']['QCB']) # update class attributes self._set_number_format() # quant format has been changed, update display self._update_MSB_LSB() #------------------------------------------------------------------------------ def ui2qdict(self): """ Triggered by modifying `ui.cmbFormat`, `ui.cmbQOvfl`, `ui.cmbQuant`, `ui.ledWF`, `ui.ledWI` or `ui.ledW` (via `_W_changed()`) or `ui.cmbQFrmt` (via `_set_number_format()`) or `ui.ledScale()` (via `_set_scale()`) or 'qdict2ui()' via `_set_number_format()` Read out the settings of the quantization comboboxes. - Store them in the filter dict `fb.fil[0]['fxqc']['QCB']` and as class attributes in the fixpoint object `self.myQ` - Emit a signal with `'view_changed':'q_coeff'` - Refresh the table """ fb.fil[0]['fxqc']['QCB'] = { 'WI':safe_eval(self.ui.ledWI.text(), self.myQ.WI, return_type='int'), 'WF':safe_eval(self.ui.ledWF.text(), self.myQ.WF, return_type='int', sign='poszero'), 'W':safe_eval(self.ui.ledW.text(), self.myQ.W, return_type='int', sign='pos'), 'quant':qstr(self.ui.cmbQuant.currentText()), 'ovfl':qstr(self.ui.cmbQOvfl.currentText()), 'frmt':qstr(self.ui.cmbFormat.currentText().lower()), 'scale':qstr(self.ui.ledScale.text()) } self.myQ.setQobj(fb.fil[0]['fxqc']['QCB']) # update fixpoint object self.sig_tx.emit({'sender':__name__, 'view_changed':'q_coeff'}) self._update_MSB_LSB() self._refresh_table() #------------------------------------------------------------------------------ def _save_dict(self): """ Save the coefficient register `self.ba` to the filter dict `fb.fil[0]['ba']`. """ logger.debug("_save_dict called") fb.fil[0]['N'] = max(len(self.ba[0]), len(self.ba[1])) - 1 self.ui2qdict() if fb.fil[0]['ft'] == 'IIR': fb.fil[0]['fc'] = 'Manual_IIR' else: fb.fil[0]['fc'] = 'Manual_FIR' # save, check and convert coeffs, check filter type try: fil_save(fb.fil[0], self.ba, 'ba', __name__) except Exception as e: # catch exception due to malformatted coefficients: logger.error("While saving the filter coefficients, " "the following error occurred:\n{0}".format(e)) if __name__ == '__main__': self.load_dict() # only needed for stand-alone test self.sig_tx.emit({'sender':__name__, 'data_changed':'input_coeffs'}) # -> input_tab_widgets qstyle_widget(self.ui.butSave, 'normal') #------------------------------------------------------------------------------ def _clear_table(self): """ Clear self.ba: Initialize coeff for a poles and a zero @ origin, a = b = [1; 0]. Refresh QTableWidget """ self.ba = [np.asarray([1., 0.]), np.asarray([1., 0.])] self._refresh_table() qstyle_widget(self.ui.butSave, 'changed') #------------------------------------------------------------------------------ def _equalize_ba_length(self): """ test and equalize if b and a subarray have different lengths: """ try: a_len = len(self.ba[1]) except IndexError: self.ba.append(np.array(1)) a_len = 1 D = len(self.ba[0]) - a_len if D > 0: # b is longer than a self.ba[1] = np.append(self.ba[1], np.zeros(D)) elif D < 0: # a is longer than b if fb.fil[0]['ft'] == 'IIR': self.ba[0] = np.append(self.ba[0], np.zeros(-D)) else: self.ba[1] = self.ba[1][:D] # discard last D elements of a #------------------------------------------------------------------------------ def _delete_cells(self): """ Delete all selected elements in self.ba by: - determining the indices of all selected cells in the P and Z arrays - deleting elements with those indices - equalizing the lengths of b and a array by appending the required number of zeros. When nothing is selected, delete the last row. Finally, the QTableWidget is refreshed from self.ba. """ sel = qget_selected(self.tblCoeff)['sel'] # get indices of all selected cells if not any(sel) and len(self.ba[0]) > 0: # delete last row self.ba = np.delete(self.ba, -1, axis=1) elif np.all(sel[0] == sel[1]) or fb.fil[0]['ft'] == 'FIR': # only complete rows selected or FIR -> delete row self.ba = np.delete(self.ba, sel[0], axis=1) else: self.ba[0][sel[0]] = 0 self.ba[1][sel[1]] = 0 #self.ba[0] = np.delete(self.ba[0], sel[0]) #self.ba[1] = np.delete(self.ba[1], sel[1]) # test and equalize if b and a array have different lengths: self._equalize_ba_length() # if length is less than 2, clear the table: this ain't no filter! if len(self.ba[0]) < 2: self._clear_table() # sets 'changed' attribute else: self._refresh_table() qstyle_widget(self.ui.butSave, 'changed') #------------------------------------------------------------------------------ def _add_cells(self): """ Add the number of selected rows to self.ba and fill new cells with zeros from the bottom. If nothing is selected, add one row at the bottom. Refresh QTableWidget. """ # get indices of all selected cells sel = qget_selected(self.tblCoeff)['sel'] if not any(sel): # nothing selected, append one row of zeros to table self.ba = np.insert(self.ba, len(self.ba[0]), 0, axis=1) #"insert" row after last elif np.all(sel[0] == sel[1]) or fb.fil[0]['ft'] == 'FIR': # only complete rows selected self.ba = np.insert(self.ba, sel[0], 0, axis=1) # elif len(sel[0]) == len(sel[1]): # self.ba = np.insert(self.ba, sel, 0, axis=1) # not allowed, sel needs to be a scalar or one-dimensional else: logger.warning("It is only possible to insert complete rows!") # The following doesn't work because the subarrays wouldn't have # the same length for a moment #self.ba[0] = np.insert(self.ba[0], sel[0], 0) #self.ba[1] = np.insert(self.ba[1], sel[1], 0) return # insert 'sel' contiguous rows before 'row': # self.ba[0] = np.insert(self.ba[0], row, np.zeros(sel)) self._equalize_ba_length() self._refresh_table() # don't tag as 'changed' when only zeros have been added at the end if any(sel): qstyle_widget(self.ui.butSave, 'changed') #------------------------------------------------------------------------------ def _set_eps(self): """ Set all coefficients = 0 in self.ba with a magnitude less than eps and refresh QTableWidget """ self.ui.eps = safe_eval(self.ui.ledEps.text(), return_type='float', sign='pos', alt_expr=self.ui.eps) self.ui.ledEps.setText(str(self.ui.eps)) #------------------------------------------------------------------------------ def _set_coeffs_zero(self): """ Set all coefficients = 0 in self.ba with a magnitude less than eps and refresh QTableWidget """ self._set_eps() idx = qget_selected(self.tblCoeff)['idx'] # get all selected indices test_val = 0. # value against which array is tested targ_val = 0. # value which is set when condition is true changed = False if not idx: # nothing selected, check whole table b_close = np.logical_and(np.isclose(self.ba[0], test_val, rtol=0, atol=self.ui.eps), (self.ba[0] != targ_val)) if np.any(b_close): # found at least one coeff where condition was true self.ba[0] = np.where(b_close, targ_val, self.ba[0]) changed = True if fb.fil[0]['ft'] == 'IIR': a_close = np.logical_and(np.isclose(self.ba[1], test_val, rtol=0, atol=self.ui.eps), (self.ba[1] != targ_val)) if np.any(a_close): self.ba[1] = np.where(a_close, targ_val, self.ba[1]) changed = True else: # only check selected cells for i in idx: if np.logical_and(np.isclose(self.ba[i[0]][i[1]], test_val, rtol=0, atol=self.ui.eps), (self.ba[i[0]][i[1]] != targ_val)): self.ba[i[0]][i[1]] = targ_val changed = True if changed: qstyle_widget(self.ui.butSave, 'changed') # mark save button as changed self._refresh_table() #------------------------------------------------------------------------------ def quant_coeffs(self): """ Quantize selected / all coefficients in self.ba and refresh QTableWidget """ idx = qget_selected(self.tblCoeff)['idx'] # get all selected indices if not idx: # nothing selected, quantize all elements self.ba[0] = self.myQ.fixp(self.ba, scaling='multdiv')[0] if fb.fil[0]['ft'] == "IIR": self.ba[1] = self.myQ.fixp(self.ba, scaling='multdiv')[0] else: for i in idx: self.ba[i[0]][i[1]] = self.myQ.fixp(self.ba[i[0]][i[1]], scaling = 'multdiv') qstyle_widget(self.ui.butSave, 'changed') self._refresh_table()