Source code for pyfda.pyfdax

# -*- 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)

"""
Mainwindow for the pyFDA app
"""
import sys

import logging
import logging.config
logger = logging.getLogger(__name__)

from pyfda.libs.tree_builder import Tree_Builder
# initialize the FilterTreeBuilder class and read config file 
tree_builder = Tree_Builder()
tree_builder.parse_conf_file()

import pyfda.pyfda_rc as rc
import pyfda.libs.pyfda_dirs as dirs # initial import constructs file paths

import matplotlib
# specify matplotlib backend for systems that have both PyQt4 and PyQt5 installed
# to avoid
# "RuntimeError: the PyQt4.QtCore and PyQt5.QtCore modules both wrap the QObject class"
matplotlib.use("Qt5Agg")
# turn off matplotlib debug messages by elevating the level to "Warning"
mpl_logger = logging.getLogger('matplotlib')
mpl_logger.setLevel(logging.WARNING)

from pyfda.libs.compat import Qt, QtCore, QApplication, QIcon

# from pyfda.libs.pyfda_lib import ANSIcolors as ACol
import numpy as np
from pyfda.pyfda_class import pyFDA

[docs] def main(): """ entry point for the pyfda application see http://pyqt.sourceforge.net/Docs/PyQt4/qapplication.html : "For any GUI application using Qt, there is precisely *one* QApplication object, no matter whether the application has 0, 1, 2 or more windows at any given time. ... Since the QApplication object does so much initialization, it must be created *before* any other objects related to the user interface are created." Environment variables controlling Qt behaviour need to be set even before initializing the QApplication object Scaling ------- - DPI: The resolution number of dots per inch in a digital print - PPI: Pixel density of an electronic image device (e.g. computer monitor) - Point: 1/72 Inch = 0.3582 mm, physical measure in typography - em: Equal to font height. For e.g. a 12 pt font, 1 em = 12 pt - Physical DPI: The PPI that a physical screen actually provides. - Logical DPI: The PPI that software claims a screen provides. This can be thought of as the PPI provided by a virtual screen created by the operating system. Font sizes are specified in logical DPI - Screen scaling: High-Resolution screens have a very high physical DPI, resulting in very small characters. Screen scaling by e.g. 125 ... 200% increases the logical DPI and hence the character size by the same amount. MacOS ~~~~~ Early displays had 72 PPI, equaling 72 Pt/Inch, i.e. 1 Pixel = 1 Point. Print-out size was equal to screen size. Windows ~~~~~~~ A 72-point font is defined to be one logical inch = 96 pixels tall. 12 pt = 12/72 = 1/6 logical inch = 96/6 pixels = 16 pixels @ 96 dpi # Enable automatic scaling based on the monitor's pixel density. This doesn't change the # size of point based fonts! # os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1" # os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" # replaced by QT_ENABLE_HIGHDPI_SCALING # Define global scale factor for the whole application, including point-sized fonts: # os.environ["QT_SCALE_FACTOR"] = "1" """ # Enable High DPI display with PyQt5 if hasattr(QtCore.Qt, 'AA_EnableHighDpiScaling'): Qt.AA_EnableHighDpiScaling = True else: logger.warning("No Qt attribute 'AA_EnableHighDpiScaling'.") # Instantiate QApplication object, passing command line arguments if len(rc.qss_rc) > 20: app = QApplication(sys.argv) app.setStyleSheet(rc.qss_rc) # this is a proper style sheet style = "'pyfda' style sheet" else: qstyle = QApplication.setStyle(rc.qss_rc) # this is just a name for a system stylesheet app = QApplication(sys.argv) if qstyle: style = f"system style sheet '{rc.qss_rc}'" else: style = f"default style sheet ('{rc.qss_rc}' not found)" if dirs.OS.lower() == "darwin": # Mac OS ref_dpi = 72 else: ref_dpi = 96 if hasattr(Qt, 'AA_UseHighDpiPixmaps'): app.setAttribute(Qt.AA_UseHighDpiPixmaps, True) else: logger.warning("Qt attribute 'AA_UseHighDpiPixmaps' not available.") ldpi = app.primaryScreen().logicalDotsPerInch() # ldpix = app.primaryScreen().logicalDotsPerInchX() # ldpiy = app.primaryScreen().logicalDotsPerInchY() pdpi = app.primaryScreen().physicalDotsPerInch() pdpix = app.primaryScreen().physicalDotsPerInchX() pdpiy = app.primaryScreen().physicalDotsPerInchY() # scr_size = app.primaryScreen().size() # pixel resolution, type QSize() screen_resolution = app.desktop().screenGeometry() avail_geometry = app.desktop().availableGeometry() pixel_ratio = app.desktop().devicePixelRatio() height, width = screen_resolution.height(), screen_resolution.width() scaling = ldpi / ref_dpi # font = QFont() # font.setPointSize(yourPointSize) # fm = QFontMetrics(font) # try to find a good value for matplotlib font size depending on screen resolution fontsize = round(9.5 * np.sqrt(pdpiy / ref_dpi) * scaling) # fontsize = round(font.pointSizeF() * 1.5 * ldpi / 96) rc.mpl_rc['font.size'] = fontsize rc.params['screen'] = {'ref_dpi': ref_dpi, 'scaling': scaling, 'height': height, 'width': width} # initialize the FilterTreeBuilder class: # read config file and construct filter tree from it tree_builder.build_widget_tree() tree_builder.init_filters() mainw = pyFDA() logger.info("Logging to {0}".format(dirs.LOG_DIR_FILE)) logger.info(f"Starting pyfda with screen resolution {width} x {height}, " f"avail: {avail_geometry.width()}x{avail_geometry.height()}") logger.info(f"with {style} and matplotlib fontsize {fontsize}.") logger.info(f"lDPI = {ldpi:.2f}, pDPI = {pdpi:.2f} ({pdpix:.2f} x {pdpiy:.2f}), pix.ratio = {pixel_ratio}") # Available signals: # - logicalDotsPerInchChanged(qreal dpi) # - physicalDotsPerInchChanged(qreal dpi) # - geometryChanged(const QRect &geometry) # - availableGeometryChanged(const QRect &geometry) # logger.info(f"size = {font.pointSize()}, {font.pointSizeF()}, {font.pixelSize()}, height = {fm.height()}") if dirs.OS.lower() == "windows": # Windows taskbar is not for "Application Windows" but for "Application # User Models", grouping several instances of an application under one # common taskbar icon. Python apps are sometimes grouped under the icon # for Pythonw.exe, sometimes the icon is just blank. The following # instructions tell Windows that pythonw is merely hosting other applications. import ctypes myappid = u'chipmuenk.pyfda.v0.9' ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) # set taskbar icon app.setWindowIcon(QIcon(':/pyfda_icon.svg')) # Sets the active window to the active widget in response to a system event app.setActiveWindow(mainw) # Set default icon for window mainw.setWindowIcon(QIcon(':/pyfda_icon.svg')) # set main window on desktop to full size # mainw.setGeometry(0, 0, width, height) # top L / top R, dx, dy # mainw.setGeometry(app.desktop().availableGeometry().size()) mainw.resize(app.desktop().availableGeometry().size()) # Give the keyboard input focus to this widget if this widget # or one of its parents is the active window: # mainw.setFocus() mainw.show() #start the application's exec loop, return the exit code to the OS app.exec_() # sys.exit(app.exec_()) and app.exec_() have same behaviour
#------------------------------------------------------------------------------ if __name__ == '__main__': main()