# -*- 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)
"""
Widget for exporting / importing and saving / loading filter data
"""
import os
import re
import markdown
from pyfda.libs.compat import (
Qt, QPushButton, QDialog, QVBoxLayout, QHBoxLayout, QIcon, QPixmap,
QSizePolicy, QTextBrowser, QGridLayout, QLabel)
from pyfda.libs.pyfda_qt_lib import qwindow_stay_on_top
import pyfda.version as version
from pyfda.libs.pyfda_lib import mod_version, CRLF
import pyfda.libs.pyfda_dirs as dirs
import pyfda.filterbroker as fb
from pyfda.pyfda_rc import params
# ------------------------------------------------------------------------------
[docs]
class AboutWindow(QDialog):
"""
Create a pop-up widget for the About Window.
"""
# sig_tx = pyqtSignal(dict) # outgoing
def __init__(self, parent=None):
super(AboutWindow, self).__init__(parent)
self.setWindowTitle("About pyFDA")
self.collect_info()
self._construct_UI()
qwindow_stay_on_top(self, True)
# ------------------------------------------------------------------------------
def _construct_UI(self):
""" initialize the User Interface """
butClipboard = QPushButton(self)
butClipboard.setIcon(QIcon(':/to_clipboard.svg'))
butClipboard.setToolTip("Copy text to clipboard.")
butAbout = QPushButton(self)
butAbout.setText("About")
butAbout.setToolTip("Display 'About' info")
butChangelog = QPushButton(self)
butChangelog.setText("Changelog")
butChangelog.setToolTip("Display changelog")
butLicMIT = QPushButton(self)
butLicMIT.setText("MIT License")
butLicMIT.setToolTip("MIT License for pyFDA source code")
butLicGPLv3 = QPushButton(self)
butLicGPLv3.setText("GPLv3 License")
butLicGPLv3.setToolTip("GPLv3 License for bundled distribution")
butClose = QPushButton(self)
butClose.setIcon(QIcon(':/circle-x.svg'))
butClose.setToolTip("Close Window.")
layGButtons = QGridLayout()
layGButtons.addWidget(butClipboard, 0, 0)
layGButtons.addWidget(butAbout, 0, 1)
layGButtons.addWidget(butChangelog, 0, 2)
layGButtons.addWidget(butLicMIT, 0, 3)
layGButtons.addWidget(butLicGPLv3, 0, 4)
layGButtons.addWidget(butClose, 0, 5)
lblInfo = QLabel(self)
lblInfo.setText(self.info_str)
lblInfo.setFixedHeight(int(lblInfo.height()*1.2))
# lblInfo.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
lblInfo.setOpenExternalLinks(True)
lblIcon = QLabel(self)
lblIcon.setPixmap(
QPixmap(':/pyfda_icon.svg').scaledToHeight(lblInfo.height(),
Qt.SmoothTransformation))
butClipboard.setFixedWidth(lblInfo.height())
butClose.setFixedWidth(lblInfo.height())
layHInfo = QHBoxLayout()
layHInfo.addWidget(lblIcon)
layHInfo.addWidget(lblInfo)
self.txtDisplay = QTextBrowser(self)
self.txtDisplay.setOpenExternalLinks(True)
self.display_about_str()
self.txtDisplay.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# self.txtDisplay.setFixedHeight(self.txtDisplay.width() * 2)
layVMain = QVBoxLayout()
# layVMain.setAlignment(Qt.AlignTop) # this affects only the first widget
layVMain.addLayout(layGButtons)
layVMain.addLayout(layHInfo)
layVMain.addWidget(self.txtDisplay)
layVMain.setContentsMargins(*params['wdg_margins_spc'])
self.setLayout(layVMain)
# self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# self.resize(0,0)
# self.adjustSize()
# QApplication.processEvents()
butClipboard.clicked.connect(
lambda: self.to_clipboard(self.info_str + self.about_str))
butAbout.clicked.connect(self.display_about_str)
butChangelog.clicked.connect(self.display_changelog)
butLicMIT.clicked.connect(self.display_MIT_lic)
butLicGPLv3.clicked.connect(self.display_GPL_lic)
butClose.clicked.connect(self.close)
# ------------------------------------------------------------------------------
[docs]
def to_clipboard(self, my_string, html=False):
"""
Copy version info to clipboard
TODO: This is stupid: md -> html -> md ?!
"""
if not html:
# remove line breaks from string
my_string = re.sub('\n', '', my_string)
# a_string.replace("\n", " ")
# map some HTML tags to control codes
mapping = [('\r\n', ' '), ('\n', ' '), ('\r', ' '),
('</th></tr>', CRLF + '=' * 20 + CRLF),
('</table>', CRLF), ('<h3>', CRLF + '*'),
('<br>', CRLF), ('<br />', CRLF), ('</tr>', CRLF),
('<hr>', '-' * 20), ('</h3>', '*' + CRLF + '-' * 20 + CRLF),
('<b>', '*'), ('</b>', '*'), ('<em>', '*'), ('</em>', '*'),
('<strong>', '*'), ('</strong>', '*'),
('</td>', '\t'), ('</th>', '\t'), (' ', ' '), ('>', '>')
]
for k, v in mapping:
my_string = my_string.replace(k, v)
# Remove remaining HTML tags and style settings
# . : match any character except newline
# * : match 0 or more repetitions of preceding RE
# ? : match 0 or one repetition of preceding REgii5t
clean = re.compile('<style>.*</style>|<.*?>')
fb.clipboard.setText(re.sub(clean, '', my_string))
else:
fb.clipboard.setText(my_string) # copy untreated string
# ------------------------------------------------------------------------------
[docs]
def collect_info(self):
"""
Collect information about version, imported modules in strings:
`self.info_str` : General info, copyright, version, link to readthedocs
This info is always visible.
`self.about_str`: OS, user name, directories, versions of installed software
"""
self.info_str = (
f"<head><style>a:link {{color: {params['link_color']}}}</style></head>"
f"<body><b><a href=https://www.github.com/chipmuenk/pyfda>pyfda</a> "
f"Version {version.__version__} (c) 2013 - 2024 Christian Münker</b><br />"
"Design, analyze and synthesize digital filters. Docs @ "
"<a href=https://pyfda.rtfd.org>pyfda.rtfd.org</a>"
" (<a href=https://media.readthedocs.org/pdf/pyfda/latest/pyfda.pdf>pdf</a>)"
"<br /></body>")
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
user_dirs_str = ""
if dirs.USER_DIRS:
for d in dirs.USER_DIRS:
user_dirs_str += d + '<br />'
else:
user_dirs_str = "None<br />"
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
os_str = (f"<b>OS:</b> {dirs.OS} {dirs.OS_VER}<br><b>User Name:</b> "
f"{dirs.USER_NAME}")
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
dirs_md = ("### Directories ###\n"
"| *Function* | *Path*|\n" # "| <!-- --> | <!-- --> |\n"
"|: ---- |: ---- |\n"
"| **Install Dir** | `{install_dir}` |\n"
"| **User Module Dir** | `{user_dir}` |\n"
"| **Home Dir** | `{home_dir}` |\n"
"| **Temp Dir** | `{temp_dir}` |\n"
"| - - - - - - - | - - - - - - - - -|\n"
"| **pyFDA Config** | `{pyfda_conf}` |\n"
"| **Log. Config** | `{log_conf}` |\n"
"| **Logfile** | `{log_file}` |"
.format(home_dir=dirs.HOME_DIR, install_dir=dirs.INSTALL_DIR,
conf_dir=dirs.CONF_DIR, user_dir=user_dirs_str[:-6],
temp_dir=dirs.TEMP_DIR, pyfda_conf=dirs.USER_CONF_DIR_FILE,
log_conf=dirs.USER_LOG_CONF_DIR_FILE,
log_file=dirs.LOG_DIR_FILE))
dirs_str = markdown.markdown(dirs_md, output_format='html5',
extensions=['markdown.extensions.tables'])
# pyinstaller needs explicit definition of extensions path
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ver_str = mod_version()
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if False: # dirs.PYINSTALLER:
self.lic_str = ""
else:
with open(os.path.join(dirs.INSTALL_DIR, "license_info.md"), 'r') as f:
self.lic_str = markdown.markdown(
f.read(), output_format='html5',
extensions=['markdown.extensions.tables'])
# pyinstaller needs explicit definition of extensions path
self.about_str = (f"<head><style>a:link {{color: {params['link_color']}}}</style></head>"
f"<body>"
f"{os_str + dirs_str + ver_str}"
f"</body>")
# ------------------------------------------------------------------------------
[docs]
def display_about_str(self):
""" Display general "About" info """
self.txtDisplay.setText(self.about_str + self.lic_str)
# ------------------------------------------------------------------------------
[docs]
def display_changelog(self):
""" Display changelog """
with open(os.path.join(dirs.INSTALL_DIR, "..", "CHANGELOG.md"), 'r') as f:
lic_str = markdown.markdown(f.read(), output_format='html5')
self.txtDisplay.setText(lic_str)
# ------------------------------------------------------------------------------
[docs]
def display_MIT_lic(self):
""" Display MIT license """
with open(os.path.join(dirs.INSTALL_DIR, "..", "LICENSE.md"), 'r') as f:
lic_str = markdown.markdown(f.read(), output_format='html5')
self.txtDisplay.setText(lic_str)
# ------------------------------------------------------------------------------
[docs]
def display_GPL_lic(self):
""" Display GPL license """
with open(os.path.join(dirs.INSTALL_DIR, "..", "LICENSE_GPLv3.md"), 'r') as f:
lic_str = markdown.markdown(f.read(), output_format='html5')
self.txtDisplay.setText(lic_str)
# =============================================================================
if __name__ == '__main__':
""" Run widget standalone with `python -m pyfda.input_widgets.input_info_about` """
import sys
from pyfda.libs.compat import QApplication
from pyfda import pyfda_rc as rc
app = QApplication(sys.argv)
app.setStyleSheet(rc.qss_rc)
fb.clipboard = QApplication.clipboard()
mainw = AboutWindow() # Test_button
app.setActiveWindow(mainw)
mainw.show()
sys.exit(app.exec_())