def __init__(self, DEBUG = False): super(InputFilter, self).__init__() self.DEBUG = DEBUG # initialize the FilterTreeBuilder class with the filter directory and # the filter file self.ftb = FilterTreeBuilder('filter_design', 'init.txt', commentChar = '#', DEBUG = DEBUG) # self.filter_initialized = False self.dmLast = '' # design method from last call self.initUI() self.setResponseType() # first time initialization
class InputFilter(QtGui.QWidget): """ Construct combo boxes for selecting the filter, consisting of: - Response Type (LP, HP, Hilbert, ...) - Filter Type (IIR, FIR, CIC ...) - DesignMethod (Butterworth, ...) """ sigSpecsChanged = pyqtSignal() def __init__(self, DEBUG = False): super(InputFilter, self).__init__() self.DEBUG = DEBUG # initialize the FilterTreeBuilder class with the filter directory and # the filter file self.ftb = FilterTreeBuilder('filter_design', 'init.txt', commentChar = '#', DEBUG = DEBUG) # self.filter_initialized = False self.dmLast = '' # design method from last call self.initUI() self.setResponseType() # first time initialization def initUI(self): """ Initialize UI with comboboxes for selecting filter """ #----------------------------------------------------------------------------- # see filterbroker.py for structure and content of "filterTree" dict #----------------------------------------------------------------------------- #---------------------------------------------------------------------- # Create combo boxes # - cmbResponseType for selecting response type rt (LP, HP, ...) # - cmbFilterType for selection of filter type (IIR, FIR, ...) # - cmbDesignMethod for selection of design method (Chebychev, ...) # and populate them from the "filterTree" dict either directly or by # calling setResponseType() : self.cmbResponseType=QtGui.QComboBox(self) self.cmbResponseType.setToolTip("Select filter response type.") self.cmbFilterType=QtGui.QComboBox(self) self.cmbFilterType.setToolTip("Select the kind of filter (recursive, transversal, ...).") self.cmbDesignMethod=QtGui.QComboBox(self) self.cmbDesignMethod.setToolTip("Select the actual filter design method.") # Adapt combobox size dynamically to largest element self.cmbResponseType.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.cmbFilterType.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.cmbDesignMethod.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) # Translate short response type ("LP") to displayed names ("Lowpass") # (correspondence is defined in filterbroker.py) and populate combo box: for rt in fb.filTree: self.cmbResponseType.addItem(fb.gD['rtNames'][rt], rt) idx = self.cmbResponseType.findData('LP') # find index for 'LP' if idx == -1: # Key 'LP' does not exist, use first entry instead idx = 0 self.cmbResponseType.setCurrentIndex(idx) # set initial index """ LAYOUT """ # see Summerfield p. 278 self.layHDynWdg = QtGui.QHBoxLayout() # for additional dynamic subwidgets self.frmDynWdg = QtGui.QFrame() # collect subwidgets in frame (no border) self.frmDynWdg.setObjectName("wdg_frmDynWdg") self.frmDynWdg.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Minimum) #Debugging: enable next line to show border of frmDnyWdg #self.frmDynWdg.setFrameStyle(QtGui.QFrame.StyledPanel|QtGui.QFrame.Raised) self.frmDynWdg.setLayout(self.layHDynWdg) layHStdWdg = QtGui.QHBoxLayout() # container for standard subwidgets spacer = QtGui.QSpacerItem(1, 0, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) layHStdWdg.addWidget(self.cmbResponseType)# QtCore.Qt.AlignLeft) layHStdWdg.addItem(spacer) layHStdWdg.addWidget(self.cmbFilterType) layHStdWdg.addItem(spacer) layHStdWdg.addWidget(self.cmbDesignMethod) # stack standard + dynamic subwidgets vertically: layVAllWdg = QtGui.QVBoxLayout() layVAllWdg.addLayout(layHStdWdg) layVAllWdg.addWidget(self.frmDynWdg) # prevent disappearing of new subwidget # spacer = QtGui.QSpacerItem(0, 1, QtGui.QSizePolicy.MinimumExpanding, # QtGui.QSizePolicy.MinimumExpanding) # layVAllWdg.addItem(spacer) self.frmMain = QtGui.QFrame() self.frmMain.setFrameStyle(QtGui.QFrame.StyledPanel|QtGui.QFrame.Sunken) self.frmMain.setLayout(layVAllWdg) layHMain = QtGui.QHBoxLayout() layHMain.addWidget(self.frmMain) layHMain.setContentsMargins(0,0,0,0) self.setLayout(layHMain) # layHMain.setSizeConstraint(QtGui.QLayout.SetFixedSize) #------------------------------------------------------------ # SIGNALS & SLOTS #------------------------------------------------------------ # Connect comboBoxes and setters: self.cmbResponseType.activated.connect(self.setResponseType) # 'LP' self.cmbResponseType.activated.connect(self.sigSpecsChanged.emit) self.cmbFilterType.activated.connect(self.setFilterType) #'IIR' self.cmbFilterType.activated.connect(self.sigSpecsChanged.emit) self.cmbDesignMethod.activated.connect(self.setDesignMethod) #'cheby1' self.cmbDesignMethod.activated.connect(self.sigSpecsChanged.emit) #------------------------------------------------------------ def loadEntries(self): """ Reload comboboxes from filter dictionary to update changed settings e.g. by loading filter design """ idx_rt = self.cmbResponseType.findData(fb.fil[0]['rt']) # find index for 'LP' self.cmbResponseType.setCurrentIndex(idx_rt) self.setResponseType() def setResponseType(self): """ Triggered when cmbResponseType (LP, HP, ...) is changed: Copy selection to self.rt and fb.fil[0] and reconstruct filter type combo If previous filter type (FIR, IIR, ...) exists for new rt, set the filter type combo box to the old setting """ # cmbBox.currentText() shows full text ('Lowpass'), # itemData only abbreviation ('LP') self.rtIdx = self.cmbResponseType.currentIndex() self.rt = str(self.cmbResponseType.itemData(self.rtIdx)) fb.fil[0]['rt'] = self.rt # copy selected rt setting to filter dict # Get list of available filter types for new rt ftList = list(fb.filTree[self.rt].keys()) # explicit list() needed for Py3 # Rebuild filter type combobox entries for new rt setting # The combobox is populated with the "long name", the internal name # is stored in comboBox.itemData self.cmbFilterType.clear() self.cmbFilterType.addItems(ftList) # Is last filter type (e.g. IIR) in list for new rt? # And has the widget been initialized? if fb.fil[0]['ft'] in ftList and self.filter_initialized: ft_idx = self.cmbFilterType.findText(fb.fil[0]['ft']) self.cmbFilterType.setCurrentIndex(ft_idx) # yes, set same ft as before else: self.cmbFilterType.setCurrentIndex(0) # no, set index 0 self.setFilterType() def setFilterType(self): """" Triggered when cmbFilterType (IIR, FIR, ...) is changed: Copy selected setting to self.ft and (re)construct design method combo, adding displayed text (e.g. "Chebychev 1") and hidden data (e.g. "cheby1") """ # cmbBox.currentText() has full text ('IIR'), # itemData only abbreviation ('IIR') which is the same in this case self.ft = str(self.cmbFilterType.currentText()) fb.fil[0]['ft'] = self.ft # Rebuild design method combobox entries for new ft setting: # The combobox is populated with the "long name", the internal name # is stored in comboBox.itemData self.cmbDesignMethod.clear() for dm in fb.filTree[self.rt][self.ft]: self.cmbDesignMethod.addItem(fb.gD['dmNames'][dm], dm) # get list of available design methods for new ft dmList = fb.filTree[self.rt][self.ft].keys() if self.DEBUG: print("dmlist", dmList) print(fb.fil[0]['dm']) # Is previous design method (e.g. ellip) in list for new ft? # And has the widget been initialized? if fb.fil[0]['dm'] in dmList and self.filter_initialized: # yes, set same dm as before, don't call setDesignMethod dm_idx = self.cmbDesignMethod.findText(fb.gD['dmNames'][fb.fil[0]['dm']]) if self.DEBUG: print("dm_idx", dm_idx) self.cmbDesignMethod.setCurrentIndex(dm_idx) else: self.cmbDesignMethod.setCurrentIndex(0) # no, set index 0 self.setDesignMethod() def setDesignMethod(self): """ Triggered when cmbDesignMethod (cheby1, ...) is changed: Instantiate filter object """ dmIdx = self.cmbDesignMethod.currentIndex() dm = str(self.cmbDesignMethod.itemData(dmIdx)) fb.fil[0]['dm'] = dm try: # has a filter object been instantiated yet? if fb.fil[0]['dm'] not in fb.filObj.name: fb.filObj = self.ftb.objectWizzard(fb.fil[0]['dm']) except AttributeError as e: # No, create a filter instance print (e) fb.filObj = self.ftb.objectWizzard(fb.fil[0]['dm']) # Check whether new design method also provides the old filter order # method. If yes, don't change it, else set first available # filter order method if fb.fil[0]['fo'] not in fb.filTree[self.rt][self.ft][dm].keys(): fb.fil[0].update({'fo':{}}) fb.fil[0]['fo'] \ = fb.filTree[self.rt][self.ft][dm].keys()[0] if self.DEBUG: print("=== InputFilter.setDesignMethod ===") print("selFilter:", fb.fil[0]) print("filterTree[dm] = ", fb.filTree[self.rt][self.ft][dm]) print("filterTree[dm].keys() = ", fb.filTree[self.rt][self.ft]\ [dm].keys()) self.filter_initialized = True self._updateDynWidgets() # check for new subwidgets and update if needed # self.sigSpecsChanged.emit() # -> input_all def _updateDynWidgets(self): """ Delete dynamically (i.e. within filter design routine) created subwidgets and create new ones, depending on requirements of filter design algorithm This does NOT work when the subwidgets to be deleted and created are identical, as the deletion is only performed when the current scope has been left (?)! Hence, it is necessary to skip this method when the new design method is the same as the old one. """ if fb.fil[0]['dm'] != self.dmLast: # Find "old" dyn. subwidgets and delete them: widgetList = self.frmDynWdg.findChildren( (QtGui.QComboBox,QtGui.QLineEdit, QtGui.QLabel, QtGui.QWidget)) # widgetListNames = [w.objectName() for w in widgetList] for w in widgetList: self.layHDynWdg.removeWidget(w) # remove widget from layout w.deleteLater() # tell Qt to delete object when the # method has completed del w # not really needed? # Try to create "new" dyn. subwidgets: if hasattr(fb.filObj, 'wdg'): try: if 'sf' in fb.filObj.wdg: a = getattr(fb.filObj, fb.filObj.wdg['sf']) self.layHDynWdg.addWidget(a, stretch = 1) self.layHDynWdg.setContentsMargins(0,0,0,0) self.frmDynWdg.setVisible(a != None) except AttributeError as e: print("sf.updateWidgets:",e) self.frmDynWdg.setVisible(False) else: self.frmDynWdg.setVisible(False) self.dmLast = fb.fil[0]['dm']