def update_UI(self, new_labels=()): """ Set labels and get corresponding values from filter dictionary. When number of entries has changed, the layout of subwidget is rebuilt, using - `self.qlabels`, a list with references to existing QLabel widgets, - `new_labels`, a list of strings from the filter_dict for the current filter design - 'num_new_labels`, their number - `self.n_cur_labels`, the number of currently visible labels / qlineedit fields """ state = new_labels[0] new_labels = new_labels[1:] self.lblUnit.setText(" in " + rt_label(fb.fil[0]['freq_specs_unit'])) num_new_labels = len(new_labels) # hide / show labels / create new subwidgets if neccessary: self._show_entries(num_new_labels) # W_lbl = max([self.qfm.width(l) for l in new_labels]) # max. label width in pixel #---------------------------- logging ----------------------------- logger.debug("update_UI: {0}-{1}-{2}".format(fb.fil[0]['rt'], fb.fil[0]['fc'], fb.fil[0]['fo'])) f_range = " (0 < <i>f</i> < <i>f<sub>S </sub></i>/2)" for i in range(num_new_labels): # Update ALL labels and corresponding values self.qlabels[i].setText(rt_label(new_labels[i])) self.qlineedit[i].setText(str(fb.fil[0][new_labels[i]])) self.qlineedit[i].setObjectName(new_labels[i]) # update ID qstyle_widget(self.qlineedit[i], state) if "sb" in new_labels[i].lower(): self.qlineedit[i].setToolTip( "<span>Corner frequency for (this) stop band" + f_range + ".</span>") elif "pb" in new_labels[i].lower(): self.qlineedit[i].setToolTip( "<span>Corner frequency for (this) pass band" + f_range + ".</span>") else: self.qlineedit[i].setToolTip( "<span>Corner frequency for (this) band" + f_range + ".</span>") self.n_cur_labels = num_new_labels # update number of currently visible labels self.sort_dict_freqs( ) # sort frequency entries in dictionary and update display
def _construct_UI(self): """ Construct the User Interface """ bfont = QFont() bfont.setBold(True) lblTitle = QLabel(str(self.title), self) # field for widget title lblTitle.setFont(bfont) lblTitle.setWordWrap(True) self.lblUnit = QLabel(self) self.lblUnit.setText("in " + rt_label(fb.fil[0]['freq_specs_unit'])) layHTitle = QHBoxLayout() layHTitle.addWidget(lblTitle) layHTitle.addWidget(self.lblUnit) layHTitle.addStretch(1) # Create a gridLayout consisting of QLabel and QLineEdit fields # for the frequency specs: self.layGSpecs = QGridLayout() # sublayout for spec fields # set the title as the first (fixed) entry in grid layout. The other # fields are added and hidden dynamically in _show_entries and _hide_entries() self.layGSpecs.addLayout(layHTitle, 0, 0, 1, 2) self.layGSpecs.setAlignment(Qt.AlignLeft) self.frmMain = QFrame(self) self.frmMain.setLayout(self.layGSpecs) self.layVMain = QVBoxLayout() # Widget main layout self.layVMain.addWidget(self.frmMain)#, Qt.AlignLeft) self.layVMain.setContentsMargins(*params['wdg_margins']) self.setLayout(self.layVMain) self.n_cur_labels = 0 # number of currently visible labels / qlineedits
def update_UI(self, new_labels=[]): """ Set labels and get corresponding values from filter dictionary. When number of entries has changed, the layout of subwidget is rebuilt, using - `self.qlabels`, a list with references to existing QLabel widgets, - `new_labels`, a list of strings from the filter_dict for the current filter design - 'num_new_labels`, their number - `self.n_cur_labels`, the number of currently visible labels / qlineedit fields """ num_new_labels = len(new_labels) if num_new_labels < self.n_cur_labels: # less new labels/qlineedit fields than before self._hide_entries(num_new_labels) elif num_new_labels > self.n_cur_labels: # more new labels, create / show new ones self._show_entries(num_new_labels) for i in range(num_new_labels): # Update ALL labels and corresponding values self.qlabels[i].setText(rt_label(new_labels[i])) self.qlineedit[i].setText(str(fb.fil[0][new_labels[i]])) self.qlineedit[i].setObjectName(new_labels[i]) # update ID self.n_cur_labels = num_new_labels # update number of currently visible labels self.load_entries( ) # display rounded filter dict entries in selected unit
def _show_entries(self, num_new_labels): """ - check whether enough subwidgets (QLabel und QLineEdit) exist for the the required number of `num_new_labels`: - create new ones if required - initialize them with dummy information - install eventFilter for new QLineEdit widgets so that the filter dict is updated automatically when a QLineEdit field has been edited. - if enough subwidgets exist already, make enough of them visible to show all spec fields """ num_tot_labels = len( self.qlabels) # number of existing labels (vis. + invis.) if num_tot_labels < num_new_labels: # new widgets need to be generated for i in range(num_tot_labels, num_new_labels): self.qlabels.append(QLabel(self)) self.qlabels[i].setText(rt_label("dummy")) self.qlineedit.append(QLineEdit("")) self.qlineedit[i].setObjectName("dummy") self.qlineedit[i].installEventFilter(self) # filter events self.layGSpecs.addWidget(self.qlabels[i], i, 0) self.layGSpecs.addWidget(self.qlineedit[i], i, 1) else: # make the right number of widgets visible for i in range(self.n_cur_labels, num_new_labels): self.qlabels[i].show() self.qlineedit[i].show()
def _show_entries(self, num_new_labels): """ - check whether enough subwidgets (QLabel und QLineEdit) exist for the the required number of `num_new_labels`: - create new ones if required - initialize them with dummy information - install eventFilter for new QLineEdit widgets so that the filter dict is updated automatically when a QLineEdit field has been edited. - if enough subwidgets exist already, make enough of them visible to show all spec fields """ num_tot_labels = len(self.qlabels) # number of existing labels / qlineedit fields if num_tot_labels < num_new_labels: # new widgets need to be generated for i in range(num_tot_labels, num_new_labels): self.qlabels.append(QLabel(self)) self.qlabels[i].setText(rt_label("dummy")) self.qlineedit.append(QLineEdit("")) self.qlineedit[i].setObjectName("dummy") self.qlineedit[i].installEventFilter(self) # filter events # first entry is title and reset button self.layGSpecs.addWidget(self.qlabels[i],i+1,0) self.layGSpecs.addWidget(self.qlineedit[i],i+1,1) else: # make the right number of widgets visible for i in range(self.n_cur_labels, num_new_labels): self.qlabels[i].show() self.qlineedit[i].show()
def _construct_UI(self): """ Construct the User Interface """ self.layVMain = QVBoxLayout() # Widget main layout layHTitle = QHBoxLayout() bfont = QFont() bfont.setBold(True) # bfont.setWeight(75) lblTitle = QLabel() # field for widget title lblTitle.setText(str(self.title)) lblTitle.setFont(bfont) lblTitle.setWordWrap(True) self.lblUnit = QLabel() self.lblUnit.setText(" in " + rt_label(fb.fil[0]['freq_specs_unit'])) layHTitle.addWidget(lblTitle) layHTitle.addWidget(self.lblUnit) layHTitle.addStretch(100) # Create a gridLayout consisting of QLabel and QLineEdit fields # for the frequency specs: self.layGSpecs = QGridLayout() # sublayout for spec fields sfFrame = QFrame() sfFrame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) sfFrame.setLayout(self.layGSpecs) self.layVMain.addLayout(layHTitle) self.layVMain.addWidget(sfFrame) self.layVMain.setContentsMargins(1, 1, 1, 1) self.setLayout(self.layVMain) self.n_cur_labels = 0 # number of currently visible labels / qlineedits
def update_UI(self, new_labels = []): """ Set labels and get corresponding values from filter dictionary. When number of entries has changed, the layout of subwidget is rebuilt, using - `self.qlabels`, a list with references to existing QLabel widgets, - `new_labels`, a list of strings from the filter_dict for the current filter design - 'num_new_labels`, their number - `self.n_cur_labels`, the number of currently visible labels / qlineedit fields """ state = new_labels[0] new_labels = new_labels[1:] num_new_labels = len(new_labels) if num_new_labels < self.n_cur_labels: # less new labels/qlineedit fields than before self._hide_entries(num_new_labels) elif num_new_labels > self.n_cur_labels: # more new labels, create / show new ones self._show_entries(num_new_labels) for i in range(num_new_labels): # Update ALL labels and corresponding values self.qlabels[i].setText(rt_label(new_labels[i])) self.qlineedit[i].setText(str(fb.fil[0][new_labels[i]])) self.qlineedit[i].setObjectName(new_labels[i]) # update ID self.qlineedit[i].setToolTip("<span>Relative weight (importance) for approximating this band.</span>") qstyle_widget(self.qlineedit[i], state) self.n_cur_labels = num_new_labels # update number of currently visible labels self.load_dict() # display rounded filter dict entries
def _construct_UI(self): """ Construct the User Interface """ bfont = QFont() bfont.setBold(True) lblTitle = QLabel(str(self.title), self) # field for widget title lblTitle.setFont(bfont) lblTitle.setWordWrap(True) self.lblUnit = QLabel(self) self.lblUnit.setText("in " + rt_label(fb.fil[0]['freq_specs_unit'])) layHTitle = QHBoxLayout() layHTitle.addWidget(lblTitle) layHTitle.addWidget(self.lblUnit) layHTitle.addStretch(1) # Create a gridLayout consisting of QLabel and QLineEdit fields # for the frequency specs: self.layGSpecs = QGridLayout() # sublayout for spec fields # set the title as the first (fixed) entry in grid layout. The other # fields are added and hidden dynamically in _show_entries and _hide_entries() self.layGSpecs.addLayout(layHTitle, 0, 0, 1, 2) self.layGSpecs.setAlignment(Qt.AlignLeft) self.frmMain = QFrame(self) self.frmMain.setLayout(self.layGSpecs) self.layVMain = QVBoxLayout() # Widget main layout self.layVMain.addWidget(self.frmMain) #, Qt.AlignLeft) self.layVMain.setContentsMargins(*params['wdg_margins']) self.setLayout(self.layVMain) self.n_cur_labels = 0 # number of currently visible labels / qlineedits
def update_UI(self, new_labels = ()): """ Set labels and get corresponding values from filter dictionary. When number of entries has changed, the layout of subwidget is rebuilt, using - `self.qlabels`, a list with references to existing QLabel widgets, - `new_labels`, a list of strings from the filter_dict for the current filter design - 'num_new_labels`, their number - `self.n_cur_labels`, the number of currently visible labels / qlineedit fields """ state = new_labels[0] new_labels = new_labels[1:] self.lblUnit.setText(" in " + rt_label(fb.fil[0]['freq_specs_unit'])) num_new_labels = len(new_labels) # hide / show labels / create new subwidgets if neccessary: self._show_entries(num_new_labels) # W_lbl = max([self.qfm.width(l) for l in new_labels]) # max. label width in pixel #---------------------------- logging ----------------------------- logger.debug("update_UI: {0}-{1}-{2}".format( fb.fil[0]['rt'],fb.fil[0]['fc'],fb.fil[0]['fo'])) f_range = " (0 < <i>f</i> < <i>f<sub>S </sub></i>/2)" for i in range(num_new_labels): # Update ALL labels and corresponding values self.qlabels[i].setText(rt_label(new_labels[i])) self.qlineedit[i].setText(str(fb.fil[0][new_labels[i]])) self.qlineedit[i].setObjectName(new_labels[i]) # update ID qstyle_widget(self.qlineedit[i], state) if "sb" in new_labels[i].lower(): self.qlineedit[i].setToolTip("<span>Corner frequency for (this) stop band" + f_range + ".</span>") elif "pb" in new_labels[i].lower(): self.qlineedit[i].setToolTip("<span>Corner frequency for (this) pass band" + f_range + ".</span>") else: self.qlineedit[i].setToolTip("<span>Corner frequency for (this) band" + f_range + ".</span>") self.n_cur_labels = num_new_labels # update number of currently visible labels self.sort_dict_freqs() # sort frequency entries in dictionary and update display
def update_UI(self, new_labels=()): """ Set labels and get corresponding values from filter dictionary. When number of entries has changed, the layout of subwidget is rebuilt, using - `self.qlabels`, a list with references to existing QLabel widgets, - `new_labels`, a list of strings from the filter_dict for the current filter design - 'num_new_labels`, their number - `self.n_cur_labels`, the number of currently visible labels / qlineedit fields """ state = new_labels[0] new_labels = new_labels[1:] # W_lbl = max([self.qfm.width(l) for l in new_labels]) # max. label width in pixel num_new_labels = len(new_labels) if num_new_labels < self.n_cur_labels: # less new labels/qlineedit fields than before self._hide_entries(num_new_labels) elif num_new_labels > self.n_cur_labels: # more new labels, create / show new ones self._show_entries(num_new_labels) tool_tipp_sb = "Min. attenuation resp. maximum level in (this) stop band" for i in range(num_new_labels): # Update ALL labels and corresponding values self.qlabels[i].setText(rt_label(new_labels[i])) self.qlineedit[i].setText(str(fb.fil[0][new_labels[i]])) self.qlineedit[i].setObjectName(new_labels[i]) # update ID if "sb" in new_labels[i].lower(): self.qlineedit[i].setToolTip("<span>" + tool_tipp_sb + " (> 0).</span>") elif "pb" in new_labels[i].lower(): self.qlineedit[i].setToolTip( "<span>Maximum ripple (> 0) in (this) pass band.<span/>" ) qstyle_widget(self.qlineedit[i], state) self.n_cur_labels = num_new_labels # update number of currently visible labels self.load_dict( ) # display rounded filter dict entries in selected unit
def update_UI(self, new_labels = ()): """ Set labels and get corresponding values from filter dictionary. When number of entries has changed, the layout of subwidget is rebuilt, using - `self.qlabels`, a list with references to existing QLabel widgets, - `new_labels`, a list of strings from the filter_dict for the current filter design - 'num_new_labels`, their number - `self.n_cur_labels`, the number of currently visible labels / qlineedit fields """ state = new_labels[0] new_labels = new_labels[1:] # W_lbl = max([self.qfm.width(l) for l in new_labels]) # max. label width in pixel num_new_labels = len(new_labels) if num_new_labels < self.n_cur_labels: # less new labels/qlineedit fields than before self._hide_entries(num_new_labels) elif num_new_labels > self.n_cur_labels: # more new labels, create / show new ones self._show_entries(num_new_labels) tool_tipp_sb = "Min. attenuation resp. maximum level in (this) stop band" for i in range(num_new_labels): # Update ALL labels and corresponding values self.qlabels[i].setText(rt_label(new_labels[i])) self.qlineedit[i].setText(str(fb.fil[0][new_labels[i]])) self.qlineedit[i].setObjectName(new_labels[i]) # update ID if "sb" in new_labels[i].lower(): self.qlineedit[i].setToolTip("<span>" + tool_tipp_sb + " (> 0).</span>") elif "pb" in new_labels[i].lower(): self.qlineedit[i].setToolTip("<span>Maximum ripple (> 0) in (this) pass band.<span/>") qstyle_widget(self.qlineedit[i], state) self.n_cur_labels = num_new_labels # update number of currently visible labels self.load_dict() # display rounded filter dict entries in selected unit
def _show_entries(self, num_new_labels): """ - check whether subwidgets need to be shown or hidden - check whether enough subwidgets (QLabel und QLineEdit) exist for the the required number of `num_new_labels`: - create new ones if required - initialize them with dummy information - install eventFilter for new QLineEdit widgets so that the filter dict is updated automatically when a QLineEdit field has been edited. - if enough subwidgets exist already, make enough of them visible to show all spec fields """ num_tot_labels = len( self.qlabels) # number of existing labels (vis. + invis.) # less new subwidgets than currently displayed -> _hide some if num_new_labels < self.n_cur_labels: # less new labels/qlineedit fields than before for i in range(num_new_labels, num_tot_labels): self.qlabels[i].hide() self.qlineedit[i].hide() # enough hidden subwidgets but need to make more labels visible elif num_tot_labels >= num_new_labels: for i in range(self.n_cur_labels, num_new_labels): self.qlabels[i].show() self.qlineedit[i].show() else: # new subwidgets need to be generated for i in range(num_tot_labels, num_new_labels): self.qlabels.append(QLabel(self)) self.qlabels[i].setText(rt_label("dummy")) self.qlineedit.append(QLineEdit("")) self.qlineedit[i].setObjectName("dummy") self.qlineedit[i].installEventFilter(self) # filter events # first entry is the title self.layGSpecs.addWidget(self.qlabels[i], i + 1, 0) self.layGSpecs.addWidget(self.qlineedit[i], i + 1, 1)
def _show_entries(self, num_new_labels): """ - check whether subwidgets need to be shown or hidden - check whether enough subwidgets (QLabel und QLineEdit) exist for the the required number of `num_new_labels`: - create new ones if required - initialize them with dummy information - install eventFilter for new QLineEdit widgets so that the filter dict is updated automatically when a QLineEdit field has been edited. - if enough subwidgets exist already, make enough of them visible to show all spec fields """ num_tot_labels = len(self.qlabels) # number of existing labels (vis. + invis.) # less new subwidgets than currently displayed -> _hide some if num_new_labels < self.n_cur_labels: # less new labels/qlineedit fields than before for i in range (num_new_labels, num_tot_labels): self.qlabels[i].hide() self.qlineedit[i].hide() # enough hidden subwidgets but need to make more labels visible elif num_tot_labels >= num_new_labels: for i in range(self.n_cur_labels, num_new_labels): self.qlabels[i].show() self.qlineedit[i].show() else: # new subwidgets need to be generated for i in range(num_tot_labels, num_new_labels): self.qlabels.append(QLabel(self)) self.qlabels[i].setText(rt_label("dummy")) self.qlineedit.append(QLineEdit("")) self.qlineedit[i].setObjectName("dummy") self.qlineedit[i].installEventFilter(self) # filter events # first entry is the title self.layGSpecs.addWidget(self.qlabels[i],i+1,0) self.layGSpecs.addWidget(self.qlineedit[i],i+1,1)
def update_UI(self, new_labels=[]): """ Set labels and get corresponding values from filter dictionary. When number of entries has changed, the layout of subwidget is rebuilt, using - `self.qlabels`, a list with references to existing QLabel widgets, - `new_labels`, a list of strings from the filter_dict for the current filter design - 'num_new_labels`, their number - `self.n_cur_labels`, the number of currently visible labels / qlineedit fields """ self.lblUnit.setText(" in " + str(fb.fil[0]['freq_specs_unit'])) self.new_labels = new_labels num_new_labels = len(new_labels) if num_new_labels < self.n_cur_labels: # less new labels/qlineedit fields than before self._hide_entries(num_new_labels) elif num_new_labels > self.n_cur_labels: # more new labels, create / show new ones self._show_entries(num_new_labels) #---------------------------- logging ----------------------------- logger.debug("update_UI: {0}-{1}-{2}".format(fb.fil[0]['rt'], fb.fil[0]['fc'], fb.fil[0]['fo'])) for i in range(num_new_labels): # Update ALL labels and corresponding values self.qlabels[i].setText(rt_label(new_labels[i])) self.qlineedit[i].setText(str(fb.fil[0][new_labels[i]])) self.qlineedit[i].setObjectName(new_labels[i]) # update ID self.n_cur_labels = num_new_labels # update number of currently visible labels self.sort_dict_freqs( ) # sort frequency entries in dictionary and update display
def draw_impz(self): """ (Re-)calculate h[n] and draw the figure """ log = self.chkLog.isChecked() stim = str(self.cmbStimulus.currentText()) periodic_sig = stim in {"Sine","Rect", "Saw"} self.lblLogBottom.setVisible(log) self.ledLogBottom.setVisible(log) self.lbldB.setVisible(log) self.lblFreq.setVisible(periodic_sig) self.ledFreq.setVisible(periodic_sig) self.lblFreqUnit.setVisible(periodic_sig) # self.lblFreqUnit.setVisible(fb.fil[0]['freq_specs_unit'] == 'f_S') self.lblFreqUnit.setText(rt_label(fb.fil[0]['freq_specs_unit'])) self.load_entry() self.bb = np.asarray(fb.fil[0]['ba'][0]) self.aa = np.asarray(fb.fil[0]['ba'][1]) sos = np.asarray(fb.fil[0]['sos']) self.f_S = fb.fil[0]['f_S'] N = self.calc_n_points(abs(int(self.ledNPoints.text()))) t = np.linspace(0, N/self.f_S, N, endpoint=False) # calculate h[n] if stim == "Pulse": x = np.zeros(N) x[0] =1.0 # create dirac impulse as input signal title_str = r'Impulse Response' H_str = r'$h[n]$' elif stim == "Step": x = np.ones(N) # create step function title_str = r'Step Response' H_str = r'$h_{\epsilon}[n]$' elif stim == "StepErr": x = np.ones(N) # create step function title_str = r'Settling Error' H_str = r'$H(0) - h_{\epsilon}[n]$' elif stim in {"Sine", "Rect"}: x = np.sin(2 * np.pi * t * float(self.ledFreq.text())) if stim == "Sine": title_str = r'Response to Sine Signal' H_str = r'$h_{\sin}[n]$' else: x = np.sign(x) title_str = r'Response to Rect. Signal' H_str = r'$h_{rect}[n]$' else: x = sig.sawtooth(t * (float(self.ledFreq.text())* 2*np.pi)) title_str = r'Response to Sawtooth Signal' H_str = r'$h_{saw}[n]$' if not np.any(sos): # no second order sections for current filter h = sig.lfilter(self.bb, self.aa, x) dc = sig.freqz(self.bb, self.aa, [0]) else: # print(sos) h = sig.sosfilt(sos, x) dc = sig.freqz(self.bb, self.aa, [0]) if stim == "StepErr": h = h - abs(dc[1]) # subtract DC value from response self.cmplx = np.any(np.iscomplex(h)) if self.cmplx: h_i = h.imag h = h.real H_i_str = r'$\Im\{$' + H_str + '$\}$' H_str = r'$\Re\{$' + H_str + '$\}$' if log: bottom = float(self.ledLogBottom.text()) H_str = r'$|$ ' + H_str + '$|$ in dB' h = np.maximum(20 * np.log10(abs(h)), bottom) if self.cmplx: h_i = np.maximum(20 * np.log10(abs(h_i)), bottom) H_i_str = r'$\log$ ' + H_i_str + ' in dB' else: bottom = 0 self._init_axes() #================ Main Plotting Routine ========================= [ml, sl, bl] = self.ax_r.stem(t, h, bottom=bottom, markerfmt='bo', linefmt='r') if self.chkPltStim.isChecked(): [ms, ss, bs] = self.ax_r.stem(t, x, bottom=bottom, markerfmt='k*', linefmt='0.5') for stem in ss: stem.set_linewidth(0.5) bs.set_visible(False) # invisible bottomline expand_lim(self.ax_r, 0.02) self.ax_r.set_title(title_str) if self.cmplx: [ml_i, sl_i, bl_i] = self.ax_i.stem(t, h_i, bottom=bottom, markerfmt='rd', linefmt='b') self.ax_i.set_xlabel(fb.fil[0]['plt_tLabel']) # self.ax_r.get_xaxis().set_ticklabels([]) # removes both xticklabels # plt.setp(ax_r.get_xticklabels(), visible=False) # is shorter but imports matplotlib, set property directly instead: [label.set_visible(False) for label in self.ax_r.get_xticklabels()] self.ax_r.set_ylabel(H_str + r'$\rightarrow $') self.ax_i.set_ylabel(H_i_str + r'$\rightarrow $') else: self.ax_r.set_xlabel(fb.fil[0]['plt_tLabel']) self.ax_r.set_ylabel(H_str + r'$\rightarrow $') if self.ACTIVE_3D: # not implemented / tested yet # plotting the stems for i in range(len(t)): self.ax3d.plot([t[i], t[i]], [h[i], h[i]], [0, h_i[i]], '-', linewidth=2, color='b', alpha=.5) # plotting a circle on the top of each stem self.ax3d.plot(t, h, h_i, 'o', markersize=8, markerfacecolor='none', color='b', label='ib') self.ax3d.set_xlabel('x') self.ax3d.set_ylabel('y') self.ax3d.set_zlabel('z') self.mplwidget.redraw()
def draw_impz(self): """ (Re-)calculate h[n] and draw the figure """ log = self.chkLog.isChecked() stim = str(self.cmbStimulus.currentText()) periodic_sig = stim in {"Cos", "Sine","Rect", "Saw"} self.lblLogBottom.setVisible(log) self.ledLogBottom.setVisible(log) self.lbldB.setVisible(log) self.lblFreq.setVisible(periodic_sig) self.ledFreq.setVisible(periodic_sig) self.lblFreqUnit.setVisible(periodic_sig) self.lblFreqUnit.setText(rt_label(fb.fil[0]['freq_specs_unit'])) self.load_dict() self.bb = np.asarray(fb.fil[0]['ba'][0]) self.aa = np.asarray(fb.fil[0]['ba'][1]) if min(len(self.aa), len(self.bb)) < 2: logger.error('No proper filter coefficients: len(a), len(b) < 2 !') return sos = np.asarray(fb.fil[0]['sos']) antiCausal = 'zpkA' in fb.fil[0] causal = not (antiCausal) self.f_S = fb.fil[0]['f_S'] N_entry = safe_eval(self.ledNPoints.text(), 0, return_type='int', sign='pos') N = self.calc_n_points(N_entry) if N_entry != 0: # automatic calculation self.ledNPoints.setText(str(N)) self.A = safe_eval(self.ledAmp.text(), self.A, return_type='float') self.ledAmp.setText(str(self.A)) t = np.linspace(0, N/self.f_S, N, endpoint=False) title_str = r'Impulse Response' # default H_str = r'$h[n]$' # default # calculate h[n] if stim == "Pulse": x = np.zeros(N) x[0] = self.A # create dirac impulse as input signal elif stim == "Step": x = self.A * np.ones(N) # create step function title_str = r'Step Response' H_str = r'$h_{\epsilon}[n]$' elif stim == "StepErr": x = self.A * np.ones(N) # create step function title_str = r'Settling Error' H_str = r'$h_{\epsilon, \infty} - h_{\epsilon}[n]$' elif stim in {"Cos"}: x = self.A * np.cos(2 * np.pi * t * float(self.ledFreq.text())) if stim == "Cos": title_str = r'Transient Response to Cosine Signal' H_str = r'$y_{\cos}[n]$' elif stim in {"Sine", "Rect"}: x = self.A * np.sin(2 * np.pi * t * float(self.ledFreq.text())) if stim == "Sine": title_str = r'Transient Response to Sine Signal' H_str = r'$y_{\sin}[n]$' else: x = self.A * np.sign(x) title_str = r'Transient Response to Rect. Signal' H_str = r'$y_{rect}[n]$' elif stim == "Saw": x = self.A * sig.sawtooth(t * (float(self.ledFreq.text())* 2*np.pi)) title_str = r'Transient Response to Sawtooth Signal' H_str = r'$y_{saw}[n]$' elif stim == "RandN": x = self.A * np.random.randn(N) title_str = r'Transient Response to Gaussian Noise' H_str = r'$y_{gauss}[n]$' elif stim == "RandU": x = self.A * (np.random.rand(N)-0.5) title_str = r'Transient Response to Uniform Noise' H_str = r'$y_{uni}[n]$' else: logger.error('Unknown stimulus "{0}"'.format(stim)) return if len(sos) > 0 and (causal): # has second order sections and is causal h = sig.sosfilt(sos, x) elif (antiCausal): h = sig.filtfilt(self.bb, self.aa, x, -1, None) else: # no second order sections or antiCausals for current filter h = sig.lfilter(self.bb, self.aa, x) dc = sig.freqz(self.bb, self.aa, [0]) if stim == "StepErr": h = h - abs(dc[1]) # subtract DC value from response h = np.real_if_close(h, tol = 1e3) # tol specified in multiples of machine eps self.cmplx = np.any(np.iscomplex(h)) if self.cmplx: h_i = h.imag h = h.real H_i_str = r'$\Im\{$' + H_str + '$\}$' H_str = r'$\Re\{$' + H_str + '$\}$' if log: self.bottom = safe_eval(self.ledLogBottom.text(), self.bottom, return_type='float') self.ledLogBottom.setText(str(self.bottom)) H_str = r'$|$ ' + H_str + '$|$ in dB' h = np.maximum(20 * np.log10(abs(h)), self.bottom) if self.cmplx: h_i = np.maximum(20 * np.log10(abs(h_i)), self.bottom) H_i_str = r'$\log$ ' + H_i_str + ' in dB' else: self.bottom = 0 self.init_axes() #================ Main Plotting Routine ========================= [ml, sl, bl] = self.ax_r.stem(t, h, bottom=self.bottom, markerfmt='o', label = '$h[n]$') stem_fmt = params['mpl_stimuli'] if self.chkPltStim.isChecked(): [ms, ss, bs] = self.ax_r.stem(t, x, bottom=self.bottom, label = 'Stim.', **stem_fmt) ms.set_mfc(stem_fmt['mfc']) ms.set_mec(stem_fmt['mec']) ms.set_ms(stem_fmt['ms']) ms.set_alpha(stem_fmt['alpha']) for stem in ss: stem.set_linewidth(stem_fmt['lw']) stem.set_color(stem_fmt['mec']) stem.set_alpha(stem_fmt['alpha']) bs.set_visible(False) # invisible bottomline expand_lim(self.ax_r, 0.02) self.ax_r.set_title(title_str) if self.cmplx: [ml_i, sl_i, bl_i] = self.ax_i.stem(t, h_i, bottom=self.bottom, markerfmt='d', label = '$h_i[n]$') self.ax_i.set_xlabel(fb.fil[0]['plt_tLabel']) # self.ax_r.get_xaxis().set_ticklabels([]) # removes both xticklabels # plt.setp(ax_r.get_xticklabels(), visible=False) # is shorter but imports matplotlib, set property directly instead: [label.set_visible(False) for label in self.ax_r.get_xticklabels()] self.ax_r.set_ylabel(H_str + r'$\rightarrow $') self.ax_i.set_ylabel(H_i_str + r'$\rightarrow $') else: self.ax_r.set_xlabel(fb.fil[0]['plt_tLabel']) self.ax_r.set_ylabel(H_str + r'$\rightarrow $') if self.ACTIVE_3D: # not implemented / tested yet # plotting the stems for i in range(len(t)): self.ax3d.plot([t[i], t[i]], [h[i], h[i]], [0, h_i[i]], '-', linewidth=2, alpha=.5) # plotting a circle on the top of each stem self.ax3d.plot(t, h, h_i, 'o', markersize=8, markerfacecolor='none', label='$h[n]$') self.ax3d.set_xlabel('x') self.ax3d.set_ylabel('y') self.ax3d.set_zlabel('z') self.redraw()
def _construct_UI(self): """ Construct the User Interface """ self.layVMain = QVBoxLayout() # Widget main layout f_units = ['f_S', 'f_Ny', 'Hz', 'kHz', 'MHz', 'GHz'] self.t_units = ['', '', 's', 'ms', r'$\mu$s', 'ns'] bfont = QFont() bfont.setBold(True) self.lblUnits = QLabel(self) self.lblUnits.setText("Freq. Unit:") self.lblUnits.setFont(bfont) self.fs_old = fb.fil[0]['f_S'] # store current sampling frequency self.ledF_S = QLineEdit() self.ledF_S.setText(str(fb.fil[0]["f_S"])) self.ledF_S.setObjectName("f_S") self.ledF_S.installEventFilter(self) # filter events self.lblF_S = QLabel(self) self.lblF_S.setText(rt_label("f_S")) self.cmbUnits = QComboBox(self) self.cmbUnits.setObjectName("cmbUnits") self.cmbUnits.addItems(f_units) self.cmbUnits.setToolTip( "Select whether frequencies are specified with respect to \n" "the sampling frequency f_S, to the Nyquist frequency \n" "f_Ny = f_S/2 or as absolute values.") self.cmbUnits.setCurrentIndex(0) # self.cmbUnits.setItemData(0, (0,QColor("#FF333D"),Qt.BackgroundColorRole))# # self.cmbUnits.setItemData(0, (QFont('Verdana', bold=True), Qt.FontRole) fRanges = [("0...½", "half"), ("0...1", "whole"), ("-½...½", "sym")] self.cmbFRange = QComboBox(self) self.cmbFRange.setObjectName("cmbFRange") for f in fRanges: self.cmbFRange.addItem(f[0], f[1]) self.cmbFRange.setToolTip("Select frequency range (whole or half).") self.cmbFRange.setCurrentIndex(0) # Combobox resizes with longest entry self.cmbUnits.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.cmbFRange.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.butSort = QToolButton(self) self.butSort.setText("Sort") self.butSort.setCheckable(True) self.butSort.setChecked(True) self.butSort.setToolTip( "Sort frequencies in ascending order when pushed.") self.butSort.setStyleSheet("QToolButton:checked {font-weight:bold}") self.layHUnits = QHBoxLayout() self.layHUnits.addWidget(self.cmbUnits) self.layHUnits.addWidget(self.cmbFRange) self.layHUnits.addWidget(self.butSort) # Create a gridLayout consisting of QLabel and QLineEdit fields # for setting f_S, the units and the actual frequency specs: self.layGSpecWdg = QGridLayout() # sublayout for spec fields self.layGSpecWdg.addWidget(self.lblF_S, 1, 0) self.layGSpecWdg.addWidget(self.ledF_S, 1, 1) self.layGSpecWdg.addWidget(self.lblUnits, 0, 0) self.layGSpecWdg.addLayout(self.layHUnits, 0, 1) frmMain = QFrame(self) frmMain.setLayout(self.layGSpecWdg) self.layVMain.addWidget(frmMain) self.layVMain.setContentsMargins(*params['wdg_margins']) self.setLayout(self.layVMain) #---------------------------------------------------------------------- # SIGNALS & SLOTs #---------------------------------------------------------------------- self.cmbUnits.currentIndexChanged.connect(self.update_UI) self.cmbFRange.currentIndexChanged.connect(self._freq_range) self.butSort.clicked.connect(self._store_sort_flag) #---------------------------------------------------------------------- self.update_UI() # first-time initialization
def __init__(self, parent): """ Pass instance `parent` of parent class (FilterCoeffs) """ super(FilterPZ_UI, self).__init__(parent) # self.parent = parent # instance of the parent (not the base) class self.eps = 1.e-4 # # tolerance value for e.g. setting P/Z to zero """ Intitialize the widget, consisting of: - top chkbox row - coefficient table - two bottom rows with action buttons """ self.bfont = QFont() self.bfont.setBold(True) self.bifont = QFont() self.bifont.setBold(True) self.bifont.setItalic(True) # q_icon_size = QSize(20, 20) # optional, size is derived from butEnable # --------------------------------------------- # UI Elements for controlling the display # --------------------------------------------- self.butEnable = QPushButton(self) self.butEnable.setIcon(QIcon(':/circle-x.svg')) q_icon_size = self.butEnable.iconSize() # <- set this for manual icon sizing self.butEnable.setIconSize(q_icon_size) self.butEnable.setCheckable(True) self.butEnable.setChecked(True) self.butEnable.setToolTip("<span>Show / hide poles and zeros in an editable table." " For high order systems, the table display might be slow.</span>") self.cmbPZFrmt = QComboBox(self) pz_formats = [('Cartesian', 'cartesian'), ('Polar (rad)', 'polar_rad'), ('Polar (pi)', 'polar_pi'), ('Polar (°)', 'polar_deg')] # display text, data # π: u'3C0, °: u'B0, ∠: u'2220 for pz in pz_formats: self.cmbPZFrmt.addItem(*pz) self.cmbPZFrmt.setSizeAdjustPolicy(QComboBox.AdjustToContents) # self.cmbPZFrmt.setEnabled(False) self.cmbPZFrmt.setToolTip("<span>Set display format for poles and zeros to" " either cartesian (x + jy) or polar (r * ∠ Ω)." " Type 'o' for '°', '<' for '∠' and 'pi' for 'π'.</span>") self.spnDigits = QSpinBox(self) self.spnDigits.setRange(0,16) self.spnDigits.setToolTip("Number of digits to display.") self.lblDigits = QLabel("Digits", self) self.lblDigits.setFont(self.bifont) self.cmbCausal = QComboBox(self) causal_types = ['Causal', 'Acausal', 'Anticausal'] for cs in causal_types: self.cmbCausal.addItem(cs) qset_cmb_box(self.cmbCausal, 'Causal') self.cmbCausal.setToolTip('<span>Set the system type. Not implemented yet.</span>') self.cmbCausal.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.cmbCausal.setEnabled(False) layHDisplay = QHBoxLayout() layHDisplay.setAlignment(Qt.AlignLeft) layHDisplay.addWidget(self.butEnable) layHDisplay.addWidget(self.cmbPZFrmt) layHDisplay.addWidget(self.spnDigits) layHDisplay.addWidget(self.lblDigits) layHDisplay.addWidget(self.cmbCausal) layHDisplay.addStretch() # --------------------------------------------- # UI Elements for setting the gain # --------------------------------------------- self.lblNorm = QLabel(rt_label("Normalize"), self) self.cmbNorm = QComboBox(self) self.cmbNorm.addItems(["None", "1", "Max"]) self.cmbNorm.setToolTip("<span>Set the gain <i>k</i> so that H(f)<sub>max</sub> is " "either 1 or the max. of the previous system.</span>") self.lblGain = QLabel(rt_label("k = "), self) self.ledGain = QLineEdit(self) self.ledGain.setToolTip("<span>Specify gain factor <i>k</i>" " (only possible for Normalize = 'None').</span>") self.ledGain.setText(str(1.)) self.ledGain.setObjectName("ledGain") layHGain = QHBoxLayout() layHGain.addWidget(self.lblNorm) layHGain.addWidget(self.cmbNorm) layHGain.addWidget(self.lblGain) layHGain.addWidget(self.ledGain) layHGain.addStretch() # --------------------------------------------- # UI Elements for loading / storing / manipulating cells and rows # --------------------------------------------- # self.cmbFilterType = QComboBox(self) # self.cmbFilterType.setObjectName("comboFilterType") # self.cmbFilterType.setToolTip("Select between IIR and FIR filte for manual entry.") # self.cmbFilterType.addItems(["FIR","IIR"]) # self.cmbFilterType.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.butAddCells = QPushButton(self) self.butAddCells.setIcon(QIcon(':/row_insert_above.svg')) self.butAddCells.setIconSize(q_icon_size) self.butAddCells.setToolTip("<SPAN>Select cells to insert a new cell above each selected cell. " "Use <SHIFT> or <CTRL> to select multiple cells. " "When nothing is selected, add a row at the end.</SPAN>") self.butDelCells = QPushButton(self) self.butDelCells.setIcon(QIcon(':/row_delete.svg')) self.butDelCells.setIconSize(q_icon_size) self.butDelCells.setToolTip("<SPAN>Delete selected cell(s) from the table. " "Use <SHIFT> or <CTRL> to select multiple cells. " "When nothing is selected, delete the last row.</SPAN>") self.butSave = QPushButton(self) self.butSave.setIcon(QIcon(':/upload.svg')) self.butSave.setIconSize(q_icon_size) self.butSave.setToolTip("<span>Save coefficients and update all plots. " "No modifications are saved before!</span>") self.butLoad = QPushButton(self) self.butLoad.setIcon(QIcon(':/download.svg')) self.butLoad.setIconSize(q_icon_size) self.butLoad.setToolTip("Reload coefficients.") self.butClear = QPushButton(self) self.butClear.setIcon(QIcon(':/trash.svg')) self.butClear.setIconSize(q_icon_size) self.butClear.setToolTip("Clear all entries.") self.butFromTable = QPushButton(self) self.butFromTable.setIcon(QIcon(':/to_clipboard.svg')) self.butFromTable.setIconSize(q_icon_size) self.butFromTable.setToolTip("<span>" "Copy table to clipboard / file, SELECTED items are copied as " "displayed. When nothing is selected, the whole table " "is copied with full precision in decimal format.</span>") self.butToTable = QPushButton(self) self.butToTable.setIcon(QIcon(':/from_clipboard.svg')) self.butToTable.setIconSize(q_icon_size) self.butToTable.setToolTip("<span>Copy clipboard / file to table.</span>") butSettingsClipboard = QPushButton(self) butSettingsClipboard.setIcon(QIcon(':/settings.svg')) butSettingsClipboard.setIconSize(q_icon_size) butSettingsClipboard.setToolTip("<span>Adjust settings for CSV format and whether " "to copy to/from clipboard or file.</span>") layHButtonsCoeffs1 = QHBoxLayout() # layHButtonsCoeffs1.addWidget(self.cmbFilterType) layHButtonsCoeffs1.addWidget(self.butAddCells) layHButtonsCoeffs1.addWidget(self.butDelCells) layHButtonsCoeffs1.addWidget(self.butClear) layHButtonsCoeffs1.addWidget(self.butSave) layHButtonsCoeffs1.addWidget(self.butLoad) layHButtonsCoeffs1.addWidget(self.butFromTable) layHButtonsCoeffs1.addWidget(self.butToTable) layHButtonsCoeffs1.addWidget(butSettingsClipboard) layHButtonsCoeffs1.addStretch() #------------------------------------------------------------------- # Eps / set zero settings # --------------------------------------------------------------------- self.butSetZero = QPushButton("= 0", self) self.butSetZero.setToolTip("<span>Set selected poles / zeros = 0 with a magnitude < ε. " "When nothing is selected, test the whole table.</span>") self.butSetZero.setIconSize(q_icon_size) lblEps = QLabel(self) lblEps.setText("<b><i>for ε</i> <</b>") self.ledEps = QLineEdit(self) self.ledEps.setToolTip("Specify tolerance value.") layHButtonsCoeffs2 = QHBoxLayout() layHButtonsCoeffs2.addWidget(self.butSetZero) layHButtonsCoeffs2.addWidget(lblEps) layHButtonsCoeffs2.addWidget(self.ledEps) layHButtonsCoeffs2.addStretch() # ######################## Main UI Layout ############################ # layout for frame (UI widget) layVMainF = QVBoxLayout() layVMainF.addLayout(layHDisplay) layVMainF.addLayout(layHGain) layVMainF.addLayout(layHButtonsCoeffs1) layVMainF.addLayout(layHButtonsCoeffs2) # This frame encompasses all UI elements frmMain = QFrame(self) frmMain.setLayout(layVMainF) layVMain = QVBoxLayout() layVMain.setAlignment(Qt.AlignTop) # this affects only the first widget (intended here) layVMain.addWidget(frmMain) layVMain.setContentsMargins(*params['wdg_margins']) self.setLayout(layVMain) #--- set initial values from dict / signal slot connections------------ self.spnDigits.setValue(params['FMT_pz']) self.ledEps.setText(str(self.eps)) butSettingsClipboard.clicked.connect(self._copy_options)
def draw_impz(self): """ (Re-)calculate h[n] and draw the figure """ log = self.chkLog.isChecked() stim = str(self.cmbStimulus.currentText()) periodic_sig = stim in {"Sine", "Rect", "Saw"} self.lblLogBottom.setVisible(log) self.ledLogBottom.setVisible(log) self.lbldB.setVisible(log) self.lblFreq.setVisible(periodic_sig) self.ledFreq.setVisible(periodic_sig) self.lblFreqUnit.setVisible(periodic_sig) self.lblFreqUnit.setText(rt_label(fb.fil[0]['freq_specs_unit'])) self.load_dict() self.bb = np.asarray(fb.fil[0]['ba'][0]) self.aa = np.asarray(fb.fil[0]['ba'][1]) if min(len(self.aa), len(self.bb)) < 2: logger.error('No proper filter coefficients: len(a), len(b) < 2 !') return sos = np.asarray(fb.fil[0]['sos']) self.f_S = fb.fil[0]['f_S'] N = self.calc_n_points(abs(int(self.ledNPoints.text()))) t = np.linspace(0, N / self.f_S, N, endpoint=False) # calculate h[n] if stim == "Pulse": x = np.zeros(N) x[0] = 1.0 # create dirac impulse as input signal title_str = r'Impulse Response' H_str = r'$h[n]$' elif stim == "Step": x = np.ones(N) # create step function title_str = r'Step Response' H_str = r'$h_{\epsilon}[n]$' elif stim == "StepErr": x = np.ones(N) # create step function title_str = r'Settling Error' H_str = r'$h_{\epsilon, \infty} - h_{\epsilon}[n]$' elif stim in {"Sine", "Rect"}: x = np.sin(2 * np.pi * t * float(self.ledFreq.text())) if stim == "Sine": title_str = r'Transient Response to Sine Signal' H_str = r'$y_{\sin}[n]$' else: x = np.sign(x) title_str = r'Transient Response to Rect. Signal' H_str = r'$y_{rect}[n]$' elif stim == "Saw": x = sig.sawtooth(t * (float(self.ledFreq.text()) * 2 * np.pi)) title_str = r'Transient Response to Sawtooth Signal' H_str = r'$y_{saw}[n]$' elif stim == "RandN": x = np.random.randn(N) title_str = r'Transient Response to Gaussian Noise' H_str = r'$y_{gauss}[n]$' elif stim == "RandU": x = np.random.rand(N) - 0.5 title_str = r'Transient Response to Uniform Noise' H_str = r'$y_{uni}[n]$' else: logger.error('Unknown stimulus "{0}"'.format(stim)) return if len(sos) > 0: # has second order sections h = sig.sosfilt(sos, x) dc = sig.freqz(self.bb, self.aa, [0]) else: # no second order sections for current filter h = sig.lfilter(self.bb, self.aa, x) dc = sig.freqz(self.bb, self.aa, [0]) if stim == "StepErr": h = h - abs(dc[1]) # subtract DC value from response self.cmplx = np.any(np.iscomplex(h)) if self.cmplx: h_i = h.imag h = h.real H_i_str = r'$\Im\{$' + H_str + '$\}$' H_str = r'$\Re\{$' + H_str + '$\}$' if log: bottom = float(self.ledLogBottom.text()) H_str = r'$|$ ' + H_str + '$|$ in dB' h = np.maximum(20 * np.log10(abs(h)), bottom) if self.cmplx: h_i = np.maximum(20 * np.log10(abs(h_i)), bottom) H_i_str = r'$\log$ ' + H_i_str + ' in dB' else: bottom = 0 self._init_axes() #================ Main Plotting Routine ========================= [ml, sl, bl] = self.ax_r.stem(t, h, bottom=bottom, markerfmt='o', label='$h[n]$') stem_fmt = params['mpl_stimuli'] if self.chkPltStim.isChecked(): [ms, ss, bs] = self.ax_r.stem(t, x, bottom=bottom, label='Stim.', **stem_fmt) ms.set_mfc(stem_fmt['mfc']) ms.set_mec(stem_fmt['mec']) ms.set_ms(stem_fmt['ms']) ms.set_alpha(stem_fmt['alpha']) for stem in ss: stem.set_linewidth(stem_fmt['lw']) stem.set_color(stem_fmt['mec']) stem.set_alpha(stem_fmt['alpha']) bs.set_visible(False) # invisible bottomline expand_lim(self.ax_r, 0.02) self.ax_r.set_title(title_str) if self.cmplx: [ml_i, sl_i, bl_i] = self.ax_i.stem(t, h_i, bottom=bottom, markerfmt='d', label='$h_i[n]$') self.ax_i.set_xlabel(fb.fil[0]['plt_tLabel']) # self.ax_r.get_xaxis().set_ticklabels([]) # removes both xticklabels # plt.setp(ax_r.get_xticklabels(), visible=False) # is shorter but imports matplotlib, set property directly instead: [label.set_visible(False) for label in self.ax_r.get_xticklabels()] self.ax_r.set_ylabel(H_str + r'$\rightarrow $') self.ax_i.set_ylabel(H_i_str + r'$\rightarrow $') else: self.ax_r.set_xlabel(fb.fil[0]['plt_tLabel']) self.ax_r.set_ylabel(H_str + r'$\rightarrow $') if self.ACTIVE_3D: # not implemented / tested yet # plotting the stems for i in range(len(t)): self.ax3d.plot([t[i], t[i]], [h[i], h[i]], [0, h_i[i]], '-', linewidth=2, alpha=.5) # plotting a circle on the top of each stem self.ax3d.plot(t, h, h_i, 'o', markersize=8, markerfacecolor='none', label='$h[n]$') self.ax3d.set_xlabel('x') self.ax3d.set_ylabel('y') self.ax3d.set_zlabel('z') self.redraw()
def _construct_UI(self): """ Intitialize the widget, consisting of: """ bfont = QFont() bfont.setBold(True) # Find which button holds the longest text: MaxTextlen = 0 longestText = "" ButLength = 0 butTexts = ["Add", "Delete", "Save", "Load", "Clear", "Set 0"] for item in butTexts: if len(item) > MaxTextlen: MaxTextlen = len(item) longestText = item + "mm" butAddRow = QPushButton(butTexts[0], self) ButLength = butAddRow.fontMetrics().boundingRect(longestText).width() self.chkPZList = QCheckBox("Show table", self) self.chkPZList.setChecked(True) self.chkPZList.setToolTip( "<span>Show filter Poles / Zeros as an editable table. " "For high order systems, this might be slow. </span>") lblRound = QLabel("Digits = ", self) self.spnRound = QSpinBox(self) self.spnRound.setRange(0, 9) self.spnRound.setValue(params['FMT_pz']) self.spnRound.setToolTip("Display d digits.") self.lblNorm = QLabel("Normalize", self) self.cmbNorm = QComboBox(self) self.cmbNorm.addItems(["None", "1", "Max"]) self.cmbNorm.setToolTip( "<span>Set the gain <i>k</i> so that H(f)<sub>max</sub> is " "either 1 or the max. of the previous system.</span>") self.lblGain = QLabel(rt_label("k = "), self) self.ledGain = QLineEdit(self) self.ledGain.setToolTip("Specify gain factor <i>k</i>.") self.ledGain.setText(str(1.)) self.ledGain.setObjectName("ledGain") # self.ledGain.setFixedSize(W8, H) self.ledGain.installEventFilter(self) self.tblPZ = QTableWidget(self) # self.tblPZ.setEditTriggers(QTableWidget.AllEditTriggers) # make everything editable self.tblPZ.setAlternatingRowColors(True) # alternating row colors) self.tblPZ.setObjectName("tblPZ") self.tblPZ.horizontalHeader().setHighlightSections( True) # highlight when selected self.tblPZ.horizontalHeader().setFont(bfont) self.tblPZ.verticalHeader().setHighlightSections(True) self.tblPZ.verticalHeader().setFont(bfont) self.tblPZ.setColumnCount(2) self.tblPZ.setItemDelegate(ItemDelegate(self)) butAddRow.setToolTip( "<SPAN>Select <i>N</i> existing rows " "to insert <i>N</i> new rows above last selected cell. " "When nothing is selected, add a row at the end.</SPAN>") butAddRow.setMaximumWidth(ButLength) butDelCell = QPushButton(butTexts[1], self) butDelCell.setToolTip( "<span>Delete selected cell(s) from the table. " "Use <SHIFT> or <CTRL> to select multiple cells. " "If nothing is selected, delete the last row.</span>") butDelCell.setMaximumWidth(ButLength) butClear = QPushButton(butTexts[4], self) butClear.setToolTip("Clear all entries.") butClear.setMaximumWidth(ButLength) butSave = QPushButton(butTexts[2], self) butSave.setToolTip("<span>Save P/Z and update all plots. " "No modifications are saved before!</span>") butSave.setMaximumWidth(ButLength) butLoad = QPushButton(butTexts[3], self) butLoad.setToolTip("Reload P / Z.") butLoad.setMaximumWidth(ButLength) butSetZero = QPushButton(butTexts[5], self) butSetZero.setToolTip( "<SPAN>Set selected cells = 0 when magnitude " "< ε. When nothing is selected, apply to " "all cells.</SPAN>") butSetZero.setMaximumWidth(ButLength) self.lblEps = QLabel("for " + rt_label("ε <"), self) self.ledSetEps = QLineEdit(self) self.ledSetEps.setToolTip("<SPAN>Specify tolerance.</SPAN>") self.ledSetEps.setText(str(1e-6)) # self.ledSetEps.setFixedSize(W8, H) # ============== UI Layout ===================================== layHChkBoxes = QHBoxLayout() layHChkBoxes.addWidget(self.chkPZList) layHChkBoxes.addStretch(1) layHChkBoxes.addWidget(lblRound) layHChkBoxes.addWidget(self.spnRound) layHGain = QHBoxLayout() layHGain.addWidget(self.lblGain) layHGain.addWidget(self.ledGain) # layHChkBoxes.addStretch(1) layHGain.addWidget(self.lblNorm) layHGain.addWidget(self.cmbNorm) layHGain.addStretch() layHButtonsPZs1 = QHBoxLayout() layHButtonsPZs1.addWidget(butAddRow) layHButtonsPZs1.addWidget(butDelCell) layHButtonsPZs1.addWidget(butSave) layHButtonsPZs1.addWidget(butLoad) layHButtonsPZs1.addStretch() layHButtonsPZs2 = QHBoxLayout() layHButtonsPZs2.addWidget(butClear) layHButtonsPZs2.addWidget(butSetZero) layHButtonsPZs2.addWidget(self.lblEps) layHButtonsPZs2.addWidget(self.ledSetEps) layHButtonsPZs2.addStretch() layVBtns = QVBoxLayout() layVBtns.addLayout(layHChkBoxes) layVBtns.addLayout(layHGain) layVBtns.addLayout(layHButtonsPZs1) layVBtns.addLayout(layHButtonsPZs2) # This frame encompasses all the buttons frmMain = QFrame(self) frmMain.setLayout(layVBtns) layVMain = QVBoxLayout() layVMain.setAlignment( Qt.AlignTop) # this affects only the first widget (intended here) layVMain.addWidget(frmMain) layVMain.addWidget(self.tblPZ) layVMain.setContentsMargins(*params['wdg_margins']) self.setLayout(layVMain) self.load_dict() # initialize table from filterbroker self._refresh_table() #---------------------------------------------------------------------- # SIGNALS & SLOTs #---------------------------------------------------------------------- self.spnRound.editingFinished.connect(self._refresh_table) butLoad.clicked.connect(self.load_dict) self.chkPZList.clicked.connect(self.load_dict) butSave.clicked.connect(self._save_entries) butDelCell.clicked.connect(self._delete_cells) butAddRow.clicked.connect(self._add_rows) butClear.clicked.connect(self._clear_table) butSetZero.clicked.connect(self._zero_PZ) # signal itemChanged is also triggered programmatically, # itemSelectionChanged is only triggered when entering cell # self.tblPZ.itemSelectionChanged.connect(self._copy_item) self.tblPZ.cellChanged.connect(self._copy_item) self.cmbNorm.activated.connect(self._copy_item)
def _construct_UI(self): """ Construct the User Interface """ self.layVMain = QVBoxLayout() # Widget main layout f_units = ['f_S', 'f_Ny', 'Hz', 'kHz', 'MHz', 'GHz'] self.t_units = ['', '', 's', 'ms', r'$\mu$s', 'ns'] bfont = QFont() bfont.setBold(True) self.lblUnits=QLabel(self) self.lblUnits.setText("Freq. Unit:") self.lblUnits.setFont(bfont) self.fs_old = fb.fil[0]['f_S'] # store current sampling frequency self.ledF_S = QLineEdit() self.ledF_S.setText(str(fb.fil[0]["f_S"])) self.ledF_S.setObjectName("f_S") self.ledF_S.installEventFilter(self) # filter events self.lblF_S = QLabel(self) self.lblF_S.setText(rt_label("f_S")) self.cmbUnits = QComboBox(self) self.cmbUnits.setObjectName("cmbUnits") self.cmbUnits.addItems(f_units) self.cmbUnits.setToolTip( "Select whether frequencies are specified with respect to \n" "the sampling frequency f_S, to the Nyquist frequency \n" "f_Ny = f_S/2 or as absolute values.") self.cmbUnits.setCurrentIndex(0) # self.cmbUnits.setItemData(0, (0,QColor("#FF333D"),Qt.BackgroundColorRole))# # self.cmbUnits.setItemData(0, (QFont('Verdana', bold=True), Qt.FontRole) fRanges = [("0...½", "half"), ("0...1","whole"), ("-½...½", "sym")] self.cmbFRange = QComboBox(self) self.cmbFRange.setObjectName("cmbFRange") for f in fRanges: self.cmbFRange.addItem(f[0],f[1]) self.cmbFRange.setToolTip("Select frequency range (whole or half).") self.cmbFRange.setCurrentIndex(0) # Combobox resizes with longest entry self.cmbUnits.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.cmbFRange.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.butSort = QToolButton(self) self.butSort.setText("Sort") self.butSort.setCheckable(True) self.butSort.setChecked(True) self.butSort.setToolTip("Sort frequencies in ascending order when pushed.") self.butSort.setStyleSheet("QToolButton:checked {font-weight:bold}") self.layHUnits = QHBoxLayout() self.layHUnits.addWidget(self.cmbUnits) self.layHUnits.addWidget(self.cmbFRange) self.layHUnits.addWidget(self.butSort) # Create a gridLayout consisting of QLabel and QLineEdit fields # for setting f_S, the units and the actual frequency specs: self.layGSpecWdg = QGridLayout() # sublayout for spec fields self.layGSpecWdg.addWidget(self.lblF_S,1,0) self.layGSpecWdg.addWidget(self.ledF_S,1,1) self.layGSpecWdg.addWidget(self.lblUnits,0,0) self.layGSpecWdg.addLayout(self.layHUnits,0,1) frmMain = QFrame(self) frmMain.setLayout(self.layGSpecWdg) self.layVMain.addWidget(frmMain) self.layVMain.setContentsMargins(*params['wdg_margins']) self.setLayout(self.layVMain) #---------------------------------------------------------------------- # SIGNALS & SLOTs #---------------------------------------------------------------------- self.cmbUnits.currentIndexChanged.connect(self.update_UI) self.cmbFRange.currentIndexChanged.connect(self._freq_range) self.butSort.clicked.connect(self._store_sort_flag) #---------------------------------------------------------------------- self.update_UI() # first-time initialization