Source code for pyfda.input_widgets.input_tab_widgets
# -*- coding: utf-8 -*-
#
# This file is part of the pyFDA project hosted at https://github.com/chipmuenk/pyfda
#
# Copyright © pyFDA Project Contributors
# Licensed under the terms of the MIT License
# (see file LICENSE in root directory for details)
"""
Tabbed container for all input widgets
"""
import sys
import importlib
import logging
logger = logging.getLogger(__name__)
from pyfda.libs.compat import QTabWidget, QWidget, QVBoxLayout, QScrollArea, pyqtSignal
SCROLL = True
from pyfda.pyfda_rc import params
import pyfda.filterbroker as fb
from pyfda.libs.pyfda_lib import pprint_log
[docs]class InputTabWidgets(QWidget):
"""
Create a tabbed widget for all input subwidgets in the list ``fb.input_widgets_list``.
This list is compiled at startup in :class:`pyfda.tree_builder.Tree_Builder`.
"""
# signals as class variables (shared between instances if more than one exists)
# incoming, connected here to individual senders, passed on to process sigmals
sig_rx = pyqtSignal(object)
# outgoing, connected in receiver (pyfdax -> plot_tab_widgets)
sig_tx = pyqtSignal(object)
def __init__(self, parent):
super(InputTabWidgets, self).__init__(parent)
self._construct_UI()
def _construct_UI(self):
"""
Initialize UI with tabbed subwidgets: Instantiate dynamically each widget
from the dict `fb.input_classes` and try to
- set the TabToolTip from the instance attribute `tool_tip`
- set the tab label from the instance attribute `tab_label`
for each widget.
- connect the available signals of all subwidgets (not all widgets have
both `sig_rx` and `sig_tx` signals).
- `self.sig_rx` is distributed to all `inst.sig_rx` signals
- all `inst.sig_tx` signals are collected in `self.sig_tx`
- `self.sig_tx.connect(self.sig_rx)` distributes incoming signals (via
pyfdax or coming from the input widgets) among all input widgets.
In order to prevent infinite loops, every widget needs to block in-
coming signals with its own name!
"""
tabWidget = QTabWidget(self)
n_wdg = 0 # number and ...
inst_wdg_str = "" # ... full names of successfully instantiated widgets
for input_class in fb.input_classes:
try:
mod_fq_name = fb.input_classes[input_class]['mod'] # fully qualified module name
mod = importlib.import_module(mod_fq_name)
wdg_class = getattr(mod, input_class)
# and instantiate it
inst = wdg_class(self)
except ImportError as e:
logger.warning('Class "{0}" could not be imported from {1}:\n{2}.'\
.format(input_class, mod_fq_name, e))
continue # unsuccessful, try next widget
if hasattr(inst, "state") and inst.state == "deactivated":
continue # with next widget
if hasattr(inst, 'tab_label'):
tabWidget.addTab(inst, inst.tab_label)
else:
tabWidget.addTab(inst, "not set")
if hasattr(inst, 'tool_tip'):
tabWidget.setTabToolTip(n_wdg, inst.tool_tip)
if hasattr(inst, 'sig_tx'):
inst.sig_tx.connect(self.sig_tx)
if hasattr(inst, 'sig_rx'):
self.sig_rx.connect(inst.sig_rx)
n_wdg += 1 # successfully instantiated one more widget
inst_wdg_str += '\t' + mod_fq_name + "." + input_class + '\n'
if len(inst_wdg_str) == 0:
logger.critical("No input widgets found!")
sys.exit()
else:
logger.debug("Imported {0:d} input classes:\n{1}"
.format(n_wdg, inst_wdg_str))
#
# TODO: document signal options
#----------------------------------------------------------------------
# GLOBAL SIGNALS & SLOTs
#----------------------------------------------------------------------
#self.sig_rx.connect(inst.sig_rx) # happens in _construct_UI()
#----------------------------------------------------------------------
# LOCAL SIGNALS & SLOTs
#----------------------------------------------------------------------
self.sig_tx.connect(self.sig_rx) # loop back to local inputs
# self.sig_rx.connect(self.log_rx) # enable for debugging
# When user has selected a different tab, trigger a redraw of current tab
tabWidget.currentChanged.connect(self.current_tab_changed)
# The following does not work: maybe current scope must be left?
# tabWidget.currentChanged.connect(tabWidget.currentWidget().redraw)
layVMain = QVBoxLayout()
#setContentsMargins -> number of pixels between frame window border
layVMain.setContentsMargins(*params['wdg_margins'])
#--------------------------------------
if SCROLL:
scroll = QScrollArea(self)
scroll.setWidget(tabWidget)
scroll.setWidgetResizable(True) # Size of monitored widget is allowed to grow:
layVMain.addWidget(scroll)
else:
layVMain.addWidget(tabWidget) # add the tabWidget directly
self.setLayout(layVMain) # set the main layout of the window
#------------------------------------------------------------------------------
[docs] def log_rx(self, dict_sig=None):
"""
Enable `self.sig_rx.connect(self.log_rx)` above for debugging.
"""
if type(dict_sig) == dict:
logger.warning("SIG_RX\n{0}"\
.format(pprint_log(dict_sig)))
else:
logger.warning("empty dict")
#------------------------------------------------------------------------------
def current_tab_changed(self):
self.sig_tx.emit({'sender':__name__, 'ui_changed':'tab'})
#------------------------------------------------------------------------
def main():
from pyfda import pyfda_rc as rc
from pyfda.libs.compat import QApplication
app = QApplication(sys.argv)
app.setStyleSheet(rc.qss_rc)
mainw = InputTabWidgets(None)
app.setActiveWindow(mainw)
mainw.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()